add last backwards-compatible version

This commit is contained in:
2021-12-14 00:33:46 -07:00
parent 68b10d413b
commit b0dd3f07f3
335 changed files with 4746 additions and 19627 deletions

View File

@@ -1,49 +0,0 @@
#include "stdafx.h"
#include "AutoComplete.h"
#include <ShlGuid.h> // CLSID_AutoComplete
#include "../helpers/COM_utils.h"
#include "../helpers/dropdown_helper.h"
#include <libPPUI/CEnumString.h>
using PP::CEnumString;
namespace {
class CACList_History : public CEnumString {
public:
CACList_History(cfg_dropdown_history_mt * var) : m_var(var) { Reset(); }
typedef ImplementCOMRefCounter<CACList_History> TImpl;
HRESULT STDMETHODCALLTYPE Reset() {
/*if (core_api::assert_main_thread())*/ {
ResetStrings();
pfc::string8 state; m_var->get_state(state);
for (const char * walk = state;;) {
const char * next = strchr(walk, cfg_dropdown_history_mt::separator);
t_size len = (next != NULL) ? next - walk : ~0;
AddStringU(walk, len);
if (next == NULL) break;
walk = next + 1;
}
}
return __super::Reset();
}
HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum) {
*ppenum = new TImpl(*this); return S_OK;
}
private:
cfg_dropdown_history_mt * const m_var;
};
}
HRESULT InitializeDropdownAC(HWND comboBox, cfg_dropdown_history_mt & var, const char * initVal) {
var.on_init(comboBox, initVal);
COMBOBOXINFO ci = {}; ci.cbSize = sizeof(ci);
if (!GetComboBoxInfo(comboBox, &ci)) {
PFC_ASSERT(!"Should not get here - GetComboBoxInfo fail!");
return E_FAIL;
}
pfc::com_ptr_t<IUnknown> acl = new CACList_History::TImpl(&var);
return InitializeSimpleAC(ci.hwndItem, acl.get_ptr(), ACO_AUTOAPPEND|ACO_AUTOSUGGEST);
}

View File

@@ -1,7 +0,0 @@
#pragma once
#include <libPPUI/AutoComplete.h>
class cfg_dropdown_history_mt;
HRESULT InitializeDropdownAC(HWND comboBox, cfg_dropdown_history_mt & var, const char * initVal);

View File

@@ -1,73 +0,0 @@
#pragma once
#include <libPPUI/CFlashWindow.h>
#include "atl-misc.h"
#include <utility>
template<typename TClass>
class ImplementBumpableElem : public TClass {
private:
typedef ImplementBumpableElem<TClass> TSelf;
public:
template<typename ... arg_t> ImplementBumpableElem( arg_t && ... arg ) : TClass(std::forward<arg_t>(arg) ... ) {_init(); }
BEGIN_MSG_MAP_EX(ImplementBumpableElem)
MSG_WM_DESTROY(OnDestroy)
CHAIN_MSG_MAP(__super)
END_MSG_MAP_HOOK()
void notify(const GUID & p_what, t_size p_param1, const void * p_param2, t_size p_param2size) {
if (p_what == ui_element_notify_visibility_changed && p_param1 == 0 && m_flash.m_hWnd != NULL) m_flash.Deactivate();
__super::notify(p_what, p_param1, p_param2, p_param2size);
}
static bool Bump() {
for(auto walk = instances.cfirst(); walk.is_valid(); ++walk) {
if ((*walk)->_bump()) return true;
}
return false;
}
~ImplementBumpableElem() throw() {
PFC_ASSERT(core_api::is_main_thread());
instances -= this;
}
private:
void OnDestroy() throw() {
m_selfDestruct = true;
m_flash.CleanUp();
SetMsgHandled(FALSE);
}
bool _bump() {
if (m_selfDestruct || this->m_hWnd == NULL) return false;
//PROBLEM: This assumes we're implementing service_base methods at this point. Explodes if called during constructors/destructors.
if (!this->m_callback->request_activation(this)) return false;
m_flash.Activate(*this);
this->set_default_focus();
return true;
}
void _init() {
m_selfDestruct = false;
PFC_ASSERT(core_api::is_main_thread());
instances += this;
}
static pfc::avltree_t<TSelf*> instances;
bool m_selfDestruct;
CFlashWindow m_flash;
};
template<typename TClass>
pfc::avltree_t<ImplementBumpableElem<TClass> *> ImplementBumpableElem<TClass>::instances;
template<typename TImpl, typename TInterface = ui_element_v2> class ui_element_impl_withpopup : public ui_element_impl<ImplementBumpableElem<TImpl>, TInterface> {
public:
t_uint32 get_flags() {return ui_element_v2::KFlagHavePopupCommand | ui_element_v2::KFlagSupportsBump;}
bool bump() {return ImplementBumpableElem<TImpl>::Bump();}
};
template<typename TImpl, typename TInterface = ui_element_v2> class ui_element_impl_visualisation : public ui_element_impl<ImplementBumpableElem<TImpl>, TInterface> {
public:
t_uint32 get_flags() {return ui_element_v2::KFlagsVisualisation | ui_element_v2::KFlagSupportsBump;}
bool bump() {return ImplementBumpableElem<TImpl>::Bump();}
};

View File

@@ -1,24 +0,0 @@
#pragma once
#include "WindowPositionUtils.h"
#include <libPPUI/CDialogResizeHelper.h>
template<typename TTracker> class CDialogResizeHelperTracking : public CDialogResizeHelper {
public:
template<typename TParam, t_size paramCount, typename TCfgVar> CDialogResizeHelperTracking(const TParam (& src)[paramCount],CRect const& minMaxRange, TCfgVar & cfgVar) : CDialogResizeHelper(src, minMaxRange), m_tracker(cfgVar) {}
BEGIN_MSG_MAP_EX(CDialogResizeHelperST)
CHAIN_MSG_MAP(CDialogResizeHelper)
CHAIN_MSG_MAP_MEMBER(m_tracker)
END_MSG_MAP()
private:
TTracker m_tracker;
};
typedef CDialogResizeHelperTracking<cfgDialogSizeTracker> CDialogResizeHelperST;
typedef CDialogResizeHelperTracking<cfgDialogPositionTracker> CDialogResizeHelperPT;
typedef CDialogResizeHelperTracking<cfgWindowSizeTracker2> CDialogResizeHelperST2;
#define REDRAW_DIALOG_ON_RESIZE() if (uMsg == WM_SIZE) RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);

View File

@@ -1,6 +1,12 @@
#pragma once
#define FB2K_COM_CATCH catch(exception_com const & e) {return e.get_code();} catch(std::bad_alloc) {return E_OUTOFMEMORY;} catch(pfc::exception_invalid_params) {return E_INVALIDARG;} catch(...) {return E_UNEXPECTED;}
#include <libPPUI/pp-COM-macros.h>
#define COM_QI_BEGIN() HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,void ** ppvObject) { if (ppvObject == NULL) return E_INVALIDARG;
#define COM_QI_ENTRY(IWhat) { if (iid == __uuidof(IWhat)) {IWhat * temp = this; temp->AddRef(); * ppvObject = temp; return S_OK;} }
#define COM_QI_ENTRY_(IWhat, IID) { if (iid == IID) {IWhat * temp = this; temp->AddRef(); * ppvObject = temp; return S_OK;} }
#define COM_QI_END() * ppvObject = NULL; return E_NOINTERFACE; }
#define FB2K_COM_CATCH PP_COM_CATCH
#define COM_QI_CHAIN(Parent) { HRESULT status = Parent::QueryInterface(iid, ppvObject); if (SUCCEEDED(status)) return status; }
#define COM_QI_SIMPLE(IWhat) COM_QI_BEGIN() COM_QI_ENTRY(IUnknown) COM_QI_ENTRY(IWhat) COM_QI_END()

View File

@@ -0,0 +1,92 @@
#include "stdafx.h"
#include "CPowerRequest.h"
#ifdef CPowerRequestAPI_Avail
// win32 API declaration duplicate - not always defined on some of the Windows versions we target
namespace winapi_substitute {
typedef struct _REASON_CONTEXT {
ULONG Version;
DWORD Flags;
union {
struct {
HMODULE LocalizedReasonModule;
ULONG LocalizedReasonId;
ULONG ReasonStringCount;
LPWSTR *ReasonStrings;
} Detailed;
LPWSTR SimpleReasonString;
} Reason;
} REASON_CONTEXT, *PREASON_CONTEXT;
//
// Power Request APIs
//
typedef REASON_CONTEXT POWER_REQUEST_CONTEXT, *PPOWER_REQUEST_CONTEXT, *LPPOWER_REQUEST_CONTEXT;
}
HANDLE CPowerRequestAPI::PowerCreateRequestNamed( const wchar_t * str ) {
winapi_substitute::REASON_CONTEXT ctx = {POWER_REQUEST_CONTEXT_VERSION, POWER_REQUEST_CONTEXT_SIMPLE_STRING};
ctx.Reason.SimpleReasonString = const_cast<wchar_t*>(str);
return this->PowerCreateRequest(&ctx);
}
CPowerRequest::CPowerRequest(const wchar_t * Reason) : m_Request(INVALID_HANDLE_VALUE), m_bSystem(), m_bDisplay() {
HMODULE kernel32 = GetModuleHandle(_T("kernel32.dll"));
if (m_API.IsValid()) {
winapi_substitute::REASON_CONTEXT ctx = {POWER_REQUEST_CONTEXT_VERSION, POWER_REQUEST_CONTEXT_SIMPLE_STRING};
ctx.Reason.SimpleReasonString = const_cast<wchar_t*>(Reason);
m_Request = m_API.PowerCreateRequest(&ctx);
}
}
void CPowerRequest::SetSystem(bool bSystem) {
if (bSystem == m_bSystem) return;
m_bSystem = bSystem;
if (m_Request != INVALID_HANDLE_VALUE) {
m_API.ToggleSystem( m_Request, bSystem );
} else {
_UpdateTES();
}
}
void CPowerRequest::SetExecution(bool bExecution) {
if (bExecution == m_bSystem) return;
m_bSystem = bExecution;
if (m_Request != INVALID_HANDLE_VALUE) {
m_API.ToggleExecution( m_Request, bExecution );
} else {
_UpdateTES();
}
}
void CPowerRequest::SetDisplay(bool bDisplay) {
if (bDisplay == m_bDisplay) return;
m_bDisplay = bDisplay;
if (m_Request != INVALID_HANDLE_VALUE) {
m_API.ToggleDisplay(m_Request, bDisplay);
} else {
_UpdateTES();
}
}
CPowerRequest::~CPowerRequest() {
if (m_Request != INVALID_HANDLE_VALUE) {
CloseHandle(m_Request);
} else {
if (m_bDisplay || m_bSystem) SetThreadExecutionState(ES_CONTINUOUS);
}
}
void CPowerRequest::_UpdateTES() {
SetThreadExecutionState(ES_CONTINUOUS | (m_bSystem ? ES_SYSTEM_REQUIRED : 0 ) | (m_bDisplay ? ES_DISPLAY_REQUIRED : 0) );
}
#endif // _WIN32

View File

@@ -0,0 +1,115 @@
#pragma once
#ifdef _WIN32
#ifdef WINAPI_FAMILY_PARTITION
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#define CPowerRequestAPI_Avail
#endif
#else // no WINAPI_FAMILY_PARTITION, desktop SDK
#define CPowerRequestAPI_Avail
#endif
#endif // _WIN32
#ifdef CPowerRequestAPI_Avail
typedef HANDLE (WINAPI * pPowerCreateRequest_t) (
__in void* Context
);
typedef BOOL (WINAPI * pPowerSetRequest_t) (
__in HANDLE PowerRequest,
__in POWER_REQUEST_TYPE RequestType
);
typedef BOOL (WINAPI * pPowerClearRequest_t) (
__in HANDLE PowerRequest,
__in POWER_REQUEST_TYPE RequestType
);
class CPowerRequestAPI {
public:
CPowerRequestAPI() : PowerCreateRequest(), PowerSetRequest(), PowerClearRequest() {
Bind();
}
bool Bind() {
HMODULE kernel32 = GetModuleHandle(_T("kernel32.dll"));
return Bind(PowerCreateRequest, kernel32, "PowerCreateRequest")
&& Bind(PowerSetRequest, kernel32, "PowerSetRequest")
&& Bind(PowerClearRequest, kernel32, "PowerClearRequest") ;
}
bool IsValid() {return PowerCreateRequest != NULL;}
void ToggleSystem(HANDLE hRequest, bool bSystem) {
Toggle(hRequest, bSystem, PowerRequestSystemRequired);
}
void ToggleExecution(HANDLE hRequest, bool bSystem) {
const POWER_REQUEST_TYPE _PowerRequestExecutionRequired = (POWER_REQUEST_TYPE)3;
const POWER_REQUEST_TYPE RequestType = IsWin8() ? _PowerRequestExecutionRequired : PowerRequestSystemRequired;
Toggle(hRequest, bSystem, RequestType);
}
void ToggleDisplay(HANDLE hRequest, bool bDisplay) {
Toggle(hRequest, bDisplay, PowerRequestDisplayRequired);
}
void Toggle(HANDLE hRequest, bool bToggle, POWER_REQUEST_TYPE what) {
if (bToggle) {
PowerSetRequest(hRequest, what);
} else {
PowerClearRequest(hRequest, what);
}
}
HANDLE PowerCreateRequestNamed( const wchar_t * str );
static bool IsWin8() {
auto ver = myGetOSVersion();
return ver >= 0x602;
}
static WORD myGetOSVersion() {
const DWORD ver = GetVersion();
return (WORD)HIBYTE(LOWORD(ver)) | ((WORD)LOBYTE(LOWORD(ver)) << 8);
}
pPowerCreateRequest_t PowerCreateRequest;
pPowerSetRequest_t PowerSetRequest;
pPowerClearRequest_t PowerClearRequest;
private:
template<typename func_t> static bool Bind(func_t & f, HMODULE dll, const char * name) {
f = reinterpret_cast<func_t>(GetProcAddress(dll, name));
return f != NULL;
}
};
class CPowerRequest {
public:
CPowerRequest(const wchar_t * Reason);
void SetSystem(bool bSystem);
void SetExecution(bool bExecution);
void SetDisplay(bool bDisplay);
~CPowerRequest();
private:
void _UpdateTES();
HANDLE m_Request;
bool m_bSystem, m_bDisplay;
CPowerRequestAPI m_API;
CPowerRequest(const CPowerRequest&);
void operator=(const CPowerRequest&);
};
#else
class CPowerRequest {
public:
CPowerRequest(const wchar_t * Reason) {}
void SetSystem(bool bSystem) {}
void SetExecution(bool bExecution) {}
void SetDisplay(bool bDisplay) {}
private:
CPowerRequest(const CPowerRequest&);
void operator=(const CPowerRequest&);
};
#endif // CPowerRequestAPI_Avail

View File

@@ -1,3 +1,54 @@
#pragma once
#include <libPPUI/CPropVariant.h>
class CPropVariant : public PROPVARIANT {
public:
CPropVariant() {init();}
~CPropVariant() {clear();}
CPropVariant( const CPropVariant & other ) {
init();
PropVariantCopy( this, &other );
}
const CPropVariant& operator=( const CPropVariant & other ) {
clear();
PropVariantCopy(this, &other);
return *this;
}
bool toInt64(int64_t & out) const {
switch( vt ) {
case VT_I1: out = (int64_t) cVal; return true;
case VT_I2: out = (int64_t) iVal; return true;
case VT_I4: out = (int64_t) lVal; return true;
case VT_I8: out = (int64_t) hVal.QuadPart; return true;
case VT_INT: out = (int64_t) intVal; return true;
default: return false;
}
}
bool toUint64(uint64_t & out) const {
switch( vt ) {
case VT_UI1: out = (uint64_t) bVal; return true;
case VT_UI2: out = (uint64_t) uiVal; return true;
case VT_UI4: out = (uint64_t) ulVal; return true;
case VT_UI8: out = (uint64_t) uhVal.QuadPart; return true;
case VT_UINT: out = (uint64_t) uintVal; return true;
default: return false;
}
}
bool toString( pfc::string_base & out ) const {
switch( vt ) {
case VT_LPSTR:
out = pfc::stringcvt::string_utf8_from_ansi( pszVal ); return true;
case VT_LPWSTR:
out = pfc::stringcvt::string_utf8_from_wide( pwszVal ); return true;
default: return false;
}
}
private:
void clear() {
PropVariantClear( this );
}
void init() {
PROPVARIANT * pv = this;
PropVariantInit( pv );
}
};

View File

@@ -1,80 +0,0 @@
#include "stdafx.h"
#include "CTableEditHelper-Legacy.h"
#include <libPPUI/listview_helper.h>
namespace InPlaceEdit {
void CTableEditHelper::TableEdit_Start(HWND p_listview, unsigned p_item, unsigned p_column, unsigned p_itemcount, unsigned p_columncount, unsigned p_basecolumn, unsigned p_flags) {
if (m_notify.is_valid() || p_columncount == 0 || p_itemcount == 0 || p_item >= p_itemcount || p_column >= p_columncount) return;
m_listview = p_listview;
m_item = p_item;
m_column = p_column;
m_itemcount = p_itemcount;
m_columncount = p_columncount;
m_basecolumn = p_basecolumn;
m_flags = p_flags;
_Start();
}
void CTableEditHelper::TableEdit_Abort(bool p_forwardcontent) {
if (m_notify.is_valid()) {
m_notify->orphan();
m_notify.release();
if (p_forwardcontent && (m_flags & KFlagReadOnly) == 0) {
if (m_content.is_valid()) {
pfc::string8 temp(*m_content);
m_content.release();
TableEdit_SetItemText(m_item, m_column, temp);
}
} else {
m_content.release();
}
SetFocus(NULL);
TableEdit_Finished();
}
}
bool CTableEditHelper::TableEdit_GetItemText(unsigned p_item, unsigned p_column, pfc::string_base & p_out, unsigned & p_linecount) {
listview_helper::get_item_text(m_listview, p_item, p_column + m_basecolumn, p_out);
p_linecount = pfc::is_multiline(p_out) ? 5 : 1;
return true;
}
void CTableEditHelper::TableEdit_SetItemText(unsigned p_item, unsigned p_column, const char * p_text) {
listview_helper::set_item_text(m_listview, p_item, p_column + m_basecolumn, p_text);
}
void CTableEditHelper::on_task_completion(unsigned p_taskid, unsigned p_state) {
if (p_taskid == KTaskID) {
m_notify.release();
if (m_content.is_valid()) {
if (p_state & InPlaceEdit::KEditFlagContentChanged) {
TableEdit_SetItemText(m_item, m_column, *m_content);
}
m_content.release();
}
/*if (InPlaceEdit::TableEditAdvance(m_item,m_column,m_itemcount,m_columncount,p_state))*/
if (TableEdit_OnEditCompleted(m_item, m_column, p_state) &&
InPlaceEdit::TableEditAdvance_ListView(m_listview, m_basecolumn, m_item, m_column, m_itemcount, m_columncount, p_state)) {
_Start();
} else {
TableEdit_Finished();
}
}
}
CTableEditHelper::~CTableEditHelper() {
if (m_notify.is_valid()) {
m_notify->orphan();
m_notify.release();
}
}
void CTableEditHelper::_Start() {
listview_helper::select_single_item(m_listview, m_item);
m_content.new_t();
unsigned linecount = 1;
if (!TableEdit_GetItemText(m_item, m_column, *m_content, linecount)) return;
m_notify = completion_notify_create(this, KTaskID);
InPlaceEdit::Start_FromListViewEx(m_listview, m_item, m_column + m_basecolumn, linecount, m_flags, m_content, m_notify);
}
}

View File

@@ -1,35 +0,0 @@
#pragma once
#include "inplace_edit.h"
#include <libPPUI/listview_helper.h>
namespace InPlaceEdit {
class CTableEditHelper {
public:
void TableEdit_Start(HWND p_listview, unsigned p_item, unsigned p_column, unsigned p_itemcount, unsigned p_columncount, unsigned p_basecolumn, unsigned p_flags = 0);
void TableEdit_Abort(bool p_forwardcontent);
bool TableEdit_IsActive() const {return m_notify.is_valid();}
virtual bool TableEdit_GetItemText(unsigned p_item, unsigned p_column, pfc::string_base & p_out, unsigned & p_linecount);
virtual void TableEdit_SetItemText(unsigned p_item, unsigned p_column, const char * p_text);
virtual void TableEdit_Finished() {}
void on_task_completion(unsigned p_taskid, unsigned p_state);
~CTableEditHelper();
protected:
HWND TableEdit_GetListView() const { return m_listview; }
//return false to abort
virtual bool TableEdit_OnEditCompleted(unsigned item, unsigned column, unsigned state) { return true; }
private:
void _Start();
enum {
KTaskID = 0xc0ffee
};
HWND m_listview;
unsigned m_item, m_column;
unsigned m_itemcount, m_columncount, m_basecolumn;
unsigned m_flags;
pfc::rcptr_t<pfc::string8> m_content;
service_ptr_t<completion_notify_orphanable> m_notify;
};
}

View File

@@ -14,18 +14,11 @@ namespace CF {
};
template<typename TWhat> class CallForwarder {
private:
CallForwarder() = delete;
protected:
CallForwarder(TWhat * ptr) : m_ptr(pfc::rcnew_t<TWhat*>(ptr)) {}
void Orphan() {
*m_ptr = NULL;
}
public:
bool IsValid() const {
PFC_ASSERT( m_ptr.is_valid() );
return m_ptr.is_valid() && *m_ptr != NULL;
}
CallForwarder(TWhat * ptr) { m_ptr.new_t(ptr); }
void Orphan() {*m_ptr = NULL;}
bool IsValid() const { return *m_ptr != NULL; }
bool IsEmpty() const { return !IsValid(); }
TWhat * operator->() const {
@@ -43,18 +36,15 @@ namespace CF {
main_thread_callback_add( new service_impl_t<_inMainThread< CallForwarder<TWhat>, arg_t> >(*this, arg) );
}
private:
const pfc::rcptr_t<TWhat*> m_ptr;
pfc::rcptr_t<TWhat*> m_ptr;
};
template<typename TWhat> class CallForwarderMaster : public CallForwarder<TWhat> {
public:
CallForwarderMaster(TWhat * ptr) : CallForwarder<TWhat>(ptr) {PFC_ASSERT(ptr!=NULL);}
CallForwarderMaster(TWhat * ptr) : CallForwarder<TWhat>(ptr) {}
~CallForwarderMaster() { this->Orphan(); }
using CallForwarder<TWhat>::Orphan;
private:
CallForwarderMaster() = delete;
CallForwarderMaster( const CallForwarderMaster<TWhat> & ) = delete;
void operator=( const CallForwarderMaster & ) = delete;
PFC_CLASS_NOT_COPYABLE(CallForwarderMaster, CallForwarderMaster<TWhat>);
};
}

View File

