add last backwards-compatible version
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();}
|
||||
};
|
||||
@@ -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);
|
||||
@@ -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()
|
||||
92
foobar2000/helpers/CPowerRequest.cpp
Normal file
92
foobar2000/helpers/CPowerRequest.cpp
Normal 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
|
||||
|
||||
115
foobar2000/helpers/CPowerRequest.h
Normal file
115
foobar2000/helpers/CPowerRequest.h
Normal 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
|
||||
@@ -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 );
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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>);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
191
foobar2000/helpers/IDataObjectUtils.cpp
Normal file
191
foobar2000/helpers/IDataObjectUtils.cpp
Normal 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
|
||||
186
foobar2000/helpers/IDataObjectUtils.h
Normal file
186
foobar2000/helpers/IDataObjectUtils.h
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
189
foobar2000/helpers/SmartStrStr.cpp
Normal file
189
foobar2000/helpers/SmartStrStr.cpp
Normal 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");
|
||||
}
|
||||
39
foobar2000/helpers/SmartStrStr.h
Normal file
39
foobar2000/helpers/SmartStrStr.h
Normal 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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
6
foobar2000/helpers/TypeFind.h
Normal file
6
foobar2000/helpers/TypeFind.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
class TypeFind {
|
||||
public:
|
||||
static LRESULT Handler(NMHDR* hdr, int subItemFrom = 0, int subItemCnt = 1);
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// fb2k mobile compat
|
||||
@@ -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); }
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
42
foobar2000/helpers/clipboard.cpp
Normal file
42
foobar2000/helpers/clipboard.cpp
Normal 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
|
||||
47
foobar2000/helpers/clipboard.h
Normal file
47
foobar2000/helpers/clipboard.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
// fb2k mobile compat
|
||||
|
||||
#include "../SDK/filesystem_helper.h"
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
119
foobar2000/helpers/gdiplus_helpers.h
Normal file
119
foobar2000/helpers/gdiplus_helpers.h
Normal 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")
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) );
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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); }
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
193
foobar2000/helpers/listview_helper.cpp
Normal file
193
foobar2000/helpers/listview_helper.cpp
Normal 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
|
||||
51
foobar2000/helpers/listview_helper.h
Normal file
51
foobar2000/helpers/listview_helper.h
Normal 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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
36
foobar2000/helpers/win32_op.h
Normal file
36
foobar2000/helpers/win32_op.h
Normal 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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Blank header for source compatibility
|
||||
// WAVEFORMATEX and such are declared here on non Windows
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user