@@ -1,10 +1,8 @@
#pragma once
#include <pfc/wait_queue.h>
#include <pfc/pool.h>
#include <pfc/threads.h>
#include <functional>
#include "rethrow.h"
#include <pfc/timers.h>
namespace ThreadUtils {
@@ -15,95 +13,46 @@ namespace ThreadUtils {
typedef std::function<void () > func_t;
typedef pfc::waitQueue<func_t> queue_t;
typedef std::function<void (abort_callback&) > funcAbortable_t;
protected:
std::function<void () > makeWorker() {
auto q = m_queue;
auto x = m_atExit;
return [q, x] {
for ( ;; ) {
func_t f;
if (!q->get(f)) break;
try { f(); } catch(...) {}
}
// No guard for atExit access, as nobody is supposed to be still able to call host object methods by the time we get here
for( auto i = x->begin(); i != x->end(); ++ i ) {
auto f = *i;
try { f(); } catch(...) {}
}
};
};
std::function<void () > makeWorker2( std::function<void()> updater, double interval) {
auto q = m_queue;
auto x = m_atExit;
return [=] {
pfc::lores_timer t; t.start();
for ( ;; ) {
{
bool bWorkReady = false;
double left = interval - t.query();
if ( left > 0 ) {
if (q->wait_read( left )) bWorkReady = true;
}
if (!bWorkReady) {
updater();
t.start();
continue;
}
}
func_t f;
if (!q->get(f)) break;
try { f(); } catch(...) {}
}
// No guard for atExit access, as nobody is supposed to be still able to call host object methods by the time we get here
for( auto i = x->begin(); i != x->end(); ++ i ) {
auto f = *i;
try { f(); } catch(...) {}
}
};
};
// For derived classes: create new instance without starting thread, supply thread using by yourself
class noCreate {};
cmdThread( noCreate ) {}
public:
cmdThread() {
pfc::splitThread( makeWorker() );
auto q = std::make_shared<queue_t>();
m_queue = q;
auto x = std::make_shared<atExit_t>();
m_atExit = x;
pfc::splitThread( [q, x] {
for ( ;; ) {
func_t f;
if (!q->get(f)) break;
try { f(); } catch(...) {}
}
// No guard for atExit access, as nobody is supposed to be still able to call host object methods by the time we get here
for( auto i = x->begin(); i != x->end(); ++ i ) {
auto f = *i;
try { f(); } catch(...) {}
}
} );
}
void atExit( func_t f ) {
m_atExit->push_back(f);
}
~cmdThread() {
m_queue->set_eof();
}
void runSynchronously( func_t f ) { runSynchronously_(f, nullptr); }
void runSynchronously_( func_t f, abort_callback * abortOrNull ) {
auto evt = m_eventPool.make();
evt->set_state(false);
auto rethrow = std::make_shared<ThreadUtils::CRethrow>();
auto worker2 = [f, rethrow, evt] {
rethrow->exec(f);
evt->set_state( true );
};
add ( worker2 );
if ( abortOrNull != nullptr ) {
abortOrNull->waitForEvent( * evt, -1 );
} else {
evt->wait_for(-1);
}
m_eventPool.put( evt );
rethrow->rethrow();
}
void runSynchronously( func_t f, abort_callback & abort ) {
runSynchronously_(f, &abort);
auto evt = m_eventPool.make();
evt->set_state(false);
auto rethrow = std::make_shared<ThreadUtils::CRethrow>();
auto worker2 = [f, rethrow, evt] {
rethrow->exec(f);
evt->set_state( true );
};
add ( worker2 );
abort.waitForEvent( * evt, -1 );
m_eventPool.put( evt );
rethrow->rethrow();
}
void runSynchronously2( funcAbortable_t f, abort_callback & abort ) {
auto subAbort = m_abortPool.make();
@@ -125,8 +74,8 @@ namespace ThreadUtils {
private:
pfc::objPool<pfc::event> m_eventPool;
pfc::objPool<abort_callback_impl> m_abortPool;
std::shared_ptr<queue_t> m_queue = std::make_shared<queue_t>();
std::shared_ptr<queue_t> m_queue;
typedef std::list<func_t> atExit_t;
std::shared_ptr<atExit_t> m_atExit = std::make_shared< atExit_t >();
std::shared_ptr<atExit_t> m_atExit;
};
}
}

View File

@@ -0,0 +1,191 @@
#include "stdafx.h"
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include "IDataObjectUtils.h"
HRESULT IDataObjectUtils::DataBlockToSTGMEDIUM(const void * blockPtr, t_size blockSize, STGMEDIUM * medium, DWORD tymed, bool bHere) throw() {
try {
if (bHere) {
switch(tymed) {
case TYMED_ISTREAM:
{
if (medium->pstm == NULL) return E_INVALIDARG;
ULONG written = 0;
HRESULT state;
state = medium->pstm->Write(blockPtr, pfc::downcast_guarded<ULONG>(blockSize),&written);
if (FAILED(state)) return state;
if (written != blockSize) return STG_E_MEDIUMFULL;
return S_OK;
}
default:
return DV_E_TYMED;
}
} else {
if (tymed & TYMED_HGLOBAL) {
HGLOBAL hMem = HGlobalFromMemblock(blockPtr, blockSize);
if (hMem == NULL) return E_OUTOFMEMORY;
medium->tymed = TYMED_HGLOBAL;
medium->hGlobal = hMem;
medium->pUnkForRelease = NULL;
return S_OK;
}
if (tymed & TYMED_ISTREAM) {
HRESULT state;
HGLOBAL hMem = HGlobalFromMemblock(blockPtr, blockSize);
if (hMem == NULL) return E_OUTOFMEMORY;
medium->tymed = TYMED_ISTREAM;
pfc::com_ptr_t<IStream> stream;
if (FAILED( state = CreateStreamOnHGlobal(hMem,TRUE,stream.receive_ptr()) ) ) {
GlobalFree(hMem);
return state;
}
{
LARGE_INTEGER wtf = {};
if (FAILED( state = stream->Seek(wtf,STREAM_SEEK_END,NULL) ) ) {
return state;
}
}
medium->pstm = stream.detach();
medium->pUnkForRelease = NULL;
return S_OK;
}
return DV_E_TYMED;
}
} catch(pfc::exception_not_implemented) {
return E_NOTIMPL;
} catch(std::bad_alloc) {
return E_OUTOFMEMORY;
} catch(...) {
return E_UNEXPECTED;
}
}
HGLOBAL IDataObjectUtils::HGlobalFromMemblock(const void * ptr,t_size size) {
HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE,size);
if (handle != NULL) {
void * destptr = GlobalLock(handle);
if (destptr == NULL) {
GlobalFree(handle);
handle = NULL;
} else {
memcpy(destptr,ptr,size);
GlobalUnlock(handle);
}
}
return handle;
}
HRESULT IDataObjectUtils::ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, pfc::array_t<t_uint8> & out) {
FORMATETC fmt = {};
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = index;
fmt.tymed = TYMED_HGLOBAL /* | TYMED_ISTREAM*/;
STGMEDIUM med = {};
HRESULT state;
if (FAILED( state = obj->GetData(&fmt,&med) ) ) return state;
ReleaseStgMediumScope relScope(&med);
return STGMEDIUMToDataBlock(med, out);
}
HRESULT IDataObjectUtils::STGMEDIUMToDataBlock(const STGMEDIUM & med, pfc::array_t<t_uint8> & out) {
switch(med.tymed) {
case TYMED_HGLOBAL:
{
CGlobalLockScope lock(med.hGlobal);
out.set_data_fromptr( (const t_uint8*) lock.GetPtr(), lock.GetSize() );
}
return S_OK;
case TYMED_ISTREAM:
{
HRESULT state;
IStream * stream = med.pstm;
LARGE_INTEGER offset = {};
STATSTG stats = {};
if (FAILED( state = stream->Stat(&stats,STATFLAG_NONAME ) ) ) return state;
t_size toRead = pfc::downcast_guarded<t_size>(stats.cbSize.QuadPart);
out.set_size(toRead);
if (FAILED( state = stream->Seek(offset,STREAM_SEEK_SET,NULL) ) ) return state;
ULONG cbRead = 0;
if (FAILED( state = stream->Read(out.get_ptr(), pfc::downcast_guarded<ULONG>(toRead), &cbRead) ) ) return state;
if (cbRead != toRead) return E_UNEXPECTED;
}
return S_OK;
default:
return DV_E_TYMED;
}
}
HRESULT IDataObjectUtils::ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, pfc::array_t<t_uint8> & out) {
return ExtractDataObjectContent(obj, format, DVASPECT_CONTENT, -1, out);
}
HRESULT IDataObjectUtils::ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index) {
FORMATETC fmt = {};
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = index;
for(t_uint32 walk = 0; walk < 32; ++walk) {
const DWORD tymed = 1 << walk;
if ((ExtractDataObjectContent_SupportedTymeds & tymed) != 0) {
fmt.tymed = tymed;
HRESULT state = obj->QueryGetData(&fmt);
if (SUCCEEDED(state)) {
if (state == S_OK) return S_OK;
} else {
if (state != DV_E_TYMED) return state;
}
}
}
return E_FAIL;
}
HRESULT IDataObjectUtils::ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format) {
return ExtractDataObjectContentTest(obj,format,DVASPECT_CONTENT,-1);
}
HRESULT IDataObjectUtils::ExtractDataObjectString(pfc::com_ptr_t<IDataObject> obj, pfc::string_base & out) {
pfc::array_t<t_uint8> data;
HRESULT state;
state = ExtractDataObjectContent(obj,CF_UNICODETEXT,data);
if (SUCCEEDED(state)) {
out = pfc::stringcvt::string_utf8_from_os_ex( (const wchar_t*) data.get_ptr(), data.get_size() / sizeof(wchar_t) );
return S_OK;
}
state = ExtractDataObjectContent(obj,CF_TEXT,data);
if (SUCCEEDED(state)) {
out = pfc::stringcvt::string_utf8_from_os_ex( (const char*) data.get_ptr(), data.get_size() / sizeof(char) );
return S_OK;
}
return E_FAIL;
}
HRESULT IDataObjectUtils::SetDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, const void * data, t_size dataSize) {
STGMEDIUM med = {};
FORMATETC fmt = {};
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = index; fmt.tymed = TYMED_HGLOBAL;
HRESULT state;
if (FAILED(state = DataBlockToSTGMEDIUM(data,dataSize,&med,TYMED_HGLOBAL,false))) return state;
return obj->SetData(&fmt,&med,TRUE);
}
HRESULT IDataObjectUtils::SetDataObjectString(pfc::com_ptr_t<IDataObject> obj, const char * str) {
pfc::stringcvt::string_wide_from_utf8 s(str);
return SetDataObjectContent(obj,CF_UNICODETEXT,DVASPECT_CONTENT,-1,s.get_ptr(), (s.length()+1) * sizeof(s[0]));
}
HRESULT IDataObjectUtils::ExtractDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD & val) {
HRESULT state;
pfc::array_t<t_uint8> buffer;
if (FAILED( state = ExtractDataObjectContent(obj, format, DVASPECT_CONTENT, -1, buffer) ) ) return state;
if (buffer.get_size() < sizeof(val)) return E_UNEXPECTED;
val = *(DWORD*) buffer.get_ptr();
return S_OK;
}
HRESULT IDataObjectUtils::SetDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD val) {
return SetDataObjectContent(obj,format,DVASPECT_CONTENT,-1,&val,sizeof(val));
}
HRESULT IDataObjectUtils::PasteSucceeded(pfc::com_ptr_t<IDataObject> obj, DWORD effect) {
return SetDataObjectDWORD(obj, RegisterClipboardFormat(CFSTR_PASTESUCCEEDED), effect);
}
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -0,0 +1,186 @@
#pragma once
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include <shlobj.h>
#include "../helpers/COM_utils.h"
namespace IDataObjectUtils {
class ReleaseStgMediumScope {
public:
ReleaseStgMediumScope(STGMEDIUM * medium) : m_medium(medium) {}
~ReleaseStgMediumScope() {if (m_medium != NULL) ReleaseStgMedium(m_medium);}
private:
STGMEDIUM * m_medium;
PFC_CLASS_NOT_COPYABLE_EX(ReleaseStgMediumScope)
};
static const DWORD DataBlockToSTGMEDIUM_SupportedTymeds = TYMED_ISTREAM | TYMED_HGLOBAL;
static const DWORD ExtractDataObjectContent_SupportedTymeds = TYMED_ISTREAM | TYMED_HGLOBAL;
HRESULT DataBlockToSTGMEDIUM(const void * blockPtr, t_size blockSize, STGMEDIUM * medium, DWORD tymed, bool bHere) throw();
HGLOBAL HGlobalFromMemblock(const void * ptr,t_size size);
HRESULT ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, pfc::array_t<t_uint8> & out);
HRESULT ExtractDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, pfc::array_t<t_uint8> & out);
HRESULT ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index);
HRESULT ExtractDataObjectContentTest(pfc::com_ptr_t<IDataObject> obj, UINT format);
HRESULT ExtractDataObjectString(pfc::com_ptr_t<IDataObject> obj, pfc::string_base & out);
HRESULT SetDataObjectString(pfc::com_ptr_t<IDataObject> obj, const char * str);
HRESULT SetDataObjectContent(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD aspect, LONG index, const void * data, t_size dataSize);
HRESULT STGMEDIUMToDataBlock(const STGMEDIUM & med, pfc::array_t<t_uint8> & out);
HRESULT ExtractDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD & val);
HRESULT SetDataObjectDWORD(pfc::com_ptr_t<IDataObject> obj, UINT format, DWORD val);
HRESULT PasteSucceeded(pfc::com_ptr_t<IDataObject> obj, DWORD effect);
class comparator_FORMATETC {
public:
static int compare(const FORMATETC & v1, const FORMATETC & v2) {
int val;
val = pfc::compare_t(v1.cfFormat,v2.cfFormat); if (val != 0) return val;
val = pfc::compare_t(v1.dwAspect,v2.dwAspect); if (val != 0) return val;
val = pfc::compare_t(v1.lindex, v2.lindex ); if (val != 0) return val;
return 0;
}
};
class CDataObjectBase : public IDataObject {
public:
COM_QI_SIMPLE(IDataObject)
HRESULT STDMETHODCALLTYPE GetData(FORMATETC * formatetc, STGMEDIUM * medium) {
return GetData_internal(formatetc,medium,false);
}
HRESULT STDMETHODCALLTYPE GetDataHere(FORMATETC * formatetc, STGMEDIUM * medium) {
return GetData_internal(formatetc,medium,true);
}
HRESULT STDMETHODCALLTYPE QueryGetData(FORMATETC * formatetc) {
if (formatetc == NULL) return E_INVALIDARG;
if ((DataBlockToSTGMEDIUM_SupportedTymeds & formatetc->tymed) == 0) return DV_E_TYMED;
try {
return RenderDataTest(formatetc->cfFormat,formatetc->dwAspect,formatetc->lindex);
} FB2K_COM_CATCH;
}
HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(FORMATETC * in, FORMATETC * out) {
//check this again
if (in == NULL || out == NULL)
return E_INVALIDARG;
*out = *in;
return DATA_S_SAMEFORMATETC;
}
HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dwDirection,IEnumFORMATETC ** ppenumFormatetc) {
if (dwDirection == DATADIR_GET) {
if (ppenumFormatetc == NULL) return E_INVALIDARG;
return CreateIEnumFORMATETC(ppenumFormatetc);
} else if (dwDirection == DATADIR_SET) {
return E_NOTIMPL;
} else {
return E_INVALIDARG;
}
}
HRESULT STDMETHODCALLTYPE SetData(FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease) {
try {
ReleaseStgMediumScope relScope(fRelease ? pmedium : NULL);
if (pFormatetc == NULL || pmedium == NULL) return E_INVALIDARG;
/*TCHAR buf[256];
if (GetClipboardFormatName(pFormatetc->cfFormat,buf,PFC_TABSIZE(buf)) > 0) {
buf[PFC_TABSIZE(buf)-1] = 0;
OutputDebugString(TEXT("SetData: ")); OutputDebugString(buf); OutputDebugString(TEXT("\n"));
} else {
OutputDebugString(TEXT("SetData: unknown clipboard format.\n"));
}*/
pfc::array_t<t_uint8> temp;
HRESULT state = STGMEDIUMToDataBlock(*pmedium,temp);
if (FAILED(state)) return state;
m_entries.set(*pFormatetc,temp);
return S_OK;
} FB2K_COM_CATCH;
}
HRESULT STDMETHODCALLTYPE DAdvise(FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection) {return OLE_E_ADVISENOTSUPPORTED;}
HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) {return OLE_E_ADVISENOTSUPPORTED;}
HRESULT STDMETHODCALLTYPE EnumDAdvise(IEnumSTATDATA ** ppenumAdvise) {return OLE_E_ADVISENOTSUPPORTED;}
protected:
virtual HRESULT RenderData(UINT format,DWORD aspect,LONG dataIndex,stream_writer_formatter<> & out) const {
FORMATETC fmt = {};
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = dataIndex;
const pfc::array_t<t_uint8> * entry = m_entries.query_ptr(fmt);
if (entry != NULL) {
out.write_raw(entry->get_ptr(), entry->get_size());
return S_OK;
}
return DV_E_FORMATETC;
}
virtual HRESULT RenderDataTest(UINT format,DWORD aspect, LONG dataIndex) const {
FORMATETC fmt = {};
fmt.cfFormat = format; fmt.dwAspect = aspect; fmt.lindex = dataIndex;
if (m_entries.have_item(fmt)) return S_OK;
return DV_E_FORMATETC;
}
typedef pfc::list_base_t<FORMATETC> TFormatList;
static void AddFormat(TFormatList & out,UINT code) {
FORMATETC fmt = {};
fmt.dwAspect = DVASPECT_CONTENT;
fmt.lindex = -1;
fmt.cfFormat = code;
for(t_size medWalk = 0; medWalk < 32; ++medWalk) {
const DWORD med = 1 << medWalk;
if ((DataBlockToSTGMEDIUM_SupportedTymeds & med) != 0) {
fmt.tymed = med;
out.add_item(fmt);
}
}
}
virtual void EnumFormats(TFormatList & out) const {
pfc::avltree_t<UINT> formats;
for(auto walk = m_entries.cfirst(); walk.is_valid(); ++walk) {
formats.add_item( walk->m_key.cfFormat );
}
for(auto walk = formats.cfirst(); walk.is_valid(); ++walk) {
AddFormat(out, *walk);
}
}
HRESULT CreateIEnumFORMATETC(IEnumFORMATETC ** outptr) const throw() {
try {
pfc::list_t<FORMATETC> out;
EnumFormats(out);
return SHCreateStdEnumFmtEtc((UINT)out.get_count(), out.get_ptr(), outptr);
} FB2K_COM_CATCH;
}
private:
HRESULT GetData_internal(FORMATETC * formatetc, STGMEDIUM * medium,bool bHere) {
if (formatetc == NULL || medium == NULL) return E_INVALIDARG;
try {
stream_writer_formatter_simple<> out;
HRESULT hr = RenderData(formatetc->cfFormat,formatetc->dwAspect,formatetc->lindex,out);
if (FAILED(hr)) return hr;
return DataBlockToSTGMEDIUM(out.m_buffer.get_ptr(),out.m_buffer.get_size(),medium,formatetc->tymed,bHere);
} FB2K_COM_CATCH;
}
typedef pfc::map_t<FORMATETC, pfc::array_t<t_uint8>, comparator_FORMATETC> t_entries;
t_entries m_entries;
};
}
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -2,8 +2,6 @@
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include <libPPUI/win32_op.h>
namespace ProcessUtils {
class PipeIO : public stream_reader, public stream_writer {
public:
@@ -162,7 +160,7 @@ namespace ProcessUtils {
try {
WIN32_OP( CreateProcess(pfc::stringcvt::string_os_from_utf8(ExePath), NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) );
} catch(std::exception const & e) {
throw failure(PFC_string_formatter() << "Could not start the worker process - " << e);
throw failure(pfc::string_formatter() << "Could not start the worker process - " << e);
}
hProcess = pi.hProcess; _Close(pi.hThread);
} catch(...) {
@@ -230,7 +228,7 @@ namespace ProcessUtils {
static pfc::string_formatter makePipeName() {
GUID id;
CoCreateGuid (&id);
return PFC_string_formatter() << "\\\\.\\pipe\\" << pfc::print_guid(id);
return pfc::string_formatter() << "\\\\.\\pipe\\" << pfc::print_guid(id);
}
static void myCreatePipeOut(HANDLE & in, HANDLE & out) {

View File

@@ -0,0 +1,189 @@
#include "StdAfx.h"
#include "SmartStrStr.h"
SmartStrStr::SmartStrStr() {
for (uint32_t walk = 128; walk < 0x10000; ++walk) {
uint32_t c = Transform(walk);
if (c != walk) {
substituions[walk].insert(c);
}
}
for (uint32_t walk = 32; walk < 0x10000; ++walk) {
auto lo = ToLower(walk);
if (lo != walk) {
auto & s = substituions[walk]; s.insert(lo);
auto iter = substituions.find(lo);
if (iter != substituions.end()) {
s.insert(iter->second.begin(), iter->second.end());
}
}
}
InitTwoCharMappings();
}
const wchar_t * SmartStrStr::matchHereW(const wchar_t * pString, const wchar_t * pUserString) const {
auto walkData = pString;
auto walkUser = pUserString;
for (;; ) {
if (*walkUser == 0) return walkData;
uint32_t cData, cUser;
size_t dData = pfc::utf16_decode_char(walkData, &cData);
size_t dUser = pfc::utf16_decode_char(walkUser, &cUser);
if (dData == 0 || dUser == 0) return nullptr;
if (cData != cUser) {
bool gotMulti = false;
{
auto multi = twoCharMappings.find(cData);
if (multi != twoCharMappings.end()) {
const char * cDataSubst = multi->second;
PFC_ASSERT(strlen(cDataSubst) == 2);
if (matchOneChar(cUser, (uint32_t)cDataSubst[0])) {
auto walkUser2 = walkUser + dUser;
uint32_t cUser2;
auto dUser2 = pfc::utf16_decode_char(walkUser2, &cUser2);
if (matchOneChar(cUser2, (uint32_t)cDataSubst[1])) {
gotMulti = true;
dUser += dUser2;
}
}
}
}
if (!gotMulti) {
if (!matchOneChar(cUser, cData)) return nullptr;
}
}
walkData += dData;
walkUser += dUser;
}
}
const char * SmartStrStr::matchHere(const char * pString, const char * pUserString) const {
const char * walkData = pString;
const char * walkUser = pUserString;
for (;; ) {
if (*walkUser == 0) return walkData;
uint32_t cData, cUser;
size_t dData = pfc::utf8_decode_char(walkData, cData);
size_t dUser = pfc::utf8_decode_char(walkUser, cUser);
if (dData == 0 || dUser == 0) return nullptr;
if (cData != cUser) {
bool gotMulti = false;
{
auto multi = twoCharMappings.find(cData);
if (multi != twoCharMappings.end()) {
const char * cDataSubst = multi->second;
PFC_ASSERT(strlen(cDataSubst) == 2);
if (matchOneChar(cUser, (uint32_t)cDataSubst[0])) {
auto walkUser2 = walkUser + dUser;
uint32_t cUser2;
auto dUser2 = pfc::utf8_decode_char(walkUser2, cUser2);
if (matchOneChar(cUser2, (uint32_t)cDataSubst[1])) {
gotMulti = true;
dUser += dUser2;
}
}
}
}
if (!gotMulti) {
if (!matchOneChar(cUser, cData)) return nullptr;
}
}
walkData += dData;
walkUser += dUser;
}
}
const char * SmartStrStr::strStrEnd(const char * pString, const char * pSubString, size_t * outFoundAt) const {
size_t walk = 0;
for (;; ) {
if (pString[walk] == 0) return nullptr;
auto end = matchHere(pString+walk, pSubString);
if (end != nullptr) {
if ( outFoundAt != nullptr ) * outFoundAt = walk;
return end;
}
size_t delta = pfc::utf8_char_len( pString + walk );
if ( delta == 0 ) return nullptr;
walk += delta;
}
}
const wchar_t * SmartStrStr::strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt) const {
size_t walk = 0;
for (;; ) {
if (pString[walk] == 0) return nullptr;
auto end = matchHereW(pString + walk, pSubString);
if (end != nullptr) {
if (outFoundAt != nullptr) * outFoundAt = walk;
return end;
}
uint32_t dontcare;
size_t delta = pfc::utf16_decode_char(pString + walk, & dontcare);
if (delta == 0) return nullptr;
walk += delta;
}
}
bool SmartStrStr::matchOneChar(uint32_t cInput, uint32_t cData) const {
if (cInput == cData) return true;
auto iter = substituions.find(cData);
if (iter == substituions.end()) return false;
auto & s = iter->second;
return s.find(cInput) != s.end();
}
uint32_t SmartStrStr::Transform(uint32_t c) {
wchar_t wide[2] = {}; char out[4] = {};
pfc::utf16_encode_char(c, wide);
BOOL fail = FALSE;
if (WideCharToMultiByte(pfc::stringcvt::codepage_ascii, 0, wide, 2, out, 4, "?", &fail) > 0) {
if (!fail) {
if (out[0] > 0 && out[1] == 0) {
c = out[0];
}
}
}
return c;
}
uint32_t SmartStrStr::ToLower(uint32_t c) {
return (uint32_t)uCharLower(c);
}
void SmartStrStr::ImportTwoCharMappings(const wchar_t * list, const char * replacement) {
PFC_ASSERT(strlen(replacement) == 2);
for (const wchar_t* ptr = list; ; ) {
wchar_t c = *ptr++;
if (c == 0) break;
twoCharMappings[(uint32_t)c] = replacement;
}
}
void SmartStrStr::InitTwoCharMappings() {
ImportTwoCharMappings(L"ÆǢǼ", "AE");
ImportTwoCharMappings(L"æǣǽ", "ae");
ImportTwoCharMappings(L"Œ", "OE");
ImportTwoCharMappings(L"œɶ", "oe");
ImportTwoCharMappings(L"DŽDZ", "DZ");
ImportTwoCharMappings(L"dždzʣʥ", "dz");
ImportTwoCharMappings(L"ß", "ss");
ImportTwoCharMappings(L"LJ", "LJ");
ImportTwoCharMappings(L"Lj", "Lj");
ImportTwoCharMappings(L"lj", "lj");
ImportTwoCharMappings(L"NJ", "NJ");
ImportTwoCharMappings(L"Nj", "Nj");
ImportTwoCharMappings(L"nj", "nj");
ImportTwoCharMappings(L"IJ", "IJ");
ImportTwoCharMappings(L"ij", "ij");
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <set>
#include <map>
#include <string>
//! Implementation of string matching for search purposes, such as media library search or typefind in list views. \n
//! Inspired by Unicode asymetic search, but not strictly implementing the Unicode asymetric search specifications. \n
//! Bootstraps its character mapping data from various Win32 API methods, requires no externally provided character mapping data. \n
//! Windows-only code. \n
//! \n
//! Keeping a global instance of it is recommended, due to one time init overhead. \n
//! Thread safety: safe to call concurrently once constructed.
class SmartStrStr {
public:
SmartStrStr();
//! Returns ptr to the end of the string if positive (for continuing search), nullptr if negative.
const char * strStrEnd(const char * pString, const char * pSubString, size_t * outFoundAt = nullptr) const;
const wchar_t * strStrEndW(const wchar_t * pString, const wchar_t * pSubString, size_t * outFoundAt = nullptr) const;
//! Returns ptr to the end of the string if positive (for continuing search), nullptr if negative.
const char * matchHere(const char * pString, const char * pUserString) const;
const wchar_t * matchHereW( const wchar_t * pString, const wchar_t * pUserString) const;
//! One-char match. Doesn't use twoCharMappings, use only if you have to operate on char by char basis rather than call the other methods.
bool matchOneChar(uint32_t cInput, uint32_t cData) const;
private:
static uint32_t Transform(uint32_t c);
static uint32_t ToLower(uint32_t c);
void ImportTwoCharMappings(const wchar_t * list, const char * replacement);
void InitTwoCharMappings();
std::map<uint32_t, std::set<uint32_t> > substituions;
std::map<uint32_t, const char* > twoCharMappings;
};

View File

@@ -10,7 +10,7 @@
#pragma once
#endif // _MSC_VER > 1000
#include "foobar2000+atl.h"
#include "../SDK/foobar2000.h"
// TODO: reference additional headers your program requires here

View File

@@ -68,68 +68,104 @@ namespace ThreadUtils {
}
}
void WaitAbortable_MsgLoop(HANDLE ev, abort_callback & abort) {
abort.check();
const HANDLE handles[2] = {ev, abort.get_abort_event()};
MultiWait_MsgLoop(handles, 2);
abort.check();
for(;;) {
SetLastError(0);
const DWORD status = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT);
switch(status) {
case WAIT_TIMEOUT:
PFC_ASSERT(!"How did we get here?");
uBugCheck();
case WAIT_OBJECT_0:
return;
case WAIT_OBJECT_0 + 1:
throw exception_aborted();
case WAIT_OBJECT_0 + 2:
ProcessPendingMessages();
break;
case WAIT_FAILED:
WIN32_OP_FAIL();
default:
uBugCheck();
}
}
}
t_size MultiWaitAbortable_MsgLoop(const HANDLE * ev, t_size evCount, abort_callback & abort) {
abort.check();
pfc::array_t<HANDLE> handles; handles.set_size(evCount + 1);
handles[0] = abort.get_abort_event();
pfc::memcpy_t(handles.get_ptr() + 1, ev, evCount);
DWORD status = MultiWait_MsgLoop(handles.get_ptr(), handles.get_count());
abort.check();
return (size_t)(status - WAIT_OBJECT_0);
}
void SleepAbortable_MsgLoop(abort_callback & abort, DWORD timeout) {
HANDLE handles[] = { abort.get_abort_event() };
MultiWait_MsgLoop(handles, 1, timeout);
abort.check();
}
bool WaitAbortable_MsgLoop(HANDLE ev, abort_callback & abort, DWORD timeout) {
abort.check();
HANDLE handles[2] = { abort.get_abort_event(), ev };
DWORD status = MultiWait_MsgLoop(handles, 2, timeout);
abort.check();
return status != WAIT_TIMEOUT;
}
DWORD MultiWait_MsgLoop(const HANDLE* ev, DWORD evCount) {
for (;; ) {
for(;;) {
SetLastError(0);
const DWORD status = MsgWaitForMultipleObjects((DWORD) evCount, ev, FALSE, INFINITE, QS_ALLINPUT);
if (status == WAIT_FAILED) WIN32_OP_FAIL();
if (status == WAIT_OBJECT_0 + evCount) {
ProcessPendingMessages();
} else if ( status >= WAIT_OBJECT_0 && status < WAIT_OBJECT_0 + evCount ) {
return status;
} else {
uBugCheck();
const DWORD status = MsgWaitForMultipleObjects(handles.get_count(), handles.get_ptr(), FALSE, INFINITE, QS_ALLINPUT);
switch(status) {
case WAIT_TIMEOUT:
PFC_ASSERT(!"How did we get here?");
uBugCheck();
case WAIT_OBJECT_0:
throw exception_aborted();
case WAIT_FAILED:
WIN32_OP_FAIL();
default:
{
t_size index = (t_size)(status - (WAIT_OBJECT_0 + 1));
if (index == evCount) {
ProcessPendingMessages();
} else if (index < evCount) {
return index;
} else {
uBugCheck();
}
}
}
}
}
DWORD MultiWait_MsgLoop(const HANDLE* ev, DWORD evCount, DWORD timeout) {
if (timeout == INFINITE) return MultiWait_MsgLoop(ev, evCount);
void SleepAbortable_MsgLoop(abort_callback & abort, DWORD timeout /*must not be INFINITE*/) {
PFC_ASSERT( timeout != INFINITE );
const DWORD entry = GetTickCount();
DWORD now = entry;
for (;;) {
const DWORD done = now - entry;
if (done >= timeout) return WAIT_TIMEOUT;
const HANDLE handles[1] = {abort.get_abort_event()};
for(;;) {
const DWORD done = GetTickCount() - entry;
if (done >= timeout) return;
SetLastError(0);
const DWORD status = MsgWaitForMultipleObjects((DWORD)evCount, ev, FALSE, timeout - done, QS_ALLINPUT);
if (status == WAIT_FAILED) WIN32_OP_FAIL();
if (status == WAIT_OBJECT_0 + evCount) {
ProcessPendingMessages();
} else if (status == WAIT_TIMEOUT || (status >= WAIT_OBJECT_0 && status < WAIT_OBJECT_0 + evCount) ) {
return status;
} else {
uBugCheck();
const DWORD status = MsgWaitForMultipleObjects(1, handles, FALSE, timeout - done, QS_ALLINPUT);
switch(status) {
case WAIT_TIMEOUT:
return;
case WAIT_OBJECT_0:
throw exception_aborted();
case WAIT_OBJECT_0 + 1:
ProcessPendingMessages();
default:
throw exception_win32(GetLastError());
}
}
}
bool WaitAbortable_MsgLoop(HANDLE ev, abort_callback & abort, DWORD timeout /*must not be INFINITE*/) {
PFC_ASSERT( timeout != INFINITE );
const DWORD entry = GetTickCount();
const HANDLE handles[2] = {ev, abort.get_abort_event()};
for(;;) {
const DWORD done = GetTickCount() - entry;
if (done >= timeout) return false;
SetLastError(0);
const DWORD status = MsgWaitForMultipleObjects(2, handles, FALSE, timeout - done, QS_ALLINPUT);
switch(status) {
case WAIT_TIMEOUT:
return false;
case WAIT_OBJECT_0:
return true;
case WAIT_OBJECT_0 + 1:
throw exception_aborted();
case WAIT_OBJECT_0 + 2:
ProcessPendingMessages();
break;
case WAIT_FAILED:
WIN32_OP_FAIL();
default:
uBugCheck();
}
now = GetTickCount();
}
}

View File

@@ -16,9 +16,6 @@ namespace ThreadUtils {
void SleepAbortable_MsgLoop(abort_callback & abort, DWORD timeout /*must not be INFINITE*/);
bool WaitAbortable_MsgLoop(HANDLE ev, abort_callback & abort, DWORD timeout /*must not be INFINITE*/);
DWORD MultiWait_MsgLoop(const HANDLE* ev, DWORD evCount, DWORD timeout);
DWORD MultiWait_MsgLoop(const HANDLE* ev, DWORD evCount);
template<typename TWhat>
class CObjectQueue {
public:

View File

@@ -0,0 +1,6 @@
#pragma once
class TypeFind {
public:
static LRESULT Handler(NMHDR* hdr, int subItemFrom = 0, int subItemCnt = 1);
};

View File

@@ -1,22 +0,0 @@
#include "stdafx.h"
#include "VolumeMap.h"
static const double powval = 2.0;
static const double silence = -100.0;
double VolumeMap::SliderToDB2(double slider) {
double v = SliderToDB(slider);
v = floor(v * 2.0 + 0.5) * 0.5;
return v;
}
double VolumeMap::SliderToDB(double slider) {
if (slider > 0) {
return pfc::max_t<double>(silence,10.0 * log(slider) / log(powval));
} else {
return silence;
}
}
double VolumeMap::DBToSlider(double volumeDB) {
return pfc::clip_t<double>(pow(powval,volumeDB / 10.0), 0, 1);
}

View File

@@ -1,7 +0,0 @@
#pragma once
namespace VolumeMap {
double SliderToDB(double slider /*0..1 range*/);
double SliderToDB2(double slider); // rounds to 0.5dB
double DBToSlider(double volumeDB);
}

View File

@@ -1,391 +0,0 @@
#pragma once
#include "win32_misc.h"
static BOOL AdjustWindowRectHelper(CWindow wnd, CRect & rc) {
const DWORD style = wnd.GetWindowLong(GWL_STYLE), exstyle = wnd.GetWindowLong(GWL_EXSTYLE);
return AdjustWindowRectEx(&rc,style,(style & WS_POPUP) ? wnd.GetMenu() != NULL : FALSE, exstyle);
}
static void AdjustRectToScreenArea(CRect & rc, CRect rcParent) {
HMONITOR monitor = MonitorFromRect(rcParent,MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = {sizeof(MONITORINFO)};
if (GetMonitorInfo(monitor,&mi)) {
const CRect clip = mi.rcWork;
if (rc.right > clip.right) rc.OffsetRect(clip.right - rc.right, 0);
if (rc.bottom > clip.bottom) rc.OffsetRect(0, clip.bottom - rc.bottom);
if (rc.left < clip.left) rc.OffsetRect(clip.left - rc.left, 0);
if (rc.top < clip.top) rc.OffsetRect(0, clip.top - rc.top);
}
}
static BOOL GetClientRectAsSC(CWindow wnd, CRect & rc) {
CRect temp;
if (!wnd.GetClientRect(temp)) return FALSE;
if (temp.IsRectNull()) return FALSE;
if (!wnd.ClientToScreen(temp)) return FALSE;
rc = temp;
return TRUE;
}
static BOOL CenterWindowGetRect(CWindow wnd, CWindow wndParent, CRect & out) {
CRect parent, child;
if (!wndParent.GetWindowRect(&parent) || !wnd.GetWindowRect(&child)) return FALSE;
{
CPoint origin = parent.CenterPoint();
origin.Offset( - child.Width() / 2, - child.Height() / 2);
child.OffsetRect( origin - child.TopLeft() );
}
AdjustRectToScreenArea(child, parent);
out = child;
return TRUE;
}
static BOOL CenterWindowAbove(CWindow wnd, CWindow wndParent) {
CRect rc;
if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
return wnd.SetWindowPos(NULL,rc,SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
static BOOL ShowWindowCentered(CWindow wnd,CWindow wndParent) {
CRect rc;
if (!CenterWindowGetRect(wnd, wndParent, rc)) return FALSE;
return wnd.SetWindowPos(HWND_TOP,rc,SWP_NOSIZE | SWP_SHOWWINDOW);
}
class cfgWindowSize : public cfg_var {
public:
cfgWindowSize(const GUID & p_guid) : cfg_var(p_guid) {}
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {
stream_writer_formatter<> str(*p_stream,p_abort); str << m_width << m_height;
}
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
stream_reader_formatter<> str(*p_stream,p_abort); str >> m_width >> m_height;
}
uint32_t m_width = UINT32_MAX, m_height = UINT32_MAX;
};
class cfgWindowSizeTracker {
public:
cfgWindowSizeTracker(cfgWindowSize & p_var) : m_var(p_var) {}
bool Apply(HWND p_wnd) {
bool retVal = false;
m_applied = false;
if (m_var.m_width != ~0 && m_var.m_height != ~0) {
CRect rect (0,0,m_var.m_width,m_var.m_height);
if (AdjustWindowRectHelper(p_wnd, rect)) {
SetWindowPos(p_wnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
retVal = true;
}
}
m_applied = true;
return retVal;
}
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
if (uMsg == WM_SIZE && m_applied) {
if (lParam != 0) {
m_var.m_width = (short)LOWORD(lParam); m_var.m_height = (short)HIWORD(lParam);
}
}
return FALSE;
}
private:
cfgWindowSize & m_var;
bool m_applied = false;
};
class cfgDialogSizeTracker : public cfgWindowSizeTracker {
public:
cfgDialogSizeTracker(cfgWindowSize & p_var) : cfgWindowSizeTracker(p_var) {}
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
if (cfgWindowSizeTracker::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) return TRUE;
if (uMsg == WM_INITDIALOG) Apply(hWnd);
return FALSE;
}
};
class cfgDialogPositionData {
public:
cfgDialogPositionData() : m_width(sizeInvalid), m_height(sizeInvalid), m_posX(posInvalid), m_posY(posInvalid) {}
void OverrideDefaultSize(t_uint32 width, t_uint32 height) {
if (m_width == sizeInvalid && m_height == sizeInvalid) {
m_width = width; m_height = height; m_posX = m_posY = posInvalid;
m_dpiX = m_dpiY = 96;
}
}
void AddWindow(CWindow wnd) {
TryFetchConfig();
m_windows += wnd;
ApplyConfig(wnd);
}
void RemoveWindow(CWindow wnd) {
if (m_windows.contains(wnd)) {
StoreConfig(wnd); m_windows -= wnd;
}
}
void FetchConfig() {TryFetchConfig();}
private:
BOOL ApplyConfig(CWindow wnd) {
ApplyDPI();
CWindow wndParent = wnd.GetParent();
UINT flags = SWP_NOACTIVATE | SWP_NOZORDER;
CRect rc;
if (!GetClientRectAsSC(wnd,rc)) return FALSE;
if (m_width != sizeInvalid && m_height != sizeInvalid && (wnd.GetWindowLong(GWL_STYLE) & WS_SIZEBOX) != 0) {
rc.right = rc.left + m_width;
rc.bottom = rc.top + m_height;
} else {
flags |= SWP_NOSIZE;
}
if (wndParent != NULL) {
CRect rcParent;
if (GetParentWndRect(wndParent, rcParent)) {
if (m_posX != posInvalid && m_posY != posInvalid) {
rc.MoveToXY( rcParent.TopLeft() + CPoint(m_posX, m_posY) );
} else {
CPoint center = rcParent.CenterPoint();
rc.MoveToXY( center.x - rc.Width() / 2, center.y - rc.Height() / 2);
}
}
}
if (!AdjustWindowRectHelper(wnd, rc)) return FALSE;
DeOverlap(wnd, rc);
{
CRect rcAdjust(0,0,1,1);
if (wndParent != NULL) {
CRect temp;
if (wndParent.GetWindowRect(temp)) rcAdjust = temp;
}
AdjustRectToScreenArea(rc, rcAdjust);
}
return wnd.SetWindowPos(NULL, rc, flags);
}
struct DeOverlapState {
CWindow m_thisWnd;
CPoint m_topLeft;
bool m_match;
};
static BOOL CALLBACK MyEnumChildProc(HWND wnd, LPARAM param) {
DeOverlapState * state = reinterpret_cast<DeOverlapState*>(param);
if (wnd != state->m_thisWnd && IsWindowVisible(wnd) ) {
CRect rc;
if (GetWindowRect(wnd, rc)) {
if (rc.TopLeft() == state->m_topLeft) {
state->m_match = true; return FALSE;
}
}
}
return TRUE;
}
static bool DeOverlapTest(CWindow wnd, CPoint topLeft) {
DeOverlapState state = {};
state.m_thisWnd = wnd; state.m_topLeft = topLeft; state.m_match = false;
EnumThreadWindows(GetCurrentThreadId(), MyEnumChildProc, reinterpret_cast<LPARAM>(&state));
return state.m_match;
}
static int DeOverlapDelta() {
return pfc::max_t<int>(GetSystemMetrics(SM_CYCAPTION),1);
}
static void DeOverlap(CWindow wnd, CRect & rc) {
const int delta = DeOverlapDelta();
for(;;) {
if (!DeOverlapTest(wnd, rc.TopLeft())) break;
rc.OffsetRect(delta,delta);
}
}
BOOL StoreConfig(CWindow wnd) {
CRect rc;
if (!GetClientRectAsSC(wnd, rc)) return FALSE;
const CSize DPI = QueryScreenDPIEx();
m_dpiX = DPI.cx; m_dpiY = DPI.cy;
m_width = rc.Width(); m_height = rc.Height();
m_posX = m_posY = posInvalid;
CWindow parent = wnd.GetParent();
if (parent != NULL) {
CRect rcParent;
if (GetParentWndRect(parent, rcParent)) {
m_posX = rc.left - rcParent.left;
m_posY = rc.top - rcParent.top;
}
}
return TRUE;
}
void TryFetchConfig() {
for(auto walk = m_windows.cfirst(); walk.is_valid(); ++walk) {
if (StoreConfig(*walk)) break;
}
}
void ApplyDPI() {
const CSize screenDPI = QueryScreenDPIEx();
if (screenDPI.cx == 0 || screenDPI.cy == 0) {
PFC_ASSERT(!"Should not get here - something seriously wrong with the OS");
return;
}
if (m_dpiX != dpiInvalid && m_dpiX != screenDPI.cx) {
if (m_width != sizeInvalid) m_width = MulDiv(m_width, screenDPI.cx, m_dpiX);
if (m_posX != posInvalid) m_posX = MulDiv(m_posX, screenDPI.cx, m_dpiX);
}
if (m_dpiY != dpiInvalid && m_dpiY != screenDPI.cy) {
if (m_height != sizeInvalid) m_height = MulDiv(m_height, screenDPI.cy, m_dpiY);
if (m_posY != posInvalid) m_posY = MulDiv(m_posY, screenDPI.cy, m_dpiY);
}
m_dpiX = screenDPI.cx;
m_dpiY = screenDPI.cy;
}
CSize GrabDPI() const {
CSize DPI(96,96);
if (m_dpiX != dpiInvalid) DPI.cx = m_dpiX;
if (m_dpiY != dpiInvalid) DPI.cy = m_dpiY;
return DPI;
}
static BOOL GetParentWndRect(CWindow wndParent, CRect & rc) {
if (!wndParent.IsIconic()) {
return wndParent.GetWindowRect(rc);
}
WINDOWPLACEMENT pl = {sizeof(pl)};
if (!wndParent.GetWindowPlacement(&pl)) return FALSE;
rc = pl.rcNormalPosition;
return TRUE;
}
pfc::avltree_t<CWindow> m_windows;
public:
t_uint32 m_width, m_height;
t_int32 m_posX, m_posY;
t_uint32 m_dpiX, m_dpiY;
enum {
posInvalid = 0x80000000,
sizeInvalid = 0xFFFFFFFF,
dpiInvalid = 0,
};
};
FB2K_STREAM_READER_OVERLOAD(cfgDialogPositionData) {
stream >> value.m_width >> value.m_height;
try {
stream >> value.m_posX >> value.m_posY >> value.m_dpiX >> value.m_dpiY;
} catch(exception_io_data) {
value.m_posX = value.m_posY = cfgDialogPositionData::posInvalid;
value.m_dpiX = value.m_dpiY = cfgDialogPositionData::dpiInvalid;
}
return stream;
}
FB2K_STREAM_WRITER_OVERLOAD(cfgDialogPositionData) {
return stream << value.m_width << value.m_height << value.m_posX << value.m_posY << value.m_dpiX << value.m_dpiY;
}
class cfgDialogPosition : public cfgDialogPositionData, public cfg_var {
public:
cfgDialogPosition(const GUID & id) : cfg_var(id) {}
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {FetchConfig(); stream_writer_formatter<> str(*p_stream, p_abort); str << *pfc::implicit_cast<cfgDialogPositionData*>(this);}
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {stream_reader_formatter<> str(*p_stream, p_abort); str >> *pfc::implicit_cast<cfgDialogPositionData*>(this);}
};
class cfgDialogPositionTracker {
public:
cfgDialogPositionTracker(cfgDialogPosition & p_var) : m_var(p_var) {}
~cfgDialogPositionTracker() {Cleanup();}
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT & lResult) {
if (uMsg == WM_CREATE || uMsg == WM_INITDIALOG) {
Cleanup();
m_wnd = hWnd;
m_var.AddWindow(m_wnd);
} else if (uMsg == WM_DESTROY) {
PFC_ASSERT( hWnd == m_wnd );
Cleanup();
}
return FALSE;
}
private:
void Cleanup() {
if (m_wnd != NULL) {
m_var.RemoveWindow(m_wnd);
m_wnd = NULL;
}
}
cfgDialogPosition & m_var;
CWindow m_wnd;
};
//! DPI-safe window size var \n
//! Stores size in pixel and original DPI\n
//! Use with cfgWindowSizeTracker2
class cfgWindowSize2 : public cfg_var {
public:
cfgWindowSize2(const GUID & p_guid) : cfg_var(p_guid) {}
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {
stream_writer_formatter<> str(*p_stream,p_abort); str << m_size.cx << m_size.cy << m_dpi.cx << m_dpi.cy;
}
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
stream_reader_formatter<> str(*p_stream,p_abort); str >> m_size.cx >> m_size.cy >> m_dpi.cx >> m_dpi.cy;
}
bool is_valid() const {
return m_size.cx > 0 && m_size.cy > 0;
}
CSize get( CSize forDPI ) const {
if ( forDPI == m_dpi ) return m_size;
CSize ret;
ret.cx = MulDiv( m_size.cx, forDPI.cx, m_dpi.cx );
ret.cy = MulDiv( m_size.cy, forDPI.cy, m_dpi.cy );
return ret;
}
CSize m_size = CSize(0,0), m_dpi = CSize(0,0);
};
//! Forward messages to this class to utilize cfgWindowSize2
class cfgWindowSizeTracker2 : public CMessageMap {
public:
cfgWindowSizeTracker2( cfgWindowSize2 & var ) : m_var(var) {}
BEGIN_MSG_MAP_EX(cfgWindowSizeTracker2)
if (uMsg == WM_CREATE || uMsg == WM_INITDIALOG) {
Apply(hWnd);
}
MSG_WM_SIZE( OnSize )
END_MSG_MAP()
bool Apply(HWND p_wnd) {
bool retVal = false;
m_applied = false;
if (m_var.is_valid()) {
CRect rect( CPoint(0,0), m_var.get( m_DPI ) );
if (AdjustWindowRectHelper(p_wnd, rect)) {
SetWindowPos(p_wnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
retVal = true;
}
}
m_applied = true;
return retVal;
}
private:
void OnSize(UINT nType, CSize size) {
if ( m_applied && size.cx > 0 && size.cy > 0 ) {
m_var.m_size = size;
m_var.m_dpi = m_DPI;
}
SetMsgHandled(FALSE);
}
cfgWindowSize2 & m_var;
bool m_applied = false;
const CSize m_DPI = QueryScreenDPIEx();
};

View File

@@ -1,3 +0,0 @@
#pragma once
// fb2k mobile compat

View File

@@ -1,291 +0,0 @@
#pragma once
#include "win32_misc.h"
#include <libPPUI/WTL-PP.h>
#include <utility>
class CMenuSelectionReceiver : public CWindowImpl<CMenuSelectionReceiver> {
public:
CMenuSelectionReceiver(HWND p_parent) {
WIN32_OP( Create(p_parent) != NULL );
}
~CMenuSelectionReceiver() {
if (m_hWnd != NULL) DestroyWindow();
}
typedef CWindowImpl<CMenuSelectionReceiver> _baseClass;
DECLARE_WND_CLASS_EX(TEXT("{DF0087DB-E765-4283-BBAB-6AB2E8AB64A1}"),0,0);
BEGIN_MSG_MAP(CMenuSelectionReceiver)
MESSAGE_HANDLER(WM_MENUSELECT,OnMenuSelect)
END_MSG_MAP()
protected:
virtual bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
return false;
}
private:
LRESULT OnMenuSelect(UINT,WPARAM p_wp,LPARAM p_lp,BOOL&) {
if (p_lp != 0) {
if (HIWORD(p_wp) & MF_POPUP) {
m_status.release();
} else {
pfc::string8 msg;
UINT cmd = LOWORD(p_wp);
if ( cmd == 0 || !QueryHint(cmd,msg)) {
m_status.release();
} else {
if (m_status.is_empty()) {
if (!static_api_ptr_t<ui_control>()->override_status_text_create(m_status)) m_status.release();
}
if (m_status.is_valid()) {
m_status->override_text(msg);
}
}
}
} else {
m_status.release();
}
return 0;
}
service_ptr_t<ui_status_text_override> m_status;
PFC_CLASS_NOT_COPYABLE(CMenuSelectionReceiver,CMenuSelectionReceiver);
};
class CMenuDescriptionMap : public CMenuSelectionReceiver {
public:
CMenuDescriptionMap(HWND p_parent) : CMenuSelectionReceiver(p_parent) {}
void Set(unsigned p_id,const char * p_description) {m_content.set(p_id,p_description);}
protected:
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
return m_content.query(p_id,p_out);
}
private:
pfc::map_t<unsigned,pfc::string8> m_content;
};
class CMenuDescriptionHybrid : public CMenuSelectionReceiver {
public:
CMenuDescriptionHybrid(HWND parent) : CMenuSelectionReceiver(parent) {}
void Set(unsigned id, const char * desc) {m_content.set(id, desc);}
void SetCM(contextmenu_manager::ptr mgr, unsigned base, unsigned max) {
m_cmMgr = mgr; m_cmMgr_base = base; m_cmMgr_max = max;
}
protected:
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
if (m_cmMgr.is_valid() && p_id >= m_cmMgr_base && p_id < m_cmMgr_max) {
return m_cmMgr->get_description_by_id(p_id - m_cmMgr_base,p_out);
}
return m_content.query(p_id,p_out);
}
private:
pfc::map_t<unsigned,pfc::string8> m_content;
contextmenu_manager::ptr m_cmMgr; unsigned m_cmMgr_base, m_cmMgr_max;
};
inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CPoint & p_point) {
return p_fmt << "(" << p_point.x << "," << p_point.y << ")";
}
inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CRect & p_rect) {
return p_fmt << "(" << p_rect.left << "," << p_rect.top << "," << p_rect.right << "," << p_rect.bottom << ")";
}
template<typename TClass>
class CAddDummyMessageMap : public TClass {
public:
BEGIN_MSG_MAP(CAddDummyMessageMap<TClass>)
END_MSG_MAP()
};
template<typename _parentClass> class CWindowFixSEH : public _parentClass { public:
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) {
__try {
return _parentClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID);
} __except(uExceptFilterProc(GetExceptionInformation())) { return FALSE; /* should not get here */ }
}
template<typename ... arg_t> CWindowFixSEH( arg_t && ... arg ) : _parentClass( std::forward<arg_t>(arg) ... ) {}
};
template<typename TClass>
class CWindowAutoLifetime : public CWindowFixSEH<TClass> {
public:
typedef CWindowFixSEH<TClass> TBase;
template<typename ... arg_t> CWindowAutoLifetime(HWND parent, arg_t && ... arg) : TBase( std::forward<arg_t>(arg) ... ) {Init(parent);}
private:
void Init(HWND parent) {WIN32_OP(this->Create(parent) != NULL);}
void OnFinalMessage(HWND wnd) {PFC_ASSERT_NO_EXCEPTION( TBase::OnFinalMessage(wnd) ); PFC_ASSERT_NO_EXCEPTION(delete this);}
};
template<typename TClass>
class ImplementModelessTracking : public TClass {
public:
template<typename ... arg_t> ImplementModelessTracking(arg_t && ... arg ) : TClass(std::forward<arg_t>(arg) ... ) {}
BEGIN_MSG_MAP_EX(ImplementModelessTracking)
MSG_WM_INITDIALOG(OnInitDialog)
MSG_WM_DESTROY(OnDestroy)
CHAIN_MSG_MAP(TClass)
END_MSG_MAP_HOOK()
private:
BOOL OnInitDialog(CWindow, LPARAM) {m_modeless.Set( this->m_hWnd ); SetMsgHandled(FALSE); return FALSE; }
void OnDestroy() {m_modeless.Set(NULL); SetMsgHandled(FALSE); }
CModelessDialogEntry m_modeless;
};
namespace fb2k {
template<typename dialog_t, typename ... arg_t> dialog_t * newDialogEx( HWND parent, arg_t && ... arg ) {
return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > ( parent, std::forward<arg_t>(arg) ... );
}
template<typename dialog_t, typename ... arg_t> dialog_t * newDialog(arg_t && ... arg) {
return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > (core_api::get_main_window(), std::forward<arg_t>(arg) ...);
}
}
class CMenuSelectionReceiver_UiElement : public CMenuSelectionReceiver {
public:
CMenuSelectionReceiver_UiElement(service_ptr_t<ui_element_instance> p_owner,unsigned p_id_base) : CMenuSelectionReceiver(p_owner->get_wnd()), m_owner(p_owner), m_id_base(p_id_base) {}
protected:
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
return m_owner->edit_mode_context_menu_get_description(p_id,m_id_base,p_out);
}
private:
const unsigned m_id_base;
const service_ptr_t<ui_element_instance> m_owner;
};
static bool window_service_trait_defer_destruction(const service_base *) {return true;}
//! Special service_impl_t replacement for service classes that also implement ATL/WTL windows.
template<typename _t_base>
class window_service_impl_t : public implement_service_query< CWindowFixSEH<_t_base> > {
private:
typedef window_service_impl_t<_t_base> t_self;
typedef implement_service_query< CWindowFixSEH<_t_base> > t_base;
public:
BEGIN_MSG_MAP_EX(window_service_impl_t)
MSG_WM_DESTROY(OnDestroyPassThru)
CHAIN_MSG_MAP(__super)
END_MSG_MAP_HOOK()
int FB2KAPI service_release() throw() {
int ret = --m_counter;
if (ret == 0) {
if (window_service_trait_defer_destruction(this) && !InterlockedExchange(&m_delayedDestroyInProgress,1)) {
PFC_ASSERT_NO_EXCEPTION( service_impl_helper::release_object_delayed(this); );
} else if (this->m_hWnd != NULL) {
if (!m_destroyWindowInProgress) { // don't double-destroy in weird scenarios
PFC_ASSERT_NO_EXCEPTION( ::DestroyWindow(this->m_hWnd) );
}
} else {
PFC_ASSERT_NO_EXCEPTION( delete this );
}
}
return ret;
}
int FB2KAPI service_add_ref() throw() {return ++m_counter;}
template<typename ... arg_t>
window_service_impl_t( arg_t && ... arg ) : t_base( std::forward<arg_t>(arg) ... ) {};
private:
void OnDestroyPassThru() {
SetMsgHandled(FALSE); m_destroyWindowInProgress = true;
}
void OnFinalMessage(HWND p_wnd) {
t_base::OnFinalMessage(p_wnd);
service_ptr_t<service_base> bump(this);
}
volatile bool m_destroyWindowInProgress = false;
volatile LONG m_delayedDestroyInProgress = 0;
pfc::refcounter m_counter;
};
namespace fb2k {
template<typename obj_t, typename ... arg_t>
service_ptr_t<obj_t> service_new_window(arg_t && ... arg) {
return new window_service_impl_t< obj_t > ( std::forward<arg_t> (arg) ... );
}
}
static void AppendMenuPopup(HMENU menu, UINT flags, CMenu & popup, const TCHAR * label) {
PFC_ASSERT( flags & MF_POPUP );
WIN32_OP( CMenuHandle(menu).AppendMenu(flags, popup, label) );
popup.Detach();
}
class CMessageMapDummy : public CMessageMap {
public:
BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
LRESULT& lResult, DWORD dwMsgMapID) {return FALSE;}
};
template<typename TDialog> class preferences_page_instance_impl : public TDialog {
public:
preferences_page_instance_impl(HWND parent, preferences_page_callback::ptr callback) : TDialog(callback) {
WIN32_OP(this->Create(parent) != NULL);
// complain early if what we created isn't a child window
PFC_ASSERT( (this->GetStyle() & (WS_POPUP|WS_CHILD)) == WS_CHILD );
}
HWND get_wnd() {return this->m_hWnd;}
};
static bool window_service_trait_defer_destruction(const preferences_page_instance *) {return false;}
template<typename TDialog> class preferences_page_impl : public preferences_page_v3 {
public:
preferences_page_instance::ptr instantiate(HWND parent, preferences_page_callback::ptr callback) {
return fb2k::service_new_window<preferences_page_instance_impl<TDialog> >(parent, callback);
}
};
class CEmbeddedDialog : public CDialogImpl<CEmbeddedDialog> {
public:
CEmbeddedDialog(CMessageMap * owner, DWORD msgMapID, UINT dialogID) : m_owner(*owner), IDD(dialogID), m_msgMapID(msgMapID) {}
BEGIN_MSG_MAP(CEmbeddedDialog)
CHAIN_MSG_MAP_ALT_MEMBER(m_owner, m_msgMapID)
END_MSG_MAP()
const DWORD m_msgMapID;
const UINT IDD;
CMessageMap & m_owner;
};
// here because of window_service_impl_t
template<typename TImpl, typename TInterface = ui_element> class ui_element_impl : public TInterface {
public:
GUID get_guid() { return TImpl::g_get_guid(); }
GUID get_subclass() { return TImpl::g_get_subclass(); }
void get_name(pfc::string_base & out) { TImpl::g_get_name(out); }
ui_element_instance::ptr instantiate(HWND parent, ui_element_config::ptr cfg, ui_element_instance_callback::ptr callback) {
PFC_ASSERT(cfg->get_guid() == get_guid());
service_nnptr_t<ui_element_instance_impl_helper> item = new window_service_impl_t<ui_element_instance_impl_helper>(cfg, callback);
item->initialize_window(parent);
return item;
}
ui_element_config::ptr get_default_configuration() { return TImpl::g_get_default_configuration(); }
ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) { return NULL; }
bool get_description(pfc::string_base & out) { out = TImpl::g_get_description(); return true; }
private:
class ui_element_instance_impl_helper : public TImpl {
public:
ui_element_instance_impl_helper(ui_element_config::ptr cfg, ui_element_instance_callback::ptr callback) : TImpl(cfg, callback) {}
GUID get_guid() { return TImpl::g_get_guid(); }
GUID get_subclass() { return TImpl::g_get_subclass(); }
HWND get_wnd() { return *this; }
};
public:
typedef ui_element_instance_impl_helper TInstance;
static TInstance const & instanceGlobals() { return *reinterpret_cast<const TInstance*>(NULL); }
};

View File

@@ -2,11 +2,11 @@
namespace bitreader_helper {
inline static size_t extract_bit(const uint8_t * p_stream,size_t p_offset) {
inline static size_t extract_bit(const t_uint8 * p_stream,size_t p_offset) {
return (p_stream[p_offset>>3] >> (7-(p_offset&7)))&1;
}
inline static size_t extract_int(const uint8_t * p_stream,size_t p_base,size_t p_width) {
inline static size_t extract_int(const t_uint8 * p_stream,size_t p_base,size_t p_width) {
size_t ret = 0;
size_t offset = p_base;
for(size_t bit=0;bit<p_width;bit++) {
@@ -15,19 +15,6 @@ namespace bitreader_helper {
}
return ret;
}
inline static void write_bit( uint8_t * p_stream, size_t p_offset, size_t bit ) {
size_t bshift = 7 - (p_offset&7);
size_t mask = 1 << bshift;
uint8_t & b = p_stream[p_offset>>3];
b = (b & ~mask) | ((bit&1) << bshift);
}
inline static void write_int( uint8_t * p_stream, size_t p_base, size_t p_width, size_t p_value) {
size_t offset = p_base;
size_t val = p_value;
for( size_t bit = 0; bit < p_width; ++ bit ) {
write_bit( p_stream, offset++, val >> (p_width - bit - 1));
}
}
class bitreader
{
@@ -42,31 +29,18 @@ public:
m_bitptr += p_bits;
}
template<typename t_ret>
t_ret peek_t(t_size p_bits) const {
size_t ptr = m_bitptr;
t_ret read_t(t_size p_bits) {
t_ret ret = 0;
for(t_size bit=0;bit<p_bits;bit++)
{
ret <<= 1;
ret |= (m_ptr[ptr >>3] >> (7-(ptr &7)))&1;
ptr++;
ret |= (m_ptr[m_bitptr>>3] >> (7-(m_bitptr&7)))&1;
m_bitptr++;
}
return ret;
}
template<typename t_ret>
t_ret read_t(t_size p_bits) {
t_ret ret = peek_t<t_ret>(p_bits);
skip(p_bits);
return ret;
}
size_t peek(size_t bits) const {
return peek_t<size_t>(bits);
}
t_size read(t_size p_bits) {return read_t<t_size>(p_bits);}
inline t_size get_bitptr() const {return m_bitptr;}
@@ -131,10 +105,6 @@ public:
m_reader.skip(p_bits);
}
size_t peek(size_t bits) {
if (bits > m_remaining) throw exception_io_data_truncation();
return m_reader.peek(bits);
}
t_size read(t_size p_bits)
{
if (p_bits > m_remaining) throw exception_io_data_truncation();
@@ -151,4 +121,4 @@ inline static t_size extract_bits(const t_uint8 * p_buffer,t_size p_base,t_size
return bitreader(p_buffer,p_base).read(p_count);
}
}
}

View File

@@ -3,12 +3,12 @@
class cfg_guidlist : public cfg_var, public pfc::list_t<GUID>
{
public:
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) override {
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {
t_uint32 n, m = pfc::downcast_guarded<t_uint32>(get_count());
p_stream->write_lendian_t(m,p_abort);
for(n=0;n<m;n++) p_stream->write_lendian_t(get_item(n),p_abort);
}
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) override {
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
t_uint32 n,count;
p_stream->read_lendian_t(count,p_abort);
m_buffer.set_size(count);

View File

@@ -0,0 +1,42 @@
#include "stdafx.h"
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include "clipboard.h"
#ifdef UNICODE
#define CF_TCHAR CF_UNICODETEXT
#else
#define CF_TCHAR CF_TEXT
#endif
namespace ClipboardHelper {
void SetRaw(UINT format,const void * data, t_size size) {
HANDLE buffer = GlobalAlloc(GMEM_DDESHARE,size);
if (buffer == NULL) throw std::bad_alloc();
try {
CGlobalLockScope lock(buffer);
PFC_ASSERT(lock.GetSize() == size);
memcpy(lock.GetPtr(),data,size);
} catch(...) {
GlobalFree(buffer); throw;
}
WIN32_OP(SetClipboardData(format,buffer) != NULL);
}
void SetString(const char * in) {
pfc::stringcvt::string_os_from_utf8 temp(in);
SetRaw(CF_TCHAR,temp.get_ptr(),(temp.length() + 1) * sizeof(TCHAR));
}
bool GetString(pfc::string_base & out) {
pfc::array_t<t_uint8> temp;
if (!GetRaw(CF_TCHAR,temp)) return false;
out = pfc::stringcvt::string_utf8_from_os(reinterpret_cast<const TCHAR*>(temp.get_ptr()),temp.get_size() / sizeof(TCHAR));
return true;
}
bool IsTextAvailable() {
return IsClipboardFormatAvailable(CF_TCHAR) == TRUE;
}
}
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -0,0 +1,47 @@
#pragma once
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include "win32_misc.h"
namespace ClipboardHelper {
class OpenScope {
public:
OpenScope() : m_open(false) {}
~OpenScope() {Close();}
void Open(HWND p_owner) {
Close();
WIN32_OP(OpenClipboard(p_owner));
m_open = true;
}
void Close() {
if (m_open) {
m_open = false;
CloseClipboard();
}
}
private:
bool m_open;
PFC_CLASS_NOT_COPYABLE_EX(OpenScope)
};
void SetRaw(UINT format,const void * buffer, t_size size);
void SetString(const char * in);
bool GetString(pfc::string_base & out);
template<typename TArray>
bool GetRaw(UINT format,TArray & out) {
pfc::assert_byte_type<typename TArray::t_item>();
HANDLE data = GetClipboardData(format);
if (data == NULL) return false;
CGlobalLockScope lock(data);
out.set_size( lock.GetSize() );
memcpy(out.get_ptr(), lock.GetPtr(), lock.GetSize() );
return true;
}
bool IsTextAvailable();
};
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -1,6 +1,5 @@
#include "StdAfx.h"
#include "stdafx.h"
#include "create_directory_helper.h"
#include <pfc/pathUtils.h>
namespace create_directory_helper
{
@@ -118,38 +117,29 @@ namespace create_directory_helper
}
pfc::string create_directory_helper::sanitize_formatted_path(pfc::stringp formatted, bool allowWC) {
return sanitize_formatted_path_ex(formatted, allowWC, pfc::io::path::charReplaceDefault);
};
pfc::string create_directory_helper::sanitize_formatted_path_ex(pfc::stringp formatted, bool allowWC, charReplace_t replace) {
pfc::string out;
t_size curSegBase = 0;
for (t_size walk = 0; ; ++walk) {
for(t_size walk = 0; ; ++walk) {
const char c = formatted[walk];
const bool end = (c == 0);
if (end || pfc::io::path::isSeparator(c)) {
if (curSegBase < walk) {
pfc::string seg(formatted + curSegBase, walk - curSegBase);
out = pfc::io::path::combine(out, pfc::io::path::validateFileName(seg, allowWC, end /*preserve ext*/, replace));
pfc::string seg( formatted + curSegBase, walk - curSegBase );
out = pfc::io::path::combine(out, pfc::io::path::validateFileName(seg, allowWC, end /*preserve ext*/));
}
if (end) break;
if ( end ) break;
curSegBase = walk + 1;
}
}
return out;
}
};
void create_directory_helper::format_filename_ex(const metadb_handle_ptr & handle, titleformat_hook * p_hook, titleformat_object::ptr spec, const char * suffix, pfc::string_base & out) {
format_filename_ex(handle, p_hook, spec, suffix, out, pfc::io::path::charReplaceDefault);
}
void create_directory_helper::format_filename_ex(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,const char * suffix, pfc::string_base & out, charReplace_t replace) {
void create_directory_helper::format_filename_ex(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,const char * suffix, pfc::string_base & out) {
pfc::string_formatter formatted;
titleformat_text_filter_myimpl filter;
filter.m_replace = replace;
handle->format_title(p_hook,formatted,spec,&filter);
formatted << suffix;
out = sanitize_formatted_path_ex(formatted, false, replace).ptr();
out = sanitize_formatted_path(formatted).ptr();
}
void create_directory_helper::format_filename(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,pfc::string_base & out) {
format_filename_ex(handle, p_hook, spec, "", out);
@@ -164,30 +154,16 @@ void create_directory_helper::format_filename(const metadb_handle_ptr & handle,t
}
}
static bool substSanity(const char * subst) {
if (subst == nullptr) return false;
for (size_t w = 0; subst[w]; ++w) {
if (pfc::io::path::isSeparator(subst[w])) return false;
}
return true;
}
void create_directory_helper::titleformat_text_filter_myimpl::write(const GUID & p_inputType,pfc::string_receiver & p_out,const char * p_data,t_size p_dataLength) {
if (p_inputType == titleformat_inputtypes::meta) {
pfc::string_formatter temp;
for(t_size walk = 0; walk < p_dataLength; ++walk) {
char c = p_data[walk];
if (c == 0) break;
const char * subst = nullptr;
if (pfc::io::path::isSeparator(c)) {
if (m_replace) {
const char * proposed = m_replace(c);
if (substSanity(proposed)) subst = proposed;
}
if (subst == nullptr) subst = "-";
c = '-';
}
if (subst != nullptr) temp.add_string(subst);
else temp.add_byte(c);
temp.add_byte(c);
}
p_out.add_string(temp);
} else p_out.add_string(p_data,p_dataLength);

View File

@@ -1,30 +1,16 @@
#pragma once
#include <functional>
#include <pfc/stringNew.h>
#ifdef FOOBAR2000_MODERN
#include "metadb_compat.h"
#include <SDK/titleformat.h>
#endif
namespace create_directory_helper {
typedef std::function<const char* (char)> charReplace_t;
void create_path(const char * p_path,abort_callback & p_abort);
void make_path(const char * parent,const char * filename,const char * extension,bool allow_new_dirs,pfc::string8 & out,bool b_really_create_dirs,abort_callback & p_dir_create_abort);
void format_filename(const metadb_handle_ptr & handle,titleformat_hook * p_hook,const char * spec,pfc::string_base & out);
void format_filename(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,pfc::string_base & out);
void format_filename_ex(const metadb_handle_ptr & handle,titleformat_hook * p_hook,titleformat_object::ptr spec,const char * suffix, pfc::string_base & out);
void format_filename_ex(const metadb_handle_ptr & handle, titleformat_hook * p_hook, titleformat_object::ptr spec, const char * suffix, pfc::string_base & out, charReplace_t replace);
pfc::string sanitize_formatted_path(pfc::stringp str, bool allowWC = false);
pfc::string sanitize_formatted_path_ex(pfc::stringp str, bool allowWC, charReplace_t replace);
class titleformat_text_filter_myimpl : public titleformat_text_filter {
public:
charReplace_t m_replace;
void write(const GUID & p_inputType,pfc::string_receiver & p_out,const char * p_data,t_size p_dataLength);
};

View File

@@ -25,19 +25,15 @@ static bool is_meta_same_everywhere(const cue_creator::t_entry_list & p_list,con
{
pfc::string8_fastalloc reference,temp;
bool first = true;
for(auto iter = p_list.first(); iter.is_valid(); ++ iter ) {
if ( ! iter->isTrackAudio() ) continue;
if ( first ) {
first = false;
if (!iter->m_infos.meta_format(p_meta,reference)) return false;
} else {
if (!iter->m_infos.meta_format(p_meta,temp)) return false;
if (strcmp(temp,reference)!=0) return false;
}
cue_creator::t_entry_list::const_iterator iter;
iter = p_list.first();
if (!iter.is_valid()) return false;
if (!iter->m_infos.meta_format(p_meta,reference)) return false;
for(;iter.is_valid();++iter)
{
if (!iter->m_infos.meta_format(p_meta,temp)) return false;
if (strcmp(temp,reference)!=0) return false;
}
return true;
}
@@ -58,53 +54,46 @@ namespace cue_creator
catalog_global = is_meta_same_everywhere(p_data,"catalog"),
songwriter_global = is_meta_same_everywhere(p_data,"songwriter");
if (genre_global) {
p_out << "REM GENRE " << format_meta(p_data.first()->m_infos,"genre") << g_eol;
}
if (date_global) {
p_out << "REM DATE " << format_meta(p_data.first()->m_infos,"date") << g_eol;
}
if (discid_global) {
p_out << "REM DISCID " << format_meta(p_data.first()->m_infos,"discid") << g_eol;
}
if (comment_global) {
p_out << "REM COMMENT " << format_meta(p_data.first()->m_infos,"comment") << g_eol;
}
if (catalog_global) {
p_out << "CATALOG " << format_meta(p_data.first()->m_infos,"catalog") << g_eol;
}
if (songwriter_global) {
p_out << "SONGWRITER \"" << format_meta(p_data.first()->m_infos,"songwriter") << "\"" << g_eol;
}
if (album_artist_global)
{
auto firstTrack = p_data.first();
while( firstTrack.is_valid() && ! firstTrack->isTrackAudio() ) ++ firstTrack;
if ( firstTrack.is_valid() ) {
if (genre_global) {
p_out << "REM GENRE " << format_meta(firstTrack->m_infos,"genre") << g_eol;
}
if (date_global) {
p_out << "REM DATE " << format_meta(firstTrack->m_infos,"date") << g_eol;
}
if (discid_global) {
p_out << "REM DISCID " << format_meta(firstTrack->m_infos,"discid") << g_eol;
}
if (comment_global) {
p_out << "REM COMMENT " << format_meta(firstTrack->m_infos,"comment") << g_eol;
}
if (catalog_global) {
p_out << "CATALOG " << format_meta(firstTrack->m_infos,"catalog") << g_eol;
}
if (songwriter_global) {
p_out << "SONGWRITER \"" << format_meta(firstTrack->m_infos,"songwriter") << "\"" << g_eol;
}
p_out << "PERFORMER \"" << format_meta(p_data.first()->m_infos,"album artist") << "\"" << g_eol;
artist_global = false;
}
else if (artist_global)
{
p_out << "PERFORMER \"" << format_meta(p_data.first()->m_infos,"artist") << "\"" << g_eol;
}
if (album_global)
{
p_out << "TITLE \"" << format_meta(p_data.first()->m_infos,"album") << "\"" << g_eol;
}
if (album_artist_global)
{
p_out << "PERFORMER \"" << format_meta(firstTrack->m_infos,"album artist") << "\"" << g_eol;
artist_global = false;
}
else if (artist_global)
{
p_out << "PERFORMER \"" << format_meta(firstTrack->m_infos,"artist") << "\"" << g_eol;
}
if (album_global)
{
p_out << "TITLE \"" << format_meta(firstTrack->m_infos,"album") << "\"" << g_eol;
}
{
replaygain_info::t_text_buffer rgbuffer;
replaygain_info rg = firstTrack->m_infos.get_replaygain();
if (rg.format_album_gain(rgbuffer))
p_out << "REM REPLAYGAIN_ALBUM_GAIN " << rgbuffer << g_eol;
if (rg.format_album_peak(rgbuffer))
p_out << "REM REPLAYGAIN_ALBUM_PEAK " << rgbuffer << g_eol;
}
}
{
replaygain_info::t_text_buffer rgbuffer;
replaygain_info rg = p_data.first()->m_infos.get_replaygain();
if (rg.format_album_gain(rgbuffer))
p_out << "REM REPLAYGAIN_ALBUM_GAIN " << rgbuffer << g_eol;
if (rg.format_album_peak(rgbuffer))
p_out << "REM REPLAYGAIN_ALBUM_PEAK " << rgbuffer << g_eol;
}
pfc::string8 last_file;
@@ -113,19 +102,11 @@ namespace cue_creator
{
if (strcmp(last_file,iter->m_file) != 0)
{
auto fileType = iter->m_fileType;
if ( fileType.length() == 0 ) fileType = "WAVE";
p_out << "FILE \"" << iter->m_file << "\" " << fileType << g_eol;
p_out << "FILE \"" << iter->m_file << "\" WAVE" << g_eol;
last_file = iter->m_file;
}
{
auto trackType = iter->m_trackType;
if (trackType.length() == 0) trackType = "AUDIO";
p_out << " TRACK " << pfc::format_int(iter->m_track_number,2) << " " << trackType << g_eol;
}
p_out << " TRACK " << pfc::format_int(iter->m_track_number,2) << " AUDIO" << g_eol;
if (iter->m_infos.meta_find("title") != pfc_infinite)
p_out << " TITLE \"" << format_meta(iter->m_infos,"title") << "\"" << g_eol;
@@ -192,11 +173,5 @@ namespace cue_creator
m_index_list.m_positions[1] = index1;
}
bool t_entry::isTrackAudio() const {
PFC_ASSERT( m_trackType.length() > 0 );
return pfc::stringEqualsI_ascii( m_trackType, "AUDIO" );
}
}

View File

@@ -7,11 +7,9 @@ namespace cue_creator
struct t_entry
{
file_info_impl m_infos;
pfc::string8 m_file, m_fileType, m_flags, m_trackType = "AUDIO";
pfc::string8 m_file,m_flags;
unsigned m_track_number;
bool isTrackAudio() const;
t_cuesheet_index_list m_index_list;
void set_simple_index(double p_time);

View File

@@ -6,7 +6,6 @@
namespace {
PFC_DECLARE_EXCEPTION(exception_cue,pfc::exception,"Invalid cuesheet");
PFC_DECLARE_EXCEPTION(exception_cue_tracktype, exception_cue, "Not an audio track")
}
static bool is_numeric(char c) {return c>='0' && c<='9';}
@@ -32,9 +31,7 @@ static void validate_file_type(const char * p_type,t_size p_type_length) {
stricmp_utf8_ex(p_type,p_type_length,"APE",pfc_infinite) != 0 &&
stricmp_utf8_ex(p_type,p_type_length,"FLAC",pfc_infinite) != 0 &&
stricmp_utf8_ex(p_type,p_type_length,"WV",pfc_infinite) != 0 &&
stricmp_utf8_ex(p_type,p_type_length,"WAVPACK",pfc_infinite) != 0 &&
// BINARY
stricmp_utf8_ex(p_type,p_type_length,"BINARY",pfc_infinite) != 0
stricmp_utf8_ex(p_type,p_type_length,"WAVPACK",pfc_infinite) != 0
)
pfc::throw_exception_with_message< exception_cue >(PFC_string_formatter() << "expected WAVE, MP3 or AIFF, got : \"" << pfc::string_part(p_type,p_type_length) << "\"");
}
@@ -137,17 +134,15 @@ namespace {
{
validate_file_type(p_type,p_type_length);
m_file.set_string(p_file,p_file_length);
m_fileType.set_string(p_type, p_type_length);
}
void on_track(unsigned p_index,const char * p_type,t_size p_type_length)
{
finalize_track(); // finalize previous track
m_trackIsAudio = stricmp_utf8_ex(p_type,p_type_length,"audio",pfc_infinite) == 0;
if (stricmp_utf8_ex(p_type,p_type_length,"audio",pfc_infinite)) pfc::throw_exception_with_message<exception_cue>("only tracks of type AUDIO supported");
//if (p_index != m_track + 1) throw exception_cue("cuesheet tracks out of order");
if (m_track != 0) finalize_track();
if (m_file.is_empty()) pfc::throw_exception_with_message<exception_cue>("declaring a track with no file set");
m_trackfile = m_file;
m_trackFileType = m_fileType;
m_track = p_index;
}
@@ -176,40 +171,38 @@ namespace {
void finalize()
{
finalize_track(); // finalize last track
if (m_track != 0)
{
finalize_track();
m_track = 0;
}
}
private:
void finalize_track()
{
if ( m_track != 0 && m_trackIsAudio ) {
if (!m_index1_set) pfc::throw_exception_with_message< exception_cue > ("INDEX 01 not set");
if (!m_index0_set) m_index_list.m_positions[0] = m_index_list.m_positions[1] - m_pregap;
if (!m_index_list.is_valid()) pfc::throw_exception_with_message< exception_cue > ("invalid index list");
cue_parser::t_cue_entry_list::iterator iter;
iter = m_out.insert_last();
if (m_trackfile.is_empty()) pfc::throw_exception_with_message< exception_cue > ("track has no file assigned");
iter->m_file = m_trackfile;
iter->m_fileType = m_trackFileType;
iter->m_track_number = m_track;
iter->m_indexes = m_index_list;
}
if (!m_index1_set) pfc::throw_exception_with_message< exception_cue > ("INDEX 01 not set");
if (!m_index0_set) m_index_list.m_positions[0] = m_index_list.m_positions[1] - m_pregap;
if (!m_index_list.is_valid()) pfc::throw_exception_with_message< exception_cue > ("invalid index list");
cue_parser::t_cue_entry_list::iterator iter;
iter = m_out.insert_last();
if (m_trackfile.is_empty()) pfc::throw_exception_with_message< exception_cue > ("track has no file assigned");
iter->m_file = m_trackfile;
iter->m_track_number = m_track;
iter->m_indexes = m_index_list;
m_index_list.reset();
m_index0_set = false;
m_index1_set = false;
m_pregap = 0;
m_track = 0; m_trackIsAudio = false;
}
bool m_index0_set,m_index1_set;
t_cuesheet_index_list m_index_list;
double m_pregap;
unsigned m_track;
bool m_trackIsAudio = false;
pfc::string8 m_file,m_fileType,m_trackfile,m_trackFileType;
pfc::string8 m_file,m_trackfile;
cue_parser::t_cue_entry_list & m_out;
};
@@ -225,7 +218,7 @@ namespace {
if (p_index == 0) pfc::throw_exception_with_message< exception_cue > ("invalid TRACK index");
if (p_index == m_wanted_track)
{
if (stricmp_utf8_ex(p_type,p_type_length,"audio",pfc_infinite)) throw exception_cue_tracktype();
if (stricmp_utf8_ex(p_type,p_type_length,"audio",pfc_infinite)) pfc::throw_exception_with_message< exception_cue > ("only tracks of type AUDIO supported");
}
m_track = p_index;
m_totaltracks++;
@@ -387,17 +380,6 @@ namespace {
};
static pfc::string_part_ref cue_line_argument( const char * base, size_t length ) {
const char * end = base + length;
while(base < end && is_spacing(base[0]) ) ++base;
while(base < end && is_spacing(end[-1]) ) --end;
if ( base + 1 < end ) {
if ( base[0] == '\"' && end[-1] == '\"' ) {
++base; --end;
}
}
return pfc::string_part(base, end-base);
}
static void g_parse_cue_line(const char * p_line,t_size p_line_length,cue_parser_callback & p_callback)
{
@@ -518,18 +500,66 @@ static void g_parse_cue_line(const char * p_line,t_size p_line_length,cue_parser
}
else if (!stricmp_utf8_ex(p_line,ptr,"title",pfc_infinite))
{
auto arg = cue_line_argument(p_line+ptr, p_line_length-ptr);
if ( arg.m_len > 0 ) p_callback.on_title( arg.m_ptr, arg.m_len );
while(ptr < p_line_length && is_spacing(p_line[ptr])) ptr++;
if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid TITLE syntax");
if (p_line[ptr] == '\"')
{
ptr++;
t_size base = ptr;
while(ptr < p_line_length && p_line[ptr] != '\"') ptr++;
if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid TITLE syntax");
t_size length = ptr-base;
ptr++;
while(ptr < p_line_length && is_spacing(p_line[ptr])) ptr++;
if (ptr != p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid TITLE syntax");
p_callback.on_title(p_line+base,length);
}
else
{
p_callback.on_title(p_line+ptr,p_line_length-ptr);
}
}
else if (!stricmp_utf8_ex(p_line,ptr,"performer",pfc_infinite))
{
auto arg = cue_line_argument(p_line + ptr, p_line_length - ptr);
if (arg.m_len > 0) p_callback.on_performer(arg.m_ptr, arg.m_len);
while(ptr < p_line_length && is_spacing(p_line[ptr])) ptr++;
if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid PERFORMER syntax");
if (p_line[ptr] == '\"')
{
ptr++;
t_size base = ptr;
while(ptr < p_line_length && p_line[ptr] != '\"') ptr++;
if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid PERFORMER syntax");
t_size length = ptr-base;
ptr++;
while(ptr < p_line_length && is_spacing(p_line[ptr])) ptr++;
if (ptr != p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid PERFORMER syntax");
p_callback.on_performer(p_line+base,length);
}
else
{
p_callback.on_performer(p_line+ptr,p_line_length-ptr);
}
}
else if (!stricmp_utf8_ex(p_line,ptr,"songwriter",pfc_infinite))
{
auto arg = cue_line_argument(p_line + ptr, p_line_length - ptr);
if (arg.m_len > 0) p_callback.on_songwriter(arg.m_ptr, arg.m_len);
while(ptr < p_line_length && is_spacing(p_line[ptr])) ptr++;
if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid SONGWRITER syntax");
if (p_line[ptr] == '\"')
{
ptr++;
t_size base = ptr;
while(ptr < p_line_length && p_line[ptr] != '\"') ptr++;
if (ptr == p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid SONGWRITER syntax");
t_size length = ptr-base;
ptr++;
while(ptr < p_line_length && is_spacing(p_line[ptr])) ptr++;
if (ptr != p_line_length) pfc::throw_exception_with_message< exception_cue > ("invalid SONGWRITER syntax");
p_callback.on_songwriter(p_line+base,length);
}
else
{
p_callback.on_songwriter(p_line+ptr,p_line_length-ptr);
}
}
else if (!stricmp_utf8_ex(p_line,ptr,"isrc",pfc_infinite))
{
@@ -640,20 +670,15 @@ namespace {
void on_file(const char * p_file,t_size p_file_length,const char * p_type,t_size p_type_length) {
validate_file_type(p_type,p_type_length);
m_file.set_string(p_file,p_file_length);
m_fileType.set_string(p_type, p_type_length);
}
void on_track(unsigned p_index,const char * p_type,t_size p_type_length)
{
finalize_track();
m_trackType.set_string( p_type, p_type_length );
if (stricmp_utf8_ex(p_type,p_type_length,"audio",pfc_infinite)) pfc::throw_exception_with_message< exception_cue > ("only tracks of type AUDIO supported");
//if (p_index != m_track + 1) throw exception_cue("cuesheet tracks out of order",0);
if (m_track != 0) finalize_track();
if (m_file.is_empty()) pfc::throw_exception_with_message< exception_cue > ("declaring a track with no file set");
m_trackfile = m_file;
m_trackFileType = m_fileType;
m_track = p_index;
}
@@ -682,7 +707,11 @@ namespace {
void on_comment(const char * p_comment,t_size p_comment_length) {}
void finalize()
{
finalize_track();
if (m_track != 0)
{
finalize_track();
m_track = 0;
}
}
void on_flags(const char * p_flags,t_size p_flags_length) {
m_flags.set_string(p_flags,p_flags_length);
@@ -690,34 +719,28 @@ namespace {
private:
void finalize_track()
{
if ( m_track != 0 ) {
if (m_track < 1 || m_track > maximumCueTrackNumber) pfc::throw_exception_with_message< exception_cue > ("track number out of range");
if (!m_index1_set) pfc::throw_exception_with_message< exception_cue > ("INDEX 01 not set");
if (!m_index0_set) m_indexes.m_positions[0] = m_indexes.m_positions[1] - m_pregap;
if (!m_indexes.is_valid()) pfc::throw_exception_with_message< exception_cue > ("invalid index list");
if (m_track < 1 || m_track > maximumCueTrackNumber) pfc::throw_exception_with_message< exception_cue > ("track number out of range");
if (!m_index1_set) pfc::throw_exception_with_message< exception_cue > ("INDEX 01 not set");
if (!m_index0_set) m_indexes.m_positions[0] = m_indexes.m_positions[1] - m_pregap;
if (!m_indexes.is_valid()) pfc::throw_exception_with_message< exception_cue > ("invalid index list");
cue_creator::t_entry_list::iterator iter;
iter = m_out.insert_last();
iter->m_track_number = m_track;
iter->m_file = m_trackfile;
iter->m_fileType = m_trackFileType;
iter->m_index_list = m_indexes;
iter->m_flags = m_flags;
iter->m_trackType = m_trackType;
}
cue_creator::t_entry_list::iterator iter;
iter = m_out.insert_last();
iter->m_track_number = m_track;
iter->m_file = m_trackfile;
iter->m_index_list = m_indexes;
iter->m_flags = m_flags;
m_pregap = 0;
m_indexes.reset();
m_index0_set = m_index1_set = false;
m_flags.reset();
m_trackType.reset();
}
bool m_index0_set,m_index1_set;
double m_pregap;
unsigned m_track;
bool m_trackIsAudio = false;
cue_creator::t_entry_list & m_out;
pfc::string8 m_file, m_fileType,m_trackfile, m_trackFileType, m_flags, m_trackType;
pfc::string8 m_file,m_trackfile,m_flags;
t_cuesheet_index_list m_indexes;
};
}
@@ -732,12 +755,11 @@ void cue_parser::parse_full(const char * p_cuesheet,cue_creator::t_entry_list &
{
cue_creator::t_entry_list::iterator iter;
for(iter=p_out.first();iter.is_valid();++iter) {
if ( iter->isTrackAudio() ) {
cue_parser_callback_retrieveinfo callback(iter->m_infos,iter->m_track_number);
g_parse_cue(p_cuesheet,callback);
callback.finalize();
}
for(iter=p_out.first();iter.is_valid();++iter)
{
cue_parser_callback_retrieveinfo callback(iter->m_infos,iter->m_track_number);
g_parse_cue(p_cuesheet,callback);
callback.finalize();
}
}
} catch(exception_cue const & e) {

View File

@@ -62,10 +62,9 @@ namespace file_info_record_helper {
namespace cue_parser
{
struct cue_entry {
pfc::string8 m_file, m_fileType;
pfc::string8 m_file;
unsigned m_track_number;
t_cuesheet_index_list m_indexes;
bool isFileBinary() const {return pfc::stringEqualsI_ascii(m_fileType, "BINARY");}
};
typedef pfc::chain_list_v2_t<cue_entry> t_cue_entry_list;
@@ -114,29 +113,22 @@ namespace cue_parser
public:
typedef input_info_writer_v2 interface_info_writer_t; // remove_tags supplied
void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
m_remote = filesystem::g_is_recognized_and_remote( p_path );
if (m_remote && p_reason == input_open_info_write) throw exception_io_object_is_remote();
m_impl.open( p_filehint, p_path, p_reason, p_abort );
if (!m_remote) {
file_info_impl info;
m_impl.get_info(info, p_abort);
m_meta.set_tag(info);
}
file_info_impl info;
m_impl.get_info(info,p_abort);
m_meta.set_tag(info);
}
t_uint32 get_subsong_count() {
return this->expose_cuesheet() ? (uint32_t) m_meta.get_cue_track_count() : 1;
return m_meta.have_cuesheet() ? (uint32_t) m_meta.get_cue_track_count() : 1;
}
t_uint32 get_subsong(t_uint32 p_index) {
return this->expose_cuesheet() ? m_meta.remap_trackno(p_index) : 0;
return m_meta.have_cuesheet() ? m_meta.remap_trackno(p_index) : 0;
}
void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort) {
if (m_remote) {
PFC_ASSERT(p_subsong == 0);
m_impl.get_info(p_info, p_abort);
} else if (p_subsong == 0) {
if (p_subsong == 0) {
m_meta.get_tag(p_info);
} else {
m_meta.get_track_info(p_subsong,p_info);
@@ -151,7 +143,7 @@ namespace cue_parser
m_decodeFrom = 0; m_decodeLength = -1; m_decodePos = 0;
} else {
double start, length;
_query_track_offsets(p_subsong,start,length);
m_meta.query_track_offsets(p_subsong,start,length);
unsigned flags2 = p_flags;
if (start > 0) flags2 &= ~input_flag_no_seeking;
flags2 &= ~input_flag_allow_inaccurate_seeking;
@@ -200,7 +192,6 @@ namespace cue_parser
}
void remove_tags(abort_callback & abort) {
PFC_ASSERT(!m_remote);
m_impl.remove_tags( abort );
file_info_impl info;
m_impl.get_info(info, abort);
@@ -208,7 +199,6 @@ namespace cue_parser
}
void retag_set_info(t_uint32 p_subsong,const file_info & p_info,abort_callback & p_abort) {
PFC_ASSERT(!m_remote);
if (p_subsong == 0) {
m_meta.set_tag(p_info);
} else {
@@ -217,7 +207,6 @@ namespace cue_parser
}
void retag_commit(abort_callback & p_abort) {
PFC_ASSERT(!m_remote);
file_info_impl info;
m_meta.get_tag(info);
m_impl.retag(pfc::implicit_cast<const file_info&>(info), p_abort);
@@ -225,12 +214,6 @@ namespace cue_parser
m_impl.get_info(info, p_abort);
m_meta.set_tag( info );
}
void _query_track_offsets(unsigned p_subsong, double& start, double& length) const {
m_meta.query_track_offsets(p_subsong,start,length);
}
bool expose_cuesheet() const {
return !m_remote && m_meta.have_cuesheet();
}
private:
bool _run(audio_chunk & chunk, mem_block_container * raw, abort_callback & aborter) {
if (m_decodeLength >= 0 && m_decodePos >= m_decodeLength) return false;
@@ -265,7 +248,7 @@ namespace cue_parser
}
t_base m_impl;
double m_decodeFrom, m_decodeLength, m_decodePos;
bool m_remote = false;
embeddedcue_metadata_manager m_meta;
};
#ifdef FOOBAR2000_HAVE_CHAPTERIZER
@@ -325,13 +308,6 @@ namespace cue_parser
instance.open(0,p_path,input_open_info_read,p_abort);
const t_uint32 total = instance.get_subsong_count();
if (instance.expose_cuesheet()) {
double start, len;
instance._query_track_offsets(1, start, len);
p_list.set_pregap( start );
}
p_list.set_chapter_count(total);
for(t_uint32 walk = 0; walk < total; ++walk) {
file_info_impl info;

View File

@@ -26,7 +26,6 @@ namespace {
void operator() (unsigned p_trackno,const track_record & p_record) {
if (p_trackno > 0) {
cue_creator::t_entry_list::iterator iter = m_entries.insert_last();
iter->m_trackType = "AUDIO";
iter->m_file = p_record.m_file;
iter->m_flags = p_record.m_flags;
iter->m_index_list = p_record.m_index_list;
@@ -124,18 +123,9 @@ namespace {
};
};
static bool meta_value_equals(const char* v1, const char* v2, bool asNumber) {
if (asNumber) {
// Special fix: leading zeros on track numbers
while( *v1 == '0' ) ++ v1;
while( *v2 == '0' ) ++ v2;
}
return strcmp(v1,v2) == 0;
}
static void strip_redundant_track_meta(unsigned p_tracknumber,const file_info & p_cueinfo,file_info_record::t_meta_map & p_meta,const char * p_metaname, bool asNumber) {
const size_t metaindex = p_cueinfo.meta_find(p_metaname);
if (metaindex == SIZE_MAX) return;
static void strip_redundant_track_meta(unsigned p_tracknumber,const file_info & p_cueinfo,file_info_record::t_meta_map & p_meta,const char * p_metaname) {
t_size metaindex = p_cueinfo.meta_find(p_metaname);
if (metaindex == ~0) return;
pfc::string_formatter namelocal;
build_cue_meta_name(p_metaname,p_tracknumber,namelocal);
{
@@ -144,8 +134,7 @@ static void strip_redundant_track_meta(unsigned p_tracknumber,const file_info &
file_info_record::t_meta_value::const_iterator iter = val->first();
for(t_size valwalk = 0, valcount = p_cueinfo.meta_enum_value_count(metaindex); valwalk < valcount; ++valwalk) {
if (iter.is_empty()) return;
if (!meta_value_equals(*iter,p_cueinfo.meta_enum_value(metaindex,valwalk), asNumber)) return;
if (strcmp(*iter,p_cueinfo.meta_enum_value(metaindex,valwalk)) != 0) return;
++iter;
}
if (!iter.is_empty()) return;
@@ -192,8 +181,8 @@ void embeddedcue_metadata_manager::get_tag(file_info & p_info) const {
//strip redundant titles and tracknumbers that the cuesheet already contains
for(cue_creator::t_entry_list::const_iterator iter = entries.first(); iter.is_valid(); ++iter) {
strip_redundant_track_meta(iter->m_track_number,iter->m_infos,output.m_meta,"tracknumber", true);
strip_redundant_track_meta(iter->m_track_number,iter->m_infos,output.m_meta,"title", false);
strip_redundant_track_meta(iter->m_track_number,iter->m_infos,output.m_meta,"tracknumber");
strip_redundant_track_meta(iter->m_track_number,iter->m_infos,output.m_meta,"title");
}

View File

@@ -3,13 +3,13 @@
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include "dialog_resize_helper.h"
static BOOL GetChildWindowRect(HWND wnd, UINT id, RECT* child)
BOOL GetChildWindowRect(HWND wnd,UINT id,RECT* child)
{
RECT temp;
HWND wndChild = GetDlgItem(wnd, id);
if (wndChild == NULL) return FALSE;
if (!GetWindowRect(wndChild, &temp)) return FALSE;
if (!MapWindowPoints(0, wnd, (POINT*)&temp, 2)) return FALSE;
if (!GetWindowRect(wndChild,&temp)) return FALSE;
if (!MapWindowPoints(0,wnd,(POINT*)&temp,2)) return FALSE;
*child = temp;
return TRUE;
}

View File

@@ -2,11 +2,9 @@
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include <libPPUI/CDialogResizeHelperCompat.h>
BOOL GetChildWindowRect(HWND wnd,UINT id,RECT* child);
// Legacy class referenced by old code
// Do not use in new code, use libPPUI instead
class dialog_resize_helper : public CDialogResizeHelperCompat
class dialog_resize_helper
{
pfc::array_t<RECT> rects;
RECT orig_client;
@@ -14,6 +12,12 @@ class dialog_resize_helper : public CDialogResizeHelperCompat
HWND sizegrip;
unsigned min_x,min_y,max_x,max_y;
public:
struct param {
unsigned short id;
unsigned short flags;
};
private:
pfc::array_t<param> m_table;
void set_parent(HWND wnd);
@@ -24,6 +28,11 @@ public:
inline void set_max_size(unsigned x,unsigned y) {max_x = x; max_y = y;}
void add_sizegrip();
enum {
X_MOVE = 1, X_SIZE = 2, Y_MOVE = 4, Y_SIZE = 8,
XY_MOVE = X_MOVE|Y_MOVE, XY_SIZE = X_SIZE|Y_SIZE,
X_MOVE_Y_SIZE = X_MOVE|Y_SIZE, X_SIZE_Y_MOVE = X_SIZE|Y_MOVE,
};
//the old way
bool process_message(HWND wnd,UINT msg,WPARAM wp,LPARAM lp);

View File

@@ -1,8 +1,5 @@
#pragma once
#include <SDK/dsp.h>
#include <pfc/map.h>
//! Duration counter class - accumulates duration using sample values, without any kind of rounding error accumulation.
class duration_counter {
public:
@@ -40,14 +37,6 @@ public:
void add(const audio_chunk & c) {
add(c.get_sample_count(), c.get_sample_rate());
}
#ifdef FOOBAR2000_HAVE_DSP
void add(dsp_chunk_list const & c) {
const size_t num = c.get_count();
for (size_t walk = 0; walk < num; ++walk) {
add(*c.get_item(walk));
}
}
#endif
void add(t_uint64 sampleCount, t_uint32 sampleRate) {
PFC_ASSERT(sampleRate > 0);
if (sampleRate > 0 && sampleCount > 0) {

View File

@@ -61,11 +61,8 @@ namespace file_list_helper
_add(temp);
}
file_list_remove_duplicates(m_data);
}
file_list_from_metadb_handle_list::file_list_from_metadb_handle_list(metadb_handle_list_cref lst, bool bDisplayPaths) {
if ( bDisplayPaths ) init_from_list_display(lst);
else init_from_list( lst );
}
t_size file_list_from_metadb_handle_list::get_count() const {return m_data.get_count();}

View File

@@ -5,8 +5,6 @@ namespace file_list_helper
//list guaranteed to be sorted by metadb::path_compare
class file_list_from_metadb_handle_list : public pfc::list_base_const_t<const char*> {
public:
file_list_from_metadb_handle_list() {}
file_list_from_metadb_handle_list( metadb_handle_list_cref lst, bool bDisplayPaths = false );
static t_size g_get_count(const list_base_const_t<metadb_handle_ptr> & p_list, t_size max = ~0);

View File

@@ -1,4 +0,0 @@
#pragma once
// fb2k mobile compat
#include "../SDK/filesystem_helper.h"

View File

@@ -74,7 +74,7 @@ namespace file_win32_helpers {
}
void writeOverlapped(HANDLE handle, HANDLE myEvent, t_filesize & position, const void * in, size_t inBytes, abort_callback & abort) {
enum {writeMAX = 16*1024*1024};
const enum {writeMAX = 16*1024*1024};
size_t done = 0;
while(done < inBytes) {
size_t delta = inBytes - done;
@@ -85,7 +85,7 @@ namespace file_win32_helpers {
}
}
void writeStreamOverlapped(HANDLE handle, HANDLE myEvent, const void * in, size_t inBytes, abort_callback & abort) {
enum {writeMAX = 16*1024*1024};
const enum {writeMAX = 16*1024*1024};
size_t done = 0;
while(done < inBytes) {
size_t delta = inBytes - done;
@@ -140,7 +140,7 @@ namespace file_win32_helpers {
throw exception_aborted();
}
size_t readOverlapped(HANDLE handle, HANDLE myEvent, t_filesize & position, void * out, size_t outBytes, abort_callback & abort) {
enum {readMAX = 16*1024*1024};
const enum {readMAX = 16*1024*1024};
size_t done = 0;
while(done < outBytes) {
size_t delta = outBytes - done;
@@ -154,7 +154,7 @@ namespace file_win32_helpers {
}
size_t readStreamOverlapped(HANDLE handle, HANDLE myEvent, void * out, size_t outBytes, abort_callback & abort) {
enum {readMAX = 16*1024*1024};
const enum {readMAX = 16*1024*1024};
size_t done = 0;
while(done < outBytes) {
size_t delta = outBytes - done;
@@ -237,44 +237,6 @@ namespace file_win32_helpers {
#endif
}
size_t lowLevelIO(HANDLE hFile, const GUID & guid, size_t arg1, void * arg2, size_t arg2size, bool canWrite, abort_callback & abort) {
if ( guid == file_lowLevelIO::guid_flushFileBuffers ) {
if (!canWrite) {
PFC_ASSERT(!"File opened for reading, not writing");
throw exception_io_denied();
}
WIN32_IO_OP( ::FlushFileBuffers(hFile) );
return 1;
} else if ( guid == file_lowLevelIO::guid_getFileTimes ) {
if ( arg2size == sizeof(file_lowLevelIO::filetimes_t) ) {
if (canWrite) WIN32_IO_OP(::FlushFileBuffers(hFile));
auto ft = reinterpret_cast<file_lowLevelIO::filetimes_t *>(arg2);
static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity");
WIN32_IO_OP( GetFileTime( hFile, (FILETIME*)&ft->creation, (FILETIME*)&ft->lastAccess, (FILETIME*)&ft->lastWrite) );
return 1;
}
} else if ( guid == file_lowLevelIO::guid_setFileTimes ) {
if (arg2size == sizeof(file_lowLevelIO::filetimes_t)) {
if (!canWrite) {
PFC_ASSERT(!"File opened for reading, not writing");
throw exception_io_denied();
}
WIN32_IO_OP(::FlushFileBuffers(hFile));
auto ft = reinterpret_cast<file_lowLevelIO::filetimes_t *>(arg2);
static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity");
const FILETIME * pCreation = nullptr;
const FILETIME * pLastAccess = nullptr;
const FILETIME * pLastWrite = nullptr;
if ( ft->creation != filetimestamp_invalid ) pCreation = (const FILETIME*)&ft->creation;
if ( ft->lastAccess != filetimestamp_invalid ) pLastAccess = (const FILETIME*)&ft->lastAccess;
if ( ft->lastWrite != filetimestamp_invalid ) pLastWrite = (const FILETIME*)&ft->lastWrite;
WIN32_IO_OP( SetFileTime(hFile, pCreation, pLastAccess, pLastWrite) );
return 1;
}
}
return 0;
}
}
#endif // _WIN32

View File

@@ -1,7 +1,5 @@
#pragma once
#include <libPPUI/win32_op.h>
#ifdef _WIN32
namespace file_win32_helpers {
t_filesize get_size(HANDLE p_handle);
@@ -14,11 +12,10 @@ namespace file_win32_helpers {
size_t readOverlapped(HANDLE handle, HANDLE myEvent, t_filesize & position, void * out, size_t outBytes, abort_callback & abort);
size_t readStreamOverlapped(HANDLE handle, HANDLE myEvent, void * out, size_t outBytes, abort_callback & abort);
HANDLE createFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, abort_callback & abort);
size_t lowLevelIO(HANDLE hFile, const GUID & guid, size_t arg1, void * arg2, size_t arg2size, bool canWrite, abort_callback & abort);
};
template<bool p_seekable,bool p_writeable>
class file_win32_wrapper_t : public service_multi_inherit<file, file_lowLevelIO> {
class file_win32_wrapper_t : public file {
public:
file_win32_wrapper_t(HANDLE p_handle) : m_handle(p_handle), m_position(0)
{
@@ -142,7 +139,7 @@ public:
t_filetimestamp get_timestamp(abort_callback & p_abort) {
p_abort.check_e();
if (p_writeable) FlushFileBuffers(m_handle);
FlushFileBuffers(m_handle);
SetLastError(ERROR_SUCCESS);
t_filetimestamp temp;
if (!GetFileTime(m_handle,0,0,(FILETIME*)&temp)) exception_io_from_win32(GetLastError());
@@ -151,17 +148,13 @@ public:
bool is_remote() {return false;}
~file_win32_wrapper_t() {CloseHandle(m_handle);}
size_t lowLevelIO(const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort) override {
return file_win32_helpers::lowLevelIO(m_handle, guid, arg1, arg2, arg2size, p_writeable, abort);
}
protected:
HANDLE m_handle;
t_filesize m_position;
};
template<bool p_writeable>
class file_win32_wrapper_overlapped_t : public service_multi_inherit< file, file_lowLevelIO > {
class file_win32_wrapper_overlapped_t : public file {
public:
file_win32_wrapper_overlapped_t(HANDLE file) : m_handle(file), m_position() {
WIN32_OP( (m_event = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL );
@@ -215,7 +208,7 @@ public:
t_filetimestamp get_timestamp(abort_callback & p_abort) {
p_abort.check_e();
if (p_writeable) FlushFileBuffers(m_handle);
FlushFileBuffers(m_handle);
SetLastError(ERROR_SUCCESS);
t_filetimestamp temp;
if (!GetFileTime(m_handle,0,0,(FILETIME*)&temp)) exception_io_from_win32(GetLastError());
@@ -243,10 +236,6 @@ public:
return new service_impl_t<file_win32_wrapper_overlapped_t<p_writeable> >(p_handle);
}
size_t lowLevelIO(const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort) override {
return file_win32_helpers::lowLevelIO(m_handle, guid, arg1, arg2, arg2size, p_writeable, abort);
}
protected:
HANDLE m_event, m_handle;
t_filesize m_position;

View File

@@ -1,16 +0,0 @@
#pragma once
#include "../SDK/foobar2000-winver.h"
#define _SECURE_ATL 1
#include "../SDK/foobar2000.h"
#include <atlbase.h>
#include <atltypes.h>
#include <atlstr.h>
#include <atlapp.h>
#include <atlctrls.h>
#include <atlwin.h>
#include <atlcom.h>
#include <atlcrack.h>

View File

@@ -13,20 +13,21 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{EE47764E-A202-4F85-A767-ABDAB4AFF35F}</ProjectGuid>
<RootNamespace>foobar2000_sdk_helpers</RootNamespace>
<WindowsTargetPlatformVersion>7.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v141_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
<PlatformToolset>v141_xp</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@@ -44,6 +45,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MinSpace</Optimization>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_WIN32_WINNT=0x501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<BufferSecurityCheck>false</BufferSecurityCheck>
<FloatingPointModel>Fast</FloatingPointModel>
@@ -54,12 +56,12 @@
<SuppressStartupBanner>true</SuppressStartupBanner>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
<AdditionalOptions>/d2notypeopt %(AdditionalOptions)</AdditionalOptions>
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<OmitFramePointers>true</OmitFramePointers>
<PreprocessorDefinitions>NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -72,6 +74,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_WIN32_WINNT=0x501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
@@ -82,6 +85,7 @@
<AdditionalIncludeDirectories>../..</AdditionalIncludeDirectories>
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -92,12 +96,12 @@
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AutoComplete.cpp" />
<ClCompile Include="clipboard.cpp" />
<ClCompile Include="CPowerRequest.cpp" />
<ClCompile Include="create_directory_helper.cpp">
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
</ClCompile>
<ClCompile Include="CTableEditHelper-Legacy.cpp" />
<ClCompile Include="cue_creator.cpp" />
<ClCompile Include="cue_parser.cpp">
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
@@ -128,15 +132,18 @@
<ClCompile Include="file_move_helper.cpp" />
<ClCompile Include="filetimetools.cpp" />
<ClCompile Include="file_win32_wrapper.cpp" />
<ClCompile Include="image_load_save.cpp" />
<ClCompile Include="inplace_edit.cpp" />
<ClCompile Include="IDataObjectUtils.cpp" />
<ClCompile Include="input_helpers.cpp" />
<ClCompile Include="input_helper_cue.cpp" />
<ClCompile Include="listview_helper.cpp">
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
</ClCompile>
<ClCompile Include="mp3_utils.cpp" />
<ClCompile Include="packet_decoder_aac_common.cpp" />
<ClCompile Include="packet_decoder_mp3_common.cpp" />
<ClCompile Include="readers.cpp" />
<ClCompile Include="seekabilizer.cpp" />
<ClCompile Include="SmartStrStr.cpp" />
<ClCompile Include="StdAfx.cpp">
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
@@ -151,12 +158,8 @@
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
</ClCompile>
<ClCompile Include="text_file_loader_v2.cpp" />
<ClCompile Include="ThreadUtils.cpp" />
<ClCompile Include="track_property_callback_impl.cpp" />
<ClCompile Include="ui_element_helpers.cpp" />
<ClCompile Include="VisUtils.cpp" />
<ClCompile Include="VolumeMap.cpp" />
<ClCompile Include="win32_dialog.cpp">
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Disabled</Optimization>
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
@@ -169,27 +172,21 @@
<ClCompile Include="writer_wav.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="advconfig_impl.h" />
<ClInclude Include="advconfig_runtime.h" />
<ClInclude Include="album_art_helpers.h" />
<ClInclude Include="atl-misc.h" />
<ClInclude Include="AutoComplete.h" />
<ClInclude Include="BumpableElem.h" />
<ClInclude Include="CDialogResizeHelper.h" />
<ClInclude Include="CmdThread.h" />
<ClInclude Include="CPowerRequest.h" />
<ClInclude Include="CPropVariant.h" />
<ClInclude Include="CSingleThreadWrapper.h" />
<ClInclude Include="CTableEditHelper-Legacy.h" />
<ClInclude Include="duration_counter.h" />
<ClInclude Include="fb2k_threads.h" />
<ClInclude Include="fb2k_wfx.h" />
<ClInclude Include="fileReadAhead.h" />
<ClInclude Include="file_readonly.h" />
<ClInclude Include="foobar2000+atl.h" />
<ClInclude Include="fullFileBuffer.h" />
<ClInclude Include="bitreader_helper.h" />
<ClInclude Include="CallForwarder.h" />
<ClInclude Include="cfg_guidlist.h" />
<ClInclude Include="clipboard.h" />
<ClInclude Include="COM_utils.h" />
<ClInclude Include="create_directory_helper.h" />
<ClInclude Include="cue_creator.h" />
@@ -204,16 +201,14 @@
<ClInclude Include="file_move_helper.h" />
<ClInclude Include="file_win32_wrapper.h" />
<ClInclude Include="filetimetools.h" />
<ClInclude Include="gdiplus_helpers.h" />
<ClInclude Include="helpers.h" />
<ClInclude Include="icon_remapping_wildcard.h" />
<ClInclude Include="image_load_save.h" />
<ClInclude Include="inplace_edit.h" />
<ClInclude Include="IDataObjectUtils.h" />
<ClInclude Include="input_fix_seeking.h" />
<ClInclude Include="input_helpers.h" />
<ClInclude Include="input_helper_cue.h" />
<ClInclude Include="input_logging.h" />
<ClInclude Include="input_stream_info_reader.h" />
<ClInclude Include="metadb_handle_set.h" />
<ClInclude Include="listview_helper.h" />
<ClInclude Include="meta_table_builder.h" />
<ClInclude Include="metadb_io_hintlist.h" />
<ClInclude Include="mp3_utils.h" />
@@ -226,22 +221,18 @@
<ClInclude Include="reader_pretend_nonseekable.h" />
<ClInclude Include="rethrow.h" />
<ClInclude Include="seekabilizer.h" />
<ClInclude Include="SmartStrStr.h" />
<ClInclude Include="StdAfx.h" />
<ClInclude Include="stream_buffer_helper.h" />
<ClInclude Include="tag_write_callback_impl.h" />
<ClInclude Include="text_file_loader.h" />
<ClInclude Include="text_file_loader_v2.h" />
<ClInclude Include="ThreadUtils.h" />
<ClInclude Include="track_property_callback_impl.h" />
<ClInclude Include="TypeFind.h" />
<ClInclude Include="ui_element_helpers.h" />
<ClInclude Include="VisUtils.h" />
<ClInclude Include="VolumeMap.h" />
<ClInclude Include="win32_dialog.h" />
<ClInclude Include="win32_misc.h" />
<ClInclude Include="WindowPositionUtils.h" />
<ClInclude Include="win32_op.h" />
<ClInclude Include="window_placement_helper.h" />
<ClInclude Include="winmm-types.h" />
<ClInclude Include="writer_wav.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@@ -11,6 +11,9 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="clipboard.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="create_directory_helper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -47,9 +50,15 @@
<ClCompile Include="filetimetools.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IDataObjectUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="input_helpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="listview_helper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mp3_utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -80,6 +89,9 @@
<ClCompile Include="file_win32_wrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CPowerRequest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="writer_wav.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -89,39 +101,15 @@
<ClCompile Include="readers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SmartStrStr.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="packet_decoder_mp3_common.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="packet_decoder_aac_common.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="inplace_edit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CTableEditHelper-Legacy.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AutoComplete.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ui_element_helpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="track_property_callback_impl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VolumeMap.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="input_helper_cue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="text_file_loader_v2.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="image_load_save.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="bitreader_helper.h">
@@ -133,6 +121,9 @@
<ClInclude Include="cfg_guidlist.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="clipboard.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="COM_utils.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -181,9 +172,15 @@
<ClInclude Include="icon_remapping_wildcard.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IDataObjectUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="input_helpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="listview_helper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="meta_table_builder.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -232,6 +229,9 @@
<ClInclude Include="fullFileBuffer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CPowerRequest.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fb2k_threads.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -247,12 +247,18 @@
<ClInclude Include="album_art_helpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="gdiplus_helpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="advconfig_runtime.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="fb2k_wfx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CPropVariant.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CSingleThreadWrapper.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -268,6 +274,12 @@
<ClInclude Include="fileReadAhead.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="win32_op.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SmartStrStr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="tag_write_callback_impl.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -286,65 +298,5 @@
<ClInclude Include="packet_decoder_aac_common.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inplace_edit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CTableEditHelper-Legacy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="foobar2000+atl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="atl-misc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="AutoComplete.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="BumpableElem.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CDialogResizeHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ui_element_helpers.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WindowPositionUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="input_logging.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="track_property_callback_impl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VolumeMap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CPropVariant.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="input_helper_cue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="advconfig_impl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="metadb_handle_set.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="text_file_loader_v2.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="file_readonly.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="winmm-types.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="image_load_save.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,119 @@
#pragma once
#include <GdiPlus.h>
#include "win32_op.h"
class GdiplusErrorHandler {
public:
void operator<<(Gdiplus::Status p_code) {
if (p_code != Gdiplus::Ok) {
throw pfc::exception(pfc::string_formatter() << "Gdiplus error (" << (unsigned) p_code << ")");
}
}
};
class GdiplusScope {
public:
GdiplusScope() {
Gdiplus::GdiplusStartupInput input;
Gdiplus::GdiplusStartupOutput output;
GdiplusErrorHandler() << Gdiplus::GdiplusStartup(&m_token,&input,&output);
}
~GdiplusScope() {
Gdiplus::GdiplusShutdown(m_token);
}
PFC_CLASS_NOT_COPYABLE_EX(GdiplusScope);
private:
ULONG_PTR m_token;
};
static HBITMAP GdiplusLoadBitmap(UINT id, const TCHAR * resType, CSize size) {
using namespace Gdiplus;
try {
pfc::ptrholder_t<uResource> resource = LoadResourceEx(core_api::get_my_instance(),MAKEINTRESOURCE(id),resType);
if (resource.is_empty()) throw pfc::exception_bug_check();
pfc::com_ptr_t<IStream> stream; stream.attach( SHCreateMemStream((const BYTE*)resource->GetPointer(), resource->GetSize()) );
if ( stream.is_empty() ) throw std::bad_alloc();
GdiplusErrorHandler EH;
pfc::ptrholder_t<Image> source = new Image(stream.get_ptr());
EH << source->GetLastStatus();
pfc::ptrholder_t<Bitmap> resized = new Bitmap(size.cx, size.cy, PixelFormat32bppARGB);
EH << resized->GetLastStatus();
{
pfc::ptrholder_t<Graphics> target = new Graphics(resized.get_ptr());
EH << target->GetLastStatus();
EH << target->SetInterpolationMode(InterpolationModeHighQuality);
EH << target->Clear(Color(0,0,0,0));
EH << target->DrawImage(source.get_ptr(), Rect(0,0,size.cx,size.cy));
}
HBITMAP bmp = NULL;
EH << resized->GetHBITMAP(Gdiplus::Color::White, & bmp );
return bmp;
} catch(...) {
PFC_ASSERT( !"Should not get here");
return NULL;
}
}
static HICON GdiplusLoadIcon(UINT id, const TCHAR * resType, CSize size) {
using namespace Gdiplus;
try {
pfc::ptrholder_t<uResource> resource = LoadResourceEx(core_api::get_my_instance(),MAKEINTRESOURCE(id),resType);
if (resource.is_empty()) throw pfc::exception_bug_check();
pfc::com_ptr_t<IStream> stream; stream.attach( SHCreateMemStream((const BYTE*)resource->GetPointer(), resource->GetSize()) );
if (stream.is_empty()) throw std::bad_alloc();
GdiplusErrorHandler EH;
pfc::ptrholder_t<Image> source = new Image(stream.get_ptr());
EH << source->GetLastStatus();
pfc::ptrholder_t<Bitmap> resized = new Bitmap(size.cx, size.cy, PixelFormat32bppARGB);
EH << resized->GetLastStatus();
{
pfc::ptrholder_t<Graphics> target = new Graphics(resized.get_ptr());
EH << target->GetLastStatus();
EH << target->SetInterpolationMode(InterpolationModeHighQuality);
EH << target->Clear(Color(0,0,0,0));
EH << target->DrawImage(source.get_ptr(), Rect(0,0,size.cx,size.cy));
}
HICON icon = NULL;
EH << resized->GetHICON(&icon);
return icon;
} catch(...) {
PFC_ASSERT( !"Should not get here");
return NULL;
}
}
static HICON GdiplusLoadPNGIcon(UINT id, CSize size) {return GdiplusLoadIcon(id, _T("PNG"), size);}
static HICON LoadPNGIcon(UINT id, CSize size) {
GdiplusScope scope;
return GdiplusLoadPNGIcon(id, size);
}
static void GdiplusLoadButtonPNG(CIcon & icon, HWND btn_, UINT image) {
CButton btn(btn_);
if (icon == NULL) {
CRect client; WIN32_OP_D( btn.GetClientRect(client) );
CSize size = client.Size();
int v = MulDiv(pfc::min_t<int>(size.cx, size.cy), 3, 4);
if (v < 8) v = 8;
icon = GdiplusLoadPNGIcon(image, CSize(v,v));
}
btn.SetIcon(icon);
}
#pragma comment(lib, "gdiplus.lib")

View File

@@ -13,6 +13,7 @@
#include "cue_parser.h"
#include "text_file_loader.h"
#include "file_list_helper.h"
#include "listview_helper.h"
#include "stream_buffer_helper.h"
#include "file_info_const_impl.h"
#include "dynamic_bitrate_helper.h"
@@ -28,6 +29,8 @@
#include "metadb_io_hintlist.h"
#include "meta_table_builder.h"
#include "icon_remapping_wildcard.h"
#include "clipboard.h"
#include "IDataObjectUtils.h"
#include "CallForwarder.h"
#include "playlist_position_reference_tracker.h"
#include "ThreadUtils.h"
@@ -37,5 +40,7 @@
#include "ProfileCache.h"
#include "file_win32_wrapper.h"
#include "fullFileBuffer.h"
#include "CPowerRequest.h"
#include "writer_wav.h"
#include "TypeFind.h"
#include "readers.h"

View File

@@ -1,88 +0,0 @@
#include "StdAfx.h"
#include "image_load_save.h"
#include <memory>
#include "../SDK/imageLoaderLite.h"
namespace fb2k {
bool imageSaveDialog(album_art_data_ptr content, HWND wndParent, const char* initDir, bool bAsync) {
pfc::string8 fileTypes = "All files|*.*";
pfc::string8 ext;
try {
auto info = fb2k::imageLoaderLite::get()->getInfo(content);
if (info.formatName) {
pfc::string8 nameCapitalized = pfc::stringToUpper( info.formatName );
ext = pfc::stringToLower( info.formatName );
if (nameCapitalized == "WEBP") nameCapitalized = "WebP";
pfc::string8 extmask;
if (ext == "jpeg") {
ext = "jpg";
extmask = "*.jpg;*.jpeg";
} else {
extmask << "*." << ext;
}
fileTypes.reset();
fileTypes << nameCapitalized << " files|" << extmask;
}
} catch (...) {}
pfc::string8 fn;
if (!uGetOpenFileName(wndParent, fileTypes, 0, ext.length() > 0 ? ext.c_str() : nullptr, "Export picture file", initDir, fn, TRUE)) return false;
auto bErrord = std::make_shared<bool>(false);
auto work = [content, fn, bErrord] {
try {
auto f = fileOpenWriteNew(fn, fb2k::noAbort, 0.5);
f->write(content->get_ptr(), content->get_size(), fb2k::noAbort);
} catch(std::exception const & e) {
* bErrord = true;
pfc::string8 msg;
msg << "Image file could not be written: " << e;
fb2k::inMainThread([msg] {
popup_message::g_show(msg, "Information");
});
}
};
if (bAsync) {
fb2k::splitTask(work);
return true;
} else {
work();
return ! *bErrord;
}
}
bool imageLoadDialog(pfc::string_base& outFN, HWND wndParent, const char* initDir) {
return !!uGetOpenFileName(wndParent, FB2K_GETOPENFILENAME_PICTUREFILES_ALL, 0, nullptr, "Import picture file", initDir, outFN, FALSE);
}
album_art_data::ptr imageLoadDialog(HWND wndParent, const char* initDir) {
album_art_data::ptr ret;
pfc::string8 fn;
if (imageLoadDialog(fn, wndParent, initDir)) {
try {
ret = readPictureFile(fn, fb2k::noAbort);
} catch (std::exception const& e) {
popup_message::g_show(PFC_string_formatter() << "Image file could not be read: " << e, "Information");
}
}
return ret;
}
album_art_data_ptr readPictureFile(const char* p_path, abort_callback& p_abort) {
PFC_ASSERT(p_path != nullptr);
PFC_ASSERT(*p_path != 0);
p_abort.check();
// Pointless, not a media file, path often from openfiledialog and not canonical
// file_lock_ptr lock = file_lock_manager::get()->acquire_read(p_path, p_abort);
file_ptr l_file;
filesystem::g_open_timeout(l_file, p_path, filesystem::open_mode_read, 0.5, p_abort);
service_ptr_t<album_art_data_impl> instance = new service_impl_t<album_art_data_impl>();
t_filesize size = l_file->get_size_ex(p_abort);
if (size > 1024 * 1024 * 64) throw std::runtime_error("File too large");
instance->from_stream(l_file.get_ptr(), (t_size)size, p_abort);
return instance;
}
}

View File

@@ -1,13 +0,0 @@
#pragma once
namespace fb2k {
bool imageLoadDialog( pfc::string_base & outFN, HWND wndParent, const char * initDir );
album_art_data::ptr imageLoadDialog( HWND wndParent, const char * initDir );
//! bAllowAsync: run file writing offthread. In such case the caller will not be made aware if writing failed. \n
//! Error popup is shown if actual file writing fails.
bool imageSaveDialog(album_art_data_ptr content, HWND wndParent, const char * initDir , bool bAllowAsync = true );
album_art_data::ptr readPictureFile( const char * path, abort_callback & a);
}

View File

@@ -1,25 +0,0 @@
#include "stdafx.h"
#include "inplace_edit.h"
// Functionality moved to libPPUI
namespace InPlaceEdit {
static reply_t wrapCN( completion_notify::ptr cn ) {
return [cn](unsigned code) { cn->on_completion(code); };
}
HWND Start(HWND p_parentwnd, const RECT & p_rect, bool p_multiline, pfc::rcptr_t<pfc::string_base> p_content, completion_notify_ptr p_notify) {
return Start(p_parentwnd, p_rect, p_multiline, p_content, wrapCN(p_notify) );
}
HWND StartEx(HWND p_parentwnd, const RECT & p_rect, unsigned p_flags, pfc::rcptr_t<pfc::string_base> p_content, completion_notify_ptr p_notify, IUnknown * ACData, DWORD ACOpts ) {
return StartEx(p_parentwnd, p_rect, p_flags, p_content, wrapCN(p_notify), ACData, ACOpts );
}
void Start_FromListView(HWND p_listview, unsigned p_item, unsigned p_subitem, unsigned p_linecount, pfc::rcptr_t<pfc::string_base> p_content, completion_notify_ptr p_notify) {
Start_FromListView(p_listview,p_item, p_subitem, p_linecount, p_content, wrapCN(p_notify) );
}
void Start_FromListViewEx(HWND p_listview, unsigned p_item, unsigned p_subitem, unsigned p_linecount, unsigned p_flags, pfc::rcptr_t<pfc::string_base> p_content, completion_notify_ptr p_notify) {
Start_FromListViewEx(p_listview, p_item, p_subitem, p_linecount, p_flags, p_content, wrapCN(p_notify) );
}
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include <libPPUI/InPlaceEdit.h>
namespace InPlaceEdit {
HWND Start(HWND p_parentwnd,const RECT & p_rect,bool p_multiline,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr p_notify);
HWND StartEx(HWND p_parentwnd,const RECT & p_rect,unsigned p_flags,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr p_notify, IUnknown * ACData = NULL, DWORD ACOpts = 0);
void Start_FromListView(HWND p_listview,unsigned p_item,unsigned p_subitem,unsigned p_linecount,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr p_notify);
void Start_FromListViewEx(HWND p_listview,unsigned p_item,unsigned p_subitem,unsigned p_linecount,unsigned p_flags,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr p_notify);
}

View File

@@ -1,221 +0,0 @@
#include "stdafx.h"
#include "input_helper_cue.h"
#include "../SDK/mem_block_container.h"
namespace {
class input_dec_binary : public input_decoder_v2 {
enum {
m_rate = 44100,
m_bps = 16,
m_channels = 2,
m_channelMask = audio_chunk::channel_config_stereo,
m_sampleBytes = (m_bps/8)*m_channels,
m_readAtOnce = 588,
m_readAtOnceBytes = m_readAtOnce * m_sampleBytes
};
public:
input_dec_binary( file::ptr f ) : m_file(f) {}
t_uint32 get_subsong_count() override {return 0;}
t_uint32 get_subsong(t_uint32 p_index) override {return 0;}
void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort) override {
p_info.reset();
p_info.info_set_int("samplerate", m_rate);
p_info.info_set_int("channels", m_channels);
p_info.info_set_int("bitspersample", m_bps);
p_info.info_set("encoding","lossless");
p_info.info_set_bitrate((m_bps * m_channels * m_rate + 500 /* rounding for bps to kbps*/ ) / 1000 /* bps to kbps */);
p_info.info_set("codec", "PCM");
try {
auto stats = get_file_stats(p_abort);
if ( stats.m_size != filesize_invalid ) {
p_info.set_length( audio_math::samples_to_time( stats.m_size / 4, 44100 ) );
}
} catch(exception_io) {}
}
t_filestats get_file_stats(abort_callback & p_abort) override {
return m_file->get_stats(p_abort);
}
void initialize(t_uint32 p_subsong,unsigned p_flags,abort_callback & p_abort) override {
m_file->reopen( p_abort );
}
bool run(audio_chunk & p_chunk,abort_callback & p_abort) override {
mem_block_container_impl stfu;
return run_raw(p_chunk, stfu, p_abort);
}
bool run_raw(audio_chunk & out, mem_block_container & outRaw, abort_callback & abort) override {
size_t bytes = m_readAtOnceBytes;
outRaw.set_size( bytes );
size_t got = m_file->read(outRaw.get_ptr(), bytes, abort);
got -= got % m_sampleBytes;
if ( got == 0 ) return false;
if ( got < bytes ) outRaw.set_size( got );
out.set_data_fixedpoint_signed( outRaw.get_ptr(), got, m_rate, m_channels, m_bps, m_channelMask);
return true;
}
void seek(double p_seconds,abort_callback & p_abort) override {
m_file->seek( audio_math::time_to_samples( p_seconds, m_rate ) * m_sampleBytes, p_abort );
}
bool can_seek() override {
return m_file->can_seek();
}
bool get_dynamic_info(file_info & p_out, double & p_timestamp_delta) override {return false;}
bool get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) override {return false;}
void on_idle(abort_callback & p_abort) override {}
void set_logger(event_logger::ptr ptr) override {}
private:
const file::ptr m_file;
};
}
void input_helper_cue::get_info_binary( const char * path, file_info & out, abort_callback & abort ) {
auto f = fileOpenReadExisting( path, abort );
auto obj = fb2k::service_new< input_dec_binary > ( f );
obj->get_info( 0, out, abort );
}
void input_helper_cue::open(service_ptr_t<file> p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,double p_start,double p_length, bool binary) {
p_abort.check();
m_start = p_start;
m_position = 0;
m_dynamic_info_trigger = false;
m_dynamic_info_track_trigger = false;
if ( binary ) {
{
const char * path = p_location.get_path();
auto f = fileOpenReadExisting( path, p_abort );
auto obj = fb2k::service_new< input_dec_binary > ( f );
m_input.attach( obj, path );
m_input.open_decoding( 0, p_flags, p_abort );
}
} else {
m_input.open(p_filehint,p_location,p_flags,p_abort,true,true);
}
if (!m_input.can_seek()) throw exception_io_object_not_seekable();
if (m_start > 0) {
m_input.seek(m_start,p_abort);
}
if (p_length > 0) {
m_length = p_length;
} else {
file_info_impl temp;
m_input.get_info(0,temp,p_abort);
double ref_length = temp.get_length();
if (ref_length <= 0) throw exception_io_data();
m_length = ref_length - m_start + p_length /* negative or zero */;
if (m_length <= 0) throw exception_io_data();
}
}
void input_helper_cue::close() {m_input.close();}
bool input_helper_cue::is_open() {return m_input.is_open();}
bool input_helper_cue::_m_input_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
if (p_raw == NULL) {
return m_input.run(p_chunk, p_abort);
} else {
return m_input.run_raw(p_chunk, *p_raw, p_abort);
}
}
bool input_helper_cue::_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
p_abort.check();
if (m_length > 0) {
if (m_position >= m_length) return false;
if (!_m_input_run(p_chunk, p_raw, p_abort)) return false;
m_dynamic_info_trigger = true;
m_dynamic_info_track_trigger = true;
t_uint64 max = (t_uint64)audio_math::time_to_samples(m_length - m_position, p_chunk.get_sample_rate());
if (max == 0)
{//handle rounding accidents, this normally shouldn't trigger
m_position = m_length;
return false;
}
t_size samples = p_chunk.get_sample_count();
if ((t_uint64)samples > max)
{
p_chunk.set_sample_count((unsigned)max);
if (p_raw != NULL) {
const t_size rawSize = p_raw->get_size();
PFC_ASSERT(rawSize % samples == 0);
p_raw->set_size((t_size)((t_uint64)rawSize * max / samples));
}
m_position = m_length;
}
else
{
m_position += p_chunk.get_duration();
}
return true;
}
else
{
if (!_m_input_run(p_chunk, p_raw, p_abort)) return false;
m_position += p_chunk.get_duration();
return true;
}
}
bool input_helper_cue::run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {
return _run(p_chunk, &p_raw, p_abort);
}
bool input_helper_cue::run(audio_chunk & p_chunk, abort_callback & p_abort) {
return _run(p_chunk, NULL, p_abort);
}
void input_helper_cue::seek(double p_seconds, abort_callback & p_abort)
{
m_dynamic_info_trigger = false;
m_dynamic_info_track_trigger = false;
if (m_length <= 0 || p_seconds < m_length) {
m_input.seek(p_seconds + m_start, p_abort);
m_position = p_seconds;
}
else {
m_position = m_length;
}
}
bool input_helper_cue::can_seek() { return true; }
void input_helper_cue::on_idle(abort_callback & p_abort) { m_input.on_idle(p_abort); }
bool input_helper_cue::get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
if (m_dynamic_info_trigger) {
m_dynamic_info_trigger = false;
return m_input.get_dynamic_info(p_out, p_timestamp_delta);
}
else {
return false;
}
}
bool input_helper_cue::get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {
if (m_dynamic_info_track_trigger) {
m_dynamic_info_track_trigger = false;
return m_input.get_dynamic_info_track(p_out, p_timestamp_delta);
}
else {
return false;
}
}
const char * input_helper_cue::get_path() const { return m_input.get_path(); }
void input_helper_cue::get_info(t_uint32 p_subsong, file_info & p_info, abort_callback & p_abort) { m_input.get_info(p_subsong, p_info, p_abort); }

View File

@@ -1,33 +0,0 @@
#pragma once
#include "input_helpers.h"
class input_helper_cue {
public:
void open(service_ptr_t<file> p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,double p_start,double p_length, bool binary = false);
static void get_info_binary( const char * path, file_info & out, abort_callback & abort );
void close();
bool is_open();
bool run(audio_chunk & p_chunk,abort_callback & p_abort);
bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort);
void seek(double seconds,abort_callback & p_abort);
bool can_seek();
void on_idle(abort_callback & p_abort);
bool get_dynamic_info(file_info & p_out,double & p_timestamp_delta);
bool get_dynamic_info_track(file_info & p_out,double & p_timestamp_delta);
void set_logger(event_logger::ptr ptr) {m_input.set_logger(ptr);}
const char * get_path() const;
void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort);
private:
bool _run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort);
bool _m_input_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort);
input_helper m_input;
double m_start,m_length,m_position;
bool m_dynamic_info_trigger,m_dynamic_info_track_trigger;
bool m_binary;
};

View File

@@ -5,7 +5,6 @@
#include "file_list_helper.h"
#include "fileReadAhead.h"
input_helper::ioFilter_t input_helper::ioFilter_full_buffer(t_filesize val ) {
if (val == 0) return nullptr;
return [val] ( file_ptr & f, const char * path, abort_callback & aborter) {
@@ -78,44 +77,34 @@ bool input_helper::need_file_reopen(const char * newPath) const {
bool input_helper::open_path(const char * path, abort_callback & abort, decodeOpen_t const & other) {
abort.check();
m_logger = other.m_logger;
if (!need_file_reopen(path)) {
if ( other.m_logger.is_valid() ) {
input_decoder_v2::ptr v2;
if (m_input->service_query_t(v2)) v2->set_logger(other.m_logger);
}
return false;
}
if (!need_file_reopen(path)) return false;
m_input.release();
service_ptr_t<file> l_file = other.m_hint;
fileOpenTools(l_file, path, other.m_ioFilters, abort);
service_ptr_t<file> l_file = other.m_hint;
fileOpenTools(l_file, path, other.m_ioFilters, abort);
TRACK_CODE("input_entry::g_open_for_decoding",
m_input ^= input_entry::g_open(input_decoder::class_guid, l_file, path, m_logger, abort, other.m_from_redirect );
);
TRACK_CODE("input_entry::g_open_for_decoding",
input_entry::g_open_for_decoding(m_input, l_file, path, abort, other.m_from_redirect)
);
#ifndef FOOBAR2000_MODERN
if (!other.m_skip_hints) {
try {
metadb_io::get()->hint_reader(m_input.get_ptr(), path, abort);
}
catch (exception_io_data) {
//Don't fail to decode when this barfs, might be barfing when reading info from another subsong than the one we're trying to decode etc.
m_input.release();
if (l_file.is_valid()) l_file->reopen(abort);
TRACK_CODE("input_entry::g_open_for_decoding",
m_input ^= input_entry::g_open(input_decoder::class_guid, l_file, path, m_logger, abort, other.m_from_redirect);
);
}
if (!other.m_skip_hints) {
try {
metadb_io::get()->hint_reader(m_input.get_ptr(), path, abort);
}
catch (exception_io_data) {
//Don't fail to decode when this barfs, might be barfing when reading info from another subsong than the one we're trying to decode etc.
m_input.release();
if (l_file.is_valid()) l_file->reopen(abort);
TRACK_CODE("input_entry::g_open_for_decoding",
input_entry::g_open_for_decoding(m_input, l_file, path, abort, other.m_from_redirect)
);
}
}
#endif
if (other.m_shim) m_input = other.m_shim(m_input, path, abort);
m_path = path;
return true;
m_path = path;
return true;
}
void input_helper::open_decoding(t_uint32 subsong, t_uint32 flags, abort_callback & p_abort) {
@@ -125,18 +114,11 @@ void input_helper::open_decoding(t_uint32 subsong, t_uint32 flags, abort_callbac
void input_helper::open(const playable_location & location, abort_callback & abort, decodeOpen_t const & other) {
open_path(location.get_path(), abort, other);
if (other.m_setSampleRate != 0) {
this->extended_param(input_params::set_preferred_sample_rate, other.m_setSampleRate, nullptr, 0);
}
set_logger(other.m_logger);
open_decoding(location.get_subsong(), other.m_flags, abort);
}
void input_helper::attach(input_decoder::ptr dec, const char * path) {
m_input = dec;
m_path = path;
}
void input_helper::open(service_ptr_t<file> p_filehint, const playable_location & p_location, unsigned p_flags, abort_callback & p_abort, bool p_from_redirect, bool p_skip_hints) {
decodeOpen_t o;
o.m_hint = p_filehint;
@@ -167,7 +149,6 @@ bool input_helper::flush_on_pause() {
void input_helper::set_logger(event_logger::ptr ptr) {
m_logger = ptr;
input_decoder_v2::ptr v2;
if (m_input->service_query_t(v2)) v2->set_logger(ptr);
}
@@ -189,11 +170,7 @@ void input_helper::seek(double seconds, abort_callback & p_abort) {
bool input_helper::can_seek() {
return m_input->can_seek();
}
bool input_helper::query_position( double & val ) {
return extended_param(input_params::query_position, 0, &val, sizeof(val) ) != 0;
}
#ifdef FOOBAR2000_MODERN
size_t input_helper::extended_param(const GUID & type, size_t arg1, void * arg2, size_t arg2size) {
input_decoder_v4::ptr v4;
if (v4 &= m_input) {
@@ -201,14 +178,17 @@ size_t input_helper::extended_param(const GUID & type, size_t arg1, void * arg2,
}
return 0;
}
#endif
input_helper::decodeInfo_t input_helper::decode_info() {
decodeInfo_t ret = {};
if (m_input.is_valid()) {
ret.m_can_seek = can_seek();
ret.m_flush_on_pause = flush_on_pause();
#ifdef FOOBAR2000_MODERN
if (ret.m_can_seek) {
ret.m_seeking_expensive = extended_param(input_params::seeking_expensive, 0, nullptr, 0) != 0;
}
#endif
}
return ret;
}
@@ -292,7 +272,7 @@ bool dead_item_filter::run(const pfc::list_base_const_t<metadb_handle_ptr> & p_l
valid_handles.sort_by_pointer();
for(t_size listidx=0;listidx<p_list.get_count();listidx++) {
bool dead = valid_handles.bsearch_by_pointer(p_list[listidx]) == ~0;
if (dead) FB2K_console_formatter() << "Dead item: " << p_list[listidx];
if (dead) console::formatter() << "Dead item: " << p_list[listidx];
p_mask.set(listidx,dead);
}
return !is_aborting();
@@ -320,7 +300,7 @@ bool input_helper::g_mark_dead(const pfc::list_base_const_t<metadb_handle_ptr> &
}
void input_info_read_helper::open(const char * p_path,abort_callback & p_abort) {
if (m_input.is_empty() || playable_location::path_compare(m_path,p_path) != 0)
if (m_input.is_empty() || metadb::path_compare(m_path,p_path) != 0)
{
TRACK_CODE("input_entry::g_open_for_info_read",input_entry::g_open_for_info_read(m_input,0,p_path,p_abort));
@@ -352,6 +332,142 @@ void input_info_read_helper::get_info_check(const playable_location & p_location
void input_helper_cue::open(service_ptr_t<file> p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,double p_start,double p_length) {
p_abort.check();
m_start = p_start;
m_position = 0;
m_dynamic_info_trigger = false;
m_dynamic_info_track_trigger = false;
m_input.open(p_filehint,p_location,p_flags,p_abort,true,true);
if (!m_input.can_seek()) throw exception_io_object_not_seekable();
if (m_start > 0) {
m_input.seek(m_start,p_abort);
}
if (p_length > 0) {
m_length = p_length;
} else {
file_info_impl temp;
m_input.get_info(0,temp,p_abort);
double ref_length = temp.get_length();
if (ref_length <= 0) throw exception_io_data();
m_length = ref_length - m_start + p_length /* negative or zero */;
if (m_length <= 0) throw exception_io_data();
}
}
void input_helper_cue::close() {m_input.close();}
bool input_helper_cue::is_open() {return m_input.is_open();}
bool input_helper_cue::_m_input_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
if (p_raw == NULL) {
return m_input.run(p_chunk, p_abort);
} else {
return m_input.run_raw(p_chunk, *p_raw, p_abort);
}
}
bool input_helper_cue::_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort) {
p_abort.check();
if (m_length > 0) {
if (m_position >= m_length) return false;
if (!_m_input_run(p_chunk, p_raw, p_abort)) return false;
m_dynamic_info_trigger = true;
m_dynamic_info_track_trigger = true;
t_uint64 max = (t_uint64)audio_math::time_to_samples(m_length - m_position, p_chunk.get_sample_rate());
if (max == 0)
{//handle rounding accidents, this normally shouldn't trigger
m_position = m_length;
return false;
}
t_size samples = p_chunk.get_sample_count();
if ((t_uint64)samples > max)
{
p_chunk.set_sample_count((unsigned)max);
if (p_raw != NULL) {
const t_size rawSize = p_raw->get_size();
PFC_ASSERT(rawSize % samples == 0);
p_raw->set_size((t_size)((t_uint64)rawSize * max / samples));
}
m_position = m_length;
}
else
{
m_position += p_chunk.get_duration();
}
return true;
}
else
{
if (!_m_input_run(p_chunk, p_raw, p_abort)) return false;
m_position += p_chunk.get_duration();
return true;
}
}
bool input_helper_cue::run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort) {
return _run(p_chunk, &p_raw, p_abort);
}
bool input_helper_cue::run(audio_chunk & p_chunk, abort_callback & p_abort) {
return _run(p_chunk, NULL, p_abort);
}
void input_helper_cue::seek(double p_seconds, abort_callback & p_abort)
{
m_dynamic_info_trigger = false;
m_dynamic_info_track_trigger = false;
if (m_length <= 0 || p_seconds < m_length) {
m_input.seek(p_seconds + m_start, p_abort);
m_position = p_seconds;
}
else {
m_position = m_length;
}
}
bool input_helper_cue::can_seek() { return true; }
void input_helper_cue::on_idle(abort_callback & p_abort) { m_input.on_idle(p_abort); }
bool input_helper_cue::get_dynamic_info(file_info & p_out, double & p_timestamp_delta) {
if (m_dynamic_info_trigger) {
m_dynamic_info_trigger = false;
return m_input.get_dynamic_info(p_out, p_timestamp_delta);
}
else {
return false;
}
}
bool input_helper_cue::get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) {
if (m_dynamic_info_track_trigger) {
m_dynamic_info_track_trigger = false;
return m_input.get_dynamic_info_track(p_out, p_timestamp_delta);
}
else {
return false;
}
}
const char * input_helper_cue::get_path() const { return m_input.get_path(); }
void input_helper_cue::get_info(t_uint32 p_subsong, file_info & p_info, abort_callback & p_abort) { m_input.get_info(p_subsong, p_info, p_abort); }
// openAudioData code
namespace {
@@ -361,7 +477,11 @@ namespace {
void init(const playable_location & loc, input_helper::decodeOpen_t const & arg, abort_callback & aborter) {
m_length = -1; m_lengthKnown = false;
m_subsong = loc.get_subsong();
m_decoder ^= input_entry::g_open( input_decoder::class_guid, arg.m_hint, loc.get_path(), arg.m_logger, aborter, arg.m_from_redirect);
input_entry::g_open_for_decoding(m_decoder, arg.m_hint, loc.get_path(), aborter, arg.m_from_redirect);
if (arg.m_logger.is_valid()) {
input_decoder_v2::ptr v2;
if ( v2 &= m_decoder ) v2->set_logger(arg.m_logger);
}
m_seekable = ( arg.m_flags & input_flag_no_seeking ) == 0 && m_decoder->can_seek();
reopenDecoder(aborter);
readChunk(aborter, true);
@@ -558,4 +678,4 @@ openAudioData_t openAudioData(playable_location const & loc, bool bSeekable, fil
oad.audioData = f;
oad.audioSpec = f->get_spec();
return oad;
}
}

View File

@@ -7,32 +7,29 @@ class input_helper {
public:
input_helper();
typedef std::function<input_decoder::ptr (input_decoder::ptr, const char*, abort_callback&) > shim_t;
typedef std::function< bool ( file::ptr &, const char *, abort_callback & ) > ioFilter_t;
typedef std::list<ioFilter_t> ioFilters_t;
struct decodeInfo_t {
bool m_flush_on_pause;
bool m_can_seek;
#ifdef FOOBAR2000_MODERN
bool m_seeking_expensive;
#endif
};
struct decodeOpen_t {
bool m_from_redirect = false;
bool m_skip_hints = false;
unsigned m_flags = 0;
file::ptr m_hint;
unsigned m_setSampleRate = 0;
ioFilters_t m_ioFilters;
decodeOpen_t() : m_from_redirect(), m_skip_hints(), m_flags() {}
event_logger::ptr m_logger;
shim_t m_shim;
bool m_from_redirect;
bool m_skip_hints;
unsigned m_flags;
file::ptr m_hint;
ioFilters_t m_ioFilters;
};
void open(service_ptr_t<file> p_filehint,metadb_handle_ptr p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect = false,bool p_skip_hints = false);
void open(service_ptr_t<file> p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,bool p_from_redirect = false,bool p_skip_hints = false);
void attach(input_decoder::ptr dec, const char * path);
void open(const playable_location & location, abort_callback & abort, decodeOpen_t const & other);
void open(metadb_handle_ptr location, abort_callback & abort, decodeOpen_t const & other) {this->open(location->get_location(), abort, other);}
@@ -54,7 +51,9 @@ public:
bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort);
void seek(double seconds,abort_callback & p_abort);
bool can_seek();
#ifdef FOOBAR2000_MODERN
size_t extended_param( const GUID & type, size_t arg1, void * arg2, size_t arg2size);
#endif
static ioFilter_t ioFilter_full_buffer(t_filesize val );
static ioFilter_t ioFilter_block_buffer(size_t val);
static ioFilter_t ioFilter_remote_read_ahead( size_t val );
@@ -67,11 +66,6 @@ public:
void set_pause(bool state);
bool flush_on_pause();
//! If this decoder has its own special position reporting, decoder-signaled logical decoding position will be returned. \n
//! Otherwise, position calculated from returned audio duration should be assumed. \n
//! Very few special-purpose decoders do this.
bool query_position( double & val );
//! Retrieves path of currently open file.
const char * get_path() const;
@@ -88,7 +82,6 @@ private:
void fileOpenTools(service_ptr_t<file> & p_file,const char * p_path, ioFilters_t const & filters, abort_callback & p_abort);
service_ptr_t<input_decoder> m_input;
pfc::string8 m_path;
event_logger::ptr m_logger;
};
class NOVTABLE dead_item_filter : public abort_callback {
@@ -112,6 +105,32 @@ private:
class input_helper_cue {
public:
void open(service_ptr_t<file> p_filehint,const playable_location & p_location,unsigned p_flags,abort_callback & p_abort,double p_start,double p_length);
void close();
bool is_open();
bool run(audio_chunk & p_chunk,abort_callback & p_abort);
bool run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort);
void seek(double seconds,abort_callback & p_abort);
bool can_seek();
void on_idle(abort_callback & p_abort);
bool get_dynamic_info(file_info & p_out,double & p_timestamp_delta);
bool get_dynamic_info_track(file_info & p_out,double & p_timestamp_delta);
void set_logger(event_logger::ptr ptr) {m_input.set_logger(ptr);}
const char * get_path() const;
void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort);
private:
bool _run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort);
bool _m_input_run(audio_chunk & p_chunk, mem_block_container * p_raw, abort_callback & p_abort);
input_helper m_input;
double m_start,m_length,m_position;
bool m_dynamic_info_trigger,m_dynamic_info_track_trigger;
};
//! openAudioData return value, see openAudioData()
struct openAudioData_t {

View File

@@ -1,35 +0,0 @@
#pragma once
class input_logging : public input_stubs {
public:
input_logging() {
set_logger(nullptr);
}
event_logger_recorder::ptr log_record( std::function<void () > f ) {
auto rec = event_logger_recorder::create();
{
pfc::vartoggle_t< event_logger::ptr > toggle( m_logger, rec );
f();
}
return rec;
}
void set_logger( event_logger::ptr logger ) {
if ( logger.is_valid() ) {
m_haveCustomLogger = true;
m_logger = logger;
} else {
m_haveCustomLogger = false;
m_logger = new service_impl_t<event_logger_fallback>();
}
}
protected:
event_logger::ptr m_logger;
bool m_haveCustomLogger = false;
};
#define FB2K_INPUT_LOG_STATUS(X) FB2K_LOG_STATUS(m_logger, X)
#define FB2K_INPUT_LOG_WARNING(X) FB2K_LOG_WARNING(m_logger, X)
#define FB2K_INPUT_LOG_ERROR(X) FB2K_LOG_ERROR(m_logger, X)

View File

@@ -0,0 +1,193 @@
#include "stdafx.h"
#ifdef FOOBAR2000_DESKTOP_WINDOWS
#include "win32_misc.h"
#include "listview_helper.h"
namespace listview_helper {
unsigned insert_item(HWND p_listview,unsigned p_index,const char * p_name,LPARAM p_param)
{
if (p_index == ~0) p_index = ListView_GetItemCount(p_listview);
LVITEM item = {};
pfc::stringcvt::string_os_from_utf8 os_string_temp(p_name);
item.mask = LVIF_TEXT | LVIF_PARAM;
item.iItem = p_index;
item.lParam = p_param;
item.pszText = const_cast<TCHAR*>(os_string_temp.get_ptr());
LRESULT ret = uSendMessage(p_listview,LVM_INSERTITEM,0,(LPARAM)&item);
if (ret < 0) return ~0;
else return (unsigned) ret;
}
unsigned insert_item2(HWND p_listview, unsigned p_index, const char * col0, const char * col1, LPARAM p_param) {
unsigned i = insert_item( p_listview, p_index, col0, p_param );
if (i != ~0) {
set_item_text( p_listview, i, 1, col1 );
}
return i;
}
unsigned insert_item3(HWND p_listview, unsigned p_index, const char * col0, const char * col1, const char * col2, LPARAM p_param) {
unsigned i = insert_item( p_listview, p_index, col0, p_param );
if (i != ~0) {
set_item_text( p_listview, i, 1, col1 );
set_item_text( p_listview, i, 2, col2 );
}
return i;
}
unsigned insert_column(HWND p_listview,unsigned p_index,const char * p_name,unsigned p_width_dlu)
{
pfc::stringcvt::string_os_from_utf8 os_string_temp(p_name);
RECT rect = {0,0,(LONG)p_width_dlu,0};
MapDialogRect(GetParent(p_listview),&rect);
LVCOLUMN data = {};
data.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
data.fmt = LVCFMT_LEFT;
data.cx = rect.right;
data.pszText = const_cast<TCHAR*>(os_string_temp.get_ptr());
LRESULT ret = uSendMessage(p_listview,LVM_INSERTCOLUMN,p_index,(LPARAM)&data);
if (ret < 0) return ~0;
else return (unsigned) ret;
}
void get_item_text(HWND p_listview,unsigned p_index,unsigned p_column,pfc::string_base & p_out) {
enum {buffer_length = 1024*64};
pfc::array_t<TCHAR> buffer; buffer.set_size(buffer_length);
ListView_GetItemText(p_listview,p_index,p_column,buffer.get_ptr(),buffer_length);
p_out = pfc::stringcvt::string_utf8_from_os(buffer.get_ptr(),buffer_length);
}
bool set_item_text(HWND p_listview,unsigned p_index,unsigned p_column,const char * p_name)
{
LVITEM item = {};
pfc::stringcvt::string_os_from_utf8 os_string_temp(p_name);
item.mask = LVIF_TEXT;
item.iItem = p_index;
item.iSubItem = p_column;
item.pszText = const_cast<TCHAR*>(os_string_temp.get_ptr());
return uSendMessage(p_listview,LVM_SETITEM,0,(LPARAM)&item) ? true : false;
}
bool is_item_selected(HWND p_listview,unsigned p_index)
{
LVITEM item = {};
item.mask = LVIF_STATE;
item.iItem = p_index;
item.stateMask = LVIS_SELECTED;
if (!uSendMessage(p_listview,LVM_GETITEM,0,(LPARAM)&item)) return false;
return (item.state & LVIS_SELECTED) ? true : false;
}
void set_item_selection(HWND p_listview,unsigned p_index,bool p_state)
{
PFC_ASSERT( ::IsWindow(p_listview) );
LVITEM item = {};
item.stateMask = LVIS_SELECTED;
item.state = p_state ? LVIS_SELECTED : 0;
WIN32_OP_D( SendMessage(p_listview,LVM_SETITEMSTATE,(WPARAM)p_index,(LPARAM)&item) );
}
bool select_single_item(HWND p_listview,unsigned p_index)
{
LRESULT temp = SendMessage(p_listview,LVM_GETITEMCOUNT,0,0);
if (temp < 0) return false;
ListView_SetSelectionMark(p_listview,p_index);
unsigned n; const unsigned m = pfc::downcast_guarded<unsigned>(temp);
for(n=0;n<m;n++) {
enum {mask = LVIS_FOCUSED | LVIS_SELECTED};
ListView_SetItemState(p_listview,n,n == p_index ? mask : 0, mask);
}
return ensure_visible(p_listview,p_index);
}
bool ensure_visible(HWND p_listview,unsigned p_index)
{
return uSendMessage(p_listview,LVM_ENSUREVISIBLE,p_index,FALSE) ? true : false;
}
}
void ListView_GetContextMenuPoint(HWND p_list,LPARAM p_coords,POINT & p_point,int & p_selection) {
POINT pt = {(short)LOWORD(p_coords),(short)HIWORD(p_coords)};
ListView_GetContextMenuPoint(p_list, pt, p_point, p_selection);
}
void ListView_GetContextMenuPoint(HWND p_list,POINT p_coords,POINT & p_point,int & p_selection) {
if (p_coords.x == -1 && p_coords.y == -1) {
int firstsel = ListView_GetFirstSelection(p_list);
if (firstsel >= 0) {
ListView_EnsureVisible(p_list, firstsel, FALSE);
RECT rect;
WIN32_OP_D( ListView_GetItemRect(p_list,firstsel,&rect,LVIR_BOUNDS) );
p_point.x = (rect.left + rect.right) / 2;
p_point.y = (rect.top + rect.bottom) / 2;
WIN32_OP_D( ClientToScreen(p_list,&p_point) );
} else {
RECT rect;
WIN32_OP_D(GetClientRect(p_list,&rect));
p_point.x = (rect.left + rect.right) / 2;
p_point.y = (rect.top + rect.bottom) / 2;
WIN32_OP_D(ClientToScreen(p_list,&p_point));
}
p_selection = firstsel;
} else {
POINT pt = p_coords; // {(short)LOWORD(p_coords),(short)HIWORD(p_coords)};
p_point = pt;
POINT client = pt;
WIN32_OP_D( ScreenToClient(p_list,&client) );
LVHITTESTINFO info = {};
info.pt = client;
p_selection = ListView_HitTest(p_list,&info);
}
}
#if 0
static bool ProbeColumn(HWND view, int index) {
LVCOLUMN col = {LVCF_ORDER};
return !! ListView_GetColumn(view, index, &col);
}
int ListView_GetColumnCount(HWND listView) {
if (!ProbeColumn(listView, 0)) return 0;
int hi = 1;
for(;;) {
if (!ProbeColumn(listView, hi)) break;
hi <<= 1;
if (hi <= 0) {
PFC_ASSERT(!"Shouldn't get here!");
return -1;
}
}
int lo = hi >> 1;
//lo is the highest known valid column, hi is the lowest known invalid, let's bsearch thru
while(lo + 1 < hi) {
PFC_ASSERT( lo < hi );
const int mid = lo + (hi - lo) / 2;
PFC_ASSERT( lo < mid && mid < hi );
if (ProbeColumn(listView, mid)) {
lo = mid;
} else {
hi = mid;
}
}
return hi;
}
#else
int ListView_GetColumnCount(HWND listView) {
HWND header = ListView_GetHeader(listView);
PFC_ASSERT(header != NULL);
return Header_GetItemCount(header);
}
#endif
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -0,0 +1,51 @@
#pragma once
#ifdef FOOBAR2000_DESKTOP_WINDOWS
namespace listview_helper
{
unsigned insert_item(HWND p_listview,unsigned p_index,const char * p_name,LPARAM p_param);//returns index of new item on success, infinite on failure
unsigned insert_column(HWND p_listview,unsigned p_index,const char * p_name,unsigned p_width_dlu);//returns index of new item on success, infinite on failure
bool set_item_text(HWND p_listview,unsigned p_index,unsigned p_column,const char * p_name);
bool is_item_selected(HWND p_listview,unsigned p_index);
void set_item_selection(HWND p_listview,unsigned p_index,bool p_state);
bool select_single_item(HWND p_listview,unsigned p_index);
bool ensure_visible(HWND p_listview,unsigned p_index);
void get_item_text(HWND p_listview,unsigned p_index,unsigned p_column,pfc::string_base & p_out);
unsigned insert_item2(HWND p_listview, unsigned p_index, const char * col0, const char * col1, LPARAM p_param = 0);
unsigned insert_item3(HWND p_listview, unsigned p_index, const char * col0, const char * col1, const char * col2, LPARAM p_param = 0);
};
static int ListView_GetFirstSelection(HWND p_listview) {
return ListView_GetNextItem(p_listview,-1,LVNI_SELECTED);
}
static int ListView_GetSingleSelection(HWND p_listview) {
if (ListView_GetSelectedCount(p_listview) != 1) return -1;
return ListView_GetFirstSelection(p_listview);
}
static int ListView_GetFocusItem(HWND p_listview) {
return ListView_GetNextItem(p_listview,-1,LVNI_FOCUSED);
}
static bool ListView_IsItemSelected(HWND p_listview,int p_index) {
return ListView_GetItemState(p_listview,p_index,LVIS_SELECTED) != 0;
}
void ListView_GetContextMenuPoint(HWND p_list,LPARAM p_coords,POINT & p_point,int & p_selection);
void ListView_GetContextMenuPoint(HWND p_list,POINT p_coords,POINT & p_point,int & p_selection);
int ListView_GetColumnCount(HWND listView);
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -1,71 +0,0 @@
#pragma once
#include <set>
class metadb_handle;
// Roughly same as pfc::avltree_t<metadb_handle_ptr> or std::set<metadb_handle_ptr>, but optimized for use with large amounts of items
class metadb_handle_set {
public:
metadb_handle_set() {}
template<typename ptr_t>
bool add_item_check(ptr_t const & item) { return add_item_check_(&*item); }
template<typename ptr_t>
bool remove_item(ptr_t const & item) { return remove_item_(&*item); }
bool add_item_check_(metadb_handle * p) {
bool rv = m_content.insert(p).second;
if (rv) p->service_add_ref();
return rv;
}
bool remove_item_(metadb_handle * p) {
bool rv = m_content.erase(p) != 0;
if (rv) p->service_release();
return rv;
}
size_t get_count() const {
return m_content.size();
}
template<typename ptr_t>
bool contains(ptr_t const & item) const {
return m_content.count(&*item) != 0;
}
template<typename ptr_t>
bool have_item(ptr_t const & item) const {
return m_content.count(&*item) != 0;
}
void operator+=(metadb_handle::ptr const & item) {
add_item_check_(item.get_ptr());
}
void operator-=(metadb_handle::ptr const & item) {
remove_item_(item.get_ptr());
}
void operator+=(metadb_handle::ptr && item) {
auto p = item.detach();
bool added = m_content.insert(p).second;
if (!added) p->service_release();
}
void remove_all() {
for (auto iter = m_content.begin(); iter != m_content.end(); ++iter) {
metadb_handle * p = (*iter);
p->service_release();
}
m_content.clear();
}
template<typename callback_t>
void enumerate(callback_t & cb) const {
for (auto iter = m_content.begin(); iter != m_content.end(); ++iter) {
cb(*iter);
}
}
typedef std::set<metadb_handle*> impl_t;
typedef impl_t::const_iterator const_iterator;
const_iterator begin() const { return m_content.begin(); }
const_iterator end() const { return m_content.end(); }
private:
std::set<metadb_handle*> m_content;
private:
metadb_handle_set(const metadb_handle_set &) = delete;
void operator=(const metadb_handle_set&) = delete;
};

View File

@@ -3,78 +3,8 @@
#include "packet_decoder_aac_common.h"
#include "../SDK/filesystem_helper.h"
#include "bitreader_helper.h"
size_t packet_decoder_aac_common::skipADTSHeader( const uint8_t * data,size_t size ) {
if ( size < 7 ) throw exception_io_data();
PFC_ASSERT( bitreader_helper::extract_int(data, 0, 12) == 0xFFF);
if (bitreader_helper::extract_bit(data, 12+1+2)) {
return 7; // ABSENT flag
}
if (size < 9) throw exception_io_data();
return 9;
}
pfc::array_t<uint8_t> packet_decoder_aac_common::parseDecoderSetup( const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size) {
if ( p_owner == owner_ADTS ) {
pfc::array_t<uint8_t> ret;
ret.resize( 2 );
ret[0] = 0; ret[1] = 0;
// ret:
// 5 bits AOT
// 4 bits freqindex
// 4 bits channelconfig
// source:
// 12 bits 0xFFF
// 4 bits disregard
// 2 bits AOT-1 @ 16
// 4 bits freqindex @ 18
// 1 bit disregard
// 3 bits channelconfig @ 23
// 26 bits total, 4 bytes minimum
if ( p_param2size < 4 ) throw exception_io_data();
const uint8_t * source = (const uint8_t*) p_param2;
if ( bitreader_helper::extract_int(source, 0, 12) != 0xFFF ) throw exception_io_data();
size_t aot = bitreader_helper::extract_int(source, 16, 2) + 1;
if ( aot >= 31 ) throw exception_io_data();
size_t freqindex = bitreader_helper::extract_int(source, 18, 4);
if ( freqindex > 12 ) throw exception_io_data();
size_t channelconfig = bitreader_helper::extract_bits( source, 23, 3);
bitreader_helper::write_int(ret.get_ptr(), 0, 5, aot);
bitreader_helper::write_int(ret.get_ptr(), 5, 4, freqindex);
bitreader_helper::write_int(ret.get_ptr(), 9, 4, channelconfig);
return ret;
} else if ( p_owner == owner_ADIF ) {
// bah
} else if ( p_owner == owner_MP4 )
{
if ( p_param1 == 0x40 || p_param1 == 0x66 || p_param1 == 0x67 || p_param1 == 0x68 ) {
pfc::array_t<uint8_t> ret;
ret.set_data_fromptr( (const uint8_t*) p_param2, p_param2size);
return ret;
}
}
else if ( p_owner == owner_matroska )
{
const matroska_setup * setup = ( const matroska_setup * ) p_param2;
if ( p_param2size == sizeof(*setup) )
{
if ( !strcmp(setup->codec_id, "A_AAC") || !strncmp(setup->codec_id, "A_AAC/", 6) ) {
pfc::array_t<uint8_t> ret;
ret.set_data_fromptr( (const uint8_t*) setup->codec_private, setup->codec_private_size );
return ret;
}
}
}
throw exception_io_data();
}
#if 0
bool packet_decoder_aac_common::parseDecoderSetup(const GUID &p_owner, t_size p_param1, const void *p_param2, t_size p_param2size, const void *&outCodecPrivate, size_t &outCodecPrivateSize) {
outCodecPrivate = NULL;
outCodecPrivateSize = 0;
@@ -103,31 +33,16 @@ bool packet_decoder_aac_common::parseDecoderSetup(const GUID &p_owner, t_size p_
return false;
}
#endif
bool packet_decoder_aac_common::testDecoderSetup( const GUID & p_owner, t_size p_param1, const void * p_param2, t_size p_param2size ) {
if ( p_owner == owner_ADTS ) { return true; }
else if ( p_owner == owner_ADIF ) { return true; }
else if ( p_owner == owner_MP4 )
{
if ( p_param1 == 0x40 || p_param1 == 0x66 || p_param1 == 0x67 || p_param1 == 0x68 ) {
return true;
}
}
else if ( p_owner == owner_matroska )
{
const matroska_setup * setup = ( const matroska_setup * ) p_param2;
if ( p_param2size == sizeof(*setup) )
{
if ( !strcmp(setup->codec_id, "A_AAC") || !strncmp(setup->codec_id, "A_AAC/", 6) ) {
return true;
}
}
}
return false;
const void * dummy1; size_t dummy2;
return parseDecoderSetup(p_owner, p_param1, p_param2, p_param2size, dummy1, dummy2);
}
namespace {
class esds_maker : public stream_writer_buffer_simple {
public:
@@ -152,8 +67,8 @@ namespace {
void packet_decoder_aac_common::make_ESDS( pfc::array_t<uint8_t> & outESDS, const void * inCodecPrivate, size_t inCodecPrivateSize ) {
if (inCodecPrivateSize > 1024*1024) throw exception_io_data(); // sanity
auto & p_abort = fb2k::noAbort;
abort_callback_dummy p_abort;
esds_maker esds4;
const uint8_t crap[] = {0x40, 0x15, 0x00, 0x00, 0x00, 0x00, 0x05, 0x34, 0x08, 0x00, 0x02, 0x3D, 0x55};
@@ -191,57 +106,3 @@ void packet_decoder_aac_common::make_ESDS( pfc::array_t<uint8_t> & outESDS, cons
// For: 12 30 56 E5 00
}
const uint32_t aac_sample_rates[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
};
static unsigned readSamplingFreq(bitreader_helper::bitreader_limited& r) {
unsigned samplingRateIndex = (unsigned)r.read(4);
if (samplingRateIndex == 15) {
return (unsigned)r.read(24);
} else {
if (samplingRateIndex >= PFC_TABSIZE(aac_sample_rates)) throw exception_io_data();
return aac_sample_rates[samplingRateIndex];
}
}
packet_decoder_aac_common::audioSpecificConfig_t packet_decoder_aac_common::parseASC(const void * p_, size_t s) {
// Source: https://wiki.multimedia.cx/index.php?title=MPEG-4_Audio
bitreader_helper::bitreader_limited r((const uint8_t*)p_, 0, s * 8);
audioSpecificConfig_t cfg = {};
cfg.m_objectType = (unsigned) r.read(5);
if (cfg.m_objectType == 31) {
cfg.m_objectType = 32 + (unsigned) r.read(6);
}
cfg.m_sampleRate = readSamplingFreq(r);
cfg.m_channels = (unsigned) r.read( 4 );
if (cfg.m_objectType == 5 || cfg.m_objectType == 29) {
cfg.m_explicitSBR = true;
cfg.m_explicitPS = (cfg.m_objectType == 29);
cfg.m_sbrRate = readSamplingFreq(r);
cfg.m_objectType = (unsigned)r.read(5);
}
switch (cfg.m_objectType) {
case 1: case 2: case 3: case 4: case 17: case 23:
cfg.m_shortWindow = (r.read(1) != 0);
break;
}
return cfg;
}
unsigned packet_decoder_aac_common::get_ASC_object_type(const void * p_, size_t s) {
// Source: https://wiki.multimedia.cx/index.php?title=MPEG-4_Audio
bitreader_helper::bitreader_limited r((const uint8_t*)p_, 0, s * 8);
unsigned objectType = (unsigned) r.read(5);
if (objectType == 31) {
objectType = 32 + (unsigned) r.read(6);
}
return objectType;
}

View File

@@ -7,10 +7,9 @@ Helper code with common AAC packet_decoder functionality. Primarily meant for fo
class packet_decoder_aac_common : public packet_decoder {
public:
static pfc::array_t<uint8_t> parseDecoderSetup( const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size);
static bool parseDecoderSetup( const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size, const void * &outCodecPrivate, size_t & outCodecPrivateSize );
static bool testDecoderSetup( const GUID & p_owner, t_size p_param1, const void * p_param2, t_size p_param2size );
static size_t skipADTSHeader( const uint8_t * data,size_t size );
static unsigned get_max_frame_dependency_()
{
return 2;
@@ -21,17 +20,4 @@ public:
}
static void make_ESDS( pfc::array_t<uint8_t> & outESDS, const void * inCodecPrivate, size_t inCodecPrivateSize );
struct audioSpecificConfig_t {
unsigned m_objectType;
unsigned m_sampleRate;
unsigned m_channels;
unsigned m_sbrRate;
bool m_shortWindow;
bool m_explicitSBR, m_explicitPS;
};
static audioSpecificConfig_t parseASC(const void *, size_t);
static unsigned get_ASC_object_type(const void *, size_t);
};

View File

@@ -38,13 +38,6 @@ public:
t_filetimestamp get_timestamp(abort_callback & p_abort) { return m_file->get_timestamp(p_abort); }
void reopen(abort_callback & p_abort) {
#if PFC_DEBUG
auto pos = get_position(p_abort);
FB2K_console_formatter() << "pretend nonseekable reader reopen @ " << pos;
if ( pos > 0 ) {
pfc::nop();
}
#endif
m_file->reopen(p_abort);
}

View File

@@ -33,17 +33,15 @@ file::ptr fullFileBuffer::open(const char * path, abort_callback & abort, file::
file::ptr f;
if (hint.is_valid()) f = hint;
else filesystem::g_open_read(f, path, abort);
if (sizeMax != filesize_invalid) {
t_filesize fs = f->get_size(abort);
if (fs > sizeMax) return f;
t_filesize fs = f->get_size(abort);
if (fs < sizeMax) /*rejects size-unknown too*/ {
try {
service_ptr_t<reader_bigmem_mirror> r = new service_impl_t<reader_bigmem_mirror>();
r->init(f, abort);
f = r;
}
catch (std::bad_alloc) {}
}
try {
service_ptr_t<reader_bigmem_mirror> r = new service_impl_t<reader_bigmem_mirror>();
r->init(f, abort);
f = r;
}
catch (std::bad_alloc) {}
return f;
}
@@ -68,7 +66,7 @@ namespace {
};
struct readAheadInstance_t {
file::ptr m_file;
size_t m_readAhead, m_wakeUpThreschold;
size_t m_readAhead;
pfc::array_t<uint8_t> m_buffer;
size_t m_bufferBegin, m_bufferEnd;
@@ -78,7 +76,6 @@ namespace {
t_filesize m_seekto;
abort_callback_impl m_abort;
bool m_remote;
bool m_atEOF = false;
bool m_haveDynamicInfo;
std::list<dynInfoEntry_t> m_dynamicInfo;
@@ -107,7 +104,6 @@ namespace {
i->m_file = chain;
i->m_remote = chain->is_remote();
i->m_readAhead = readAhead;
i->m_wakeUpThreschold = readAhead * 3 / 4;
i->m_buffer.set_size_discard( readAhead * 2 );
i->m_bufferBegin = 0; i->m_bufferEnd = 0;
i->m_canWrite.set_state(true);
@@ -122,55 +118,41 @@ namespace {
}
}
fb2k::splitTask( [i] {
#ifdef PFC_SET_THREAD_DESCRIPTION
PFC_SET_THREAD_DESCRIPTION("Fb2k Read-Ahead Thread");
#endif
pfc::splitThread( [i] {
worker(*i);
} );
}
static void waitHelper( pfc::event & evt, abort_callback & aborter ) {
pfc::event::g_twoEventWait( evt.get_handle(), aborter.get_abort_event(), -1);
aborter.check();
}
t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
auto & i = * m_instance;
size_t done = 0;
bool initial = true;
while( done < p_bytes ) {
if ( !initial ) {
// Do not invoke waiting with common case read with lots of data in the buffer
pfc::event::g_twoEventWait( i.m_canRead.get_handle(), p_abort.get_abort_event(), -1);
}
p_abort.check();
waitHelper( i.m_canRead, p_abort );
pfc::mutexScope guard ( i.m_guard );
size_t got = i.m_bufferEnd - i.m_bufferBegin;
if (got == 0) {
i.m_error.rethrow();
if ( initial && ! i.m_atEOF ) {
initial = false; continue; // proceed to wait for more data
}
break; // EOF
}
size_t delta = pfc::min_t<size_t>( p_bytes - done, got );
const bool wakeUpBefore = got < i.m_wakeUpThreschold;
auto bufptr = i.m_buffer.get_ptr();
if ( p_buffer != nullptr ) memcpy( (uint8_t*) p_buffer + done, bufptr + i.m_bufferBegin, delta );
memcpy( (uint8_t*) p_buffer + done, bufptr + i.m_bufferBegin, delta );
done += delta;
i.m_bufferBegin += delta;
got -= delta;
m_position += delta;
if (!i.m_error.didFail() && !i.m_atEOF) {
if (!i.m_error.didFail()) {
if ( got == 0 ) i.m_canRead.set_state( false );
const bool wakeUpNow = got < i.m_wakeUpThreschold;
// Only set the event when *crossing* the boundary
// we will get a lot of wakeUpNow when nearing EOF
if ( wakeUpNow && ! wakeUpBefore ) i.m_canWrite.set_state( true );
if ( got < 3 * i.m_readAhead / 4 ) i.m_canWrite.set_state( true );
}
initial = false;
if ( i.m_atEOF ) break; // go no further
}
// FB2K_console_formatter() << "ReadAhead read: " << p_bytes << " => " << done;
return done;
}
t_filesize get_size(abort_callback & p_abort) {
@@ -187,16 +169,6 @@ namespace {
if (!m_canSeek) throw exception_io_object_not_seekable();
if ( m_stats.m_size != filesize_invalid && p_position > m_stats.m_size ) throw exception_io_seek_out_of_range();
auto posNow = get_position(p_abort);
if ( p_position >= posNow && p_position < posNow + m_instance->m_readAhead ) {
// FB2K_console_formatter() << "ReadAhead skip: " << posNow << " => " << p_position;
auto toSkip = p_position - posNow;
if ( toSkip > 0 ) read(nullptr, (size_t) toSkip, p_abort);
return;
}
// FB2K_console_formatter() << "ReadAhead seek: " << posNow << " => " << p_position;
seekInternal( p_position );
}
bool can_seek() {
@@ -220,23 +192,19 @@ namespace {
}
void reopen( abort_callback & p_abort ) {
if ( get_position( p_abort ) == 0 ) return;
p_abort.check();
seekInternal( seek_reopen );
}
bool get_static_info(class file_info & p_out) {
if ( ! m_haveStaticInfo ) return false;
mergeInfo(p_out, m_staticInfo);
p_out = m_staticInfo;
return true;
}
bool is_dynamic_info_enabled() {
return m_instance->m_haveDynamicInfo;
}
static void mergeInfo( file_info & out, const file_info & in ) {
out.copy_meta(in);
out.overwrite_info(in);
}
bool get_dynamic_info_v2(class file_info & out, t_filesize & outOffset) {
auto & i = * m_instance;
if ( ! i.m_haveDynamicInfo ) return false;
@@ -252,10 +220,8 @@ namespace {
if ( ptr == i.m_dynamicInfo.begin() ) return false;
auto iter = ptr; --iter;
mergeInfo(out, iter->m_info);
outOffset = iter->m_offset;
out = iter->m_info; outOffset = iter->m_offset;
i.m_dynamicInfo.erase( i.m_dynamicInfo.begin(), ptr );
return true;
}
private:
@@ -266,7 +232,6 @@ namespace {
i.m_bufferBegin = i.m_bufferEnd = 0;
i.m_canWrite.set_state(true);
i.m_seekto = p_position;
i.m_atEOF = false;
i.m_canRead.set_state(false);
m_position = ( p_position == seek_reopen ) ? 0 : p_position;
@@ -274,9 +239,8 @@ namespace {
static void worker( readAheadInstance_t & i ) {
ThreadUtils::CRethrow err;
err.exec( [&i] {
bool atEOF = false;
uint8_t* bufptr = i.m_buffer.get_ptr();
const size_t readAtOnceLimit = i.m_remote ? 256 : 4*1024;
const size_t readAtOnceLimit = i.m_remote ? 256 : 64*1024;
for ( ;; ) {
i.m_canWrite.wait_for(-1);
size_t readHowMuch = 0, readOffset = 0;
@@ -291,7 +255,6 @@ namespace {
}
i.m_seekto = filesize_invalid;
atEOF = false;
}
size_t got = i.m_bufferEnd - i.m_bufferBegin;
@@ -317,8 +280,6 @@ namespace {
if ( readHowMuch > 0 ) {
readHowMuch = i.m_file->read( bufptr + readOffset, readHowMuch, i.m_abort );
if ( readHowMuch == 0 ) atEOF = true;
if ( i.m_haveDynamicInfo ) {
file_dynamicinfo::ptr dyn;
if ( dyn &= i.m_file ) {
@@ -337,16 +298,14 @@ namespace {
{
pfc::mutexScope guard( i.m_guard );
i.m_abort.check();
if ( i.m_seekto != filesize_invalid ) {
// Seek request happened while we were reading - discard and continue
continue;
}
i.m_atEOF = atEOF;
i.m_canRead.set_state( true );
i.m_bufferEnd += readHowMuch;
size_t got = i.m_bufferEnd - i.m_bufferBegin;
if ( atEOF || got >= i.m_readAhead ) i.m_canWrite.set_state(false);
if ( got >= i.m_readAhead ) i.m_canWrite.set_state(false);
if ( dynInfoGot ) {
i.m_dynamicInfo.push_back( std::move(dynInfo) );

View File

@@ -21,7 +21,7 @@ public:
service_ptr_t<file> l_tempfile;
try {
openTempFile(l_tempfile, p_abort);
} catch(exception_io) {return false;}
} catch(exception_io_denied) {return false;}
p_out = m_tempfile = l_tempfile;
return true;
}
@@ -31,14 +31,6 @@ public:
//WARNING: if this errors out, it may leave caller with null file pointer; take appropriate measures not to crash in such cases
void finalize(service_ptr_t<file> & p_owner,abort_callback & p_abort) {
if (m_tempfile.is_valid()) {
m_tempfile->flushFileBuffers_(p_abort);
if (p_owner.is_valid()) {
try {
file::g_copy_creation_time(p_owner, m_tempfile, p_abort);
} catch (exception_io) {}
}
m_tempfile.release();
p_owner.release();
handleFileMove(m_temppath, m_origpath, p_abort);
@@ -48,7 +40,6 @@ public:
// Alternate finalizer without owner file object, caller takes responsibility for closing the source file before calling
void finalize_no_reopen( abort_callback & p_abort ) {
if (m_tempfile.is_valid()) {
m_tempfile->flushFileBuffers_(p_abort);
m_tempfile.release();
handleFileMove(m_temppath, m_origpath, p_abort);
}
@@ -57,8 +48,9 @@ public:
if (m_tempfile.is_valid()) {
m_tempfile.release();
try {
retryOnSharingViolation( 1, fb2k::noAbort, [&] {
m_fs->remove(m_temppath, fb2k::noAbort);
abort_callback_dummy noAbort;
retryOnSharingViolation( 1, noAbort, [&] {
m_fs->remove(m_temppath, noAbort);
} );
} catch(...) {}
}
@@ -70,7 +62,7 @@ private:
void handleFileMove(const char * from, const char * to, abort_callback & abort) {
PFC_ASSERT(m_fs->is_our_path(from));
PFC_ASSERT(m_fs->is_our_path(to));
FB2K_RETRY_FILE_MOVE(m_fs->replace_file(from, to, abort), abort, 10 );
retryOnSharingViolation(10, abort, [&] { m_fs->replace_file(from, to, abort); } );
}
pfc::string8 m_origpath;
pfc::string8 m_temppath;

View File

@@ -59,7 +59,6 @@ namespace text_file_loader
return;
}
if (!memcmp(utf8_header, temp, 3)) is_utf8 = true;
else if (is_utf8) p_out.add_string(temp,3);
else ansitemp.add_string(temp, 3);
mem.set_size(delta);
@@ -90,7 +89,7 @@ namespace text_file_loader
char * asdf = mem.get_ptr();
p_file->read_object(asdf,size,p_abort);
asdf[size]=0;
if (size>=3 && !memcmp(utf8_header,asdf,3)) {
if (size>3 && !memcmp(utf8_header,asdf,3)) {
is_utf8 = true;
p_out.add_string(asdf+3);
} else if (forceUTF8) {

View File

@@ -1,25 +0,0 @@
#include "StdAfx.h"
#include "text_file_loader_v2.h"
#include "text_file_loader.h"
void text_file_loader_v2::load(file::ptr f, abort_callback & abort) {
m_lines.clear();
bool dummy;
text_file_loader::read_v2(f, abort, m_data, dummy, m_forceUTF8);
m_lines.reserve(128);
char * p = const_cast<char*>(m_data.get_ptr());
bool line = false;
const size_t len = m_data.length();
for (size_t walk = 0; walk < len; ++walk) {
char & c = p[walk];
if (c == '\n' || c == '\r') {
c = 0;
line = false;
} else if (!line) {
m_lines.push_back(&c);
line = true;
}
}
}

View File

@@ -1,14 +0,0 @@
#pragma once
#include <vector>
#include <list>
class text_file_loader_v2 {
public:
bool m_forceUTF8 = false;
void load(file::ptr f, abort_callback & abort);
std::vector< const char * > m_lines;
pfc::string8 m_data;
};

View File

@@ -1,124 +0,0 @@
#include "StdAfx.h"
#include <list>
#include <memory>
#include "track_property_callback_impl.h"
void track_property_callback_impl::set_property(const char * p_group, double p_sortpriority, const char * p_name, const char * p_value) {
propertyname_container temp;
temp.m_name = p_name;
temp.m_priority = p_sortpriority;
pfc::string8 fixEOL;
if (m_cutMultiLine && strchr(p_value, '\n') != nullptr) {
fixEOL = p_value; fixEOL.fix_eol(); p_value = fixEOL;
}
m_entries.find_or_add(p_group).set(temp, p_value);
}
bool track_property_callback_impl::is_group_wanted(const char * p_group) {
if (m_groupFilter) return m_groupFilter(p_group);
return true;
}
void track_property_callback_impl::merge(track_property_callback_impl const & other) {
for (auto iterGroup = other.m_entries.first(); iterGroup.is_valid(); ++iterGroup) {
auto & in = iterGroup->m_value;
auto & out = m_entries[iterGroup->m_key];
for (auto iterEntry = in.first(); iterEntry.is_valid(); ++iterEntry) {
out.set(iterEntry->m_key, iterEntry->m_value);
}
}
}
static bool is_filtered_info_field(const char * p_name) {
service_ptr_t<track_property_provider> ptr;
service_enum_t<track_property_provider> e;
while (e.next(ptr)) {
if (ptr->is_our_tech_info(p_name)) return true;
}
return false;
}
static const char strGroupOther[] = "Other";
static void enumOtherHere(track_property_callback_impl & callback, metadb_info_container::ptr info_) {
const file_info * infoptr = &info_->info();
for (t_size n = 0, m = infoptr->info_get_count(); n < m; n++) {
const char * name = infoptr->info_enum_name(n);
if (!is_filtered_info_field(name)) {
pfc::string_formatter temp;
temp << "<";
uAddStringUpper(temp, name);
temp << ">";
callback.set_property("Other", 0, temp, infoptr->info_enum_value(n));
}
}
}
static void enumOther( track_property_callback_impl & callback, metadb_handle_list_cref items, track_property_provider_v3_info_source * infoSource ) {
if (items.get_count() == 1 ) {
enumOtherHere(callback, infoSource->get_info(0) );
}
}
void enumerateTrackProperties( track_property_callback_impl & callback, std::function< metadb_handle_list_cref () > itemsSource, std::function<track_property_provider_v3_info_source*()> infoSource, std::function<abort_callback& () > abortSource) {
if ( core_api::is_main_thread() ) {
// should not get here like this
// but that does make our job easier
auto & items = itemsSource();
auto info = infoSource();
track_property_provider::ptr ptr;
service_enum_t<track_property_provider> e;
while (e.next(ptr)) {
ptr->enumerate_properties_helper(items, info, callback, abortSource() );
}
if ( callback.is_group_wanted( strGroupOther ) ) {
enumOther(callback, items, info );
}
return;
}
std::list<std::shared_ptr<pfc::event> > lstWaitFor;
std::list<std::shared_ptr< track_property_callback_impl > > lstMerge;
track_property_provider::ptr ptr;
service_enum_t<track_property_provider> e;
while (e.next(ptr)) {
auto evt = std::make_shared<pfc::event>();
auto cb = std::make_shared< track_property_callback_impl >(callback); // clone watched group info
auto work = [ptr, itemsSource, evt, cb, infoSource, abortSource] {
try {
ptr->enumerate_properties_helper(itemsSource(), infoSource(), *cb, abortSource());
} catch (...) {}
evt->set_state(true);
};
track_property_provider_v4::ptr v4;
if (v4 &= ptr) {
// Supports v4 = split a worker thread, work in parallel
pfc::splitThread(work);
} else {
// No v4 = delegate to main thread. Ugly but gets the job done.
fb2k::inMainThread(work);
}
lstWaitFor.push_back(std::move(evt));
lstMerge.push_back(std::move(cb));
}
if (callback.is_group_wanted(strGroupOther)) {
enumOther(callback, itemsSource(), infoSource());
}
for (auto i = lstWaitFor.begin(); i != lstWaitFor.end(); ++i) {
abortSource().waitForEvent(**i, -1);
}
for (auto i = lstMerge.begin(); i != lstMerge.end(); ++i) {
callback.merge(** i);
}
}

View File

@@ -1,51 +0,0 @@
#pragma once
#include <functional>
class groupname_comparator {
public:
static int compare(pfc::stringp p_name1,pfc::stringp p_name2) {
int temp = uStringCompare(p_name1,p_name2);
if (temp != 0) return temp;
return strcmp(p_name1,p_name2);
}
};
struct propertyname_container {
pfc::string m_name;
double m_priority;
};
class propertyname_comparator {
public:
static int compare(const propertyname_container & p_item1,const propertyname_container & p_item2) {
int state = pfc::compare_t(p_item1.m_priority,p_item2.m_priority);
if (state != 0) return state;
return uStringCompare(p_item1.m_name.ptr(),p_item2.m_name.ptr());
}
};
typedef pfc::map_t<propertyname_container,pfc::string,propertyname_comparator> property_group;
typedef pfc::map_t<pfc::string,property_group,groupname_comparator> t_property_group_list;
class track_property_callback_impl : public track_property_callback_v2 {
public:
void set_property(const char * p_group,double p_sortpriority,const char * p_name,const char * p_value) override;
bool is_group_wanted(const char * p_group) override;
void merge( track_property_callback_impl const & other );
t_property_group_list m_entries;
bool m_cutMultiLine = false;
typedef std::function<bool ( const char * ) > groupFilter_t;
groupFilter_t m_groupFilter;
};
// Helper function to walk all track property providers in an optimized multithread manner
// Various *source arguments have been std::function'd so you can reference your own data structures gracefully
// If the function is aborted, it returns immediately - while actual worker threads may not yet have completed, and may still reference *source arguments.
// You must ensure - by means of std::shared_ptr<> or such - that all of the *source arguments remain accessible even after enumerateTrackProperties() returns, until the std::functions are released.
// Legacy track property providers that do not support off main thread operation will be invoked via main_thread_callback in main thread, and the function will stall until they have returned (unless aborted).
void enumerateTrackProperties(track_property_callback_impl & callback, std::function< metadb_handle_list_cref() > itemsSource, std::function<track_property_provider_v3_info_source*()> infoSource, std::function<abort_callback& () > abortSource);

View File

@@ -1,390 +0,0 @@
#include "stdafx.h"
#if FOOBAR2000_TARGET_VERSION >= 79
#include "ui_element_helpers.h"
#include <libPPUI/IDataObjectUtils.h>
#include "atl-misc.h"
namespace ui_element_helpers {
ui_element_instance_ptr instantiate_dummy(HWND p_parent,ui_element_config::ptr cfg, ui_element_instance_callback_ptr p_callback) {
service_ptr_t<ui_element> ptr;
if (!find(ptr,pfc::guid_null)) uBugCheck();
return ptr->instantiate(p_parent,cfg,p_callback);
}
ui_element_instance_ptr instantiate(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback) {
try {
service_ptr_t<ui_element> ptr;
if (!find(ptr,cfg->get_guid())) throw exception_io_data("UI Element Not Found");
auto ret = ptr->instantiate(p_parent,cfg,p_callback);
if (ret.is_empty()) throw std::runtime_error("Null UI Element returned");
return ret;
} catch(std::exception const & e) {
console::complain("UI Element instantiation failure",e);
return instantiate_dummy(p_parent,cfg,p_callback);
}
}
ui_element_instance_ptr update(ui_element_instance_ptr p_element,HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback) {
if (p_element.is_valid() && cfg->get_guid() == p_element->get_guid()) {
p_element->set_configuration(cfg);
return p_element;
} else {
return instantiate(p_parent,cfg,p_callback);
}
}
bool find(service_ptr_t<ui_element> & p_out,const GUID & p_guid) {
return service_by_guid(p_out,p_guid);
}
ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) {
service_ptr_t<ui_element> ptr;
if (!find(ptr,cfg->get_guid())) return NULL;
try {
return ptr->enumerate_children(cfg);
} catch(exception_io_data) {
return NULL;
}
}
};
void ui_element_helpers::replace_with_new_element(ui_element_instance_ptr & p_item,const GUID & p_guid,HWND p_parent,ui_element_instance_callback_ptr p_callback) {
auto & l_abort = fb2k::noAbort;
ui_element_config::ptr cfg;
try {
if (p_item.is_empty()) {
service_ptr_t<ui_element> ptr;
if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found");
cfg = ptr->get_default_configuration();
p_item = ptr->instantiate(p_parent,cfg,p_callback);
} else if (p_item->get_guid() != p_guid) {
service_ptr_t<ui_element> ptr;
if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found");
cfg = ptr->import(p_item->get_configuration());
//p_item.release();
if (cfg.is_empty()) cfg = ptr->get_default_configuration();
p_item = ptr->instantiate(p_parent,cfg,p_callback);
}
} catch(std::exception const & e) {
console::complain("UI Element instantiation failure",e);
if (cfg.is_empty()) cfg = ui_element_config::g_create_empty();
p_item = instantiate_dummy(p_parent,cfg,p_callback);
}
}
namespace {
class CMyMenuSelectionReceiver : public CMenuSelectionReceiver {
public:
CMyMenuSelectionReceiver(HWND p_wnd,ui_element_helpers::ui_element_edit_tools * p_host,ui_element_instance_ptr p_client,unsigned p_client_id,unsigned p_host_base,unsigned p_client_base) : CMenuSelectionReceiver(p_wnd), m_host(p_host), m_client(p_client), m_client_id(p_client_id), m_host_base(p_host_base), m_client_base(p_client_base) {}
bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
if (p_id >= m_client_base) {
return m_client->edit_mode_context_menu_get_description(p_id,m_client_base,p_out);
} else if (p_id >= m_host_base) {
return m_host->host_edit_mode_context_menu_get_description(m_client_id,p_id,m_host_base,p_out);
} else {
const char * msg = ui_element_helpers::ui_element_edit_tools::description_from_menu_command(p_id);
if (msg == NULL) return false;
p_out = msg;
return true;
}
}
private:
ui_element_helpers::ui_element_edit_tools * const m_host;
ui_element_instance_ptr const m_client;
unsigned const m_client_id,m_host_base,m_client_base;
};
};
namespace HostHelperIDs {
enum {ID_LABEL, ID_REPLACE = 1, ID_ADD_NEW, ID_CUT, ID_COPY, ID_PASTE, ID_CUSTOM_BASE};
}
void ui_element_helpers::ui_element_edit_tools::standard_edit_context_menu(LPARAM p_point,ui_element_instance_ptr p_item,unsigned p_id,HWND p_parent) {
using namespace HostHelperIDs;
static_api_ptr_t<ui_element_common_methods> api;
POINT pt;
bool fromkeyboard = false;
if (p_point == (LPARAM)(-1)) {
fromkeyboard = true;
if (!p_item->edit_mode_context_menu_get_focus_point(pt)) {
CRect rect;
WIN32_OP_D( CWindow(p_item->get_wnd()).GetWindowRect(&rect) );
pt = rect.CenterPoint();
}
} else {
pt.x = (short)LOWORD(p_point);
pt.y = (short)HIWORD(p_point);
}
CMenu menu;
WIN32_OP( menu.CreatePopupMenu() );
const GUID sourceItemGuid = p_item->get_guid();
const bool sourceItemEmpty = !!(sourceItemGuid == pfc::guid_null);
if (sourceItemEmpty) {
WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_ADD_NEW,TEXT(AddNewUIElementCommand)) );
WIN32_OP_D( menu.SetMenuDefaultItem(ID_ADD_NEW) );
} else {
service_ptr_t<ui_element> elem;
pfc::string8 name;
if (find(elem,sourceItemGuid)) {
elem->get_name(name);
} else {
name = "<unknown element>";
}
WIN32_OP_D( menu.AppendMenu(MF_STRING | MF_DISABLED,ID_LABEL,pfc::stringcvt::string_os_from_utf8(name)) );
WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) );
WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand)) );
}
WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) );
//menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand));
WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_CUT,TEXT(CutUIElementCommand)) );
WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_COPY,TEXT(CopyUIElementCommand)) );
WIN32_OP_D( menu.AppendMenu(MF_STRING | (api->is_paste_available() ? 0 : (MF_DISABLED|MF_GRAYED)),ID_PASTE,TEXT(PasteUIElementCommand)) );
unsigned custom_walk = ID_CUSTOM_BASE;
unsigned custom_base_host = ~0, custom_base_client = ~0;
if (host_edit_mode_context_menu_test(p_id,pt,fromkeyboard)) {
menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT(""));
custom_base_host = custom_walk;
host_edit_mode_context_menu_build(p_id,pt,fromkeyboard,menu,custom_walk);
}
{
if (p_item->edit_mode_context_menu_test(pt,fromkeyboard)) {
WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) );
custom_base_client = custom_walk;
p_item->edit_mode_context_menu_build(pt,fromkeyboard,menu,custom_walk);
}
}
int cmd;
{
ui_element_highlight_scope s(p_item->get_wnd());
CMyMenuSelectionReceiver receiver(p_parent,this,p_item,p_id,custom_base_host,custom_base_client);
cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,pt.x,pt.y,receiver);
}
if (cmd > 0) {
switch(cmd) {
case ID_REPLACE:
case ID_ADD_NEW:
replace_dialog(p_item->get_wnd(),p_id,p_item->get_guid());
break;
case ID_COPY:
api->copy(p_item);
break;
case ID_CUT:
api->copy(p_item);
p_item.release();
host_replace_element(p_id,pfc::guid_null);
break;
case ID_PASTE:
host_paste_element(p_id);
break;
default:
if ((unsigned)cmd >= custom_base_client) {
p_item->edit_mode_context_menu_command(pt,fromkeyboard,(unsigned)cmd,custom_base_client);
} else if ((unsigned)cmd >= custom_base_host) {
host_edit_mode_context_menu_command(p_id,pt,fromkeyboard,(unsigned)cmd,custom_base_host);
}
break;
}
}
}
void ui_element_helpers::ui_element_edit_tools::on_elem_replace(unsigned p_id, GUID const & newGuid) {
m_replace_dialog.Detach();
if ( newGuid != pfc::guid_null ) {
host_replace_element(p_id,newGuid);
}
}
void ui_element_helpers::ui_element_edit_tools::replace_dialog(HWND p_parent,unsigned p_id,const GUID & p_current) {
_release_replace_dialog();
auto ks = m_killSwitch;
auto reply = ui_element_replace_dialog_notify::create( [=] ( GUID newGUID ) {
if ( !*ks) {
on_elem_replace(p_id, newGUID );
}
} );
HWND dlg = ui_element_common_methods_v3::get()->replace_element_dialog_start( p_parent, p_current, reply );
m_replace_dialog.Attach( dlg );
}
const char * ui_element_helpers::ui_element_edit_tools::description_from_menu_command(unsigned p_id) {
using namespace HostHelperIDs;
switch(p_id) {
case ID_REPLACE:
return ReplaceUIElementDescription;
case ID_CUT:
return CutUIElementDescription;
case ID_COPY:
return CopyUIElementDescription;
case ID_PASTE:
return PasteUIElementDescription;
case ID_ADD_NEW:
return AddNewUIElementDescription;
default:
return NULL;
}
}
void ui_element_helpers::ui_element_edit_tools::_release_replace_dialog() {
if (m_replace_dialog.m_hWnd != NULL) {
m_replace_dialog.DestroyWindow();
}
}
bool ui_element_helpers::enforce_min_max_info(CWindow p_window,ui_element_min_max_info const & p_info) {
CRect rect;
WIN32_OP_D( p_window.GetWindowRect(&rect) );
t_uint32 width = (t_uint32) rect.Width();
t_uint32 height = (t_uint32) rect.Height();
bool changed = false;
if (width < p_info.m_min_width) {changed = true; width = p_info.m_min_width;}
if (width > p_info.m_max_width) {changed = true; width = p_info.m_max_width;}
if (height < p_info.m_min_height) {changed = true; height = p_info.m_min_height;}
if (height > p_info.m_max_height) {changed = true; height = p_info.m_max_height;}
if (changed) {
WIN32_OP_D( p_window.SetWindowPos(NULL,0,0,width,height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER) );
}
return changed;
}
void ui_element_helpers::handle_WM_GETMINMAXINFO(LPARAM p_lp,const ui_element_min_max_info & p_myinfo) {
MINMAXINFO * info = reinterpret_cast<MINMAXINFO *>(p_lp);
/*console::formatter() << "handle_WM_GETMINMAXINFO";
console::formatter() << p_myinfo.m_min_width << ", " << p_myinfo.m_min_height;
console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize;*/
pfc::max_acc(info->ptMinTrackSize.x,(LONG)p_myinfo.m_min_width);
pfc::max_acc(info->ptMinTrackSize.y,(LONG)p_myinfo.m_min_height);
if ((LONG) p_myinfo.m_max_width >= 0) pfc::min_acc(info->ptMaxTrackSize.x, (LONG) p_myinfo.m_max_width);
if ((LONG) p_myinfo.m_max_height >= 0) pfc::min_acc(info->ptMaxTrackSize.y, (LONG) p_myinfo.m_max_height);
//console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize;
}
bool ui_element_helpers::ui_element_edit_tools::host_paste_element(unsigned p_id) {
pfc::com_ptr_t<IDataObject> obj;
if (SUCCEEDED(OleGetClipboard(obj.receive_ptr()))) {
DWORD effect;
ui_element_config::ptr cfg;
if (static_api_ptr_t<ui_element_common_methods>()->parse_dataobject(obj,cfg,effect)) {
host_replace_element(p_id, cfg);
IDataObjectUtils::SetDataObjectDWORD(obj, RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT), effect);
IDataObjectUtils::PasteSucceeded(obj,effect);
if (effect == DROPEFFECT_MOVE) OleSetClipboard(NULL);
return true;
}
}
return false;
}
bool ui_element_helpers::recurse_for_elem_config(ui_element_config::ptr root, ui_element_config::ptr & out, const GUID & toFind) {
const GUID rootID = root->get_guid();
if (rootID == toFind) {
out = root; return true;
}
ui_element::ptr elem;
if (!find(elem, rootID)) return false;
ui_element_children_enumerator::ptr children;
try {
children = elem->enumerate_children(root);
} catch(exception_io_data) {return false;}
if (children.is_empty()) return false;
const t_size childrenTotal = children->get_count();
for(t_size walk = 0; walk < childrenTotal; ++walk) {
if (recurse_for_elem_config(children->get_item(walk), out, toFind)) return true;
}
return false;
}
bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityVisibleChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority) {
double bestPriority = 0;
ui_element_instance_ptr best;
t_size bestWhich = ~0;
const t_size count = host_get_children_count();
for (t_size walk = 0; walk < count; ++walk) if (this->host_is_child_visible(walk) ) {
ui_element_instance_ptr item = host_get_child(walk);
if (item.is_valid() ) {
const double priority = item->get_focus_priority();
if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) {
best = item; bestPriority = priority; bestWhich = walk;
}
}
}
if (best.is_empty()) return false;
out = best; outPriority = bestPriority; outWhich = bestWhich; return true;
}
bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority, const GUID & subclass) {
double bestPriority = 0;
ui_element_instance_ptr best;
t_size bestWhich = ~0;
const t_size count = host_get_children_count();
for (t_size walk = 0; walk < count; ++walk) {
ui_element_instance_ptr item = host_get_child(walk);
if (item.is_valid()) {
double priority;
if (item->get_focus_priority_subclass(priority, subclass)) {
if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) {
best = item; bestPriority = priority; bestWhich = walk;
}
}
}
}
if (best.is_empty()) return false;
out = best; outPriority = bestPriority; outWhich = bestWhich; return true;
}
void ui_element_instance_standard_context_menu(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) {
CPoint pt;
bool fromKeyboard;
if (p_pt == -1) {
fromKeyboard = true;
if (!p_elem->edit_mode_context_menu_get_focus_point(pt)) {
CRect rc;
WIN32_OP_D(GetWindowRect(p_elem->get_wnd(), rc));
pt = rc.CenterPoint();
}
} else {
fromKeyboard = false;
pt = p_pt;
}
if (p_elem->edit_mode_context_menu_test(pt, fromKeyboard)) {
const unsigned idBase = 1;
CMenu menu;
WIN32_OP(menu.CreatePopupMenu());
p_elem->edit_mode_context_menu_build(pt, fromKeyboard, menu, idBase);
int cmd;
{
CMenuSelectionReceiver_UiElement receiver(p_elem, idBase);
cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, receiver);
}
if (cmd > 0) p_elem->edit_mode_context_menu_command(pt, fromKeyboard, cmd, idBase);
}
}
void ui_element_instance_standard_context_menu_eh(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) {
try {
ui_element_instance_standard_context_menu(p_elem, p_pt);
} catch (std::exception const & e) {
console::complain("Context menu failure", e);
}
}
#endif // FOOBAR2000_TARGET_VERSION >= 79

View File

@@ -1,377 +0,0 @@
#pragma once
// ====================================================================================================
// ui_element_helpers
// A framework for creating UI Elements that host other elements.
// All foo_ui_std elements that host other elements - such as splitters or tabs - are based on this.
// Note that API 79 (v1.4) or newer is required, earlier did not provide ui_element_common_methods_v3.
// ====================================================================================================
#if FOOBAR2000_TARGET_VERSION >= 79
#include <memory>
#include <functional>
namespace ui_element_helpers {
template<typename t_receiver> class ui_element_instance_callback_multi_impl : public ui_element_instance_callback_v3 {
public:
ui_element_instance_callback_multi_impl(t_size id, t_receiver * p_receiver) : m_receiver(p_receiver), m_id(id) {}
void on_min_max_info_change() {
if (m_receiver != NULL) m_receiver->on_min_max_info_change();
}
bool query_color(const GUID & p_what,t_ui_color & p_out) {
if (m_receiver != NULL) return m_receiver->query_color(p_what,p_out);
else return false;
}
bool request_activation(service_ptr_t<class ui_element_instance> p_item) {
if (m_receiver) return m_receiver->request_activation(m_id);
else return false;
}
bool is_edit_mode_enabled() {
if (m_receiver) return m_receiver->is_edit_mode_enabled();
else return false;
}
void request_replace(service_ptr_t<class ui_element_instance> p_item) {
if (m_receiver) m_receiver->request_replace(m_id);
}
t_ui_font query_font_ex(const GUID & p_what) {
if (m_receiver) return m_receiver->query_font_ex(p_what);
else return NULL;
}
t_size notify(ui_element_instance * source, const GUID & what, t_size param1, const void * param2, t_size param2size) {
if (m_receiver) return m_receiver->host_notify(source, what, param1, param2, param2size);
else return 0;
}
void orphan() {m_receiver = NULL;}
bool is_elem_visible(service_ptr_t<class ui_element_instance> elem) {
if (m_receiver) return m_receiver->is_elem_visible(m_id);
else return false;
}
void override_id(t_size id) {m_id = id;}
void on_alt_pressed(bool) {}
private:
t_size m_id;
t_receiver * m_receiver;
};
class ui_element_instance_callback_receiver_multi {
public:
virtual void on_min_max_info_change() {}
virtual bool query_color(const GUID & p_what,t_ui_color & p_out) {return false;}
virtual bool request_activation(t_size which) {return false;}
virtual bool is_edit_mode_enabled() {return false;}
virtual void request_replace(t_size which) {}
virtual t_ui_font query_font_ex(const GUID&) {return NULL;}
virtual bool is_elem_visible(t_size which) {return true;}
virtual t_size host_notify(ui_element_instance * source, const GUID & what, t_size param1, const void * param2, t_size param2size) {return 0;}
void ui_element_instance_callback_handle_remove(bit_array const & mask, t_size const oldCount) {
t_callback_list newCallbacks;
t_size newWalk = 0;
for(t_size walk = 0; walk < oldCount; ++walk) {
if (mask[walk]) {
t_callback_ptr ptr;
if (m_callbacks.query(walk,ptr)) {
ptr->orphan(); m_callbacks.remove(walk);
}
} else {
if (newWalk != walk) {
t_callback_ptr ptr;
if (m_callbacks.query(walk,ptr)) {
m_callbacks.remove(walk);
ptr->override_id(newWalk);
m_callbacks.set(newWalk,ptr);
}
}
++newWalk;
}
}
}
void ui_element_instance_callback_handle_reorder(const t_size * order, t_size count) {
t_callback_list newCallbacks;
for(t_size walk = 0; walk < count; ++walk) {
t_callback_ptr ptr;
if (m_callbacks.query(order[walk],ptr)) {
ptr->override_id(walk);
newCallbacks.set(walk,ptr);
m_callbacks.remove(order[walk]);
}
}
PFC_ASSERT( m_callbacks.get_count() == 0 );
ui_element_instance_callback_release_all();
m_callbacks = newCallbacks;
}
ui_element_instance_callback_ptr ui_element_instance_callback_get_ptr(t_size which) {
t_callback_ptr ptr;
if (!m_callbacks.query(which,ptr)) {
ptr = new service_impl_t<t_callback>(which,this);
m_callbacks.set(which,ptr);
}
return ptr;
}
ui_element_instance_callback_ptr ui_element_instance_callback_create(t_size which) {
ui_element_instance_callback_release(which);
t_callback_ptr ptr = new service_impl_t<t_callback>(which,this);
m_callbacks.set(which,ptr);
return ptr;
}
void ui_element_instance_callback_release_all() {
for(t_callback_list::const_iterator walk = m_callbacks.first(); walk.is_valid(); ++walk) {
walk->m_value->orphan();
}
m_callbacks.remove_all();
}
void ui_element_instance_callback_release(t_size which) {
t_callback_ptr ptr;
if (m_callbacks.query(which,ptr)) {
ptr->orphan();
m_callbacks.remove(which);
}
}
protected:
~ui_element_instance_callback_receiver_multi() {
ui_element_instance_callback_release_all();
}
ui_element_instance_callback_receiver_multi() {}
private:
typedef ui_element_instance_callback_receiver_multi t_self;
typedef ui_element_instance_callback_multi_impl<t_self> t_callback;
typedef service_ptr_t<t_callback> t_callback_ptr;
typedef pfc::map_t<t_size,t_callback_ptr> t_callback_list;
t_callback_list m_callbacks;
};
//! Parses container tree to find configuration of specified element inside a layout configuration.
bool recurse_for_elem_config(ui_element_config::ptr root, ui_element_config::ptr & out, const GUID & toFind);
ui_element_instance_ptr create_root_container(HWND p_parent,ui_element_instance_callback_ptr p_callback);
ui_element_instance_ptr instantiate(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback);
ui_element_instance_ptr instantiate_dummy(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback);
ui_element_instance_ptr update(ui_element_instance_ptr p_element,HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback);
bool find(service_ptr_t<ui_element> & p_out,const GUID & p_guid);
ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg);
void replace_with_new_element(ui_element_instance_ptr & p_item,const GUID & p_guid,HWND p_parent,ui_element_instance_callback_ptr p_callback);
class ui_element_highlight_scope {
public:
ui_element_highlight_scope(HWND wndElem) {
m_highlight = ui_element_common_methods_v3::get()->highlight_element( wndElem );
}
~ui_element_highlight_scope() {
DestroyWindow(m_highlight);
}
private:
ui_element_highlight_scope(const ui_element_highlight_scope&) = delete;
void operator=(const ui_element_highlight_scope&) = delete;
HWND m_highlight;
};
//! Helper class; provides edit-mode context menu functionality and interacts with "Replace UI Element" dialog. \n
//! Do not use directly - derive from ui_element_instance_host_base instead.
class ui_element_edit_tools {
public:
//! Override me
virtual void host_replace_element(unsigned p_id, ui_element_config::ptr cfg) {}
//! Override me
virtual void host_replace_element(unsigned p_id,const GUID & p_newguid) {}
//! Override me optionally if you customize edit mode context menu
virtual bool host_edit_mode_context_menu_test(unsigned p_childid,const POINT & p_point,bool p_fromkeyboard) {return false;}
//! Override me optionally if you customize edit mode context menu
virtual void host_edit_mode_context_menu_build(unsigned p_childid,const POINT & p_point,bool p_fromkeyboard,HMENU p_menu,unsigned & p_id_base) {}
//! Override me optionally if you customize edit mode context menu
virtual void host_edit_mode_context_menu_command(unsigned p_childid,const POINT & p_point,bool p_fromkeyboard,unsigned p_id,unsigned p_id_base) {}
//! Override me optionally if you customize edit mode context menu
virtual bool host_edit_mode_context_menu_get_description(unsigned p_childid,unsigned p_id,unsigned p_id_base,pfc::string_base & p_out) {return false;}
//! Initiates "Replace UI Element" dialog for one of your sub-elements.
void replace_dialog(HWND p_parent,unsigned p_id,const GUID & p_current);
//! Shows edit mode context menu for your element.
void standard_edit_context_menu(LPARAM p_point,ui_element_instance_ptr p_item,unsigned p_id,HWND p_parent);
static const char * description_from_menu_command(unsigned p_id);
bool host_paste_element(unsigned p_id);
BEGIN_MSG_MAP(ui_element_edit_tools)
MESSAGE_HANDLER(WM_DESTROY,OnDestroy)
END_MSG_MAP()
protected:
ui_element_edit_tools() {}
private:
void on_elem_replace(unsigned p_id,GUID const & newElem);
void _release_replace_dialog();
LRESULT OnDestroy(UINT,WPARAM,LPARAM,BOOL& bHandled) {bHandled = FALSE; *m_killSwitch = true; _release_replace_dialog(); return 0;}
CWindow m_replace_dialog;
std::shared_ptr<bool> m_killSwitch = std::make_shared<bool>();
ui_element_edit_tools( const ui_element_edit_tools & ) = delete;
void operator=( const ui_element_edit_tools & ) = delete;
};
//! Base class for ui_element_instances that host other elements.
class ui_element_instance_host_base : public ui_element_instance, protected ui_element_instance_callback_receiver_multi, protected ui_element_edit_tools {
protected:
// Any derived class must pass their messages to us, by CHAIN_MSG_MAP(ui_element_instance_host_base)
BEGIN_MSG_MAP(ui_element_instance_host_base)
CHAIN_MSG_MAP(ui_element_edit_tools)
MESSAGE_HANDLER(WM_SETTINGCHANGE,OnSettingChange);
END_MSG_MAP()
//override me
virtual ui_element_instance_ptr host_get_child(t_size which) = 0;
//override me
virtual t_size host_get_children_count() = 0;
//override me (tabs)
virtual void host_bring_to_front(t_size which) {}
//override me
virtual void on_min_max_info_change() {m_callback->on_min_max_info_change();}
//override me
virtual void host_replace_child(t_size which) = 0;
virtual bool host_is_child_visible(t_size which) {return true;}
void host_child_visibility_changed(t_size which, bool state) {
if (m_callback->is_elem_visible_(this)) {
ui_element_instance::ptr item = host_get_child(which);
if (item.is_valid()) item->notify(ui_element_notify_visibility_changed,state ? 1 : 0,NULL,0);
}
}
bool is_elem_visible(t_size which) {
if (!m_callback->is_elem_visible_(this)) return false;
return this->host_is_child_visible(which);
}
GUID get_subclass() {return ui_element_subclass_containers;}
double get_focus_priority() {
ui_element_instance_ptr item; double priority; t_size which;
if (!grabTopPriorityVisibleChild(item,which,priority)) return 0;
return priority;
}
void set_default_focus() {
ui_element_instance_ptr item; double priority; t_size which;
if (!grabTopPriorityVisibleChild(item,which,priority)) {
this->set_default_focus_fallback();
} else {
host_bring_to_front(which);
item->set_default_focus();
}
}
bool get_focus_priority_subclass(double & p_out,const GUID & p_subclass) {
ui_element_instance_ptr item; double priority; t_size which;
if (!grabTopPriorityChild(item,which,priority,p_subclass)) return false;
p_out = priority;
return true;
}
bool set_default_focus_subclass(const GUID & p_subclass) {
ui_element_instance_ptr item; double priority; t_size which;
if (!grabTopPriorityChild(item,which,priority,p_subclass)) return false;
host_bring_to_front(which);
return item->set_default_focus_subclass(p_subclass);
}
void notify(const GUID & p_what, t_size p_param1, const void * p_param2, t_size p_param2size) {
if (p_what == ui_element_notify_visibility_changed) {
const t_size total = host_get_children_count();
for(t_size walk = 0; walk < total; ++walk) {
if (this->host_is_child_visible(walk)) {
ui_element_instance_ptr item = host_get_child(walk);
if (item.is_valid()) item->notify(p_what,p_param1,p_param2,p_param2size);
}
}
} else if (p_what == ui_element_notify_get_element_labels) {
handleGetLabels(p_param1, p_param2, p_param2size);
} else {
const t_size total = host_get_children_count();
for(t_size walk = 0; walk < total; ++walk) {
ui_element_instance_ptr item = host_get_child(walk);
if (item.is_valid()) item->notify(p_what,p_param1,p_param2,p_param2size);
}
}
}
bool query_color(const GUID & p_what,t_ui_color & p_out) {return m_callback->query_color(p_what,p_out);}
bool request_activation(t_size which) {
if (!m_callback->request_activation(this)) return false;
host_bring_to_front(which);
return true;
}
bool is_edit_mode_enabled() {return m_callback->is_edit_mode_enabled();}
t_ui_font query_font_ex(const GUID& id) {return m_callback->query_font_ex(id);}
void request_replace(t_size which) {
host_replace_child(which);
}
private:
void handleGetLabelsChild(ui_element_instance::ptr child, t_size which, t_size param1, const void * param2, t_size param2size) {
if (child->get_subclass() == ui_element_subclass_containers) {
child->notify(ui_element_notify_get_element_labels, param1, param2, param2size);
} else if (child->get_guid() != pfc::guid_null && child->get_wnd() != NULL && this->host_is_child_visible(which)) {
FB2K_DYNAMIC_ASSERT(param2 != NULL);
reinterpret_cast<ui_element_notify_get_element_labels_callback*>(const_cast<void*>(param2))->set_visible_element(child);
}
}
void handleGetLabels(t_size param1, const void * param2, t_size param2size) {
const t_size childrenTotal = host_get_children_count();
for(t_size childWalk = 0; childWalk < childrenTotal; ++childWalk) {
ui_element_instance_ptr item = host_get_child(childWalk);
if (item.is_valid()) handleGetLabelsChild(item, childWalk, param1, param2, param2size);
}
}
LRESULT OnSettingChange(UINT msg,WPARAM wp,LPARAM lp,BOOL& bHandled) {
bHandled = FALSE;
const t_size total = host_get_children_count();
for(t_size walk = 0; walk < total; ++walk) {
ui_element_instance::ptr item = host_get_child(walk);
if (item.is_valid()) {
::SendMessage(item->get_wnd(),msg,wp,lp);
}
}
return 0;
}
t_size whichChild(ui_element_instance_ptr child) {
const t_size count = host_get_children_count();
for(t_size walk = 0; walk < count; ++walk) {
if (child == host_get_child(walk)) return walk;
}
return ~0;
}
bool childPriorityCompare(t_size which, double priority, double bestPriority) {
if (host_is_child_visible(which)) return priority >= bestPriority;
else return priority > bestPriority;
}
bool grabTopPriorityVisibleChild(ui_element_instance_ptr & out,t_size & outWhich,double & outPriority);
bool grabTopPriorityChild(ui_element_instance_ptr & out,t_size & outWhich,double & outPriority,const GUID & subclass);
protected:
ui_element_instance_host_base(ui_element_instance_callback::ptr callback) : m_callback(callback) {}
const ui_element_instance_callback::ptr m_callback;
};
bool enforce_min_max_info(CWindow p_window,ui_element_min_max_info const & p_info);
void handle_WM_GETMINMAXINFO(LPARAM p_lp,const ui_element_min_max_info & p_myinfo);
};
void ui_element_instance_standard_context_menu(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt);
void ui_element_instance_standard_context_menu_eh(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt);
#endif // FOOBAR2000_TARGET_VERSION >= 79

View File

@@ -1,5 +1,7 @@
#include "stdafx.h"
#include "win32_misc.h"
#include "TypeFind.h"
#include "SmartStrStr.h"
#ifdef FOOBAR2000_MOBILE_WINDOWS
#include <pfc/pp-winapi.h>
@@ -119,6 +121,153 @@ void registerclass_scope_delayed::toggle_off() {
}
}
unsigned QueryScreenDPI(HWND wnd) {
HDC dc = GetDC(wnd);
unsigned ret = GetDeviceCaps(dc,LOGPIXELSY);
ReleaseDC(wnd,dc);
return ret;
}
unsigned QueryScreenDPI_X(HWND wnd) {
HDC dc = GetDC(wnd);
unsigned ret = GetDeviceCaps(dc,LOGPIXELSX);
ReleaseDC(wnd,dc);
return ret;
}
unsigned QueryScreenDPI_Y(HWND wnd) {
HDC dc = GetDC(wnd);
unsigned ret = GetDeviceCaps(dc,LOGPIXELSY);
ReleaseDC(wnd,dc);
return ret;
}
SIZE QueryScreenDPIEx(HWND wnd) {
HDC dc = GetDC(wnd);
SIZE ret = { GetDeviceCaps(dc,LOGPIXELSX), GetDeviceCaps(dc,LOGPIXELSY) };
ReleaseDC(wnd,dc);
return ret;
}
bool IsMenuNonEmpty(HMENU menu) {
unsigned n,m=GetMenuItemCount(menu);
for(n=0;n<m;n++) {
if (GetSubMenu(menu,n)) return true;
if (!(GetMenuState(menu,n,MF_BYPOSITION)&MF_SEPARATOR)) return true;
}
return false;
}
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL() {
const DWORD code = GetLastError();
PFC_ASSERT( code != NO_ERROR );
throw exception_win32(code);
}
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL_CRITICAL(const char * what) {
const DWORD code = GetLastError();
PFC_ASSERT( code != NO_ERROR );
pfc::string_formatter msg; msg << what << " failure #" << (uint32_t)code;
TRACK_CODE(msg.get_ptr(), uBugCheck());
}
#ifdef _DEBUG
void WIN32_OP_D_FAIL(const wchar_t * _Message, const wchar_t *_File, unsigned _Line) {
const DWORD code = GetLastError();
pfc::array_t<wchar_t> msgFormatted; msgFormatted.set_size(pfc::strlen_t(_Message) + 64);
wsprintfW(msgFormatted.get_ptr(), L"%s (code: %u)", _Message, code);
if (IsDebuggerPresent()) {
OutputDebugString(TEXT("WIN32_OP_D() failure:\n"));
OutputDebugString(msgFormatted.get_ptr());
OutputDebugString(TEXT("\n"));
pfc::crash();
}
_wassert(msgFormatted.get_ptr(),_File,_Line);
}
#endif
void GetOSVersionString(pfc::string_base & out) {
out.reset(); GetOSVersionStringAppend(out);
}
static bool running_under_wine(void) {
HMODULE module = GetModuleHandle(_T("ntdll.dll"));
if (!module) return false;
return GetProcAddress(module, "wine_server_call") != NULL;
}
static bool FetchWineInfoAppend(pfc::string_base & out) {
typedef const char *(__cdecl *t_wine_get_build_id)(void);
typedef void (__cdecl *t_wine_get_host_version)( const char **sysname, const char **release );
const HMODULE ntdll = GetModuleHandle(_T("ntdll.dll"));
if (ntdll == NULL) return false;
t_wine_get_build_id wine_get_build_id;
t_wine_get_host_version wine_get_host_version;
wine_get_build_id = (t_wine_get_build_id)GetProcAddress(ntdll, "wine_get_build_id");
wine_get_host_version = (t_wine_get_host_version)GetProcAddress(ntdll, "wine_get_host_version");
if (wine_get_build_id == NULL || wine_get_host_version == NULL) {
if (GetProcAddress(ntdll, "wine_server_call") != NULL) {
out << "wine (unknown version)";
return true;
}
return false;
}
const char * sysname = NULL; const char * release = NULL;
wine_get_host_version(&sysname, &release);
out << wine_get_build_id() << ", on: " << sysname << " / " << release;
return true;
}
void GetOSVersionStringAppend(pfc::string_base & out) {
if (FetchWineInfoAppend(out)) return;
OSVERSIONINFO ver = {}; ver.dwOSVersionInfoSize = sizeof(ver);
WIN32_OP( GetVersionEx(&ver) );
SYSTEM_INFO info = {};
GetNativeSystemInfo(&info);
out << "Windows " << (int)ver.dwMajorVersion << "." << (int)ver.dwMinorVersion << "." << (int)ver.dwBuildNumber;
if (ver.szCSDVersion[0] != 0) out << " " << pfc::stringcvt::string_utf8_from_os(ver.szCSDVersion, PFC_TABSIZE(ver.szCSDVersion));
switch(info.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64:
out << " x64"; break;
case PROCESSOR_ARCHITECTURE_IA64:
out << " IA64"; break;
case PROCESSOR_ARCHITECTURE_INTEL:
out << " x86"; break;
}
}
void SetDefaultMenuItem(HMENU p_menu,unsigned p_id) {
MENUITEMINFO info = {sizeof(info)};
info.fMask = MIIM_STATE;
GetMenuItemInfo(p_menu,p_id,FALSE,&info);
info.fState |= MFS_DEFAULT;
SetMenuItemInfo(p_menu,p_id,FALSE,&info);
}
bool SetClipboardDataBlock(UINT p_format, const void * p_block, t_size p_block_size) {
bool success = false;
if (OpenClipboard(NULL)) {
EmptyClipboard();
HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, p_block_size);
if (handle == NULL) {
CloseClipboard();
throw std::bad_alloc();
}
{CGlobalLock lock(handle);memcpy(lock.GetPtr(), p_block, p_block_size);}
if (SetClipboardData(p_format, handle) == NULL) {
GlobalFree(handle);//todo?
}
else {
success = true;
}
CloseClipboard();
}
return success;
}
void CModelessDialogEntry::Set(HWND p_new) {
auto api = modeless_dialog_manager::get();
if (m_wnd) api->remove(m_wnd);
@@ -183,6 +332,16 @@ void winLocalFileScope::close() {
m_path.clear();
}
LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc) {
LRESULT status;
POINT pt = { 0, 0 }, pt_old = { 0,0 };
MapWindowPoints(p_from, p_to, &pt, 1);
OffsetWindowOrgEx(p_dc, pt.x, pt.y, &pt_old);
status = SendMessage(p_to, WM_ERASEBKGND, (WPARAM)p_dc, 0);
SetWindowOrgEx(p_dc, pt_old.x, pt_old.y, 0);
return status;
}
bool IsWindowsS() {
bool ret = false;
#if FB2K_TARGET_MICROSOFT_STORE
@@ -208,8 +367,58 @@ bool IsWindowsS() {
return ret;
}
WORD GetOSVersion() {
// wrap libPPUI function
return ::GetOSVersionCode();
// TYPEFIND IMPLEMENTATION
namespace TypeFindImpl {
static size_t _ItemCount(HWND wnd) {
return ListView_GetItemCount(wnd);
}
static const wchar_t * _ItemText(HWND wnd, size_t index, int subItem = 0) {
NMLVDISPINFO info = {};
info.hdr.code = LVN_GETDISPINFO;
info.hdr.idFrom = GetDlgCtrlID(wnd);
info.hdr.hwndFrom = wnd;
info.item.iItem = index;
info.item.iSubItem = subItem;
info.item.mask = LVIF_TEXT;
::SendMessage(::GetParent(wnd), WM_NOTIFY, info.hdr.idFrom, reinterpret_cast<LPARAM>(&info));
if (info.item.pszText == NULL) return L"";
return info.item.pszText;
}
};
LRESULT TypeFind::Handler(NMHDR* hdr, int subItemFrom, int subItemCnt) {
using namespace TypeFindImpl;
NMLVFINDITEM * info = reinterpret_cast<NMLVFINDITEM*>(hdr);
const HWND wnd = hdr->hwndFrom;
if (info->lvfi.flags & LVFI_NEARESTXY) return -1;
const size_t count = _ItemCount(wnd);
if (count == 0) return -1;
const size_t base = (size_t)info->iStart % count;
static SmartStrStr tool;
for (size_t walk = 0; walk < count; ++walk) {
const size_t index = (walk + base) % count;
for (int subItem = subItemFrom; subItem < subItemFrom + subItemCnt; ++subItem) {
if (tool.matchHereW(_ItemText(wnd, index, subItem), info->lvfi.psz)) return (LRESULT)index;
}
}
for (size_t walk = 0; walk < count; ++walk) {
const size_t index = (walk + base) % count;
for (int subItem = subItemFrom; subItem < subItemFrom + subItemCnt; ++subItem) {
if ( tool.strStrEndW(_ItemText(wnd, index, subItem), info->lvfi.psz) ) return (LRESULT)index;
}
}
return -1;
}
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -3,8 +3,19 @@
#ifdef _WIN32
#include <libPPUI/win32_op.h>
#include <libPPUI/win32_utility.h>
#include "win32_op.h"
class CloseHandleScope {
public:
CloseHandleScope(HANDLE handle) throw() : m_handle(handle) {}
~CloseHandleScope() throw() { CloseHandle(m_handle); }
HANDLE Detach() throw() { return pfc::replace_t(m_handle, INVALID_HANDLE_VALUE); }
HANDLE Get() const throw() { return m_handle; }
void Close() throw() { CloseHandle(Detach()); }
PFC_CLASS_NOT_COPYABLE_EX(CloseHandleScope)
private:
HANDLE m_handle;
};
class mutexScope {
@@ -20,7 +31,8 @@ private:
class registerclass_scope_delayed {
public:
registerclass_scope_delayed() {}
registerclass_scope_delayed() : m_class(0) {}
bool is_registered() const {return m_class != 0;}
void toggle_on(UINT p_style,WNDPROC p_wndproc,int p_clsextra,int p_wndextra,HICON p_icon,HCURSOR p_cursor,HBRUSH p_background,const TCHAR * p_classname,const TCHAR * p_menuname);
void toggle_off();
@@ -28,14 +40,51 @@ public:
~registerclass_scope_delayed() {toggle_off();}
private:
registerclass_scope_delayed(const registerclass_scope_delayed &) = delete;
const registerclass_scope_delayed & operator=(const registerclass_scope_delayed &) = delete;
registerclass_scope_delayed(const registerclass_scope_delayed &) {throw pfc::exception_not_implemented();}
const registerclass_scope_delayed & operator=(const registerclass_scope_delayed &) {throw pfc::exception_not_implemented();}
ATOM m_class = 0;
ATOM m_class;
};
typedef CGlobalLockScope CGlobalLock; // compatibility
typedef CGlobalLockScope CGlobalLock;//for compatibility, implementation moved elsewhere
bool SetClipboardDataBlock(UINT p_format, const void * p_block, t_size p_block_size);
template<typename t_array>
static bool SetClipboardDataBlock(UINT p_format,const t_array & p_array) {
PFC_STATIC_ASSERT( sizeof(p_array[0]) == 1 );
return SetClipboardDataBlock(p_format,p_array.get_ptr(),p_array.get_size());
}
template<typename t_array>
static bool GetClipboardDataBlock(UINT p_format,t_array & p_array) {
PFC_STATIC_ASSERT( sizeof(p_array[0]) == 1 );
if (OpenClipboard(NULL)) {
HANDLE handle = GetClipboardData(p_format);
if (handle == NULL) {
CloseClipboard();
return false;
}
{
CGlobalLock lock(handle);
const t_size size = lock.GetSize();
try {
p_array.set_size(size);
} catch(...) {
CloseClipboard();
throw;
}
memcpy(p_array.get_ptr(),lock.GetPtr(),size);
}
CloseClipboard();
return true;
} else {
return false;
}
}
class OleInitializeScope {
public:
@@ -55,7 +104,17 @@ private:
PFC_CLASS_NOT_COPYABLE_EX(CoInitializeScope)
};
WORD GetOSVersion();
unsigned QueryScreenDPI(HWND wnd = NULL);
unsigned QueryScreenDPI_X(HWND wnd = NULL);
unsigned QueryScreenDPI_Y(HWND wnd = NULL);
SIZE QueryScreenDPIEx(HWND wnd = NULL);
static WORD GetOSVersion() {
const DWORD ver = GetVersion();
return (WORD)HIBYTE(LOWORD(ver)) | ((WORD)LOBYTE(LOWORD(ver)) << 8);
}
#if _WIN32_WINNT >= 0x501
#define WS_EX_COMPOSITED_Safe() WS_EX_COMPOSITED
@@ -66,6 +125,26 @@ static DWORD WS_EX_COMPOSITED_Safe() {
#endif
class EnableWindowScope {
public:
EnableWindowScope(HWND p_window,BOOL p_state) throw() : m_window(p_window) {
m_oldState = IsWindowEnabled(m_window);
EnableWindow(m_window,p_state);
}
~EnableWindowScope() throw() {
EnableWindow(m_window,m_oldState);
}
private:
BOOL m_oldState;
HWND m_window;
};
bool IsMenuNonEmpty(HMENU menu);
class CModelessDialogEntry {
public:
CModelessDialogEntry() : m_wnd() {}
@@ -78,6 +157,12 @@ private:
HWND m_wnd;
};
void GetOSVersionString(pfc::string_base & out);
void GetOSVersionStringAppend(pfc::string_base & out);
void SetDefaultMenuItem(HMENU p_menu,unsigned p_id);
class CDLL {
public:
#ifdef _DEBUG
@@ -120,7 +205,7 @@ public:
void open(const char * inPath, file::ptr inReader, abort_callback & aborter);
void close();
winLocalFileScope() {}
winLocalFileScope() : m_isTemp() {}
winLocalFileScope(const char * inPath, file::ptr inReader, abort_callback & aborter) : m_isTemp() {
open(inPath, inReader, aborter);
}
@@ -130,9 +215,8 @@ public:
}
const wchar_t * Path() const { return m_path.c_str(); }
bool isTemp() const { return m_isTemp; }
private:
bool m_isTemp = false;
bool m_isTemp;
std::wstring m_path;
};
@@ -163,6 +247,8 @@ private:
CMutex & m_mutex;
};
LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc);
bool IsWindowsS();
#endif // _WIN32

View File

@@ -0,0 +1,36 @@
#pragma once
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL();
PFC_NORETURN PFC_NOINLINE void WIN32_OP_FAIL_CRITICAL(const char * what);
#ifdef _DEBUG
void WIN32_OP_D_FAIL(const wchar_t * _Message, const wchar_t *_File, unsigned _Line);
#endif
//Throws an exception when (OP) evaluates to false/zero.
#define WIN32_OP(OP) \
{ \
SetLastError(NO_ERROR); \
if (!(OP)) WIN32_OP_FAIL(); \
}
// Kills the application with appropriate debug info when (OP) evaluates to false/zero.
#define WIN32_OP_CRITICAL(WHAT, OP) \
{ \
SetLastError(NO_ERROR); \
if (!(OP)) WIN32_OP_FAIL_CRITICAL(WHAT); \
}
//WIN32_OP_D() acts like an assert specialized for win32 operations in debug build, ignores the return value / error codes in release build.
//Use WIN32_OP_D() instead of WIN32_OP() on operations that are extremely unlikely to fail, so failure condition checks are performed in the debug build only, to avoid bloating release code with pointless error checks.
#ifdef _DEBUG
#define WIN32_OP_D(OP) \
{ \
SetLastError(NO_ERROR); \
if (!(OP)) WIN32_OP_D_FAIL(PFC_WIDESTRING(#OP), PFC_WIDESTRING(__FILE__), __LINE__); \
}
#else
#define WIN32_OP_D(OP) (void)( (OP), 0);
#endif

View File

@@ -40,7 +40,7 @@ static bool test_rect(const RECT * rc) {
}
bool cfg_window_placement_common::read_from_window(HWND window)
bool cfg_window_placement::read_from_window(HWND window)
{
WINDOWPLACEMENT wp = {};
if (g_is_enabled()) {
@@ -53,8 +53,6 @@ bool cfg_window_placement_common::read_from_window(HWND window)
/*if (wp.showCmd == SW_SHOWNORMAL) {
GetWindowRect(window, &wp.rcNormalPosition);
}*/
if ( !IsWindowVisible( window ) ) wp.showCmd = SW_HIDE;
}
/*else
{
@@ -65,26 +63,23 @@ bool cfg_window_placement_common::read_from_window(HWND window)
return m_data.length == sizeof(m_data);
}
bool cfg_window_placement_common::apply_to_window(HWND window, bool allowHidden) {
void cfg_window_placement::on_window_creation_silent(HWND window) {
PFC_ASSERT(!m_windows.have_item(window));
m_windows.add_item(window);
}
bool cfg_window_placement::on_window_creation(HWND window)
{
bool ret = false;
PFC_ASSERT(!m_windows.have_item(window));
m_windows.add_item(window);
if (g_is_enabled())
{
if (m_data.length == sizeof(m_data) && test_rect(&m_data.rcNormalPosition))
if (m_data.length==sizeof(m_data) && test_rect(&m_data.rcNormalPosition))
{
if (allowHidden || m_data.showCmd != SW_HIDE) {
if (m_data.showCmd == SW_HIDE && (m_data.flags & WPF_RESTORETOMAXIMIZED)) {
// Special case of hidden-from-maximized
auto fix = m_data;
fix.showCmd = SW_SHOWMINIMIZED;
if (SetWindowPlacement(window, &fix)) {
ShowWindow(window, SW_HIDE);
ret = true;
}
} else {
if (SetWindowPlacement(window, &m_data)) {
ret = true;
}
}
if (SetWindowPlacement(window,&m_data))
{
ret = true;
}
}
}
@@ -92,17 +87,6 @@ bool cfg_window_placement_common::apply_to_window(HWND window, bool allowHidden)
return ret;
}
void cfg_window_placement::on_window_creation_silent(HWND window) {
PFC_ASSERT(!m_windows.have_item(window));
m_windows.add_item(window);
}
bool cfg_window_placement::on_window_creation(HWND window, bool allowHidden) {
PFC_ASSERT(!m_windows.have_item(window));
m_windows.add_item(window);
return apply_to_window(window, allowHidden);
}
void cfg_window_placement::on_window_destruction(HWND window)
{
@@ -113,12 +97,6 @@ void cfg_window_placement::on_window_destruction(HWND window)
}
}
void cfg_window_placement_common::get_data_raw(stream_writer* p_stream, abort_callback& p_abort) {
if (m_data.length == sizeof(m_data)) {
p_stream->write_object(&m_data, sizeof(m_data), p_abort);
}
}
void cfg_window_placement::get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {
if (g_is_enabled()) {
{
@@ -130,16 +108,25 @@ void cfg_window_placement::get_data_raw(stream_writer * p_stream,abort_callback
}
}
cfg_window_placement_common::get_data_raw(p_stream, p_abort);
if (m_data.length == sizeof(m_data)) {
p_stream->write_object(&m_data,sizeof(m_data),p_abort);
}
}
}
void cfg_window_placement_common::set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
void cfg_window_placement::set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
if (p_sizehint == 0) return;
WINDOWPLACEMENT temp;
if (p_sizehint == sizeof(temp)) {
p_stream->read_object(&temp, sizeof(temp), p_abort);
if (temp.length == sizeof(temp)) m_data = temp;
}
try {
p_stream->read_object(&temp,sizeof(temp),p_abort);
} catch(exception_io_data) {return;}
if (temp.length == sizeof(temp)) m_data = temp;
}
cfg_window_placement::cfg_window_placement(const GUID & p_guid) : cfg_var(p_guid)
{
memset(&m_data,0,sizeof(m_data));
}

View File

@@ -2,38 +2,21 @@
#ifdef FOOBAR2000_DESKTOP_WINDOWS
class cfg_window_placement_common : public cfg_var {
public:
cfg_window_placement_common(const GUID& guid) : cfg_var(guid) {}
bool read_from_window(HWND window);
bool apply_to_window(HWND window, bool allowHidden);
protected:
void get_data_raw(stream_writer* p_stream, abort_callback& p_abort) override;
void set_data_raw(stream_reader* p_stream, t_size p_sizehint, abort_callback& p_abort) override;
WINDOWPLACEMENT m_data = {};
};
class cfg_window_placement : public cfg_window_placement_common
class cfg_window_placement : public cfg_var
{
public:
bool on_window_creation(HWND window, bool allowHidden = false);//returns true if window position has been changed, false if not
bool on_window_creation(HWND window);//returns true if window position has been changed, false if not
void on_window_creation_silent(HWND window);
void on_window_destruction(HWND window);
cfg_window_placement(const GUID& guid) : cfg_window_placement_common(guid) {}
protected:
void get_data_raw(stream_writer* p_stream, abort_callback& p_abort) override;
bool read_from_window(HWND window);
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort);
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort);
cfg_window_placement(const GUID & p_guid);
private:
pfc::list_hybrid_t<HWND,2> m_windows;
WINDOWPLACEMENT m_data;
};
class cfg_window_placement_v2 : public cfg_window_placement_common {
public:
cfg_window_placement_v2(const GUID& guid) : cfg_window_placement_common(guid) {}
// All already in cfg_window_placement_common
};
class cfg_window_size : public cfg_var
{
public:
@@ -48,4 +31,4 @@ private:
t_uint32 m_width,m_height;
};
#endif // FOOBAR2000_DESKTOP_WINDOWS
#endif // FOOBAR2000_DESKTOP_WINDOWS

View File

@@ -1,4 +0,0 @@
#pragma once
// Blank header for source compatibility
// WAVEFORMATEX and such are declared here on non Windows

View File

@@ -376,11 +376,12 @@ static std::vector<uint8_t> makeWavHeader( const wavWriterSetup_t & setup, t_fil
temp->seek( 0, aborter );
temp->read_object( &ret[0], s, aborter );
}
return ret;
return std::move(ret);
}
file::ptr makeLiveWAVFile( const wavWriterSetup_t & setup, file::ptr data ) {
t_filesize size = data->get_size( fb2k::noAbort );
auto vec = makeWavHeader( setup, size, fb2k::noAbort );
abort_callback_dummy aborter;
t_filesize size = data->get_size( aborter );
auto vec = makeWavHeader( setup, size, aborter );
return new service_impl_t< fileWav >( std::move(vec), data );
}