add last backwards-compatible version
This commit is contained in:
23
foobar2000/ATLHelpers/ATLHelpers.h
Normal file
23
foobar2000/ATLHelpers/ATLHelpers.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "ATLHelpersLean.h"
|
||||
|
||||
// #pragma message("Avoid using this header. #include ATLHelpersLean.h then individual helpers instead")
|
||||
|
||||
#include "../helpers/helpers.h"
|
||||
|
||||
#include "WTL-PP.h"
|
||||
#include "misc.h"
|
||||
#include "GDIUtils.h"
|
||||
|
||||
#include "WindowPositionUtils.h"
|
||||
#include "CDialogResizeHelper.h"
|
||||
|
||||
#include "BumpableElem.h"
|
||||
|
||||
#include "inplace_edit.h"
|
||||
#include "inplace_edit_v2.h"
|
||||
|
||||
#include "AutoComplete.h"
|
||||
|
||||
#include "Controls.h"
|
||||
184
foobar2000/ATLHelpers/AutoComplete.cpp
Normal file
184
foobar2000/ATLHelpers/AutoComplete.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include "stdafx.h"
|
||||
#include <ShlGuid.h> // CLSID_AutoComplete
|
||||
#include "../helpers/COM_utils.h"
|
||||
#include "../helpers/dropdown_helper.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class CEnumString : public IEnumString {
|
||||
public:
|
||||
typedef pfc::chain_list_v2_t<pfc::array_t<TCHAR> > t_data;
|
||||
CEnumString(const t_data & in) : m_data(in) {m_walk = m_data.first();}
|
||||
CEnumString() {}
|
||||
|
||||
void AddString(const TCHAR * in) {
|
||||
m_data.insert_last()->set_data_fromptr(in, _tcslen(in) + 1);
|
||||
m_walk = m_data.first();
|
||||
}
|
||||
void AddStringU(const char * in, t_size len) {
|
||||
pfc::array_t<TCHAR> & arr = * m_data.insert_last();
|
||||
arr.set_size( pfc::stringcvt::estimate_utf8_to_wide( in, len ) );
|
||||
pfc::stringcvt::convert_utf8_to_wide( arr.get_ptr(), arr.get_size(), in, len);
|
||||
m_walk = m_data.first();
|
||||
}
|
||||
void AddStringU(const char * in) {
|
||||
pfc::array_t<TCHAR> & arr = * m_data.insert_last();
|
||||
arr.set_size( pfc::stringcvt::estimate_utf8_to_wide( in ) );
|
||||
pfc::stringcvt::convert_utf8_to_wide_unchecked( arr.get_ptr(), in );
|
||||
m_walk = m_data.first();
|
||||
}
|
||||
void ResetStrings() {m_walk.invalidate(); m_data.remove_all();}
|
||||
|
||||
typedef ImplementCOMRefCounter<CEnumString> TImpl;
|
||||
COM_QI_BEGIN()
|
||||
COM_QI_ENTRY(IUnknown)
|
||||
COM_QI_ENTRY(IEnumString)
|
||||
COM_QI_END()
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Next( ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) {
|
||||
if (rgelt == NULL) return E_INVALIDARG;
|
||||
ULONG done = 0;
|
||||
while( done < celt && m_walk.is_valid()) {
|
||||
rgelt[done] = CoStrDup(m_walk->get_ptr());
|
||||
++m_walk; ++done;
|
||||
}
|
||||
if (pceltFetched != NULL) *pceltFetched = done;
|
||||
return done == celt ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
static TCHAR * CoStrDup(const TCHAR * in) {
|
||||
const size_t lenBytes = (_tcslen(in) + 1) * sizeof(TCHAR);
|
||||
TCHAR * out = reinterpret_cast<TCHAR*>(CoTaskMemAlloc(lenBytes));
|
||||
if (out) memcpy(out, in, lenBytes);
|
||||
return out;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Skip(ULONG celt) {
|
||||
while(celt > 0) {
|
||||
if (m_walk.is_empty()) return S_FALSE;
|
||||
--celt; ++m_walk;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Reset() {
|
||||
m_walk = m_data.first();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum) {
|
||||
*ppenum = new TImpl(*this); return S_OK;
|
||||
}
|
||||
private:
|
||||
t_data m_data;
|
||||
t_data::const_iterator m_walk;
|
||||
};
|
||||
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 InitializeSimpleAC(HWND edit, IUnknown * vals, DWORD opts) {
|
||||
pfc::com_ptr_t<IAutoComplete> ac;
|
||||
HRESULT hr;
|
||||
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_ALL, IID_IAutoComplete, (void**)ac.receive_ptr());
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - CoCreateInstance/IAutoComplete fail!"); return hr;
|
||||
}
|
||||
hr = ac->Init(edit, vals, NULL, NULL);
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - ac->Init fail!"); return hr;
|
||||
}
|
||||
|
||||
pfc::com_ptr_t<IAutoComplete2> ac2;
|
||||
hr = ac->QueryInterface( ac2.receive_ptr() );
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - ac->QueryInterface fail!"); return hr;
|
||||
}
|
||||
hr = ac2->SetOptions(opts);
|
||||
if (FAILED(hr)) {
|
||||
PFC_ASSERT(!"Should not get here - ac2->SetOptions fail!"); return hr;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string8> valueEnum) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
while(valueEnum.is_valid()) {
|
||||
acl->AddStringU(*valueEnum); ++valueEnum;
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<const char *> valueEnum) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
while(valueEnum.is_valid()) {
|
||||
acl->AddStringU(*valueEnum); ++valueEnum;
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string> valueEnum) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
while(valueEnum.is_valid()) {
|
||||
acl->AddStringU(valueEnum->ptr()); ++valueEnum;
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
|
||||
pfc::com_ptr_t<IUnknown> CreateACList() {
|
||||
return new CEnumString::TImpl();
|
||||
}
|
||||
|
||||
void CreateACList_AddItem(IUnknown * theList, const char * item) {
|
||||
static_cast<CEnumString*>(theList)->AddStringU( item );
|
||||
}
|
||||
|
||||
HRESULT InitializeEditAC(HWND edit, pfc::const_iterator<pfc::string8> valueEnum, DWORD opts) {
|
||||
pfc::com_ptr_t<IUnknown> acl = CreateACList(valueEnum);
|
||||
return InitializeSimpleAC(edit, acl.get_ptr(), opts);
|
||||
}
|
||||
HRESULT InitializeEditAC(HWND edit, const char * values, DWORD opts) {
|
||||
pfc::com_ptr_t<CEnumString> acl = new CEnumString::TImpl();
|
||||
for(const char * walk = values;;) {
|
||||
const char * next = strchr(walk, cfg_dropdown_history_mt::separator);
|
||||
if (next == NULL) {acl->AddStringU(walk, ~0); break;}
|
||||
acl->AddStringU(walk, next - walk);
|
||||
walk = next + 1;
|
||||
}
|
||||
return InitializeSimpleAC(edit, acl.get_ptr(), opts);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -2,11 +2,15 @@
|
||||
|
||||
#include <ShlDisp.h>
|
||||
|
||||
HRESULT InitializeEditAC(HWND edit, pfc::const_iterator<pfc::string8> valueEnum, DWORD opts = ACO_AUTOAPPEND | ACO_AUTOSUGGEST);
|
||||
class cfg_dropdown_history_mt;
|
||||
|
||||
HRESULT InitializeDropdownAC(HWND comboBox, cfg_dropdown_history_mt & var, const char * initVal);
|
||||
HRESULT InitializeEditAC(HWND edit, const char * values, DWORD opts = ACO_AUTOAPPEND|ACO_AUTOSUGGEST);
|
||||
HRESULT InitializeEditAC(HWND edit, pfc::const_iterator<pfc::string8> valueEnum, DWORD opts = ACO_AUTOAPPEND|ACO_AUTOSUGGEST);
|
||||
HRESULT InitializeSimpleAC(HWND edit, IUnknown * vals, DWORD opts);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string8> valueEnum);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<const char *> valueEnum);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList(pfc::const_iterator<pfc::string> valueEnum);
|
||||
pfc::com_ptr_t<IUnknown> CreateACList();
|
||||
void CreateACList_AddItem(IUnknown * theList, const char * item);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <libPPUI/CFlashWindow.h>
|
||||
#include "atl-misc.h"
|
||||
#include "CFlashWindow.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -39,7 +38,7 @@ private:
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
bool _bump() {
|
||||
if (m_selfDestruct || this->m_hWnd == NULL) return false;
|
||||
if (m_selfDestruct || 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);
|
||||
@@ -62,12 +61,12 @@ pfc::avltree_t<ImplementBumpableElem<TClass> *> ImplementBumpableElem<TClass>::i
|
||||
|
||||
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;}
|
||||
t_uint32 get_flags() {return KFlagHavePopupCommand | 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;}
|
||||
t_uint32 get_flags() {return KFlagsVisualisation | KFlagSupportsBump;}
|
||||
bool bump() {return ImplementBumpableElem<TImpl>::Bump();}
|
||||
};
|
||||
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <vsstyle.h>
|
||||
#include "WTL-PP.h"
|
||||
#include "win32_op.h"
|
||||
|
||||
typedef CWinTraits<WS_CHILD|WS_TABSTOP,0> CButtonLiteTraits;
|
||||
|
||||
@@ -31,24 +29,22 @@ public:
|
||||
unsigned Measure() {
|
||||
auto font = myGetFont();
|
||||
LOGFONT lf;
|
||||
WIN32_OP_D(font.GetLogFont(lf));
|
||||
font.GetLogFont( lf );
|
||||
MakeBoldFont( lf );
|
||||
CFont bold;
|
||||
WIN32_OP_D(bold.CreateFontIndirect(&lf));
|
||||
bold.CreateFontIndirect(&lf);
|
||||
CWindowDC dc(*this);
|
||||
auto oldFont = dc.SelectFont( bold );
|
||||
CSize size (0,0);
|
||||
CSize sizeSpace (0,0);
|
||||
|
||||
{
|
||||
CString measure;
|
||||
measure = L"#";
|
||||
measure += m_textDrawMe;
|
||||
WIN32_OP_D(dc.GetTextExtent(measure, measure.GetLength(), &size));
|
||||
}
|
||||
|
||||
dc.GetTextExtent(m_textDrawMe, m_textDrawMe.GetLength(), &size);
|
||||
dc.GetTextExtent(L" ", 1, &sizeSpace);
|
||||
|
||||
dc.SelectFont( oldFont );
|
||||
|
||||
return size.cx;
|
||||
return size.cx + sizeSpace.cx;
|
||||
}
|
||||
std::function< void (HWND) > TabCycleHandler;
|
||||
std::function< HBRUSH (CDCHandle) > CtlColorHandler;
|
||||
@@ -222,9 +218,8 @@ private:
|
||||
return false;
|
||||
}
|
||||
if ( cMsg == WM_LBUTTONUP ) {
|
||||
bool wasPressed = m_pressed;
|
||||
if ( m_pressed ) OnClicked();
|
||||
TogglePressed(false);
|
||||
if ( wasPressed ) OnClicked();
|
||||
return false;
|
||||
}
|
||||
CRect rcClient;
|
||||
141
foobar2000/ATLHelpers/CDialogResizeHelper.cpp
Normal file
141
foobar2000/ATLHelpers/CDialogResizeHelper.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "../helpers/win32_misc.h"
|
||||
#include "CDialogResizeHelper.h"
|
||||
|
||||
bool CDialogResizeHelper::EvalRect(UINT id, CRect & out) const {
|
||||
for(t_size walk = 0; walk < m_table.get_size(); ++walk) {
|
||||
if (m_table[walk].id == id) {
|
||||
CRect client; WIN32_OP_D( m_thisWnd.GetClientRect(client) );
|
||||
return _EvalRect(walk, client.Size(), out);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDialogResizeHelper::_EvalRect(t_size index, CSize wndSize, CRect & out) const {
|
||||
CRect rc;
|
||||
const Param & e = m_table[index];
|
||||
if (m_origRects.query(e.id, rc)) {
|
||||
int delta_x = wndSize.cx - m_rcOrigClient.right,
|
||||
delta_y = wndSize.cy - m_rcOrigClient.bottom;
|
||||
|
||||
rc.left += pfc::rint32( e.snapLeft * delta_x );
|
||||
rc.right += pfc::rint32( e.snapRight * delta_x );
|
||||
rc.top += pfc::rint32(e.snapTop * delta_y );
|
||||
rc.bottom += pfc::rint32(e.snapBottom * delta_y );
|
||||
|
||||
out = rc;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::OnSize(UINT, CSize newSize)
|
||||
{
|
||||
if (m_thisWnd != NULL) {
|
||||
HDWP hWinPosInfo = BeginDeferWindowPos( m_table.get_size() + (m_sizeGrip != NULL ? 1 : 0) );
|
||||
for(t_size n = 0; n < m_table.get_size(); ++n) {
|
||||
CRect rc;
|
||||
if (_EvalRect(n, newSize, rc)) {
|
||||
hWinPosInfo = DeferWindowPos(hWinPosInfo, m_thisWnd.GetDlgItem(m_table[n].id), 0, rc.left,rc.top,rc.Width(),rc.Height(),SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
|
||||
}
|
||||
}
|
||||
if (m_sizeGrip != NULL)
|
||||
{
|
||||
RECT rc, rc_grip;
|
||||
if (m_thisWnd.GetClientRect(&rc) && m_sizeGrip.GetWindowRect(&rc_grip)) {
|
||||
DWORD flags = SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOCOPYBITS;
|
||||
if (IsZoomed(m_thisWnd)) flags |= SWP_HIDEWINDOW;
|
||||
else flags |= SWP_SHOWWINDOW;
|
||||
hWinPosInfo = DeferWindowPos(hWinPosInfo, m_sizeGrip, NULL, rc.right - (rc_grip.right - rc_grip.left), rc.bottom - (rc_grip.bottom - rc_grip.top), 0, 0, flags );
|
||||
}
|
||||
}
|
||||
EndDeferWindowPos(hWinPosInfo);
|
||||
//RedrawWindow(m_thisWnd, NULL, NULL, RDW_UPDATENOW | RDW_ALLCHILDREN);
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::OnGetMinMaxInfo(LPMINMAXINFO info) const {
|
||||
CRect r;
|
||||
const DWORD dwStyle = m_thisWnd.GetWindowLong(GWL_STYLE);
|
||||
const DWORD dwExStyle = m_thisWnd.GetWindowLong(GWL_EXSTYLE);
|
||||
if (max_x && max_y)
|
||||
{
|
||||
r.left = 0; r.right = max_x;
|
||||
r.top = 0; r.bottom = max_y;
|
||||
MapDialogRect(m_thisWnd,&r);
|
||||
AdjustWindowRectEx(&r, dwStyle, FALSE, dwExStyle);
|
||||
info->ptMaxTrackSize.x = r.right - r.left;
|
||||
info->ptMaxTrackSize.y = r.bottom - r.top;
|
||||
}
|
||||
if (min_x && min_y)
|
||||
{
|
||||
r.left = 0; r.right = min_x;
|
||||
r.top = 0; r.bottom = min_y;
|
||||
MapDialogRect(m_thisWnd,&r);
|
||||
AdjustWindowRectEx(&r, dwStyle, FALSE, dwExStyle);
|
||||
info->ptMinTrackSize.x = r.right - r.left;
|
||||
info->ptMinTrackSize.y = r.bottom - r.top;
|
||||
}
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::OnInitDialog(CWindow thisWnd) {
|
||||
m_origRects.remove_all();
|
||||
m_thisWnd = thisWnd;
|
||||
m_thisWnd.GetClientRect(&m_rcOrigClient);
|
||||
for(t_size n = 0; n < m_table.get_size(); n++) {
|
||||
CRect rc;
|
||||
const UINT id = m_table[n].id;
|
||||
if (GetChildWindowRect(m_thisWnd,id,&rc)) {
|
||||
m_origRects.set(id, rc);
|
||||
}
|
||||
}
|
||||
AddSizeGrip();
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void CDialogResizeHelper::OnDestroy() {
|
||||
m_sizeGrip = NULL; m_thisWnd = NULL;
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void CDialogResizeHelper::AddSizeGrip()
|
||||
{
|
||||
if (m_thisWnd != NULL && m_sizeGrip == NULL)
|
||||
{
|
||||
if (m_thisWnd.GetWindowLong(GWL_STYLE) & WS_POPUP) {
|
||||
m_sizeGrip = CreateWindowEx(0, WC_SCROLLBAR, _T(""), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN,
|
||||
0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
m_thisWnd, (HMENU)0, NULL, NULL);
|
||||
if (m_sizeGrip != NULL)
|
||||
{
|
||||
RECT rc, rc_grip;
|
||||
if (m_thisWnd.GetClientRect(&rc) && m_sizeGrip.GetWindowRect(&rc_grip)) {
|
||||
m_sizeGrip.SetWindowPos(NULL, rc.right - (rc_grip.right - rc_grip.left), rc.bottom - (rc_grip.bottom - rc_grip.top), 0, 0, SWP_NOZORDER | SWP_NOSIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CDialogResizeHelper::InitTable(const Param* table, t_size tableSize) {
|
||||
m_table.set_data_fromptr(table, tableSize);
|
||||
}
|
||||
void CDialogResizeHelper::InitTable(const ParamOld * table, t_size tableSize) {
|
||||
m_table.set_size(tableSize);
|
||||
for(t_size walk = 0; walk < tableSize; ++walk) {
|
||||
const ParamOld in = table[walk];
|
||||
Param entry = {};
|
||||
entry.id = table[walk].id;
|
||||
if (in.flags & dialog_resize_helper::X_MOVE) entry.snapLeft = entry.snapRight = 1;
|
||||
else if (in.flags & dialog_resize_helper::X_SIZE) entry.snapRight = 1;
|
||||
if (in.flags & dialog_resize_helper::Y_MOVE) entry.snapTop = entry.snapBottom = 1;
|
||||
else if (in.flags & dialog_resize_helper::Y_SIZE) entry.snapBottom = 1;
|
||||
m_table[walk] = entry;
|
||||
}
|
||||
}
|
||||
void CDialogResizeHelper::InitMinMax(const CRect & range) {
|
||||
min_x = range.left; min_y = range.top; max_x = range.right; max_y = range.bottom;
|
||||
}
|
||||
69
foobar2000/ATLHelpers/CDialogResizeHelper.h
Normal file
69
foobar2000/ATLHelpers/CDialogResizeHelper.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/dialog_resize_helper.h"
|
||||
#include "WindowPositionUtils.h"
|
||||
|
||||
class CDialogResizeHelper : public CMessageMap {
|
||||
public:
|
||||
typedef dialog_resize_helper::param ParamOld;
|
||||
|
||||
struct Param {
|
||||
t_uint32 id;
|
||||
float snapLeft, snapTop, snapRight, snapBottom;
|
||||
};
|
||||
private:
|
||||
void AddSizeGrip();
|
||||
public:
|
||||
inline void set_min_size(unsigned x,unsigned y) {min_x = x; min_y = y;}
|
||||
inline void set_max_size(unsigned x,unsigned y) {max_x = x; max_y = y;}
|
||||
|
||||
|
||||
BEGIN_MSG_MAP_EX(CDialogResizeHelper)
|
||||
if (uMsg == WM_INITDIALOG) OnInitDialog(hWnd);
|
||||
MSG_WM_SIZE(OnSize)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo)
|
||||
END_MSG_MAP()
|
||||
|
||||
template<typename TParam, t_size paramCount> CDialogResizeHelper(const TParam (& src)[paramCount],CRect const& minMaxRange = CRect(0,0,0,0)) {
|
||||
InitTable(src, paramCount);
|
||||
InitMinMax(minMaxRange);
|
||||
}
|
||||
|
||||
void InitTable(const Param* table, t_size tableSize);
|
||||
void InitTable(const ParamOld * table, t_size tableSize);
|
||||
void InitMinMax(const CRect & range);
|
||||
|
||||
bool EvalRect(UINT id, CRect & out) const;
|
||||
|
||||
private:
|
||||
bool _EvalRect(t_size index, CSize wndSize, CRect & out) const;
|
||||
void OnGetMinMaxInfo(LPMINMAXINFO lpMMI) const;
|
||||
void OnSize(UINT nType, CSize size);
|
||||
void OnInitDialog(CWindow thisWnd);
|
||||
void OnDestroy();
|
||||
|
||||
pfc::array_t<Param> m_table;
|
||||
pfc::map_t<UINT, CRect> m_origRects;
|
||||
CRect m_rcOrigClient;
|
||||
CWindow m_thisWnd, m_sizeGrip;
|
||||
unsigned min_x,min_y,max_x,max_y;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
#define REDRAW_DIALOG_ON_RESIZE() if (uMsg == WM_SIZE) RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
|
||||
3
foobar2000/ATLHelpers/CEditWithButtons.cpp
Normal file
3
foobar2000/ATLHelpers/CEditWithButtons.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "stdafx.h"
|
||||
#include "CEditWithButtons.h"
|
||||
// All in the header - CPP present to make MS intellisense stop showing nonsensical errors
|
||||
@@ -45,17 +45,52 @@ public:
|
||||
typedef std::function<void () > handler_t;
|
||||
typedef std::function<bool (const wchar_t*) > condition_t;
|
||||
|
||||
void AddMoreButton( std::function<void ()> f );
|
||||
void AddClearButton( const wchar_t * clearVal = L"", bool bHandleEsc = false);
|
||||
void AddButton( const wchar_t * str, handler_t handler, condition_t condition = nullptr, const wchar_t * drawAlternateText = nullptr );
|
||||
void AddMoreButton( std::function<void ()> f ) {
|
||||
AddButton(L"\x2026", f);
|
||||
}
|
||||
void AddClearButton( const wchar_t * clearVal = L"") {
|
||||
std::wstring clearValCopy ( clearVal );
|
||||
auto handler = [this, clearValCopy] {
|
||||
this->SetWindowText(clearValCopy.c_str());
|
||||
};
|
||||
auto condition = [clearValCopy] ( const wchar_t * txt ) -> bool {
|
||||
return clearValCopy != txt;
|
||||
};
|
||||
// Present "clear" to accessibility APIs but actually draw a multiplication x sign
|
||||
AddButton(L"clear", handler, condition, L"\x00D7");
|
||||
}
|
||||
|
||||
static unsigned DefaultFixedWidth() {return GetSystemMetrics(SM_CXVSCROLL) * 3 / 4;}
|
||||
void AddButton( const wchar_t * str, handler_t handler, condition_t condition = nullptr, const wchar_t * drawAlternateText = nullptr ) {
|
||||
Button_t btn;
|
||||
btn.handler = handler;
|
||||
btn.title = str;
|
||||
btn.condition = condition;
|
||||
btn.visible = EvalCondition( btn, nullptr );
|
||||
|
||||
if ( drawAlternateText != nullptr ) {
|
||||
btn.titleDraw = drawAlternateText;
|
||||
}
|
||||
|
||||
m_buttons.push_back(std::move(btn) );
|
||||
RefreshButtons();
|
||||
}
|
||||
|
||||
static unsigned DefaultFixedWidth() {
|
||||
return GetSystemMetrics(SM_CXVSCROLL) * 3 / 4;
|
||||
}
|
||||
void SetFixedWidth(unsigned fw = DefaultFixedWidth() ) {
|
||||
m_fixedWidth = fw;
|
||||
RefreshButtons();
|
||||
}
|
||||
CRect RectOfButton( const wchar_t * text );
|
||||
|
||||
CRect RectOfButton( const wchar_t * text ) {
|
||||
for ( auto i = m_buttons.begin(); i != m_buttons.end(); ++ i ) {
|
||||
if ( i->title == text && i->wnd != NULL ) {
|
||||
CRect rc;
|
||||
if (i->wnd.GetWindowRect ( rc )) return rc;
|
||||
}
|
||||
}
|
||||
return CRect();
|
||||
}
|
||||
void Invalidate() {
|
||||
__super::Invalidate();
|
||||
for( auto i = m_buttons.begin(); i != m_buttons.end(); ++i ) {
|
||||
@@ -75,11 +110,10 @@ public:
|
||||
void SetHasAutoComplete(bool bValue = true) {
|
||||
m_hasAutoComplete = bValue;
|
||||
}
|
||||
void RefreshConditions(const wchar_t * newText = nullptr);
|
||||
private:
|
||||
LRESULT OnCheckConditions( UINT msg, WPARAM wp, LPARAM lp ) {
|
||||
if ( msg == MSG_CHECKCONDITIONS && wp == MSG_CHECKCONDITIONS_MAGIC1 && lp == MSG_CHECKCONDITIONS_MAGIC2 ) {
|
||||
this->RefreshConditions();
|
||||
this->RefreshConditions(nullptr);
|
||||
} else {
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
@@ -200,13 +234,132 @@ private:
|
||||
void TabFocusPrevNext(bool bPrev) {
|
||||
FindDialog().PostMessage(WM_NEXTDLGCTL, bPrev ? TRUE : FALSE, FALSE);
|
||||
}
|
||||
void TabCycleButtons(HWND wnd);
|
||||
void TabCycleButtons(HWND wnd) {
|
||||
for( auto i = m_buttons.begin(); i != m_buttons.end(); ++ i ) {
|
||||
if ( i->wnd == wnd ) {
|
||||
if (IsShiftPressed()) {
|
||||
// back
|
||||
for ( ;; ) {
|
||||
if (i == m_buttons.begin()) {
|
||||
TabFocusThis(m_hWnd); break;
|
||||
} else {
|
||||
--i;
|
||||
if ( i->visible ) {
|
||||
TabFocusThis(i->wnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// forward
|
||||
for ( ;; ) {
|
||||
++i;
|
||||
if (i == m_buttons.end()) {
|
||||
TabFocusThis(m_hWnd);
|
||||
TabFocusPrevNext(false);
|
||||
break;
|
||||
} else {
|
||||
if ( i->visible ) {
|
||||
TabFocusThis(i->wnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ButtonWantTab( HWND wnd );
|
||||
bool EvalCondition( Button_t & btn, const wchar_t * newText );
|
||||
void Layout(CSize size, CFontHandle fontSetMe);
|
||||
unsigned MeasureButton(Button_t const & button );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool ButtonWantTab( HWND wnd ) {
|
||||
if ( IsShiftPressed() ) return true;
|
||||
if ( m_buttons.size() == 0 ) return false; // should not be possible
|
||||
auto last = m_buttons.rbegin();
|
||||
if ( wnd == last->wnd ) return false; // not for last button
|
||||
return true;
|
||||
}
|
||||
bool EvalCondition( Button_t & btn, const wchar_t * newText ) {
|
||||
if (!btn.condition) return true;
|
||||
if ( newText != nullptr ) return btn.condition( newText );
|
||||
TCHAR text[256] = {};
|
||||
GetWindowText(text, 256);
|
||||
text[255] = 0;
|
||||
return btn.condition( text );
|
||||
}
|
||||
void RefreshConditions( const wchar_t * newText ) {
|
||||
bool changed = false;
|
||||
for( auto i = m_buttons.begin(); i != m_buttons.end(); ++i ) {
|
||||
bool status = EvalCondition(*i, newText);
|
||||
if ( status != i->visible ) {
|
||||
i->visible = status; changed = true;
|
||||
}
|
||||
}
|
||||
if ( changed ) {
|
||||
Layout();
|
||||
}
|
||||
}
|
||||
void Layout(CSize size, CFontHandle fontSetMe) {
|
||||
if ( m_buttons.size() == 0 ) return;
|
||||
|
||||
int walk = size.cx;
|
||||
|
||||
HDWP dwp = BeginDeferWindowPos( (int) m_buttons.size() );
|
||||
for( auto iter = m_buttons.rbegin(); iter != m_buttons.rend(); ++iter ) {
|
||||
if (! iter->visible ) {
|
||||
if (::GetFocus() == iter->wnd ) {
|
||||
this->SetFocus();
|
||||
}
|
||||
::DeferWindowPos( dwp, iter->wnd, NULL, 0,0,0,0, SWP_NOZORDER | SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE );
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->wnd == NULL) {
|
||||
auto b = std::make_shared< CButtonLite > ( );
|
||||
iter->buttonImpl = b;
|
||||
b->Create( *this, NULL, iter->title.c_str() );
|
||||
if ( iter->titleDraw.length() > 0 ) b->DrawAlternateText( iter->titleDraw.c_str() );
|
||||
CFontHandle font = fontSetMe;
|
||||
if ( font == NULL ) font = GetFont();
|
||||
b->SetFont( font );
|
||||
b->ClickHandler = iter->handler;
|
||||
b->CtlColorHandler = [=] (CDCHandle dc) -> HBRUSH {
|
||||
return this->OnColorBtn( dc, NULL );
|
||||
};
|
||||
b->TabCycleHandler = [=] (HWND wnd) {
|
||||
TabCycleButtons(wnd);
|
||||
};
|
||||
b->WantTabCheck = [=] (HWND wnd) -> bool {
|
||||
return ButtonWantTab(wnd);
|
||||
};
|
||||
if (! IsWindowEnabled( ) ) b->EnableWindow(FALSE);
|
||||
iter->wnd = * b;
|
||||
} else if ( fontSetMe ) {
|
||||
iter->wnd.SetFont( fontSetMe );
|
||||
}
|
||||
|
||||
unsigned delta = MeasureButton(*iter);
|
||||
int left = walk - delta;
|
||||
|
||||
if ( iter->wnd != NULL ) {
|
||||
CRect rc;
|
||||
rc.top = 0;
|
||||
rc.bottom = size.cy;
|
||||
rc.left = left;
|
||||
rc.right = walk;
|
||||
::DeferWindowPos( dwp, iter->wnd, NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOZORDER | SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
walk = left;
|
||||
}
|
||||
EndDeferWindowPos( dwp );
|
||||
this->SetMargins(0, size.cx - walk, EC_RIGHTMARGIN );
|
||||
}
|
||||
|
||||
unsigned MeasureButton(Button_t const & button ) {
|
||||
if ( m_fixedWidth != 0 ) return m_fixedWidth;
|
||||
|
||||
return button.buttonImpl->Measure();
|
||||
}
|
||||
unsigned m_fixedWidth;
|
||||
std::list< Button_t > m_buttons;
|
||||
bool m_hasAutoComplete = false;
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_op.h"
|
||||
|
||||
typedef CWinTraits<WS_POPUP,WS_EX_TRANSPARENT|WS_EX_LAYERED|WS_EX_TOPMOST|WS_EX_TOOLWINDOW> CFlashWindowTraits;
|
||||
|
||||
class CFlashWindow : public CWindowImpl<CFlashWindow,CWindow,CFlashWindowTraits> {
|
||||
@@ -64,6 +62,6 @@ private:
|
||||
return 0;
|
||||
}
|
||||
CWindow m_parent;
|
||||
uint32_t m_tickCount;
|
||||
t_uint32 m_tickCount;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_op.h"
|
||||
#include "../ATLHelpers/misc.h"
|
||||
|
||||
typedef CWinTraits<WS_POPUP,WS_EX_LAYERED> _COverlayWindowTraits;
|
||||
typedef CWinTraits<WS_POPUP,WS_EX_LAYERED> __COverlayWindowTraits;
|
||||
|
||||
class CIconOverlayWindow : public CWindowImpl<CIconOverlayWindow,CWindow,_COverlayWindowTraits> {
|
||||
class CIconOverlayWindow : public CWindowImpl<CIconOverlayWindow,CWindow,__COverlayWindowTraits> {
|
||||
public:
|
||||
DECLARE_WND_CLASS_EX(TEXT("{384298D0-4370-4f9b-9C36-49FC1A396DC7}"),0,(-1));
|
||||
|
||||
@@ -15,7 +15,7 @@ public:
|
||||
ColorKey = 0xc0ffee
|
||||
};
|
||||
|
||||
BEGIN_MSG_MAP_EX(CIconOverlayWindow)
|
||||
BEGIN_MSG_MAP(CIconOverlayWindow)
|
||||
MESSAGE_HANDLER(WM_CREATE,OnCreate);
|
||||
MESSAGE_HANDLER(WM_PAINT,OnPaint);
|
||||
MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkgnd);
|
||||
@@ -2,31 +2,19 @@
|
||||
|
||||
#include "CIconOverlayWindow.h"
|
||||
#include <utility>
|
||||
#include "ppresources.h"
|
||||
#include "win32_utility.h"
|
||||
|
||||
class CMiddleDragCommon {
|
||||
public:
|
||||
template<typename TBase>
|
||||
class CMiddleDragImpl : public TBase {
|
||||
private:
|
||||
typedef CMiddleDragImpl<TBase> TSelf;
|
||||
enum {
|
||||
KTimerID = 0x389675f8,
|
||||
KTimerPeriod = 15,
|
||||
};
|
||||
static double myPow(double p_val, double p_exp);
|
||||
static double ProcessMiddleDragDeltaInternal(double p_delta);
|
||||
static double radiusHelper(double p_x, double p_y);
|
||||
static int mySGN(LONG v);
|
||||
static int32_t Round(double val, double & acc);
|
||||
static LONG LineToPixelsHelper(LONG & p_overflow, LONG p_pixels, LONG p_dpi, LONG p_lineWidth);
|
||||
};
|
||||
|
||||
template<typename TBase>
|
||||
class CMiddleDragImpl : public TBase, protected CMiddleDragCommon {
|
||||
private:
|
||||
typedef CMiddleDragImpl<TBase> TSelf;
|
||||
public:
|
||||
template<typename ... arg_t> CMiddleDragImpl( arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) {}
|
||||
template<typename ... arg_t> CMiddleDragImpl( arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) { m_active = false; m_accX = m_accY = 0; }
|
||||
|
||||
BEGIN_MSG_MAP_EX(TSelf)
|
||||
BEGIN_MSG_MAP(TSelf)
|
||||
MESSAGE_HANDLER(WM_TIMER,OnTimer);
|
||||
MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
|
||||
MESSAGE_HANDLER(WM_MBUTTONDOWN,OnMButtonDown);
|
||||
@@ -50,10 +38,10 @@ protected:
|
||||
return !m_active;
|
||||
}
|
||||
private:
|
||||
bool m_active = false, m_dragged = false;
|
||||
bool m_active, m_dragged;
|
||||
CPoint m_base;
|
||||
CIconOverlayWindow m_overlay;
|
||||
double m_accX = 0, m_accY = 0;
|
||||
double m_accX, m_accY;
|
||||
|
||||
LRESULT OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
|
||||
if (m_overlay != NULL) m_overlay.DestroyWindow();
|
||||
@@ -74,9 +62,9 @@ private:
|
||||
return 0;
|
||||
}
|
||||
EndDrag();
|
||||
this->SetFocus();
|
||||
SetFocus();
|
||||
CPoint pt(p_lp);
|
||||
WIN32_OP_D( this->ClientToScreen(&pt) );
|
||||
WIN32_OP_D( ClientToScreen(&pt) );
|
||||
StartDrag(pt);
|
||||
return 0;
|
||||
}
|
||||
@@ -85,7 +73,7 @@ private:
|
||||
if (m_active) {
|
||||
if (!m_dragged) {
|
||||
CPoint pt(p_lp);
|
||||
WIN32_OP_D( this->ClientToScreen(&pt) );
|
||||
WIN32_OP_D( ClientToScreen(&pt) );
|
||||
if (pt != m_base) {
|
||||
m_dragged = true;
|
||||
}
|
||||
@@ -107,10 +95,10 @@ private:
|
||||
::SetCapture(NULL);
|
||||
|
||||
if (m_overlay == NULL) {
|
||||
PFC_ASSERT( this->m_hWnd != NULL );
|
||||
PFC_ASSERT( m_hWnd != NULL );
|
||||
if (m_overlay.Create(*this) == NULL) {PFC_ASSERT(!"Should not get here!"); return;}
|
||||
HANDLE temp;
|
||||
WIN32_OP_D( (temp = LoadImage(GetThisModuleHandle(),MAKEINTRESOURCE(CPPUIResources::get_IDI_SCROLL()),IMAGE_ICON,32,32,LR_DEFAULTCOLOR)) != NULL );
|
||||
WIN32_OP_D( (temp = LoadImage(core_api::get_my_instance(),MAKEINTRESOURCE(IDI_SCROLL),IMAGE_ICON,32,32,LR_DEFAULTCOLOR)) != NULL );
|
||||
m_overlay.AttachIcon((HICON) temp);
|
||||
}
|
||||
|
||||
@@ -120,8 +108,8 @@ private:
|
||||
m_dragged = false;
|
||||
m_base = p_point;
|
||||
m_accX = m_accY = 0;
|
||||
this->SetCapture();
|
||||
this->SetTimer(KTimerID,KTimerPeriod);
|
||||
SetCapture();
|
||||
SetTimer(KTimerID,KTimerPeriod);
|
||||
|
||||
{
|
||||
CSize radius(16,16);
|
||||
@@ -138,7 +126,7 @@ private:
|
||||
void HandleEndDrag() {
|
||||
if (m_active) {
|
||||
m_active = false;
|
||||
this->KillTimer(KTimerID);
|
||||
KillTimer(KTimerID);
|
||||
if (m_overlay != NULL) m_overlay.ShowWindow(SW_HIDE);
|
||||
}
|
||||
}
|
||||
@@ -152,7 +140,7 @@ private:
|
||||
LRESULT OnTimer(UINT,WPARAM p_wp,LPARAM,BOOL& bHandled) {
|
||||
switch(p_wp) {
|
||||
case KTimerID:
|
||||
this->MoveViewOriginDelta(ProcessMiddleDragDelta(CPoint(GetCursorPos()) - m_base));
|
||||
this->MoveViewOriginDelta(ProcessMiddleDragDelta(CPoint((LPARAM)GetMessagePos()) - m_base));
|
||||
return 0;
|
||||
default:
|
||||
bHandled = FALSE;
|
||||
@@ -160,6 +148,20 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
static double myPow(double p_val,double p_exp) {
|
||||
double ex = pow(pfc::abs_t(p_val),p_exp);
|
||||
return p_val < 0 ? -ex : ex;
|
||||
}
|
||||
|
||||
static double ProcessMiddleDragDeltaInternal(double p_delta) {
|
||||
p_delta *= (double) KTimerPeriod / 25; /*originally calculated for 25ms timer interval*/
|
||||
return myPow(p_delta * 0.05,2.0);
|
||||
}
|
||||
|
||||
static double radiusHelper(double p_x,double p_y) {
|
||||
return sqrt(p_x * p_x + p_y * p_y);
|
||||
}
|
||||
|
||||
CPoint ProcessMiddleDragDelta(CPoint p_delta) {
|
||||
const CSize dpi = QueryScreenDPIEx();
|
||||
if (dpi.cx <= 0 || dpi.cy <= 0 || p_delta == CPoint(0,0)) return CPoint(0,0);
|
||||
@@ -176,17 +178,21 @@ private:
|
||||
xVal = deltaTotal;
|
||||
} else {
|
||||
const double ratio = (double) p_delta.x / (double) p_delta.y;
|
||||
yVal = sqrt((deltaTotal*deltaTotal) / (1.0 + (ratio*ratio)));
|
||||
yVal = sqrt(pfc::sqr_t(deltaTotal) / (1.0 + pfc::sqr_t(ratio)));
|
||||
xVal = yVal * ratio;
|
||||
}
|
||||
|
||||
xVal = mySGN(p_delta.x) * fabs(xVal);
|
||||
yVal = mySGN(p_delta.y) * fabs(yVal);
|
||||
|
||||
xVal = pfc::sgn_t( p_delta.x ) * pfc::abs_t(xVal);
|
||||
yVal = pfc::sgn_t( p_delta.y ) * pfc::abs_t(yVal);
|
||||
|
||||
return CPoint(Round(xVal / dpiMulX, m_accX), Round(yVal / dpiMulY, m_accY));
|
||||
}
|
||||
|
||||
static t_int32 Round(double val, double & acc) {
|
||||
val += acc;
|
||||
t_int32 ret = pfc::rint32(val);
|
||||
acc = val - ret;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TBase>
|
||||
@@ -196,7 +202,7 @@ private:
|
||||
public:
|
||||
template<typename ... arg_t> CMiddleDragWrapper(arg_t && ... arg ) : TBase(std::forward<arg_t>(arg) ... ) { m_overflow = CPoint(0, 0); m_lineWidth = CSize(4, 16); }
|
||||
|
||||
BEGIN_MSG_MAP_EX(TSelf)
|
||||
BEGIN_MSG_MAP(TSelf)
|
||||
MESSAGE_HANDLER(WM_CAPTURECHANGED,OnCaptureChanged);
|
||||
CHAIN_MSG_MAP(TBase)
|
||||
END_MSG_MAP()
|
||||
@@ -212,7 +218,7 @@ public:
|
||||
|
||||
private:
|
||||
void FireScrollMessage(UINT p_msg,int p_delta) {
|
||||
UINT count = (UINT)(p_delta<0?-p_delta:p_delta);
|
||||
UINT count = (UINT)pfc::abs_t(p_delta);
|
||||
const UINT which = (p_msg == WM_HSCROLL ? SB_HORZ : SB_VERT);
|
||||
SCROLLINFO si = {}; si.cbSize = sizeof(si); si.fMask = SIF_PAGE | SIF_RANGE;
|
||||
if (!this->GetScrollInfo(which, &si) || si.nPage <= 0) return;
|
||||
@@ -230,13 +236,21 @@ private:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LONG LineToPixelsHelper(LONG & p_overflow,LONG p_pixels,LONG p_dpi,LONG p_lineWidth) {
|
||||
const int lineWidth = MulDiv(p_lineWidth,p_dpi,96);
|
||||
if (lineWidth == 0) return 0;
|
||||
p_overflow += p_pixels;
|
||||
LONG ret = p_overflow / lineWidth;
|
||||
p_overflow -= ret * lineWidth;
|
||||
return ret;
|
||||
}
|
||||
|
||||
CPoint MiddleDrag_PixelsToLines(CPoint & p_overflow,CPoint p_pixels) {
|
||||
const CSize dpi = QueryScreenDPIEx();
|
||||
if (dpi.cx <= 0 || dpi.cy <= 0) return CPoint(0,0);
|
||||
CPoint pt;
|
||||
pt.x = CMiddleDragCommon::LineToPixelsHelper(p_overflow.x,p_pixels.x,dpi.cx,m_lineWidth.cx);
|
||||
pt.y = CMiddleDragCommon::LineToPixelsHelper(p_overflow.y,p_pixels.y,dpi.cy,m_lineWidth.cy);
|
||||
pt.x = LineToPixelsHelper(p_overflow.x,p_pixels.x,dpi.cx,m_lineWidth.cx);
|
||||
pt.y = LineToPixelsHelper(p_overflow.y,p_pixels.y,dpi.cy,m_lineWidth.cy);
|
||||
return pt;
|
||||
}
|
||||
|
||||
@@ -257,9 +271,7 @@ typedef CMiddleDragImpl<CMiddleDragWrapper<CWindow_DummyMsgMap<> > > CMiddleDrag
|
||||
template<typename TBase = CWindow>
|
||||
class CContainedWindow_MsgMap : public CContainedWindowT<CWindow_DummyMsgMap<TBase> > {
|
||||
protected:
|
||||
CContainedWindow_MsgMap() : CContainedWindowT<CWindow_DummyMsgMap<TBase> >(msgMapCast(this)) {}
|
||||
private:
|
||||
template<typename t> static CMessageMap * msgMapCast(t* arg) { return arg; }
|
||||
CContainedWindow_MsgMap() : CContainedWindowT<CWindow_DummyMsgMap<TBase> >(pfc::implicit_cast<CMessageMap*>(this)) {}
|
||||
};
|
||||
|
||||
template<typename TBase>
|
||||
@@ -270,7 +282,7 @@ template<typename TBase> class CMiddleDragImplCtrlHookEx : public CMiddleDragImp
|
||||
public:
|
||||
CMiddleDragImplCtrlHookEx(CMessageMap * hook, DWORD hookMapID = 0) : m_hook(*hook), m_hookMapID(hookMapID) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CMiddleDragImplCtrlHookEx)
|
||||
BEGIN_MSG_MAP(CMiddleDragImplCtrlHookEx)
|
||||
CHAIN_MSG_MAP(CMiddleDragImplCtrlHook<TBase>)
|
||||
CHAIN_MSG_MAP_ALT_MEMBER(m_hook,m_hookMapID);
|
||||
END_MSG_MAP()
|
||||
166
foobar2000/ATLHelpers/Controls.h
Normal file
166
foobar2000/ATLHelpers/Controls.h
Normal file
@@ -0,0 +1,166 @@
|
||||
#pragma once
|
||||
|
||||
#include "misc.h"
|
||||
|
||||
void PaintSeparatorControl(CWindow wnd);
|
||||
|
||||
class CStaticSeparator : public CContainedWindowT<CStatic>, private CMessageMap {
|
||||
public:
|
||||
CStaticSeparator() : CContainedWindowT<CStatic>(this, 0) {}
|
||||
BEGIN_MSG_MAP_EX(CSeparator)
|
||||
MSG_WM_PAINT(OnPaint)
|
||||
MSG_WM_SETTEXT(OnSetText)
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
Invalidate();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
void OnPaint(CDCHandle) {
|
||||
PaintSeparatorControl(*this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<typename TClass>
|
||||
class CTextControl : public CWindowRegisteredT<TClass> {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CTextControl)
|
||||
MSG_WM_SETFONT(OnSetFont)
|
||||
MSG_WM_GETFONT(OnGetFont)
|
||||
MSG_WM_SETTEXT(OnSetText)
|
||||
CHAIN_MSG_MAP(__super)
|
||||
END_MSG_MAP()
|
||||
private:
|
||||
HFONT OnGetFont() {
|
||||
return m_font;
|
||||
}
|
||||
void OnSetFont(HFONT font, BOOL bRedraw) {
|
||||
m_font = font;
|
||||
if (bRedraw) Invalidate();
|
||||
}
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
Invalidate();SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
CFontHandle m_font;
|
||||
};
|
||||
|
||||
#ifndef VSCLASS_TEXTSTYLE
|
||||
//
|
||||
// TEXTSTYLE class parts and states
|
||||
//
|
||||
#define VSCLASS_TEXTSTYLE L"TEXTSTYLE"
|
||||
|
||||
enum TEXTSTYLEPARTS {
|
||||
TEXT_MAININSTRUCTION = 1,
|
||||
TEXT_INSTRUCTION = 2,
|
||||
TEXT_BODYTITLE = 3,
|
||||
TEXT_BODYTEXT = 4,
|
||||
TEXT_SECONDARYTEXT = 5,
|
||||
TEXT_HYPERLINKTEXT = 6,
|
||||
TEXT_EXPANDED = 7,
|
||||
TEXT_LABEL = 8,
|
||||
TEXT_CONTROLLABEL = 9,
|
||||
};
|
||||
|
||||
enum HYPERLINKTEXTSTATES {
|
||||
TS_HYPERLINK_NORMAL = 1,
|
||||
TS_HYPERLINK_HOT = 2,
|
||||
TS_HYPERLINK_PRESSED = 3,
|
||||
TS_HYPERLINK_DISABLED = 4,
|
||||
};
|
||||
|
||||
enum CONTROLLABELSTATES {
|
||||
TS_CONTROLLABEL_NORMAL = 1,
|
||||
TS_CONTROLLABEL_DISABLED = 2,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
class CStaticThemed : public CContainedWindowT<CStatic>, private CMessageMap {
|
||||
public:
|
||||
CStaticThemed() : CContainedWindowT<CStatic>(this, 0), m_id(), m_fallback() {}
|
||||
BEGIN_MSG_MAP_EX(CStaticThemed)
|
||||
MSG_WM_PAINT(OnPaint)
|
||||
MSG_WM_THEMECHANGED(OnThemeChanged)
|
||||
MSG_WM_SETTEXT(OnSetText)
|
||||
END_MSG_MAP()
|
||||
|
||||
void SetThemePart(int id) {m_id = id; if (m_hWnd != NULL) Invalidate();}
|
||||
private:
|
||||
int OnSetText(LPCTSTR lpstrText) {
|
||||
Invalidate();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
void OnThemeChanged() {
|
||||
m_theme.Release();
|
||||
m_fallback = false;
|
||||
}
|
||||
void OnPaint(CDCHandle) {
|
||||
if (m_fallback) {
|
||||
SetMsgHandled(FALSE); return;
|
||||
}
|
||||
if (m_theme == NULL) {
|
||||
m_theme.OpenThemeData(*this, L"TextStyle");
|
||||
if (m_theme == NULL) {
|
||||
m_fallback = true; SetMsgHandled(FALSE); return;
|
||||
}
|
||||
}
|
||||
CPaintDC dc(*this);
|
||||
TCHAR buffer[512] = {};
|
||||
GetWindowText(buffer, _countof(buffer));
|
||||
const int txLen = pfc::strlen_max_t(buffer, _countof(buffer));
|
||||
CRect contentRect;
|
||||
WIN32_OP_D( GetClientRect(contentRect) );
|
||||
SelectObjectScope scopeFont(dc, GetFont());
|
||||
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
|
||||
dc.SetBkMode(TRANSPARENT);
|
||||
|
||||
if (txLen > 0) {
|
||||
CRect rcText(contentRect);
|
||||
DWORD flags = 0;
|
||||
DWORD style = GetStyle();
|
||||
if (style & SS_LEFT) flags |= DT_LEFT;
|
||||
else if (style & SS_RIGHT) flags |= DT_RIGHT;
|
||||
else if (style & SS_CENTER) flags |= DT_CENTER;
|
||||
if (style & SS_ENDELLIPSIS) flags |= DT_END_ELLIPSIS;
|
||||
|
||||
HRESULT retval = DrawThemeText(m_theme, dc, m_id, 0, buffer, txLen, flags, 0, rcText);
|
||||
PFC_ASSERT( SUCCEEDED( retval ) );
|
||||
}
|
||||
}
|
||||
int m_id;
|
||||
CTheme m_theme;
|
||||
bool m_fallback;
|
||||
};
|
||||
class CStaticMainInstruction : public CStaticThemed {
|
||||
public:
|
||||
CStaticMainInstruction() { SetThemePart(TEXT_MAININSTRUCTION); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CSeparator : public CTextControl<CSeparator> {
|
||||
public:
|
||||
BEGIN_MSG_MAP_EX(CSeparator)
|
||||
MSG_WM_PAINT(OnPaint)
|
||||
MSG_WM_ENABLE(OnEnable)
|
||||
CHAIN_MSG_MAP(__super)
|
||||
END_MSG_MAP()
|
||||
|
||||
static const TCHAR * GetClassName() {
|
||||
return _T("foobar2000:separator");
|
||||
}
|
||||
private:
|
||||
void OnEnable(BOOL bEnable) {
|
||||
Invalidate();
|
||||
}
|
||||
void OnPaint(CDCHandle dc) {
|
||||
PaintSeparatorControl(*this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "win32_op.h"
|
||||
|
||||
static HBITMAP CreateDIB24(CSize size) {
|
||||
struct {
|
||||
@@ -90,7 +89,7 @@ public:
|
||||
}
|
||||
private:
|
||||
void _initWnd(CWindow wnd) {
|
||||
this->SubclassWindow(wnd); this->SetFont(m_font);
|
||||
SubclassWindow(wnd); SetFont(m_font);
|
||||
}
|
||||
CFont m_font;
|
||||
};
|
||||
@@ -252,10 +251,3 @@ private:
|
||||
HDC m_dc;
|
||||
COLORREF m_oldCol;
|
||||
};
|
||||
|
||||
static CSize GetBitmapSize( HBITMAP bmp ) {
|
||||
CBitmapHandle h ( bmp );
|
||||
BITMAP bm = {};
|
||||
WIN32_OP_D( h.GetBitmap(bm) );
|
||||
return CSize(bm.bmWidth, bm.bmHeight);
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
#pragma once
|
||||
// Various WTL extensions that are not fb2k specific and can be reused in other WTL based software
|
||||
|
||||
#include <Uxtheme.h>
|
||||
#include <functional>
|
||||
|
||||
#define ATLASSERT_SUCCESS(X) {auto RetVal = (X); ATLASSERT( RetVal ); }
|
||||
|
||||
class NoRedrawScope {
|
||||
@@ -79,7 +76,6 @@ private:
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CImageListContainer : public CImageList {
|
||||
public:
|
||||
CImageListContainer() {}
|
||||
@@ -126,12 +122,9 @@ public:
|
||||
|
||||
class CEditPPHooks : public CContainedWindowT<CEdit>, private CMessageMap {
|
||||
public:
|
||||
bool HandleCtrlA = true, NoEscSteal = false, NoEnterSteal = false;
|
||||
bool HandleCtrlA, NoEscSteal, NoEnterSteal;
|
||||
|
||||
std::function<void ()> onEnterKey;
|
||||
std::function<void ()> onEscKey;
|
||||
|
||||
CEditPPHooks(CMessageMap * hookMM = nullptr, int hookMMID = 0) : CContainedWindowT<CEdit>(this, 0), m_hookMM(hookMM), m_hookMMID(hookMMID) {}
|
||||
CEditPPHooks(CMessageMap * hookMM = nullptr, int hookMMID = 0) : CContainedWindowT<CEdit>(this, 0), HandleCtrlA(true), NoEscSteal(), NoEnterSteal(), m_suppressChar(), m_hookMM(hookMM), m_hookMMID(hookMMID) {}
|
||||
|
||||
BEGIN_MSG_MAP_EX(CEditPPHooks)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
@@ -178,38 +171,26 @@ private:
|
||||
}
|
||||
void OnChar(UINT nChar, UINT, UINT nFlags) {
|
||||
if (m_suppressChar != 0) {
|
||||
if (nChar == m_suppressChar) return;
|
||||
}
|
||||
if (m_suppressScanCode != 0) {
|
||||
UINT code = nFlags & 0xFF;
|
||||
if (code == m_suppressScanCode) return;
|
||||
if (code == m_suppressChar) return;
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
m_suppressChar = 0;
|
||||
m_suppressScanCode = 0;
|
||||
if (HandleCtrlA) {
|
||||
if (nChar == 'A') {
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
m_suppressScanCode = nFlags & 0xFF;
|
||||
m_suppressChar = nFlags & 0xFF;
|
||||
this->SetSelAll(); return;
|
||||
}
|
||||
}
|
||||
if ( nChar == VK_BACK ) {
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
m_suppressScanCode = nFlags & 0xFF;
|
||||
m_suppressChar = nFlags & 0xFF;
|
||||
DeleteLastWord( *this ) ; return;
|
||||
}
|
||||
}
|
||||
if ( nChar == VK_RETURN && onEnterKey ) {
|
||||
m_suppressChar = nChar;
|
||||
onEnterKey(); return;
|
||||
}
|
||||
if ( nChar == VK_ESCAPE && onEscKey ) {
|
||||
m_suppressChar = nChar;
|
||||
onEscKey(); return;
|
||||
}
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
@@ -222,15 +203,9 @@ private:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch(lpMsg->wParam) {
|
||||
case VK_ESCAPE:
|
||||
if (onEscKey) {
|
||||
return DLGC_WANTMESSAGE;
|
||||
}
|
||||
SetMsgHandled(!!NoEscSteal);
|
||||
return 0;
|
||||
case VK_RETURN:
|
||||
if (onEnterKey) {
|
||||
return DLGC_WANTMESSAGE;
|
||||
}
|
||||
SetMsgHandled(!!NoEnterSteal);
|
||||
return 0;
|
||||
default:
|
||||
@@ -242,7 +217,7 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
UINT m_suppressChar = 0, m_suppressScanCode = 0;
|
||||
UINT m_suppressChar;
|
||||
CMessageMap * const m_hookMM;
|
||||
const int m_hookMMID;
|
||||
};
|
||||
@@ -413,8 +388,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
CSRWlock(const CSRWlock&) = delete;
|
||||
void operator=(const CSRWlock&) = delete;
|
||||
CSRWlock(const CSRWlock&);
|
||||
void operator=(const CSRWlock&);
|
||||
|
||||
SRWLOCK theLock;
|
||||
#if _WIN32_WINNT < 0x600
|
||||
@@ -455,8 +430,8 @@ public:
|
||||
else LeaveCriticalSection(&cs);
|
||||
}
|
||||
private:
|
||||
CSRWorCS(const CSRWorCS&) = delete;
|
||||
void operator=(const CSRWorCS&) = delete;
|
||||
CSRWorCS(const CSRWorCS&);
|
||||
void operator=(const CSRWorCS&);
|
||||
|
||||
CSRWlock srw;
|
||||
CRITICAL_SECTION cs;
|
||||
@@ -464,11 +439,3 @@ private:
|
||||
#else
|
||||
typedef CSRWlock CSRWorCS;
|
||||
#endif
|
||||
|
||||
|
||||
template<typename TBase> class CContainedWindowSimpleT : public CContainedWindowT<TBase>, public CMessageMap {
|
||||
public:
|
||||
CContainedWindowSimpleT() : CContainedWindowT<TBase>(this) {}
|
||||
BEGIN_MSG_MAP(CContainedWindowSimpleT)
|
||||
END_MSG_MAP()
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_misc.h"
|
||||
#include "../helpers/win32_misc.h"
|
||||
|
||||
static BOOL AdjustWindowRectHelper(CWindow wnd, CRect & rc) {
|
||||
const DWORD style = wnd.GetWindowLong(GWL_STYLE), exstyle = wnd.GetWindowLong(GWL_EXSTYLE);
|
||||
@@ -56,20 +56,20 @@ static BOOL ShowWindowCentered(CWindow wnd,CWindow wndParent) {
|
||||
|
||||
class cfgWindowSize : public cfg_var {
|
||||
public:
|
||||
cfgWindowSize(const GUID & p_guid) : cfg_var(p_guid) {}
|
||||
cfgWindowSize(const GUID & p_guid) : cfg_var(p_guid), m_width(~0), m_height(~0) {}
|
||||
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;
|
||||
stream_writer_formatter<>(*p_stream,p_abort) << 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;
|
||||
stream_reader_formatter<>(*p_stream,p_abort) >> m_width >> m_height;
|
||||
}
|
||||
|
||||
uint32_t m_width = UINT32_MAX, m_height = UINT32_MAX;
|
||||
t_uint32 m_width, m_height;
|
||||
};
|
||||
|
||||
class cfgWindowSizeTracker {
|
||||
public:
|
||||
cfgWindowSizeTracker(cfgWindowSize & p_var) : m_var(p_var) {}
|
||||
cfgWindowSizeTracker(cfgWindowSize & p_var) : m_var(p_var), m_applied(false) {}
|
||||
|
||||
bool Apply(HWND p_wnd) {
|
||||
bool retVal = false;
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
}
|
||||
private:
|
||||
cfgWindowSize & m_var;
|
||||
bool m_applied = false;
|
||||
bool m_applied;
|
||||
};
|
||||
|
||||
class cfgDialogSizeTracker : public cfgWindowSizeTracker {
|
||||
@@ -290,8 +290,8 @@ FB2K_STREAM_WRITER_OVERLOAD(cfgDialogPositionData) {
|
||||
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);}
|
||||
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {FetchConfig(); stream_writer_formatter<>(*p_stream, p_abort) << *pfc::implicit_cast<cfgDialogPositionData*>(this);}
|
||||
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {stream_reader_formatter<>(*p_stream, p_abort) >> *pfc::implicit_cast<cfgDialogPositionData*>(this);}
|
||||
};
|
||||
|
||||
class cfgDialogPositionTracker {
|
||||
@@ -321,71 +321,3 @@ private:
|
||||
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();
|
||||
};
|
||||
122
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj
Normal file
122
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{622E8B19-8109-4717-BD4D-9657AA78363E}</ProjectGuid>
|
||||
<RootNamespace>foobar2000_ATL_helpers</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v141_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
|
||||
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
|
||||
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions>/d2notypeopt %(AdditionalOptions)</AdditionalOptions>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<StringPooling>true</StringPooling>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AutoComplete.cpp" />
|
||||
<ClCompile Include="CDialogResizeHelper.cpp" />
|
||||
<ClCompile Include="CEditWithButtons.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="inplace_edit.cpp" />
|
||||
<ClCompile Include="inplace_edit_v2.cpp" />
|
||||
<ClCompile Include="misc.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui_element_helpers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ATLHelpers.h" />
|
||||
<ClInclude Include="ATLHelpersLean.h" />
|
||||
<ClInclude Include="AutoComplete.h" />
|
||||
<ClInclude Include="BumpableElem.h" />
|
||||
<ClInclude Include="CButtonLite.h" />
|
||||
<ClInclude Include="CDialogResizeHelper.h" />
|
||||
<ClInclude Include="CEditWithButtons.h" />
|
||||
<ClInclude Include="CFlashWindow.h" />
|
||||
<ClInclude Include="CIconOverlayWindow.h" />
|
||||
<ClInclude Include="CMiddleDragImpl.h" />
|
||||
<ClInclude Include="Controls.h" />
|
||||
<ClInclude Include="GDIUtils.h" />
|
||||
<ClInclude Include="inplace_edit.h" />
|
||||
<ClInclude Include="inplace_edit_v2.h" />
|
||||
<ClInclude Include="misc.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="ui_element_helpers.h" />
|
||||
<ClInclude Include="WindowPositionUtils.h" />
|
||||
<ClInclude Include="WTL-PP.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
102
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj.filters
Normal file
102
foobar2000/ATLHelpers/foobar2000_ATL_helpers.vcxproj.filters
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AutoComplete.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CDialogResizeHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="inplace_edit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="inplace_edit_v2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="misc.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CEditWithButtons.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui_element_helpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ATLHelpers.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="GDIUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="inplace_edit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="inplace_edit_v2.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WTL-PP.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ATLHelpersLean.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="WindowPositionUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="misc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CFlashWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Controls.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CEditWithButtons.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CButtonLite.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CIconOverlayWindow.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CMiddleDragImpl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui_element_helpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
477
foobar2000/ATLHelpers/inplace_edit.cpp
Normal file
477
foobar2000/ATLHelpers/inplace_edit.cpp
Normal file
@@ -0,0 +1,477 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "inplace_edit.h"
|
||||
#include "AutoComplete.h"
|
||||
#include "misc.h"
|
||||
#include "WTL-PP.h"
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x20E
|
||||
#endif
|
||||
|
||||
using namespace InPlaceEdit;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
enum {
|
||||
MSG_COMPLETION = WM_USER,
|
||||
MSG_DISABLE_EDITING
|
||||
};
|
||||
|
||||
|
||||
static pfc::avltree_t<HWND> g_editboxes;
|
||||
static HHOOK g_hook = NULL /*, g_keyHook = NULL*/;
|
||||
|
||||
static void GAbortEditing(HWND edit, t_uint32 code) {
|
||||
CWindow parent = ::GetParent(edit);
|
||||
parent.SendMessage(MSG_DISABLE_EDITING);
|
||||
parent.PostMessage(MSG_COMPLETION, code, 0);
|
||||
}
|
||||
|
||||
static void GAbortEditing(t_uint32 code) {
|
||||
for(auto walk = g_editboxes.cfirst(); walk.is_valid(); ++walk ) {
|
||||
GAbortEditing(*walk, code);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSamePopup(CWindow wnd1, CWindow wnd2) {
|
||||
return FindOwningPopup(wnd1) == FindOwningPopup(wnd2);
|
||||
}
|
||||
|
||||
static void MouseEventTest(HWND target, CPoint pt, bool isWheel) {
|
||||
for(auto walk = g_editboxes.cfirst(); walk.is_valid(); ++walk) {
|
||||
CWindow edit ( *walk );
|
||||
bool cancel = false;
|
||||
if (target != edit && IsSamePopup(target, edit)) {
|
||||
cancel = true;
|
||||
} else if (isWheel) {
|
||||
CWindow target2 = WindowFromPoint(pt);
|
||||
if (target2 != edit && IsSamePopup(target2, edit)) {
|
||||
cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cancel) GAbortEditing(edit, KEditLostFocus);
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK GMouseProc(int nCode,WPARAM wParam,LPARAM lParam) {
|
||||
if (nCode == HC_ACTION) {
|
||||
const MOUSEHOOKSTRUCT * mhs = reinterpret_cast<const MOUSEHOOKSTRUCT *>(lParam);
|
||||
switch(wParam) {
|
||||
case WM_NCLBUTTONDOWN:
|
||||
case WM_NCRBUTTONDOWN:
|
||||
case WM_NCMBUTTONDOWN:
|
||||
case WM_NCXBUTTONDOWN:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_XBUTTONDOWN:
|
||||
MouseEventTest(mhs->hwnd, mhs->pt, false);
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
case WM_MOUSEHWHEEL:
|
||||
MouseEventTest(mhs->hwnd, mhs->pt, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(g_hook,nCode,wParam,lParam);
|
||||
}
|
||||
#if 0
|
||||
static LRESULT CALLBACK GKeyboardProc(int code, WPARAM wp, LPARAM lp) {
|
||||
if (code == HC_ACTION && (lp & (1<<31)) == 0) {
|
||||
switch(wp) {
|
||||
case VK_RETURN:
|
||||
if (!IsKeyPressed(VK_LCONTROL) && !IsKeyPressed(VK_RCONTROL)) {
|
||||
GAbortEditing(KEditEnter);
|
||||
}
|
||||
break;
|
||||
case VK_TAB:
|
||||
GAbortEditing(IsKeyPressed(VK_SHIFT) ? KEditShiftTab : KEditTab);
|
||||
break;
|
||||
case VK_ESCAPE:
|
||||
GAbortEditing(KEditAborted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(g_keyHook,code,wp,lp);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void on_editbox_creation(HWND p_editbox) {
|
||||
PFC_ASSERT( core_api::is_main_thread() );
|
||||
g_editboxes.add(p_editbox);
|
||||
if (g_hook == NULL) {
|
||||
g_hook = SetWindowsHookEx(WH_MOUSE,GMouseProc,NULL,GetCurrentThreadId());
|
||||
}
|
||||
/*if (g_keyHook == NULL) {
|
||||
g_keyHook = SetWindowsHookEx(WH_KEYBOARD, GKeyboardProc, NULL, GetCurrentThreadId());
|
||||
}*/
|
||||
}
|
||||
static void UnhookHelper(HHOOK & hook) {
|
||||
HHOOK v = pfc::replace_null_t(hook);
|
||||
if (v != NULL) UnhookWindowsHookEx(v);
|
||||
}
|
||||
static void on_editbox_destruction(HWND p_editbox) {
|
||||
PFC_ASSERT( core_api::is_main_thread() );
|
||||
g_editboxes.remove_item(p_editbox);
|
||||
if (g_editboxes.get_count() == 0) {
|
||||
UnhookHelper(g_hook); /*UnhookHelper(g_keyHook);*/
|
||||
}
|
||||
}
|
||||
|
||||
class CInPlaceEditBox : public CContainedWindowSimpleT<CEdit> {
|
||||
public:
|
||||
CInPlaceEditBox() : m_selfDestruct(), m_suppressChar() {}
|
||||
BEGIN_MSG_MAP_EX(CInPlaceEditBox)
|
||||
//MSG_WM_CREATE(OnCreate)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
MSG_WM_GETDLGCODE(OnGetDlgCode)
|
||||
MSG_WM_KILLFOCUS(OnKillFocus)
|
||||
MSG_WM_CHAR(OnChar)
|
||||
MSG_WM_KEYDOWN(OnKeyDown)
|
||||
END_MSG_MAP()
|
||||
void OnCreation() {
|
||||
m_typableScope.Set(m_hWnd);
|
||||
on_editbox_creation(m_hWnd);
|
||||
}
|
||||
private:
|
||||
void OnDestroy() {
|
||||
m_selfDestruct = true;
|
||||
m_typableScope.Set(NULL);
|
||||
on_editbox_destruction(m_hWnd);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
int OnCreate(LPCREATESTRUCT lpCreateStruct) {
|
||||
OnCreation();
|
||||
SetMsgHandled(FALSE);
|
||||
return 0;
|
||||
}
|
||||
UINT OnGetDlgCode(LPMSG lpMsg) {
|
||||
if (lpMsg == NULL) {
|
||||
return DLGC_WANTALLKEYS;
|
||||
} else {
|
||||
switch(lpMsg->message) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
switch(lpMsg->wParam) {
|
||||
case VK_TAB:
|
||||
case VK_ESCAPE:
|
||||
case VK_RETURN:
|
||||
return DLGC_WANTALLKEYS;
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
}
|
||||
default:
|
||||
SetMsgHandled(FALSE); return 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
void OnKillFocus(CWindow wndFocus) {
|
||||
ForwardCompletion(KEditLostFocus);
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
if (m_suppressChar != 0) {
|
||||
UINT code = nFlags & 0xFF;
|
||||
if (code == m_suppressChar) return;
|
||||
}
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
|
||||
m_suppressChar = nFlags & 0xFF;
|
||||
switch(nChar) {
|
||||
case VK_BACK:
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
CEditPPHooks::DeleteLastWord( * this );
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
if (GetHotkeyModifierFlags() == MOD_CONTROL) {
|
||||
this->SetSelAll(); return;
|
||||
}
|
||||
break;
|
||||
case VK_RETURN:
|
||||
if (!IsKeyPressed(VK_LCONTROL) && !IsKeyPressed(VK_RCONTROL)) {
|
||||
ForwardCompletion(KEditEnter);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case VK_TAB:
|
||||
ForwardCompletion(IsKeyPressed(VK_SHIFT) ? KEditShiftTab : KEditTab);
|
||||
return;
|
||||
case VK_ESCAPE:
|
||||
ForwardCompletion(KEditAborted);
|
||||
return;
|
||||
}
|
||||
m_suppressChar = 0;
|
||||
SetMsgHandled(FALSE);
|
||||
}
|
||||
|
||||
void ForwardCompletion(t_uint32 code) {
|
||||
if (IsWindowEnabled()) {
|
||||
CWindow owner = GetParent();
|
||||
owner.SendMessage(MSG_DISABLE_EDITING);
|
||||
owner.PostMessage(MSG_COMPLETION,code,0);
|
||||
EnableWindow(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
CTypableWindowScope m_typableScope;
|
||||
bool m_selfDestruct;
|
||||
UINT m_suppressChar;
|
||||
};
|
||||
|
||||
class InPlaceEditContainer : public CWindowImpl<InPlaceEditContainer> {
|
||||
public:
|
||||
DECLARE_WND_CLASS_EX(_T("{54340C80-248C-4b8e-8CD4-D624A8E9377B}"),0,-1);
|
||||
|
||||
|
||||
HWND Create(CWindow parent) {
|
||||
|
||||
RECT rect_cropped;
|
||||
{
|
||||
RECT client;
|
||||
WIN32_OP_D(parent.GetClientRect(&client));
|
||||
IntersectRect(&rect_cropped,&client,&m_initRect);
|
||||
}
|
||||
const DWORD containerStyle = WS_BORDER|WS_CHILD;
|
||||
AdjustWindowRect(&rect_cropped,containerStyle,FALSE);
|
||||
|
||||
|
||||
|
||||
WIN32_OP( __super::Create(parent,rect_cropped, NULL, containerStyle) != NULL );
|
||||
|
||||
try {
|
||||
CRect rcClient;
|
||||
WIN32_OP_D(GetClientRect(rcClient));
|
||||
|
||||
|
||||
DWORD style = WS_CHILD|WS_VISIBLE;//parent is invisible now
|
||||
if (m_flags & KFlagMultiLine) style |= WS_VSCROLL|ES_MULTILINE;
|
||||
else style |= ES_AUTOHSCROLL;
|
||||
if (m_flags & KFlagReadOnly) style |= ES_READONLY;
|
||||
if (m_flags & KFlagAlignCenter) style |= ES_CENTER;
|
||||
else if (m_flags & KFlagAlignRight) style |= ES_RIGHT;
|
||||
else style |= ES_LEFT;
|
||||
|
||||
|
||||
CEdit edit;
|
||||
|
||||
WIN32_OP( edit.Create(*this, rcClient, NULL, style, 0, ID_MYEDIT) != NULL );
|
||||
edit.SetFont(parent.GetFont());
|
||||
|
||||
if (m_ACData.is_valid()) InitializeSimpleAC(edit, m_ACData.get_ptr(), m_ACOpts);
|
||||
m_edit.SubclassWindow(edit);
|
||||
m_edit.OnCreation();
|
||||
|
||||
uSetWindowText(m_edit,*m_content);
|
||||
m_edit.SetSelAll();
|
||||
} catch(...) {
|
||||
PostMessage(MSG_COMPLETION,InPlaceEdit::KEditAborted,0);
|
||||
return m_hWnd;
|
||||
}
|
||||
|
||||
ShowWindow(SW_SHOW);
|
||||
m_edit.SetFocus();
|
||||
|
||||
m_initialized = true;
|
||||
|
||||
PFC_ASSERT( m_hWnd != NULL );
|
||||
|
||||
return m_hWnd;
|
||||
}
|
||||
|
||||
InPlaceEditContainer(const RECT & p_rect,t_uint32 p_flags,pfc::rcptr_t<pfc::string_base> p_content,completion_notify_ptr p_notify, IUnknown * ACData, DWORD ACOpts)
|
||||
: m_content(p_content), m_notify(p_notify), m_completed(false), m_initialized(false), m_changed(false), m_disable_editing(false), m_initRect(p_rect), m_flags(p_flags), m_selfDestruct(), m_ACData(ACData), m_ACOpts(ACOpts)
|
||||
{
|
||||
}
|
||||
|
||||
enum {ID_MYEDIT = 666};
|
||||
|
||||
BEGIN_MSG_MAP_EX(InPlaceEditContainer)
|
||||
MESSAGE_HANDLER_EX(WM_CTLCOLOREDIT, MsgForwardToParent)
|
||||
MESSAGE_HANDLER_EX(WM_CTLCOLORSTATIC, MsgForwardToParent)
|
||||
MESSAGE_HANDLER_EX(WM_MOUSEWHEEL, MsgLostFocus)
|
||||
MESSAGE_HANDLER_EX(WM_MOUSEHWHEEL, MsgLostFocus)
|
||||
MESSAGE_HANDLER_SIMPLE(MSG_DISABLE_EDITING, OnMsgDisableEditing)
|
||||
MESSAGE_HANDLER_EX(MSG_COMPLETION, OnMsgCompletion)
|
||||
COMMAND_HANDLER_EX(ID_MYEDIT, EN_CHANGE, OnEditChange)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
END_MSG_MAP()
|
||||
|
||||
HWND GetEditBox() const {return m_edit;}
|
||||
|
||||
private:
|
||||
void OnDestroy() {m_selfDestruct = true;}
|
||||
|
||||
LRESULT MsgForwardToParent(UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
return GetParent().SendMessage(msg, wParam, lParam);
|
||||
}
|
||||
LRESULT MsgLostFocus(UINT, WPARAM, LPARAM) {
|
||||
PostMessage(MSG_COMPLETION,InPlaceEdit::KEditLostFocus,0);
|
||||
return 0;
|
||||
}
|
||||
void OnMsgDisableEditing() {
|
||||
ShowWindow(SW_HIDE);
|
||||
GetParent().UpdateWindow();
|
||||
m_disable_editing = true;
|
||||
}
|
||||
LRESULT OnMsgCompletion(UINT, WPARAM wParam, LPARAM lParam) {
|
||||
PFC_ASSERT(m_initialized);
|
||||
if ((wParam & KEditMaskReason) != KEditLostFocus) {
|
||||
GetParent().SetFocus();
|
||||
}
|
||||
OnCompletion(wParam);
|
||||
if (!m_selfDestruct) {
|
||||
m_selfDestruct = true;
|
||||
DestroyWindow();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void OnEditChange(UINT, int, CWindow source) {
|
||||
if (m_initialized && !m_disable_editing) {
|
||||
uGetWindowText(source,*m_content);
|
||||
m_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void OnCompletion(unsigned p_status) {
|
||||
if (!m_completed) {
|
||||
m_completed = true;
|
||||
p_status &= KEditMaskReason;
|
||||
unsigned code = p_status;
|
||||
if (m_changed && p_status != KEditAborted) code |= KEditFlagContentChanged;
|
||||
if (m_notify.is_valid()) m_notify->on_completion(code);
|
||||
}
|
||||
}
|
||||
|
||||
const pfc::rcptr_t<pfc::string_base> m_content;
|
||||
const completion_notify_ptr m_notify;
|
||||
bool m_completed;
|
||||
bool m_initialized, m_changed;
|
||||
bool m_disable_editing;
|
||||
bool m_selfDestruct;
|
||||
const CRect m_initRect;
|
||||
const t_uint32 m_flags;
|
||||
CInPlaceEditBox m_edit;
|
||||
|
||||
const pfc::com_ptr_t<IUnknown> m_ACData;
|
||||
const DWORD m_ACOpts;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
static void fail(completion_notify_ptr p_notify) {
|
||||
completion_notify::g_signal_completion_async(p_notify,KEditAborted);
|
||||
}
|
||||
|
||||
HWND InPlaceEdit::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 StartEx(p_parentwnd,p_rect,p_multiline ? KFlagMultiLine : 0, p_content,p_notify);
|
||||
}
|
||||
|
||||
void InPlaceEdit::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_FromListViewEx(p_listview,p_item,p_subitem,p_linecount,0,p_content,p_notify);
|
||||
}
|
||||
|
||||
bool InPlaceEdit::TableEditAdvance_ListView(HWND p_listview,unsigned p_column_base,unsigned & p_item,unsigned & p_column, unsigned p_item_count,unsigned p_column_count, unsigned p_whathappened) {
|
||||
if (p_column >= p_column_count) return false;
|
||||
|
||||
|
||||
pfc::array_t<t_size> orderRev;
|
||||
{
|
||||
pfc::array_t<unsigned> order;
|
||||
const unsigned orderExCount = /*p_column_base + p_column_count*/ ListView_GetColumnCount(p_listview);
|
||||
PFC_ASSERT( orderExCount >= p_column_base + p_column_count );
|
||||
pfc::array_t<int> orderEx; orderEx.set_size(orderExCount);
|
||||
if (!ListView_GetColumnOrderArray(p_listview,orderExCount,orderEx.get_ptr())) {
|
||||
PFC_ASSERT(!"Should not get here - probably mis-calculated column count");
|
||||
return false;
|
||||
}
|
||||
order.set_size(p_column_count);
|
||||
for(unsigned walk = 0; walk < p_column_count; ++walk) order[walk] = orderEx[p_column_base + walk];
|
||||
|
||||
orderRev.set_size(p_column_count); order_helper::g_fill(orderRev);
|
||||
pfc::sort_get_permutation_t(order,pfc::compare_t<unsigned,unsigned>,p_column_count,orderRev.get_ptr());
|
||||
}
|
||||
|
||||
unsigned columnVisible = (unsigned)orderRev[p_column];
|
||||
|
||||
|
||||
if (!TableEditAdvance(p_item,columnVisible,p_item_count,p_column_count,p_whathappened)) return false;
|
||||
|
||||
p_column = (unsigned)order_helper::g_find_reverse(orderRev.get_ptr(),columnVisible);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InPlaceEdit::TableEditAdvance(unsigned & p_item,unsigned & p_column, unsigned p_item_count,unsigned p_column_count, unsigned p_whathappened) {
|
||||
if (p_item >= p_item_count || p_column >= p_column_count) return false;
|
||||
int delta = 0;
|
||||
|
||||
switch(p_whathappened & KEditMaskReason) {
|
||||
case KEditEnter:
|
||||
delta = (int) p_column_count;
|
||||
break;
|
||||
case KEditTab:
|
||||
delta = 1;
|
||||
break;
|
||||
case KEditShiftTab:
|
||||
delta = -1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
while(delta > 0) {
|
||||
p_column++;
|
||||
if (p_column >= p_column_count) {
|
||||
p_column = 0;
|
||||
p_item++;
|
||||
if (p_item >= p_item_count) return false;
|
||||
}
|
||||
delta--;
|
||||
}
|
||||
while(delta < 0) {
|
||||
if (p_column == 0) {
|
||||
if (p_item == 0) return false;
|
||||
p_item--;
|
||||
p_column = p_column_count;
|
||||
}
|
||||
p_column--;
|
||||
delta++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
HWND InPlaceEdit::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) {
|
||||
try {
|
||||
PFC_ASSERT( (CWindow(p_parentwnd).GetWindowLong(GWL_STYLE) & WS_CLIPCHILDREN) != 0 );
|
||||
return (new CWindowAutoLifetime<InPlaceEditContainer>(p_parentwnd,p_rect,p_flags,p_content,p_notify, ACData, ACOpts))->GetEditBox();
|
||||
} catch(...) {
|
||||
fail(p_notify);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void InPlaceEdit::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) {
|
||||
try {
|
||||
ListView_EnsureVisible(p_listview,p_item,FALSE);
|
||||
RECT itemrect;
|
||||
WIN32_OP_D( ListView_GetSubItemRect(p_listview,p_item,p_subitem,LVIR_LABEL,&itemrect) );
|
||||
|
||||
const bool multiline = p_linecount > 1;
|
||||
if (multiline) {
|
||||
itemrect.bottom = itemrect.top + (itemrect.bottom - itemrect.top) * p_linecount;
|
||||
}
|
||||
|
||||
StartEx(p_listview,itemrect,p_flags | (multiline ? KFlagMultiLine : 0),p_content,p_notify);
|
||||
} catch(...) {
|
||||
fail(p_notify);
|
||||
}
|
||||
}
|
||||
132
foobar2000/ATLHelpers/inplace_edit.h
Normal file
132
foobar2000/ATLHelpers/inplace_edit.h
Normal file
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/listview_helper.h"
|
||||
|
||||
namespace InPlaceEdit {
|
||||
|
||||
enum {
|
||||
KEditAborted = 0,
|
||||
KEditTab,
|
||||
KEditShiftTab,
|
||||
KEditEnter,
|
||||
KEditLostFocus,
|
||||
|
||||
KEditMaskReason = 0xFF,
|
||||
KEditFlagContentChanged = 0x100,
|
||||
|
||||
KFlagReadOnly = 1 << 0,
|
||||
KFlagMultiLine = 1 << 1,
|
||||
KFlagAlignCenter = 1 << 2,
|
||||
KFlagAlignRight = 1 << 3,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
bool TableEditAdvance(unsigned & p_item,unsigned & p_column, unsigned p_item_count,unsigned p_column_count, unsigned p_whathappened);
|
||||
bool TableEditAdvance_ListView(HWND p_listview,unsigned p_column_base,unsigned & p_item,unsigned & p_column, unsigned p_item_count,unsigned p_column_count, unsigned p_whathappened);
|
||||
|
||||
|
||||
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) {
|
||||
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 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 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) {
|
||||
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;
|
||||
}
|
||||
virtual void 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);
|
||||
}
|
||||
|
||||
virtual void TableEdit_Finished() {}
|
||||
|
||||
void 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() {
|
||||
if (m_notify.is_valid()) {
|
||||
m_notify->orphan();
|
||||
m_notify.release();
|
||||
}
|
||||
}
|
||||
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() {
|
||||
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);
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
147
foobar2000/ATLHelpers/inplace_edit_v2.cpp
Normal file
147
foobar2000/ATLHelpers/inplace_edit_v2.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "../helpers/win32_misc.h"
|
||||
#include "inplace_edit.h"
|
||||
#include "inplace_edit_v2.h"
|
||||
#include "AutoComplete.h"
|
||||
|
||||
|
||||
namespace InPlaceEdit {
|
||||
|
||||
t_size CTableEditHelperV2::ColumnToPosition(t_size col) const {
|
||||
PFC_ASSERT( TableEdit_IsColumnEditable(col) );
|
||||
pfc::array_t<t_size> colOrder; GrabColumnOrder(colOrder);
|
||||
t_size skipped = 0;
|
||||
for(t_size walk = 0; walk < colOrder.get_size(); ++walk) {
|
||||
const t_size curCol = colOrder[walk];
|
||||
if (TableEdit_IsColumnEditable(curCol)) {
|
||||
if (curCol == col) return skipped;
|
||||
++skipped;
|
||||
}
|
||||
}
|
||||
PFC_ASSERT( !"Should not get here." );
|
||||
return ~0;
|
||||
}
|
||||
t_size CTableEditHelperV2::PositionToColumn(t_size pos) const {
|
||||
pfc::array_t<t_size> colOrder; GrabColumnOrder(colOrder);
|
||||
t_size skipped = 0;
|
||||
for(t_size walk = 0; walk < colOrder.get_size(); ++walk) {
|
||||
const t_size curCol = colOrder[walk];
|
||||
if (TableEdit_IsColumnEditable(curCol)) {
|
||||
if (skipped == pos) return curCol;
|
||||
++skipped;
|
||||
}
|
||||
}
|
||||
PFC_ASSERT( !"Should not get here." );
|
||||
return ~0;
|
||||
}
|
||||
t_size CTableEditHelperV2::EditableColumnCount() const {
|
||||
const t_size total = TableEdit_GetColumnCount();
|
||||
t_size found = 0;
|
||||
for(t_size walk = 0; walk < total; ++walk) {
|
||||
if (TableEdit_IsColumnEditable(walk)) found++;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool CTableEditHelperV2::TableEdit_Advance(t_size & item, t_size & subItem, t_uint32 whathappened) {
|
||||
//moo
|
||||
unsigned _item((unsigned)item), _subItem((unsigned)ColumnToPosition(subItem));
|
||||
if (!InPlaceEdit::TableEditAdvance(_item,_subItem,(unsigned) TableEdit_GetItemCount(), (unsigned) EditableColumnCount(), whathappened)) return false;
|
||||
item = _item; subItem = PositionToColumn(_subItem);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::TableEdit_Abort(bool forwardContent) {
|
||||
if (this->have_task(KTaskID)) {
|
||||
this->orphan_task(KTaskID);
|
||||
if (forwardContent && (m_editFlags & KFlagReadOnly) == 0) {
|
||||
if (m_editData.is_valid()) {
|
||||
pfc::string8 temp(*m_editData);
|
||||
TableEdit_SetField(m_editItem,m_editSubItem, temp);
|
||||
}
|
||||
}
|
||||
m_editData.release();
|
||||
SetFocus(NULL);
|
||||
TableEdit_Finished();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::TableEdit_Start(t_size item, t_size subItem) {
|
||||
PFC_ASSERT( TableEdit_IsColumnEditable( subItem ) );
|
||||
m_editItem = item; m_editSubItem = subItem;
|
||||
_ReStart();
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::_ReStart() {
|
||||
PFC_ASSERT( m_editItem < TableEdit_GetItemCount() );
|
||||
PFC_ASSERT( m_editSubItem < TableEdit_GetColumnCount() );
|
||||
|
||||
TableEdit_SetItemFocus(m_editItem,m_editSubItem);
|
||||
m_editData.new_t();
|
||||
t_size lineCount = 1;
|
||||
TableEdit_GetField(m_editItem, m_editSubItem, *m_editData, lineCount);
|
||||
m_editFlags = TableEdit_GetEditFlags(m_editItem, m_editSubItem);
|
||||
|
||||
RECT rc = TableEdit_GetItemRect(m_editItem, m_editSubItem);
|
||||
if (lineCount > 1) {
|
||||
rc.bottom = rc.top + (rc.bottom - rc.top) * lineCount;
|
||||
m_editFlags |= KFlagMultiLine;
|
||||
}
|
||||
pfc::com_ptr_t<IUnknown> acl;
|
||||
if (!TableEdit_GetAutoComplete(m_editItem, m_editSubItem, acl)) acl.release();
|
||||
|
||||
InPlaceEdit::StartEx(TableEdit_GetParentWnd(), rc, m_editFlags, m_editData, create_task(KTaskID), acl.get_ptr(), ACO_AUTOSUGGEST);
|
||||
}
|
||||
|
||||
void CTableEditHelperV2::on_task_completion(unsigned id, unsigned status) {
|
||||
if (id == KTaskID) {
|
||||
orphan_task(KTaskID);
|
||||
if (m_editData.is_valid()) {
|
||||
if (status & InPlaceEdit::KEditFlagContentChanged) {
|
||||
TableEdit_SetField(m_editItem,m_editSubItem,*m_editData);
|
||||
}
|
||||
m_editData.release();
|
||||
}
|
||||
|
||||
if (TableEdit_Advance(m_editItem,m_editSubItem,status)) {
|
||||
_ReStart();
|
||||
} else {
|
||||
TableEdit_Finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void CTableEditHelperV2_ListView::TableEdit_GetColumnOrder(t_size * out, t_size count) const {
|
||||
pfc::array_t<int> temp; temp.set_size(count);
|
||||
WIN32_OP_D( ListView_GetColumnOrderArray( TableEdit_GetParentWnd(), count, temp.get_ptr() ) );
|
||||
for(t_size walk = 0; walk < count; ++walk) out[walk] = temp[walk];
|
||||
}
|
||||
|
||||
RECT CTableEditHelperV2_ListView::TableEdit_GetItemRect(t_size item, t_size subItem) const {
|
||||
RECT rc;
|
||||
WIN32_OP_D( ListView_GetSubItemRect(TableEdit_GetParentWnd(),item,subItem,LVIR_LABEL,&rc) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
void CTableEditHelperV2_ListView::TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount) {
|
||||
listview_helper::get_item_text( TableEdit_GetParentWnd(), item, subItem, out);
|
||||
lineCount = pfc::is_multiline(out) ? 5 : 1;
|
||||
}
|
||||
void CTableEditHelperV2_ListView::TableEdit_SetField(t_size item, t_size subItem, const char * value) {
|
||||
WIN32_OP_D( listview_helper::set_item_text( TableEdit_GetParentWnd(), item, subItem, value) );
|
||||
}
|
||||
t_size CTableEditHelperV2_ListView::TableEdit_GetItemCount() const {
|
||||
LRESULT temp;
|
||||
WIN32_OP_D( ( temp = ListView_GetItemCount( TableEdit_GetParentWnd() ) ) >= 0 );
|
||||
return (t_size) temp;
|
||||
}
|
||||
void CTableEditHelperV2_ListView::TableEdit_SetItemFocus(t_size item, t_size subItem) {
|
||||
WIN32_OP_D( listview_helper::select_single_item( TableEdit_GetParentWnd(), item ) );
|
||||
}
|
||||
}
|
||||
55
foobar2000/ATLHelpers/inplace_edit_v2.h
Normal file
55
foobar2000/ATLHelpers/inplace_edit_v2.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "../helpers/listview_helper.h"
|
||||
|
||||
namespace InPlaceEdit {
|
||||
class NOVTABLE CTableEditHelperV2 : protected completion_notify_receiver {
|
||||
public:
|
||||
virtual RECT TableEdit_GetItemRect(t_size item, t_size subItem) const = 0;
|
||||
virtual void TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount) = 0;
|
||||
virtual void TableEdit_SetField(t_size item, t_size subItem, const char * value) = 0;
|
||||
virtual HWND TableEdit_GetParentWnd() const = 0;
|
||||
virtual bool TableEdit_Advance(t_size & item, t_size & subItem, t_uint32 whathappened);
|
||||
virtual void TableEdit_Finished() {}
|
||||
virtual t_size TableEdit_GetItemCount() const = 0;
|
||||
virtual t_size TableEdit_GetColumnCount() const = 0;
|
||||
virtual void TableEdit_SetItemFocus(t_size item, t_size subItem) = 0;
|
||||
virtual bool TableEdit_IsColumnEditable(t_size subItem) const {return true;}
|
||||
virtual void TableEdit_GetColumnOrder(t_size * out, t_size count) const {order_helper::g_fill(out,count);}
|
||||
virtual t_uint32 TableEdit_GetEditFlags(t_size item, t_size subItem) const {return 0;}
|
||||
virtual bool TableEdit_GetAutoComplete(t_size item, t_size subItem, pfc::com_ptr_t<IUnknown> & out) {return false;}
|
||||
void TableEdit_Start(t_size item, t_size subItem);
|
||||
void TableEdit_Abort(bool forwardContent);
|
||||
bool TableEdit_IsActive() const {return have_task(KTaskID);}
|
||||
protected:
|
||||
void on_task_completion(unsigned p_id,unsigned p_status);
|
||||
private:
|
||||
t_size ColumnToPosition(t_size col) const;
|
||||
t_size PositionToColumn(t_size pos) const;
|
||||
t_size EditableColumnCount() const;
|
||||
void GrabColumnOrder(pfc::array_t<t_size> & buffer) const {buffer.set_size(TableEdit_GetColumnCount()); TableEdit_GetColumnOrder(buffer.get_ptr(), buffer.get_size());}
|
||||
void _ReStart();
|
||||
|
||||
t_size m_editItem, m_editSubItem;
|
||||
t_uint32 m_editFlags;
|
||||
pfc::rcptr_t<pfc::string8> m_editData;
|
||||
static const unsigned KTaskID = 0x6f0a3de6;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class NOVTABLE CTableEditHelperV2_ListView : public CTableEditHelperV2 {
|
||||
public:
|
||||
RECT TableEdit_GetItemRect(t_size item, t_size subItem) const;
|
||||
void TableEdit_GetField(t_size item, t_size subItem, pfc::string_base & out, t_size & lineCount);
|
||||
void TableEdit_SetField(t_size item, t_size subItem, const char * value);
|
||||
|
||||
t_size TableEdit_GetColumnCount() const {return (t_size) ListView_GetColumnCount(TableEdit_GetParentWnd());}
|
||||
|
||||
t_size TableEdit_GetItemCount() const;
|
||||
void TableEdit_SetItemFocus(t_size item, t_size subItem);
|
||||
|
||||
void TableEdit_GetColumnOrder(t_size * out, t_size count) const;
|
||||
};
|
||||
}
|
||||
100
foobar2000/ATLHelpers/misc.cpp
Normal file
100
foobar2000/ATLHelpers/misc.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "stdafx.h"
|
||||
#include "Controls.h"
|
||||
#include "../helpers/win32_misc.h"
|
||||
|
||||
void PaintSeparatorControl(CWindow wnd) {
|
||||
CPaintDC dc(wnd);
|
||||
TCHAR buffer[512] = {};
|
||||
wnd.GetWindowText(buffer, _countof(buffer));
|
||||
const int txLen = pfc::strlen_max_t(buffer, _countof(buffer));
|
||||
CRect contentRect;
|
||||
WIN32_OP_D( wnd.GetClientRect(contentRect) );
|
||||
|
||||
dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
|
||||
dc.SetBkMode(TRANSPARENT);
|
||||
|
||||
{
|
||||
CBrushHandle brush = (HBRUSH) wnd.GetParent().SendMessage(WM_CTLCOLORSTATIC, (WPARAM) (HDC) dc, (LPARAM) wnd.m_hWnd);
|
||||
if (brush != NULL) dc.FillRect(contentRect, brush);
|
||||
}
|
||||
SelectObjectScope scopeFont(dc, wnd.GetFont());
|
||||
|
||||
if (txLen > 0) {
|
||||
CRect rcText(contentRect);
|
||||
if ( !wnd.IsWindowEnabled() ) {
|
||||
dc.SetTextColor( GetSysColor(COLOR_GRAYTEXT) );
|
||||
}
|
||||
WIN32_OP_D( dc.DrawText(buffer,txLen,rcText,DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE | DT_VCENTER | DT_LEFT ) > 0);
|
||||
// WIN32_OP_D( dc.GrayString(NULL, NULL, (LPARAM) buffer, txLen, rcText.left, rcText.top, rcText.Width(), rcText.Height() ) );
|
||||
}
|
||||
|
||||
SIZE txSize, probeSize;
|
||||
const TCHAR probe[] = _T("#");
|
||||
if (dc.GetTextExtent(buffer,txLen,&txSize) && dc.GetTextExtent(probe, _countof(probe), &probeSize)) {
|
||||
int spacing = txSize.cx > 0 ? (probeSize.cx / 4) : 0;
|
||||
if (txSize.cx + spacing < contentRect.Width()) {
|
||||
const CPoint center = contentRect.CenterPoint();
|
||||
CRect rcEdge(contentRect.left + txSize.cx + spacing, center.y, contentRect.right, contentRect.bottom);
|
||||
WIN32_OP_D( dc.DrawEdge(rcEdge, EDGE_ETCHED, BF_TOP) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if _WIN32_WINNT >= 0x501
|
||||
void HeaderControl_SetSortIndicator(CHeaderCtrl header, int column, bool isUp) {
|
||||
const int total = header.GetItemCount();
|
||||
for(int walk = 0; walk < total; ++walk) {
|
||||
HDITEM item = {}; item.mask = HDI_FORMAT;
|
||||
if (header.GetItem(walk,&item)) {
|
||||
DWORD newFormat = item.fmt;
|
||||
newFormat &= ~( HDF_SORTUP | HDF_SORTDOWN );
|
||||
if (walk == column) {
|
||||
newFormat |= isUp ? HDF_SORTUP : HDF_SORTDOWN;
|
||||
}
|
||||
if (newFormat != item.fmt) {
|
||||
item.fmt = newFormat;
|
||||
header.SetItem(walk,&item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "win32_misc.h"
|
||||
#include <libPPUI/WTL-PP.h>
|
||||
#include "../helpers/win32_misc.h"
|
||||
#include "WTL-PP.h"
|
||||
#include <utility>
|
||||
|
||||
class CMenuSelectionReceiver : public CWindowImpl<CMenuSelectionReceiver> {
|
||||
@@ -129,7 +129,7 @@ public:
|
||||
CHAIN_MSG_MAP(TClass)
|
||||
END_MSG_MAP_HOOK()
|
||||
private:
|
||||
BOOL OnInitDialog(CWindow, LPARAM) {m_modeless.Set( this->m_hWnd ); SetMsgHandled(FALSE); return FALSE; }
|
||||
BOOL OnInitDialog(CWindow, LPARAM) {m_modeless.Set( m_hWnd ); SetMsgHandled(FALSE); return FALSE; }
|
||||
void OnDestroy() {m_modeless.Set(NULL); SetMsgHandled(FALSE); }
|
||||
CModelessDialogEntry m_modeless;
|
||||
};
|
||||
@@ -155,6 +155,45 @@ private:
|
||||
const service_ptr_t<ui_element_instance> m_owner;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
#if _WIN32_WINNT >= 0x501
|
||||
void HeaderControl_SetSortIndicator(CHeaderCtrl header, int column, bool isUp);
|
||||
#endif
|
||||
|
||||
class CTypableWindowScope {
|
||||
public:
|
||||
CTypableWindowScope() : m_wnd() {}
|
||||
~CTypableWindowScope() {Set(NULL);}
|
||||
void Set(HWND wnd) {
|
||||
try {
|
||||
if (m_wnd != NULL) {
|
||||
static_api_ptr_t<ui_element_typable_window_manager>()->remove(m_wnd);
|
||||
}
|
||||
m_wnd = wnd;
|
||||
if (m_wnd != NULL) {
|
||||
static_api_ptr_t<ui_element_typable_window_manager>()->add(m_wnd);
|
||||
}
|
||||
} catch(exception_service_not_found) {
|
||||
m_wnd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
HWND m_wnd;
|
||||
PFC_CLASS_NOT_COPYABLE_EX(CTypableWindowScope);
|
||||
};
|
||||
|
||||
|
||||
template<typename TBase> class CContainedWindowSimpleT : public CContainedWindowT<TBase>, public CMessageMap {
|
||||
public:
|
||||
CContainedWindowSimpleT() : CContainedWindowT<TBase>(this) {}
|
||||
BEGIN_MSG_MAP(CContainedWindowSimpleT)
|
||||
END_MSG_MAP()
|
||||
};
|
||||
|
||||
|
||||
static bool window_service_trait_defer_destruction(const service_base *) {return true;}
|
||||
|
||||
|
||||
@@ -175,9 +214,9 @@ public:
|
||||
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) {
|
||||
} else if (m_hWnd != NULL) {
|
||||
if (!m_destroyWindowInProgress) { // don't double-destroy in weird scenarios
|
||||
PFC_ASSERT_NO_EXCEPTION( ::DestroyWindow(this->m_hWnd) );
|
||||
PFC_ASSERT_NO_EXCEPTION( ::DestroyWindow(m_hWnd) );
|
||||
}
|
||||
} else {
|
||||
PFC_ASSERT_NO_EXCEPTION( delete this );
|
||||
@@ -202,12 +241,6 @@ private:
|
||||
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 );
|
||||
@@ -221,27 +254,143 @@ public:
|
||||
LRESULT& lResult, DWORD dwMsgMapID) {return FALSE;}
|
||||
};
|
||||
|
||||
class CPopupTooltipMessage {
|
||||
public:
|
||||
CPopupTooltipMessage(DWORD style = TTS_BALLOON | TTS_NOPREFIX) : m_style(style | WS_POPUP), m_toolinfo(), m_shutDown() {}
|
||||
void ShowFocus(const TCHAR * message, CWindow wndParent) {
|
||||
Show(message, wndParent); wndParent.SetFocus();
|
||||
}
|
||||
void Show(const TCHAR * message, CWindow wndParent) {
|
||||
if (m_shutDown || (message == NULL && m_tooltip.m_hWnd == NULL)) return;
|
||||
Initialize();
|
||||
Hide();
|
||||
|
||||
if (message != NULL) {
|
||||
CRect rect;
|
||||
WIN32_OP_D( wndParent.GetWindowRect(rect) );
|
||||
ShowInternal(message, wndParent, rect);
|
||||
}
|
||||
}
|
||||
void ShowEx(const TCHAR * message, CWindow wndParent, CRect rect) {
|
||||
if (m_shutDown) return;
|
||||
Initialize();
|
||||
Hide();
|
||||
ShowInternal(message, wndParent, rect);
|
||||
}
|
||||
void Hide() {
|
||||
if (m_tooltip.m_hWnd != NULL && m_tooltip.GetToolCount() > 0) {
|
||||
m_tooltip.TrackActivate(&m_toolinfo,FALSE);
|
||||
m_tooltip.DelTool(&m_toolinfo);
|
||||
}
|
||||
}
|
||||
|
||||
void CleanUp() {
|
||||
if (m_tooltip.m_hWnd != NULL) {
|
||||
m_tooltip.DestroyWindow();
|
||||
}
|
||||
}
|
||||
void ShutDown() {
|
||||
m_shutDown = true; CleanUp();
|
||||
}
|
||||
private:
|
||||
void ShowInternal(const TCHAR * message, CWindow wndParent, CRect rect) {
|
||||
|
||||
PFC_ASSERT( !m_shutDown );
|
||||
PFC_ASSERT( message != NULL );
|
||||
PFC_ASSERT( wndParent != NULL );
|
||||
|
||||
if ( _tcschr( message, '\n') != nullptr ) {
|
||||
m_tooltip.SetMaxTipWidth( rect.Width() );
|
||||
}
|
||||
m_toolinfo.cbSize = sizeof(m_toolinfo);
|
||||
m_toolinfo.uFlags = TTF_TRACK|TTF_IDISHWND|TTF_ABSOLUTE|TTF_TRANSPARENT|TTF_CENTERTIP;
|
||||
m_toolinfo.hwnd = wndParent;
|
||||
m_toolinfo.uId = 0;
|
||||
m_toolinfo.lpszText = const_cast<TCHAR*>(message);
|
||||
m_toolinfo.hinst = NULL; //core_api::get_my_instance();
|
||||
if (m_tooltip.AddTool(&m_toolinfo)) {
|
||||
m_tooltip.TrackPosition(rect.CenterPoint().x,rect.bottom);
|
||||
m_tooltip.TrackActivate(&m_toolinfo,TRUE);
|
||||
}
|
||||
}
|
||||
void Initialize() {
|
||||
if (m_tooltip.m_hWnd == NULL) {
|
||||
WIN32_OP( m_tooltip.Create( NULL , NULL, NULL, m_style) );
|
||||
}
|
||||
}
|
||||
CContainedWindowSimpleT<CToolTipCtrl> m_tooltip;
|
||||
TOOLINFO m_toolinfo;
|
||||
const DWORD m_style;
|
||||
bool m_shutDown;
|
||||
};
|
||||
|
||||
|
||||
template<typename T> class CDialogWithTooltip : public CDialogImpl<T> {
|
||||
public:
|
||||
BEGIN_MSG_MAP(CDialogWithTooltip)
|
||||
MSG_WM_DESTROY(OnDestroy)
|
||||
END_MSG_MAP()
|
||||
|
||||
void ShowTip(UINT id, const TCHAR * label) {
|
||||
m_tip.Show(label, GetDlgItem(id));
|
||||
}
|
||||
void ShowTip(HWND child, const TCHAR * label) {
|
||||
m_tip.Show(label, child);
|
||||
}
|
||||
|
||||
void ShowTipF(UINT id, const TCHAR * label) {
|
||||
m_tip.ShowFocus(label, GetDlgItem(id));
|
||||
}
|
||||
void ShowTipF(HWND child, const TCHAR * label) {
|
||||
m_tip.ShowFocus(label, child);
|
||||
}
|
||||
void HideTip() {m_tip.Hide();}
|
||||
private:
|
||||
void OnDestroy() {m_tip.ShutDown(); SetMsgHandled(FALSE); }
|
||||
CPopupTooltipMessage m_tip;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void ListView_FixContextMenuPoint(CListViewCtrl list,CPoint & coords) {
|
||||
if (coords == CPoint(-1,-1)) {
|
||||
int selWalk = -1;
|
||||
CRect rcClient; WIN32_OP_D(list.GetClientRect(rcClient));
|
||||
for(;;) {
|
||||
selWalk = list.GetNextItem(selWalk, LVNI_SELECTED);
|
||||
if (selWalk < 0) {
|
||||
CRect rc;
|
||||
WIN32_OP_D( list.GetWindowRect(&rc) );
|
||||
coords = rc.CenterPoint();
|
||||
return;
|
||||
}
|
||||
CRect rcItem, rcVisible;
|
||||
WIN32_OP_D( list.GetItemRect(selWalk, &rcItem, LVIR_BOUNDS) );
|
||||
if (rcVisible.IntersectRect(rcItem, rcClient)) {
|
||||
coords = rcVisible.CenterPoint();
|
||||
WIN32_OP_D( list.ClientToScreen(&coords) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 );
|
||||
}
|
||||
preferences_page_instance_impl(HWND parent, preferences_page_callback::ptr callback) : TDialog(callback) {WIN32_OP(this->Create(parent) != NULL);}
|
||||
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);
|
||||
return new window_service_impl_t<preferences_page_instance_impl<TDialog> >(parent, callback);
|
||||
}
|
||||
};
|
||||
|
||||
8
foobar2000/ATLHelpers/stdafx.cpp
Normal file
8
foobar2000/ATLHelpers/stdafx.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// foobar2000_ATL_helpers.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
||||
15
foobar2000/ATLHelpers/stdafx.h
Normal file
15
foobar2000/ATLHelpers/stdafx.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
|
||||
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
|
||||
#endif
|
||||
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
||||
#include "ATLHelpersLean.h"
|
||||
@@ -3,8 +3,8 @@
|
||||
#if FOOBAR2000_TARGET_VERSION >= 79
|
||||
|
||||
#include "ui_element_helpers.h"
|
||||
#include <libPPUI/IDataObjectUtils.h>
|
||||
#include "atl-misc.h"
|
||||
#include "../helpers/IDataObjectUtils.h"
|
||||
#include "misc.h"
|
||||
|
||||
namespace ui_element_helpers {
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ui_element_helpers {
|
||||
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;
|
||||
return std::move(ret);
|
||||
} catch(std::exception const & e) {
|
||||
console::complain("UI Element instantiation failure",e);
|
||||
return instantiate_dummy(p_parent,cfg,p_callback);
|
||||
@@ -51,7 +51,7 @@ namespace ui_element_helpers {
|
||||
};
|
||||
|
||||
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;
|
||||
abort_callback_dummy l_abort;
|
||||
ui_element_config::ptr cfg;
|
||||
try {
|
||||
if (p_item.is_empty()) {
|
||||
@@ -350,41 +350,4 @@ bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityChild(ui_
|
||||
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
|
||||
@@ -371,7 +371,4 @@ namespace ui_element_helpers {
|
||||
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
|
||||
@@ -22,21 +22,3 @@ bool abort_callback::waitForEvent( pfc::eventHandle_t evtHandle, double timeOut
|
||||
default: uBugCheck();
|
||||
}
|
||||
}
|
||||
|
||||
bool abort_callback::waitForEvent(pfc::event& evt, double timeOut) {
|
||||
return waitForEvent(evt.get_handle(), timeOut);
|
||||
}
|
||||
|
||||
void abort_callback::waitForEvent(pfc::eventHandle_t evtHandle) {
|
||||
bool status = waitForEvent(evtHandle, -1); (void)status;
|
||||
PFC_ASSERT(status); // should never return false
|
||||
}
|
||||
|
||||
void abort_callback::waitForEvent(pfc::event& evt) {
|
||||
bool status = waitForEvent(evt, -1); (void)status;
|
||||
PFC_ASSERT(status); // should never return false
|
||||
}
|
||||
|
||||
namespace fb2k {
|
||||
abort_callback_dummy noAbort;
|
||||
}
|
||||
|
||||
@@ -40,17 +40,8 @@ public:
|
||||
//! Sleeps p_timeout_seconds or less when aborted, returns true when execution should continue, false when not.
|
||||
bool sleep_ex(double p_timeout_seconds) const;
|
||||
|
||||
//! Waits for an event. Returns true if event is now signaled, false if the specified period has elapsed and the event did not become signaled. \n
|
||||
//! Throws exception_aborted if aborted.
|
||||
bool waitForEvent( pfc::eventHandle_t evtHandle, double timeOut );
|
||||
//! Waits for an event. Returns true if event is now signaled, false if the specified period has elapsed and the event did not become signaled. \n
|
||||
//! Throws exception_aborted if aborted.
|
||||
bool waitForEvent(pfc::event& evt, double timeOut);
|
||||
|
||||
//! Waits for an event. Returns once the event became signaled; throw exception_aborted if abort occurred first.
|
||||
void waitForEvent(pfc::eventHandle_t evtHandle);
|
||||
//! Waits for an event. Returns once the event became signaled; throw exception_aborted if abort occurred first.
|
||||
void waitForEvent(pfc::event& evt);
|
||||
bool waitForEvent( pfc::event & evt, double timeOut ) {return waitForEvent( evt.get_handle(), timeOut ); }
|
||||
protected:
|
||||
abort_callback() {}
|
||||
~abort_callback() {}
|
||||
@@ -73,8 +64,8 @@ public:
|
||||
abort_callback_event get_abort_event() const {return m_event.get_handle();}
|
||||
|
||||
private:
|
||||
abort_callback_impl(const abort_callback_impl &) = delete;
|
||||
const abort_callback_impl & operator=(const abort_callback_impl&) = delete;
|
||||
abort_callback_impl(const abort_callback_impl &);
|
||||
const abort_callback_impl & operator=(const abort_callback_impl&);
|
||||
|
||||
volatile bool m_aborting;
|
||||
pfc::event m_event;
|
||||
@@ -110,10 +101,4 @@ using namespace foobar2000_io;
|
||||
PP::aborter aborter_pfcv2( aborterRef ); \
|
||||
PP::aborterScope l_aborterScope( aborter_pfcv2 );
|
||||
|
||||
|
||||
namespace fb2k {
|
||||
// A shared abort_callback_dummy instance
|
||||
extern abort_callback_dummy noAbort;
|
||||
}
|
||||
|
||||
#endif //_foobar2000_sdk_abort_callback_h_
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
#include "foobar2000.h"
|
||||
|
||||
bool advconfig_entry::is_branch() {
|
||||
advconfig_branch::ptr branch;
|
||||
return branch &= this;
|
||||
}
|
||||
|
||||
bool advconfig_entry::g_find(service_ptr_t<advconfig_entry>& out, const GUID & id) {
|
||||
service_enum_t<advconfig_entry> e; service_ptr_t<advconfig_entry> ptr; while(e.next(ptr)) { if (ptr->get_guid() == id) {out = ptr; return true;} } return false;
|
||||
}
|
||||
|
||||
t_uint32 advconfig_entry::get_preferences_flags_() {
|
||||
{
|
||||
|
||||
@@ -11,10 +11,11 @@ public:
|
||||
virtual void reset() = 0;
|
||||
virtual double get_sort_priority() = 0;
|
||||
|
||||
bool is_branch();
|
||||
t_uint32 get_preferences_flags_();
|
||||
|
||||
static bool g_find(service_ptr_t<advconfig_entry>& out, const GUID & id);
|
||||
static bool g_find(service_ptr_t<advconfig_entry>& out, const GUID & id) {
|
||||
service_enum_t<advconfig_entry> e; service_ptr_t<advconfig_entry> ptr; while(e.next(ptr)) { if (ptr->get_guid() == id) {out = ptr; return true;} } return false;
|
||||
}
|
||||
|
||||
template<typename outptr> static bool g_find_t(outptr & out, const GUID & id) {
|
||||
service_ptr_t<advconfig_entry> temp;
|
||||
@@ -23,7 +24,7 @@ public:
|
||||
}
|
||||
|
||||
static const GUID guid_root;
|
||||
static const GUID guid_branch_tagging,guid_branch_decoding,guid_branch_tools,guid_branch_playback,guid_branch_display,guid_branch_debug, guid_branch_tagging_general;
|
||||
static const GUID guid_branch_tagging,guid_branch_decoding,guid_branch_tools,guid_branch_playback,guid_branch_display,guid_branch_debug;
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(advconfig_entry);
|
||||
};
|
||||
@@ -249,8 +250,8 @@ public:
|
||||
advconfig_integer_factory_(const char * p_name,const GUID & p_guid,const GUID & p_parent,double p_priority,t_uint64 p_initialstate,t_uint64 p_min,t_uint64 p_max, t_uint32 p_prefFlags = 0)
|
||||
: service_factory_single_t<advconfig_entry_integer_impl_<int_t_> >(p_name,p_guid,p_parent,p_priority,p_initialstate,p_min,p_max,p_prefFlags) {}
|
||||
|
||||
int_t get() const {return this->get_static_instance().get_state_int();}
|
||||
void set(int_t val) {this->get_static_instance().set_state_int(val);}
|
||||
int_t get() const {return get_static_instance().get_state_int();}
|
||||
void set(int_t val) {get_static_instance().set_state_int(val);}
|
||||
|
||||
operator int_t() const {return get();}
|
||||
int_t operator=(int_t val) {set(val); return val;}
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
#include "foobar2000.h"
|
||||
|
||||
GUID album_art_extractor::get_guid() {
|
||||
album_art_extractor_v2::ptr v2;
|
||||
if ( v2 &= this ) return v2->get_guid();
|
||||
return pfc::guid_null;
|
||||
}
|
||||
|
||||
GUID album_art_editor::get_guid() {
|
||||
album_art_editor_v2::ptr v2;
|
||||
if ( v2 &= this ) return v2->get_guid();
|
||||
return pfc::guid_null;
|
||||
}
|
||||
|
||||
bool album_art_extractor_instance::query(const GUID & what, album_art_data::ptr & out, abort_callback & abort) {
|
||||
try { out = query(what, abort); return true; } catch (exception_album_art_not_found) { return false; }
|
||||
}
|
||||
@@ -20,23 +8,9 @@ bool album_art_extractor_instance::have_entry(const GUID & what, abort_callback
|
||||
try { query(what, abort); return true; } catch(exception_album_art_not_found) { return false; }
|
||||
}
|
||||
|
||||
void album_art_editor_instance::remove_all_() {
|
||||
album_art_editor_instance_v2::ptr v2;
|
||||
if ( v2 &= this ) {
|
||||
v2->remove_all();
|
||||
} else {
|
||||
for( size_t walk = 0; walk < album_art_ids::num_types(); ++ walk ) {
|
||||
try {
|
||||
this->remove( album_art_ids::query_type( walk ) );
|
||||
} catch(exception_io_data) {}
|
||||
catch(exception_album_art_not_found) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool album_art_editor::g_get_interface(service_ptr_t<album_art_editor> & out,const char * path) {
|
||||
service_enum_t<album_art_editor> e; ptr ptr;
|
||||
auto ext = pfc::string_extension(path);
|
||||
pfc::string_extension ext(path);
|
||||
while(e.next(ptr)) {
|
||||
if (ptr->is_our_path(path,ext)) { out = ptr; return true; }
|
||||
}
|
||||
@@ -48,18 +22,6 @@ bool album_art_editor::g_is_supported_path(const char * path) {
|
||||
}
|
||||
|
||||
album_art_editor_instance_ptr album_art_editor::g_open(file_ptr p_filehint,const char * p_path,abort_callback & p_abort) {
|
||||
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
{
|
||||
input_manager_v2::ptr m;
|
||||
if (fb2k::std_api_try_get(m)) {
|
||||
album_art_editor_instance::ptr ret;
|
||||
ret ^= m->open_v2(album_art_editor_instance::class_guid, p_filehint, p_path, false, nullptr, p_abort);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
album_art_editor::ptr obj;
|
||||
if (!g_get_interface(obj, p_path)) throw exception_album_art_unsupported_format();
|
||||
return obj->open(p_filehint, p_path, p_abort);
|
||||
@@ -68,7 +30,7 @@ album_art_editor_instance_ptr album_art_editor::g_open(file_ptr p_filehint,const
|
||||
|
||||
bool album_art_extractor::g_get_interface(service_ptr_t<album_art_extractor> & out,const char * path) {
|
||||
service_enum_t<album_art_extractor> e; ptr ptr;
|
||||
auto ext = pfc::string_extension(path);
|
||||
pfc::string_extension ext(path);
|
||||
while(e.next(ptr)) {
|
||||
if (ptr->is_our_path(path,ext)) { out = ptr; return true; }
|
||||
}
|
||||
@@ -79,17 +41,6 @@ bool album_art_extractor::g_is_supported_path(const char * path) {
|
||||
ptr ptr; return g_get_interface(ptr,path);
|
||||
}
|
||||
album_art_extractor_instance_ptr album_art_extractor::g_open(file_ptr p_filehint,const char * p_path,abort_callback & p_abort) {
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
{
|
||||
input_manager_v2::ptr m;
|
||||
if (fb2k::std_api_try_get(m)) {
|
||||
album_art_extractor_instance::ptr ret;
|
||||
ret ^= m->open_v2(album_art_extractor_instance::class_guid, p_filehint, p_path, false, nullptr, p_abort);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
album_art_extractor::ptr obj;
|
||||
if (!g_get_interface(obj, p_path)) throw exception_album_art_unsupported_format();
|
||||
return obj->open(p_filehint, p_path, p_abort);
|
||||
@@ -121,65 +72,3 @@ now_playing_album_art_notify * now_playing_album_art_notify_manager::add(std::fu
|
||||
add(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct aa_t {
|
||||
GUID type; const char * name;
|
||||
};
|
||||
static const GUID guids[] = {
|
||||
album_art_ids::cover_front,
|
||||
album_art_ids::cover_back,
|
||||
album_art_ids::artist,
|
||||
album_art_ids::disc,
|
||||
album_art_ids::icon,
|
||||
};
|
||||
static const char * const names[] = {
|
||||
"front cover",
|
||||
"back cover",
|
||||
"artist",
|
||||
"disc",
|
||||
"icon"
|
||||
};
|
||||
static const char * const names2[] = {
|
||||
"Front Cover",
|
||||
"Back Cover",
|
||||
"Artist",
|
||||
"Disc",
|
||||
"Icon"
|
||||
};
|
||||
}
|
||||
|
||||
size_t album_art_ids::num_types() {
|
||||
PFC_STATIC_ASSERT( PFC_TABSIZE( guids ) == PFC_TABSIZE( names ) );
|
||||
PFC_STATIC_ASSERT( PFC_TABSIZE( guids ) == PFC_TABSIZE( names2 ) );
|
||||
return PFC_TABSIZE( guids );
|
||||
}
|
||||
|
||||
GUID album_art_ids::query_type(size_t idx) {
|
||||
PFC_ASSERT( idx < PFC_TABSIZE( guids ) );
|
||||
return guids[idx];
|
||||
}
|
||||
|
||||
const char * album_art_ids::query_name(size_t idx) {
|
||||
PFC_ASSERT( idx < PFC_TABSIZE( names ) );
|
||||
return names[idx];
|
||||
}
|
||||
|
||||
const char * album_art_ids::name_of(const GUID & id) {
|
||||
for( size_t w = 0; w < num_types(); ++w ) {
|
||||
if ( query_type(w) == id ) return query_name(w);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char * album_art_ids::query_capitalized_name( size_t idx ) {
|
||||
PFC_ASSERT( idx < PFC_TABSIZE( names2 ) );
|
||||
return names2[idx];
|
||||
}
|
||||
|
||||
const char * album_art_ids::capitalized_name_of( const GUID & id) {
|
||||
for( size_t w = 0; w < num_types(); ++w ) {
|
||||
if ( query_type(w) == id ) return query_capitalized_name(w);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,6 @@ public:
|
||||
};
|
||||
|
||||
typedef service_ptr_t<album_art_data> album_art_data_ptr;
|
||||
namespace fb2k {
|
||||
typedef album_art_data_ptr memBlockRef;
|
||||
}
|
||||
|
||||
//! Namespace containing identifiers of album art types.
|
||||
namespace album_art_ids {
|
||||
@@ -42,14 +39,6 @@ namespace album_art_ids {
|
||||
//! Artist picture.
|
||||
static const GUID artist = { 0x9a654042, 0xacd1, 0x43f7, { 0xbf, 0xcf, 0xd3, 0xec, 0xf, 0xfe, 0x40, 0xfa } };
|
||||
|
||||
size_t num_types();
|
||||
GUID query_type( size_t );
|
||||
// returns lowercase name
|
||||
const char * query_name( size_t );
|
||||
const char * name_of( const GUID & );
|
||||
// returns Capitalized name
|
||||
const char * query_capitalized_name( size_t );
|
||||
const char * capitalized_name_of( const GUID & );
|
||||
};
|
||||
|
||||
PFC_DECLARE_EXCEPTION(exception_album_art_not_found,exception_io_not_found,"Attached picture not found");
|
||||
@@ -80,9 +69,6 @@ public:
|
||||
|
||||
//! Finalizes file tag update operation.
|
||||
virtual void commit(abort_callback & p_abort) = 0;
|
||||
|
||||
//! Helper; see album_art_editor_instance_v2::remove_all();
|
||||
void remove_all_();
|
||||
};
|
||||
|
||||
class NOVTABLE album_art_editor_instance_v2 : public album_art_editor_instance {
|
||||
@@ -98,7 +84,6 @@ typedef service_ptr_t<album_art_editor_instance> album_art_editor_instance_ptr;
|
||||
//! Entrypoint class for accessing album art extraction functionality. Register your own implementation to allow album art extraction from your media file format. \n
|
||||
//! If you want to extract album art from a media file, it's recommended that you use album_art_manager API instead of calling album_art_extractor directly.
|
||||
class NOVTABLE album_art_extractor : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(album_art_extractor);
|
||||
public:
|
||||
//! Returns whether the specified file is one of formats supported by our album_art_extractor implementation.
|
||||
//! @param p_path Path to file being queried.
|
||||
@@ -115,21 +100,11 @@ public:
|
||||
static album_art_extractor_instance_ptr g_open(file_ptr p_filehint,const char * p_path,abort_callback & p_abort);
|
||||
static album_art_extractor_instance_ptr g_open_allowempty(file_ptr p_filehint,const char * p_path,abort_callback & p_abort);
|
||||
|
||||
//! Returns GUID of the corresponding input class. Null GUID if none.
|
||||
GUID get_guid();
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class NOVTABLE album_art_extractor_v2 : public album_art_extractor {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(album_art_extractor_v2 , album_art_extractor);
|
||||
public:
|
||||
//! Returns GUID of the corresponding input class. Null GUID if none.
|
||||
virtual GUID get_guid() = 0;
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(album_art_extractor);
|
||||
};
|
||||
|
||||
//! Entrypoint class for accessing album art editing functionality. Register your own implementation to allow album art editing on your media file format.
|
||||
class NOVTABLE album_art_editor : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(album_art_editor);
|
||||
public:
|
||||
//! Returns whether the specified file is one of formats supported by our album_art_editor implementation.
|
||||
//! @param p_path Path to file being queried.
|
||||
@@ -148,20 +123,11 @@ public:
|
||||
|
||||
static album_art_editor_instance_ptr g_open(file_ptr p_filehint,const char * p_path,abort_callback & p_abort);
|
||||
|
||||
//! Returns GUID of the corresponding input class. Null GUID if none.
|
||||
GUID get_guid();
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(album_art_editor);
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class NOVTABLE album_art_editor_v2 : public album_art_editor {
|
||||
FB2K_MAKE_SERVICE_INTERFACE( album_art_editor_v2, album_art_editor )
|
||||
public:
|
||||
//! Returns GUID of the corresponding input class. Null GUID if none.
|
||||
virtual GUID get_guid() = 0;
|
||||
};
|
||||
|
||||
//! \since 0.9.5
|
||||
//! Helper API for extracting album art from APEv2 tags.
|
||||
//! Helper API for extracting album art from APEv2 tags - introduced in 0.9.5.
|
||||
class NOVTABLE tag_processor_album_art_utils : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(tag_processor_album_art_utils)
|
||||
public:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#pragma once
|
||||
//! Implements album_art_data.
|
||||
class album_art_data_impl : public album_art_data {
|
||||
public:
|
||||
@@ -51,37 +50,32 @@ private:
|
||||
};
|
||||
|
||||
//! Helper implementation of album_art_extractor - reads album art from arbitrary file formats that comply with APEv2 tagging specification.
|
||||
class album_art_extractor_impl_stdtags : public album_art_extractor_v2 {
|
||||
class album_art_extractor_impl_stdtags : public album_art_extractor {
|
||||
public:
|
||||
//! @param exts Semicolon-separated list of file format extensions to support.
|
||||
album_art_extractor_impl_stdtags(const char * exts, const GUID & guid) : m_guid(guid) {
|
||||
album_art_extractor_impl_stdtags(const char * exts) {
|
||||
pfc::splitStringSimple_toList(m_extensions,';',exts);
|
||||
}
|
||||
|
||||
bool is_our_path(const char * p_path,const char * p_extension) override {
|
||||
bool is_our_path(const char * p_path,const char * p_extension) {
|
||||
return m_extensions.have_item(p_extension);
|
||||
}
|
||||
|
||||
album_art_extractor_instance_ptr open(file_ptr p_filehint,const char * p_path,abort_callback & p_abort) override {
|
||||
album_art_extractor_instance_ptr open(file_ptr p_filehint,const char * p_path,abort_callback & p_abort) {
|
||||
PFC_ASSERT( is_our_path(p_path, pfc::string_extension(p_path) ) );
|
||||
file_ptr l_file ( p_filehint );
|
||||
if (l_file.is_empty()) filesystem::g_open_read(l_file, p_path, p_abort);
|
||||
return tag_processor_album_art_utils::get()->open( l_file, p_abort );
|
||||
}
|
||||
|
||||
GUID get_guid() override {
|
||||
return m_guid;
|
||||
}
|
||||
private:
|
||||
pfc::avltree_t<pfc::string,pfc::string::comparatorCaseInsensitiveASCII> m_extensions;
|
||||
const GUID m_guid;
|
||||
};
|
||||
|
||||
//! Helper implementation of album_art_editor - edits album art from arbitrary file formats that comply with APEv2 tagging specification.
|
||||
class album_art_editor_impl_stdtags : public album_art_editor_v2 {
|
||||
class album_art_editor_impl_stdtags : public album_art_editor {
|
||||
public:
|
||||
//! @param exts Semicolon-separated list of file format extensions to support.
|
||||
album_art_editor_impl_stdtags(const char * exts, const GUID & guid) : m_guid(guid) {
|
||||
album_art_editor_impl_stdtags(const char * exts) {
|
||||
pfc::splitStringSimple_toList(m_extensions,';',exts);
|
||||
}
|
||||
|
||||
@@ -95,12 +89,8 @@ public:
|
||||
if (l_file.is_empty()) filesystem::g_open(l_file, p_path, filesystem::open_mode_write_existing, p_abort);
|
||||
return tag_processor_album_art_utils::get()->edit( l_file, p_abort );
|
||||
}
|
||||
GUID get_guid() override {
|
||||
return m_guid;
|
||||
}
|
||||
private:
|
||||
pfc::avltree_t<pfc::string,pfc::string::comparatorCaseInsensitiveASCII> m_extensions;
|
||||
const GUID m_guid;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace foobar2000_io {
|
||||
class archive;
|
||||
|
||||
class NOVTABLE archive_callback : public abort_callback {
|
||||
public:
|
||||
virtual bool on_entry(archive * owner,const char * url,const t_filestats & p_stats,const service_ptr_t<file> & p_reader) = 0;
|
||||
};
|
||||
|
||||
//! Interface for archive reader services. When implementing, derive from archive_impl rather than from deriving from archive directly.
|
||||
class NOVTABLE archive : public filesystem {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(archive,filesystem);
|
||||
public:
|
||||
//! Lists archive contents. \n
|
||||
//! May be called with any path, not only path accepted by is_our_archive.
|
||||
virtual void archive_list(const char * p_path,const service_ptr_t<file> & p_reader,archive_callback & p_callback,bool p_want_readers) = 0;
|
||||
|
||||
//! Optional method to weed out unsupported formats prior to calling archive_list. \n
|
||||
//! Use this to suppress calls to archive_list() to avoid spurious exceptions being thrown. \n
|
||||
//! Implemented via archive_v2.
|
||||
bool is_our_archive( const char * path );
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
//! New 1.5 series API, though allowed to implement/call in earlier versions. \n
|
||||
//! Suppresses spurious C++ exceptions on all files not recognized as archives by this instance.
|
||||
class NOVTABLE archive_v2 : public archive {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(archive_v2, archive)
|
||||
public:
|
||||
|
||||
//! Optional method to weed out unsupported formats prior to calling archive_list. \n
|
||||
//! Use this to suppress calls to archive_list() to avoid spurious exceptions being thrown.
|
||||
virtual bool is_our_archive( const char * path ) = 0;
|
||||
};
|
||||
|
||||
//! \since 1.6
|
||||
//! New 1.6 series API, though allowed to implement/call in earlier versions.
|
||||
class NOVTABLE archive_v3 : public archive_v2 {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(archive_v3, archive_v2)
|
||||
public:
|
||||
//! Determine supported archive file types. \n
|
||||
//! Returns a list of extensions, colon delimited, e.g.: "zip,rar,7z"
|
||||
virtual void list_extensions(pfc::string_base & out) = 0;
|
||||
};
|
||||
|
||||
//! Root class for archive implementations. Derive from this instead of from archive directly.
|
||||
class NOVTABLE archive_impl : public archive_v3 {
|
||||
private:
|
||||
//do not override these
|
||||
bool get_canonical_path(const char * path,pfc::string_base & out);
|
||||
bool is_our_path(const char * path);
|
||||
bool get_display_path(const char * path,pfc::string_base & out);
|
||||
void remove(const char * path,abort_callback & p_abort);
|
||||
void move(const char * src,const char * dst,abort_callback & p_abort);
|
||||
bool is_remote(const char * src);
|
||||
bool relative_path_create(const char * file_path,const char * playlist_path,pfc::string_base & out);
|
||||
bool relative_path_parse(const char * relative_path,const char * playlist_path,pfc::string_base & out);
|
||||
void open(service_ptr_t<file> & p_out,const char * path, t_open_mode mode,abort_callback & p_abort);
|
||||
void create_directory(const char * path,abort_callback &);
|
||||
void list_directory(const char * p_path,directory_callback & p_out,abort_callback & p_abort);
|
||||
void get_stats(const char * p_path,t_filestats & p_stats,bool & p_is_writeable,abort_callback & p_abort);
|
||||
void list_extensions(pfc::string_base & out) override { out = get_archive_type(); }
|
||||
protected:
|
||||
//override these
|
||||
virtual const char * get_archive_type()=0;//eg. "zip", must be lowercase
|
||||
virtual t_filestats get_stats_in_archive(const char * p_archive,const char * p_file,abort_callback & p_abort) = 0;
|
||||
virtual void open_archive(service_ptr_t<file> & p_out,const char * archive,const char * file, abort_callback & p_abort) = 0;//opens for reading
|
||||
public:
|
||||
//override these
|
||||
virtual void archive_list(const char * path,const service_ptr_t<file> & p_reader,archive_callback & p_out,bool p_want_readers)= 0 ;
|
||||
virtual bool is_our_archive( const char * path ) = 0;
|
||||
|
||||
static bool g_is_unpack_path(const char * path);
|
||||
static bool g_parse_unpack_path(const char * path,pfc::string_base & archive,pfc::string_base & file);
|
||||
static bool g_parse_unpack_path_ex(const char * path,pfc::string_base & archive,pfc::string_base & file, pfc::string_base & type);
|
||||
static void g_make_unpack_path(pfc::string_base & path,const char * archive,const char * file,const char * type);
|
||||
void make_unpack_path(pfc::string_base & path,const char * archive,const char * file);
|
||||
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class archive_factory_t : public service_factory_single_t<T> {};
|
||||
}
|
||||
@@ -101,7 +101,7 @@ template<bool byteSwap, bool isSigned> static void _import32any(const void * in,
|
||||
for(size_t walk = 0; walk < count; ++walk) {
|
||||
uint32_t v = *inPtr++;
|
||||
if (byteSwap) v = pfc::byteswap_t(v);
|
||||
if (!isSigned) v ^= 0x80000000u; // to signed
|
||||
if (!isSigned) v ^= 0x80000000; // to signed
|
||||
*out++ = (audio_sample) (int32_t) v * factor;
|
||||
}
|
||||
}
|
||||
@@ -325,14 +325,8 @@ void audio_chunk::set_data_floatingpoint_ex(const void * ptr,t_size size,unsigne
|
||||
set_channels(nch,p_channel_config);
|
||||
}
|
||||
|
||||
pfc::string8 audio_chunk::formatChunkSpec() const {
|
||||
pfc::string8 msg;
|
||||
msg << get_sample_rate() << " Hz, " << get_channels() << ":0x" << pfc::format_hex(get_channel_config(), 2) << " channels, " << get_sample_count() << " samples";
|
||||
return msg;
|
||||
}
|
||||
|
||||
void audio_chunk::debugChunkSpec() const {
|
||||
FB2K_DebugLog() << "Chunk: " << this->formatChunkSpec();
|
||||
FB2K_DebugLog() << "Chunk: " << get_sample_rate() << " Hz, " << get_channels() << ":0x" << pfc::format_hex(get_channel_config(),2) << " channels, " << get_sample_count() << " samples";
|
||||
}
|
||||
|
||||
#if PFC_DEBUG
|
||||
@@ -347,7 +341,7 @@ void audio_chunk::assert_valid(const char * ctx) const {
|
||||
bool audio_chunk::is_valid() const
|
||||
{
|
||||
unsigned nch = get_channels();
|
||||
if (nch == 0 || nch > 32) return false;
|
||||
if (nch==0 || nch>256) return false;
|
||||
if (!g_is_valid_sample_rate(get_srate())) return false;
|
||||
t_size samples = get_sample_count();
|
||||
if (samples==0 || samples >= 0x80000000ul / (sizeof(audio_sample) * nch) ) return false;
|
||||
@@ -596,19 +590,13 @@ bool audio_chunk::spec_t::equals( const spec_t & v1, const spec_t & v2 ) {
|
||||
return v1.sampleRate == v2.sampleRate && v1.chanCount == v2.chanCount && v1.chanMask == v2.chanMask;
|
||||
}
|
||||
|
||||
pfc::string8 audio_chunk::spec_t::toString(const char * delim) const {
|
||||
pfc::string8 audio_chunk::spec_t::toString() const {
|
||||
pfc::string_formatter temp;
|
||||
if ( sampleRate > 0 ) temp << sampleRate << "Hz";
|
||||
if (chanCount > 0) {
|
||||
if ( temp.length() > 0 ) temp << delim;
|
||||
temp << chanCount << "ch";
|
||||
}
|
||||
|
||||
temp << sampleRate << "Hz " << chanCount << "ch";
|
||||
if ( chanMask != audio_chunk::channel_config_mono && chanMask != audio_chunk::channel_config_stereo ) {
|
||||
pfc::string8 strMask;
|
||||
audio_chunk::g_formatChannelMaskDesc( chanMask, strMask );
|
||||
if ( temp.length() > 0) temp << delim;
|
||||
temp << strMask;
|
||||
temp << " " << strMask;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
@@ -689,14 +677,3 @@ WAVEFORMATEXTENSIBLE audio_chunk::spec_t::toWFXEXWithBPS(uint32_t bps) const {
|
||||
return wfxe;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
void audio_chunk::append(const audio_chunk& other) {
|
||||
if (other.get_spec() != this->get_spec()) {
|
||||
throw pfc::exception_invalid_params();
|
||||
}
|
||||
|
||||
this->grow_data_size(get_used_size() + other.get_used_size());
|
||||
audio_sample* p = this->get_data() + get_used_size();
|
||||
memcpy(p, other.get_data(), other.get_used_size() * sizeof(audio_sample));
|
||||
set_sample_count(get_sample_count() + other.get_sample_count());
|
||||
}
|
||||
|
||||
@@ -45,9 +45,6 @@ public:
|
||||
channel_config_5point1_side = channel_front_left | channel_front_right | channel_front_center | channel_lfe | channel_side_left | channel_side_right,
|
||||
channel_config_7point1 = channel_config_5point1 | channel_side_left | channel_side_right,
|
||||
|
||||
channels_back_left_right = channel_back_left | channel_back_right,
|
||||
channels_side_left_right = channel_side_left | channel_side_right,
|
||||
|
||||
defined_channel_count = 18,
|
||||
};
|
||||
|
||||
@@ -139,7 +136,6 @@ public:
|
||||
bool is_valid() const;
|
||||
|
||||
void debugChunkSpec() const;
|
||||
pfc::string8 formatChunkSpec() const;
|
||||
#if PFC_DEBUG
|
||||
void assert_valid(const char * ctx) const;
|
||||
#else
|
||||
@@ -162,7 +158,7 @@ public:
|
||||
inline void reset() {
|
||||
set_sample_count(0);
|
||||
set_srate(0);
|
||||
set_channels(0,0);
|
||||
set_channels(0);
|
||||
set_data_size(0);
|
||||
}
|
||||
|
||||
@@ -289,7 +285,7 @@ public:
|
||||
WAVEFORMATEXTENSIBLE toWFXEXWithBPS(uint32_t bps) const;
|
||||
#endif
|
||||
|
||||
pfc::string8 toString( const char * delim = " " ) const;
|
||||
pfc::string8 toString() const;
|
||||
};
|
||||
static spec_t makeSpec(uint32_t rate, uint32_t channels);
|
||||
static spec_t makeSpec(uint32_t rate, uint32_t channels, uint32_t chanMask);
|
||||
@@ -297,8 +293,6 @@ public:
|
||||
|
||||
spec_t get_spec() const;
|
||||
void set_spec(const spec_t &);
|
||||
|
||||
void append(const audio_chunk& other);
|
||||
protected:
|
||||
audio_chunk() {}
|
||||
~audio_chunk() {}
|
||||
@@ -353,17 +347,17 @@ public:
|
||||
audio_sample * get_data() {throw pfc::exception_not_implemented();}
|
||||
const audio_sample * get_data() const {return m_data;}
|
||||
t_size get_data_size() const {return m_samples * m_channels;}
|
||||
void set_data_size(t_size) {throw pfc::exception_not_implemented();}
|
||||
void set_data_size(t_size p_new_size) {throw pfc::exception_not_implemented();}
|
||||
|
||||
unsigned get_srate() const {return m_sample_rate;}
|
||||
void set_srate(unsigned) {throw pfc::exception_not_implemented();}
|
||||
void set_srate(unsigned val) {throw pfc::exception_not_implemented();}
|
||||
unsigned get_channels() const {return m_channels;}
|
||||
unsigned get_channel_config() const {return m_channel_config;}
|
||||
void set_channels(unsigned,unsigned) {throw pfc::exception_not_implemented();}
|
||||
void set_channels(unsigned p_count,unsigned p_config) {throw pfc::exception_not_implemented();}
|
||||
|
||||
t_size get_sample_count() const {return m_samples;}
|
||||
|
||||
void set_sample_count(t_size) {throw pfc::exception_not_implemented();}
|
||||
void set_sample_count(t_size val) {throw pfc::exception_not_implemented();}
|
||||
|
||||
private:
|
||||
t_size m_samples;
|
||||
|
||||
@@ -88,27 +88,32 @@ static const unsigned g_audio_channel_config_table[] =
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right | audio_chunk::channel_lfe,
|
||||
audio_chunk::channel_config_5point1,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right | audio_chunk::channel_lfe | audio_chunk::channel_front_center_right | audio_chunk::channel_front_center_left,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right | audio_chunk::channel_front_center | audio_chunk::channel_lfe | audio_chunk::channel_front_center_right | audio_chunk::channel_front_center_left,
|
||||
};
|
||||
|
||||
static const unsigned g_audio_channel_config_table_xiph[] =
|
||||
{
|
||||
0,
|
||||
audio_chunk::channel_config_7point1,
|
||||
0,
|
||||
audio_chunk::channel_config_7point1 | audio_chunk::channel_front_center_right | audio_chunk::channel_front_center_left,
|
||||
audio_chunk::channel_config_mono,
|
||||
audio_chunk::channel_config_stereo,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_front_center,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_back_left | audio_chunk::channel_back_right | audio_chunk::channel_front_center,
|
||||
audio_chunk::channel_config_5point1,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_front_center | audio_chunk::channel_lfe | audio_chunk::channel_back_center | audio_chunk::channel_side_left | audio_chunk::channel_side_right,
|
||||
audio_chunk::channel_front_left | audio_chunk::channel_front_right | audio_chunk::channel_front_center | audio_chunk::channel_lfe | audio_chunk::channel_back_left | audio_chunk::channel_back_right | audio_chunk::channel_side_left | audio_chunk::channel_side_right,
|
||||
};
|
||||
|
||||
unsigned audio_chunk::g_guess_channel_config(unsigned count)
|
||||
{
|
||||
if (count == 0) return 0;
|
||||
if (count > 32) throw exception_io_data();
|
||||
unsigned ret = 0;
|
||||
if (count < PFC_TABSIZE(g_audio_channel_config_table)) ret = g_audio_channel_config_table[count];
|
||||
if (ret == 0) {
|
||||
ret = (1 << count) - 1;
|
||||
}
|
||||
PFC_ASSERT(g_count_channels(ret) == count);
|
||||
return ret;
|
||||
if (count >= PFC_TABSIZE(g_audio_channel_config_table)) return 0;
|
||||
return g_audio_channel_config_table[count];
|
||||
}
|
||||
|
||||
unsigned audio_chunk::g_guess_channel_config_xiph(unsigned count) {
|
||||
return g_guess_channel_config(count);
|
||||
if (count == 0 || count >= PFC_TABSIZE(g_audio_channel_config_table_xiph)) throw exception_io_data();
|
||||
return g_audio_channel_config_table_xiph[count];
|
||||
}
|
||||
|
||||
unsigned audio_chunk::g_channel_index_from_flag(unsigned p_config,unsigned p_flag) {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
//! This class handles conversion of audio data (audio_chunk) to various linear PCM types, with optional dithering.
|
||||
|
||||
class NOVTABLE audio_postprocessor : public service_base
|
||||
|
||||
@@ -28,10 +28,7 @@ public:
|
||||
template<typename t_array> void get_configuration(t_array & p_out) {
|
||||
PFC_STATIC_ASSERT( sizeof(p_out[0]) == 1 );
|
||||
typedef pfc::array_t<t_uint8,pfc::alloc_fast_aggressive> t_temp; t_temp temp;
|
||||
{
|
||||
stream_writer_buffer_append_ref_t<t_temp> writer(temp);
|
||||
get_configuration(&writer,fb2k::noAbort);
|
||||
}
|
||||
get_configuration(&stream_writer_buffer_append_ref_t<t_temp>(temp),abort_callback_dummy());
|
||||
p_out = temp;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
t_inttype m_val;
|
||||
protected:
|
||||
void get_data_raw(stream_writer * p_stream,abort_callback & p_abort) {p_stream->write_lendian_t(m_val,p_abort);}
|
||||
void set_data_raw(stream_reader * p_stream,t_size,abort_callback & p_abort) {
|
||||
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
|
||||
t_inttype temp;
|
||||
p_stream->read_lendian_t(temp,p_abort);//alter member data only on success, this will throw an exception when something isn't right
|
||||
m_val = temp;
|
||||
@@ -88,11 +88,9 @@ public:
|
||||
|
||||
typedef cfg_int_t<t_int32> cfg_int;
|
||||
typedef cfg_int_t<t_uint32> cfg_uint;
|
||||
//! Since relevant byteswapping functions also understand GUIDs, this can be used to declare a cfg_guid.
|
||||
//! Since relevant byteswapping functions also understand GUIDs, this can be abused to declare a cfg_guid.
|
||||
typedef cfg_int_t<GUID> cfg_guid;
|
||||
typedef cfg_int_t<bool> cfg_bool;
|
||||
typedef cfg_int_t<float> cfg_float;
|
||||
typedef cfg_int_t<double> cfg_double;
|
||||
|
||||
//! String config variable. Stored in the stream with int32 header containing size in bytes, followed by non-null-terminated UTF-8 data.\n
|
||||
//! Note that cfg_var class and its derivatives may be only instantiated statically (as static objects or members of other static objects), NEVER dynamically (operator new, local variables, members of objects instantiated as such).
|
||||
@@ -120,7 +118,7 @@ protected:
|
||||
get(temp);
|
||||
p_stream->write_object(temp.get_ptr(), temp.length(),p_abort);
|
||||
}
|
||||
void set_data_raw(stream_reader * p_stream,t_size,abort_callback & p_abort) {
|
||||
void set_data_raw(stream_reader * p_stream,t_size p_sizehint,abort_callback & p_abort) {
|
||||
pfc::string8_fastalloc temp;
|
||||
p_stream->read_string_raw(temp,p_abort);
|
||||
set(temp);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
//! Generic service for receiving notifications about async operation completion. Used by various other services.
|
||||
@@ -22,7 +20,7 @@ public:
|
||||
//! Implementation helper.
|
||||
class completion_notify_dummy : public completion_notify {
|
||||
public:
|
||||
void on_completion(unsigned) {}
|
||||
void on_completion(unsigned p_code) {}
|
||||
};
|
||||
|
||||
//! Implementation helper.
|
||||
@@ -70,7 +68,7 @@ public:
|
||||
~completion_notify_receiver();
|
||||
void orphan_all_tasks();
|
||||
|
||||
virtual void on_task_completion(unsigned p_id,unsigned p_status) {(void)p_id;(void)p_status;}
|
||||
virtual void on_task_completion(unsigned p_id,unsigned p_status) {}
|
||||
private:
|
||||
static void orphanfunc(unsigned,completion_notify_orphanable_nnptr p_item) {p_item->orphan();}
|
||||
pfc::map_t<unsigned,completion_notify_orphanable_nnptr> m_tasks;
|
||||
|
||||
@@ -14,7 +14,8 @@ bool component_installation_validator::have_other_file(const char * fn) {
|
||||
path << fn;
|
||||
try {
|
||||
try {
|
||||
bool v = filesystem::g_exists(path, fb2k::noAbort);
|
||||
abort_callback_dummy aborter;
|
||||
bool v = filesystem::g_exists(path, aborter);
|
||||
PFC_ASSERT( v );
|
||||
return v;
|
||||
} catch(std::exception const & e) {
|
||||
|
||||
@@ -146,20 +146,23 @@ namespace {
|
||||
t_size config_object::get_data_raw(void * p_out,t_size p_bytes) {
|
||||
t_size ret = 0;
|
||||
stream_writer_fixedbuffer stream(p_out,p_bytes,ret);
|
||||
get_data(&stream,fb2k::noAbort);
|
||||
abort_callback_dummy aborter;
|
||||
get_data(&stream,aborter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
t_size config_object::get_data_raw_length() {
|
||||
t_size ret = 0;
|
||||
stream_writer_get_length stream(ret);
|
||||
get_data(&stream,fb2k::noAbort);
|
||||
abort_callback_dummy aborter;
|
||||
get_data(&stream,aborter);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void config_object::set_data_raw(const void * p_data,t_size p_bytes, bool p_notify) {
|
||||
stream_reader_memblock_ref stream(p_data,p_bytes);
|
||||
set_data(&stream,fb2k::noAbort,p_notify);
|
||||
abort_callback_dummy aborter;
|
||||
set_data(&stream,aborter,p_notify);
|
||||
}
|
||||
|
||||
void config_object::set_data_string(const char * p_data,t_size p_length) {
|
||||
@@ -168,7 +171,8 @@ void config_object::set_data_string(const char * p_data,t_size p_length) {
|
||||
|
||||
void config_object::get_data_string(pfc::string_base & p_out) {
|
||||
stream_writer_string stream(p_out);
|
||||
get_data(&stream,fb2k::noAbort);
|
||||
abort_callback_dummy aborter;
|
||||
get_data(&stream,aborter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
//! Reserved for future use.
|
||||
typedef void * t_glyph;
|
||||
|
||||
@@ -123,7 +121,6 @@ public:
|
||||
//! Return DEFAULT_ON to show this item in the context menu by default - useful for most cases. \n
|
||||
//! Return DEFAULT_OFF to hide this item in the context menu by default - useful for rarely used utility commands. \n
|
||||
//! Return FORCE_OFF to hide this item by default and prevent the user from making it visible (very rarely used). \n
|
||||
//! foobar2000 v1.6 and newer: FORCE_OFF items are meant for being shown only in the keyboard shortcut list, not anywhere else. \n
|
||||
//! Values returned by this method should be constant for this context menu item and not change later. Do not use this to conditionally hide the item - return false from get_display_data() instead.
|
||||
virtual t_enabled_state get_enabled_state(unsigned p_index) = 0;
|
||||
//! Executes the menu item command without going thru the instantiate_item path. For items with dynamically-generated sub-items, p_node is identifies of the sub-item command to execute.
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace core_api {
|
||||
const char * get_profile_path();
|
||||
|
||||
//! Returns a path to <file name> in fb2k profile folder.
|
||||
inline pfc::string8 pathInProfile(const char * fileName) { pfc::string8 p( core_api::get_profile_path() ); p.add_filename( fileName ); return p; }
|
||||
inline pfc::string8 pathInProfile(const char * fileName) { pfc::string8 p( core_api::get_profile_path() ); p.add_filename( fileName ); return std::move(p); }
|
||||
|
||||
//! Returns whether foobar2000 has been installed in "portable" mode.
|
||||
bool is_portable_mode_enabled();
|
||||
|
||||
@@ -85,9 +85,6 @@ void dsp_chunk_list::remove_bad_chunks()
|
||||
audio_chunk * chunk = get_item(idx);
|
||||
if (!chunk->is_valid())
|
||||
{
|
||||
#if PFC_DEBUG
|
||||
FB2K_console_formatter() << "Removing bad chunk: " << chunk->formatChunkSpec();
|
||||
#endif
|
||||
chunk->reset();
|
||||
remove_by_idx(idx);
|
||||
blah = true;
|
||||
@@ -285,42 +282,6 @@ dsp_chain_config_impl::~dsp_chain_config_impl()
|
||||
m_data.delete_all();
|
||||
}
|
||||
|
||||
pfc::string8 dsp_preset::get_owner_name() const {
|
||||
pfc::string8 ret;
|
||||
dsp_entry::ptr obj;
|
||||
if (dsp_entry::g_get_interface(obj, this->get_owner())) {
|
||||
obj->get_name(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
pfc::string8 dsp_preset::get_owner_name_debug() const {
|
||||
pfc::string8 ret;
|
||||
dsp_entry::ptr obj;
|
||||
if (dsp_entry::g_get_interface(obj, this->get_owner())) {
|
||||
obj->get_name(ret);
|
||||
} else {
|
||||
ret = "[unknown]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
pfc::string8 dsp_preset::debug() const {
|
||||
pfc::string8 ret;
|
||||
ret << this->get_owner_name_debug() << " :: " << pfc::print_guid(this->get_owner()) << " :: " << pfc::format_hexdump(this->get_data(), this->get_data_size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
pfc::string8 dsp_chain_config::debug() const {
|
||||
const size_t count = get_count();
|
||||
pfc::string8 ret;
|
||||
ret << "dsp_chain_config: " << count << " items";
|
||||
for (size_t walk = 0; walk < count; ++walk) {
|
||||
ret << "\n" << get_item(walk).debug();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dsp_preset::contents_to_stream(stream_writer * p_stream,abort_callback & p_abort) const {
|
||||
t_uint32 size = pfc::downcast_guarded<t_uint32>(get_data_size());
|
||||
p_stream->write_lendian_t(get_owner(),p_abort);
|
||||
@@ -470,24 +431,6 @@ bool dsp_chain_config::equals(dsp_chain_config const & v1, dsp_chain_config cons
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool dsp_chain_config::equals_debug(dsp_chain_config const& v1, dsp_chain_config const& v2) {
|
||||
FB2K_DebugLog() << "Comparing DSP chains";
|
||||
const t_size count = v1.get_count();
|
||||
if (count != v2.get_count()) {
|
||||
FB2K_DebugLog() << "Count mismatch, " << count << " vs " << v2.get_count();
|
||||
return false;
|
||||
}
|
||||
for (t_size walk = 0; walk < count; ++walk) {
|
||||
if (v1.get_item(walk) != v2.get_item(walk)) {
|
||||
FB2K_DebugLog() << "Item " << (walk+1) << " mismatch";
|
||||
FB2K_DebugLog() << "Item 1: " << v1.get_item(walk).debug();
|
||||
FB2K_DebugLog() << "Item 2: " << v2.get_item(walk).debug();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
FB2K_DebugLog() << "DSP chains are identical";
|
||||
return true;
|
||||
}
|
||||
void dsp_chain_config::get_name_list(pfc::string_base & p_out) const
|
||||
{
|
||||
const t_size count = get_count();
|
||||
@@ -537,20 +480,4 @@ bool dsp_entry_v2::show_config_popup(dsp_preset & p_data,HWND p_parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void resampler_manager::make_chain_(dsp_chain_config& outChain, unsigned rateFrom, unsigned rateTo, float qualityScale) {
|
||||
resampler_manager_v2::ptr v2;
|
||||
if (v2 &= this) {
|
||||
v2->make_chain(outChain, rateFrom, rateTo, qualityScale);
|
||||
} else {
|
||||
outChain.remove_all();
|
||||
auto obj = this->get_resampler(rateFrom, rateTo);
|
||||
if (obj.is_valid()) {
|
||||
dsp_preset_impl p;
|
||||
if (obj->create_preset(p, rateTo, qualityScale)) {
|
||||
outChain.add_item(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // FOOBAR2000_HAVE_DSP
|
||||
|
||||
@@ -76,7 +76,8 @@ public:
|
||||
virtual void run_v2(dsp_chunk_list * p_chunk_list,const metadb_handle_ptr & p_cur_file,int p_flags,abort_callback & p_abort) = 0;
|
||||
private:
|
||||
void run(dsp_chunk_list * p_chunk_list,const metadb_handle_ptr & p_cur_file,int p_flags) {
|
||||
run_v2(p_chunk_list,p_cur_file,p_flags,fb2k::noAbort);
|
||||
abort_callback_dummy dummy;
|
||||
run_v2(p_chunk_list,p_cur_file,p_flags,dummy);
|
||||
}
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE(dsp_v2,dsp);
|
||||
@@ -205,10 +206,6 @@ public:
|
||||
bool operator!=(const dsp_preset & p_other) const {
|
||||
return !(*this == p_other);
|
||||
}
|
||||
|
||||
pfc::string8 get_owner_name() const;
|
||||
pfc::string8 get_owner_name_debug() const;
|
||||
pfc::string8 debug() const;
|
||||
protected:
|
||||
dsp_preset() {}
|
||||
~dsp_preset() {}
|
||||
@@ -451,9 +448,6 @@ public:
|
||||
void get_name_list(pfc::string_base & p_out) const;
|
||||
|
||||
static bool equals(dsp_chain_config const & v1, dsp_chain_config const & v2);
|
||||
static bool equals_debug(dsp_chain_config const& v1, dsp_chain_config const& v2);
|
||||
|
||||
pfc::string8 debug() const;
|
||||
|
||||
bool operator==(const dsp_chain_config & other) const {return equals(*this, other);}
|
||||
bool operator!=(const dsp_chain_config & other) const {return !equals(*this, other);}
|
||||
@@ -525,7 +519,7 @@ private:
|
||||
//! Helper.
|
||||
class dsp_preset_parser : public stream_reader_formatter<> {
|
||||
public:
|
||||
dsp_preset_parser(const dsp_preset & in) : m_data(in), _m_stream(in.get_data(),in.get_data_size()), stream_reader_formatter(_m_stream,fb2k::noAbort) {}
|
||||
dsp_preset_parser(const dsp_preset & in) : m_data(in), _m_stream(in.get_data(),in.get_data_size()), stream_reader_formatter(_m_stream,_m_abort) {}
|
||||
|
||||
void reset() {_m_stream.reset();}
|
||||
t_size get_remaining() const {return _m_stream.get_remaining();}
|
||||
@@ -537,13 +531,14 @@ public:
|
||||
GUID get_owner() const {return m_data.get_owner();}
|
||||
private:
|
||||
const dsp_preset & m_data;
|
||||
abort_callback_dummy _m_abort;
|
||||
stream_reader_memblock_ref _m_stream;
|
||||
};
|
||||
|
||||
//! Helper.
|
||||
class dsp_preset_builder : public stream_writer_formatter<> {
|
||||
public:
|
||||
dsp_preset_builder() : stream_writer_formatter(_m_stream,fb2k::noAbort) {}
|
||||
dsp_preset_builder() : stream_writer_formatter(_m_stream,_m_abort) {}
|
||||
void finish(const GUID & id, dsp_preset & out) {
|
||||
out.set_owner(id);
|
||||
out.set_data(_m_stream.m_buffer.get_ptr(), _m_stream.m_buffer.get_size());
|
||||
@@ -552,6 +547,7 @@ public:
|
||||
_m_stream.m_buffer.set_size(0);
|
||||
}
|
||||
private:
|
||||
abort_callback_dummy _m_abort;
|
||||
stream_writer_buffer_simple _m_stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ double dsp_manager::run(dsp_chunk_list * p_list,const metadb_handle_ptr & p_cur_
|
||||
}
|
||||
|
||||
|
||||
// Recycle existing DSPs in a special case when user has apparently only altered settings of one of DSPs.
|
||||
//HACK: recycle existing DSPs in a special case when user has apparently only altered settings of one of DSPs.
|
||||
if (newchain.get_count() == m_chain.get_count()) {
|
||||
t_size data_mismatch_count = 0;
|
||||
t_size owner_mismatch_count = 0;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! Helper class for running audio data through a DSP chain.
|
||||
class dsp_manager {
|
||||
public:
|
||||
dsp_manager() {}
|
||||
dsp_manager() : m_config_changed(false) {}
|
||||
|
||||
//! Alters the DSP chain configuration. Should be called before the first run() to set the configuration but can be also called anytime later between run() calls.
|
||||
void set_config( const dsp_chain_config & p_data );
|
||||
@@ -32,7 +32,7 @@ private:
|
||||
|
||||
t_dsp_chain m_chain;
|
||||
dsp_chain_config_impl m_config;
|
||||
bool m_config_changed = false;
|
||||
bool m_config_changed;
|
||||
|
||||
void dsp_run(t_dsp_chain::const_iterator p_iter,dsp_chunk_list * list,const metadb_handle_ptr & cur_file,unsigned flags,double & latency,abort_callback&);
|
||||
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(FOOBAR2000_DESKTOP) || PFC_DEBUG
|
||||
// RATIONALE
|
||||
// Mobile target doesn't really care about event logging, logger interface exists there only for source compat
|
||||
// We can use macros to suppress all PFC_string_formatter bloat for targets that do not care about any of this
|
||||
#define FB2K_HAVE_EVENT_LOGGER
|
||||
#endif
|
||||
|
||||
class NOVTABLE event_logger : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(event_logger, service_base);
|
||||
public:
|
||||
@@ -35,16 +26,8 @@ public:
|
||||
static event_logger_recorder::ptr create();
|
||||
};
|
||||
|
||||
#ifdef FB2K_HAVE_EVENT_LOGGER
|
||||
|
||||
#define FB2K_LOG_STATUS(X,Y) (X)->log_status(Y)
|
||||
#define FB2K_LOG_WARNING(X,Y) (X)->log_warning(Y)
|
||||
#define FB2K_LOG_ERROR(X,Y) (X)->log_error(Y)
|
||||
|
||||
#else
|
||||
|
||||
#define FB2K_LOG_STATUS(X,Y) ((void)0)
|
||||
#define FB2K_LOG_WARNING(X,Y) ((void)0)
|
||||
#define FB2K_LOG_ERROR(X,Y) ((void)0)
|
||||
|
||||
#endif
|
||||
#define FB2K_HAVE_EVENT_LOGGER
|
||||
@@ -11,3 +11,5 @@ PFC_DECLARE_EXCEPTION(exception_osfailure, exception_messagebox, "Internal error
|
||||
PFC_DECLARE_EXCEPTION(exception_out_of_resources, exception_messagebox, "Not enough system resources available.");
|
||||
|
||||
PFC_DECLARE_EXCEPTION(exception_configdamaged, exception_messagebox, "Internal error - configuration files are unreadable.");
|
||||
|
||||
PFC_DECLARE_EXCEPTION(exception_profileaccess, exception_messagebox, "Internal error - cannot access configuration folder.");
|
||||
|
||||
@@ -3,7 +3,7 @@ namespace {
|
||||
|
||||
#define FILE_CACHED_DEBUG_LOG 0
|
||||
|
||||
class file_cached_impl_v2 : public service_multi_inherit< file_cached, file_lowLevelIO > {
|
||||
class file_cached_impl_v2 : public file_cached {
|
||||
public:
|
||||
enum {minBlockSize = 4096};
|
||||
enum {maxSkipSize = 128*1024};
|
||||
@@ -20,15 +20,6 @@ public:
|
||||
m_can_seek = m_base->can_seek();
|
||||
_reinit(p_abort);
|
||||
}
|
||||
size_t lowLevelIO(const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort) override {
|
||||
abort.check();
|
||||
file_lowLevelIO::ptr ll;
|
||||
if ( ll &= m_base ) {
|
||||
flush_buffer();
|
||||
return ll->lowLevelIO(guid, arg1, arg2, arg2size, abort );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
private:
|
||||
void _reinit(abort_callback & p_abort) {
|
||||
m_position = 0;
|
||||
@@ -222,7 +213,7 @@ private:
|
||||
size_t m_readSize;
|
||||
};
|
||||
|
||||
class file_cached_impl : public service_multi_inherit< file_cached, file_lowLevelIO > {
|
||||
class file_cached_impl : public file_cached {
|
||||
public:
|
||||
file_cached_impl(t_size blocksize) {
|
||||
m_buffer.set_size(blocksize);
|
||||
@@ -249,15 +240,7 @@ private:
|
||||
flush_buffer();
|
||||
}
|
||||
public:
|
||||
size_t lowLevelIO(const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort) override {
|
||||
abort.check();
|
||||
file_lowLevelIO::ptr ll;
|
||||
if ( ll &= m_base ) {
|
||||
flush_buffer();
|
||||
return ll->lowLevelIO(guid, arg1, arg2, arg2size, abort);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
||||
t_uint8 * outptr = (t_uint8*)p_buffer;
|
||||
t_size done = 0;
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
#define _atoi64 atoll
|
||||
#endif
|
||||
|
||||
const float replaygain_info::peak_invalid = -1;
|
||||
const float replaygain_info::gain_invalid = -1000;
|
||||
|
||||
t_size file_info::meta_find_ex(const char * p_name,t_size p_name_length) const
|
||||
{
|
||||
t_size n, m = meta_get_count();
|
||||
@@ -414,19 +411,6 @@ void file_info::info_calculate_bitrate(t_filesize p_filesize,double p_length)
|
||||
if ( b > 0 ) info_set_bitrate(b);
|
||||
}
|
||||
|
||||
bool file_info::is_encoding_overkill() const {
|
||||
auto bs = info_get_int("bitspersample");
|
||||
auto extra = info_get("bitspersample_extra");
|
||||
if ( bs <= 24 ) return false; // fixedpoint up to 24bit, OK
|
||||
if ( bs > 32 ) return true; // fixed or float beyond 32bit, overkill
|
||||
|
||||
if ( extra != nullptr ) {
|
||||
if (strcmp(extra, "fixed-point") == 0) return true; // int32, overkill
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_info::is_encoding_lossy() const {
|
||||
const char * encoding = info_get("encoding");
|
||||
if (encoding != NULL) {
|
||||
|
||||
@@ -7,7 +7,7 @@ struct replaygain_info
|
||||
enum {text_buffer_size = 16 };
|
||||
typedef char t_text_buffer[text_buffer_size];
|
||||
|
||||
static const float peak_invalid, gain_invalid;
|
||||
enum { peak_invalid = -1, gain_invalid = -1000 };
|
||||
|
||||
static bool g_format_gain(float p_value,char p_buffer[text_buffer_size]);
|
||||
static bool g_format_peak(float p_value,char p_buffer[text_buffer_size]);
|
||||
@@ -18,15 +18,14 @@ struct replaygain_info
|
||||
inline bool format_album_peak(char p_buffer[text_buffer_size]) const {return g_format_peak(m_album_peak,p_buffer);}
|
||||
inline bool format_track_peak(char p_buffer[text_buffer_size]) const {return g_format_peak(m_track_peak,p_buffer);}
|
||||
|
||||
static float g_parse_gain_text(const char * p_text, t_size p_text_len = SIZE_MAX);
|
||||
void set_album_gain_text(const char * p_text,t_size p_text_len = SIZE_MAX);
|
||||
void set_track_gain_text(const char * p_text,t_size p_text_len = SIZE_MAX);
|
||||
void set_album_peak_text(const char * p_text,t_size p_text_len = SIZE_MAX);
|
||||
void set_track_peak_text(const char * p_text,t_size p_text_len = SIZE_MAX);
|
||||
void set_album_gain_text(const char * p_text,t_size p_text_len = pfc_infinite);
|
||||
void set_track_gain_text(const char * p_text,t_size p_text_len = pfc_infinite);
|
||||
void set_album_peak_text(const char * p_text,t_size p_text_len = pfc_infinite);
|
||||
void set_track_peak_text(const char * p_text,t_size p_text_len = pfc_infinite);
|
||||
|
||||
static bool g_is_meta_replaygain(const char * p_name,t_size p_name_len = SIZE_MAX);
|
||||
static bool g_is_meta_replaygain(const char * p_name,t_size p_name_len = pfc_infinite);
|
||||
bool set_from_meta_ex(const char * p_name,t_size p_name_len,const char * p_value,t_size p_value_len);
|
||||
inline bool set_from_meta(const char * p_name,const char * p_value) {return set_from_meta_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline bool set_from_meta(const char * p_name,const char * p_value) {return set_from_meta_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
|
||||
inline bool is_album_gain_present() const {return m_album_gain != gain_invalid;}
|
||||
inline bool is_track_gain_present() const {return m_track_gain != gain_invalid;}
|
||||
@@ -158,29 +157,29 @@ public:
|
||||
bool info_remove_ex(const char * p_name,t_size p_name_length);
|
||||
const char * info_get_ex(const char * p_name,t_size p_name_length) const;
|
||||
|
||||
inline t_size meta_find(const char * p_name) const {return meta_find_ex(p_name,SIZE_MAX);}
|
||||
inline bool meta_exists(const char * p_name) const {return meta_exists_ex(p_name,SIZE_MAX);}
|
||||
inline void meta_remove_field(const char * p_name) {meta_remove_field_ex(p_name,SIZE_MAX);}
|
||||
inline t_size meta_set(const char * p_name,const char * p_value) {return meta_set_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline void meta_insert_value(t_size p_index,t_size p_value_index,const char * p_value) {meta_insert_value_ex(p_index,p_value_index,p_value,SIZE_MAX);}
|
||||
inline void meta_add_value(t_size p_index,const char * p_value) {meta_add_value_ex(p_index,p_value,SIZE_MAX);}
|
||||
inline const char* meta_get(const char * p_name,t_size p_index) const {return meta_get_ex(p_name,SIZE_MAX,p_index);}
|
||||
inline t_size meta_get_count_by_name(const char * p_name) const {return meta_get_count_by_name_ex(p_name,SIZE_MAX);}
|
||||
inline t_size meta_add(const char * p_name,const char * p_value) {return meta_add_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline void meta_modify_value(t_size p_index,t_size p_value_index,const char * p_value) {meta_modify_value_ex(p_index,p_value_index,p_value,SIZE_MAX);}
|
||||
inline t_size meta_find(const char * p_name) const {return meta_find_ex(p_name,pfc_infinite);}
|
||||
inline bool meta_exists(const char * p_name) const {return meta_exists_ex(p_name,pfc_infinite);}
|
||||
inline void meta_remove_field(const char * p_name) {meta_remove_field_ex(p_name,pfc_infinite);}
|
||||
inline t_size meta_set(const char * p_name,const char * p_value) {return meta_set_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
inline void meta_insert_value(t_size p_index,t_size p_value_index,const char * p_value) {meta_insert_value_ex(p_index,p_value_index,p_value,pfc_infinite);}
|
||||
inline void meta_add_value(t_size p_index,const char * p_value) {meta_add_value_ex(p_index,p_value,pfc_infinite);}
|
||||
inline const char* meta_get(const char * p_name,t_size p_index) const {return meta_get_ex(p_name,pfc_infinite,p_index);}
|
||||
inline t_size meta_get_count_by_name(const char * p_name) const {return meta_get_count_by_name_ex(p_name,pfc_infinite);}
|
||||
inline t_size meta_add(const char * p_name,const char * p_value) {return meta_add_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
inline void meta_modify_value(t_size p_index,t_size p_value_index,const char * p_value) {meta_modify_value_ex(p_index,p_value_index,p_value,pfc_infinite);}
|
||||
|
||||
|
||||
|
||||
inline t_size info_set(const char * p_name,const char * p_value) {return info_set_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline t_size info_find(const char * p_name) const {return info_find_ex(p_name,SIZE_MAX);}
|
||||
inline bool info_exists(const char * p_name) const {return info_exists_ex(p_name,SIZE_MAX);}
|
||||
inline bool info_remove(const char * p_name) {return info_remove_ex(p_name,SIZE_MAX);}
|
||||
inline const char * info_get(const char * p_name) const {return info_get_ex(p_name,SIZE_MAX);}
|
||||
inline t_size info_set(const char * p_name,const char * p_value) {return info_set_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
inline t_size info_find(const char * p_name) const {return info_find_ex(p_name,pfc_infinite);}
|
||||
inline bool info_exists(const char * p_name) const {return info_exists_ex(p_name,pfc_infinite);}
|
||||
inline bool info_remove(const char * p_name) {return info_remove_ex(p_name,pfc_infinite);}
|
||||
inline const char * info_get(const char * p_name) const {return info_get_ex(p_name,pfc_infinite);}
|
||||
|
||||
bool info_set_replaygain_ex(const char * p_name,t_size p_name_len,const char * p_value,t_size p_value_len);
|
||||
inline bool info_set_replaygain(const char * p_name,const char * p_value) {return info_set_replaygain_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline bool info_set_replaygain(const char * p_name,const char * p_value) {return info_set_replaygain_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
void info_set_replaygain_auto_ex(const char * p_name,t_size p_name_len,const char * p_value,t_size p_value_len);
|
||||
inline void info_set_replaygain_auto(const char * p_name,const char * p_value) {info_set_replaygain_auto_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline void info_set_replaygain_auto(const char * p_name,const char * p_value) {info_set_replaygain_auto_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
|
||||
|
||||
|
||||
@@ -188,12 +187,12 @@ public:
|
||||
void copy_info_single(const file_info & p_source,t_size p_index);
|
||||
void copy_meta_single_by_name_ex(const file_info & p_source,const char * p_name,t_size p_name_length);
|
||||
void copy_info_single_by_name_ex(const file_info & p_source,const char * p_name,t_size p_name_length);
|
||||
inline void copy_meta_single_by_name(const file_info & p_source,const char * p_name) {copy_meta_single_by_name_ex(p_source,p_name,SIZE_MAX);}
|
||||
inline void copy_info_single_by_name(const file_info & p_source,const char * p_name) {copy_info_single_by_name_ex(p_source,p_name,SIZE_MAX);}
|
||||
inline void copy_meta_single_by_name(const file_info & p_source,const char * p_name) {copy_meta_single_by_name_ex(p_source,p_name,pfc_infinite);}
|
||||
inline void copy_info_single_by_name(const file_info & p_source,const char * p_name) {copy_info_single_by_name_ex(p_source,p_name,pfc_infinite);}
|
||||
void reset();
|
||||
void reset_replaygain();
|
||||
void copy_meta_single_rename_ex(const file_info & p_source,t_size p_index,const char * p_new_name,t_size p_new_name_length);
|
||||
inline void copy_meta_single_rename(const file_info & p_source,t_size p_index,const char * p_new_name) {copy_meta_single_rename_ex(p_source,p_index,p_new_name,SIZE_MAX);}
|
||||
inline void copy_meta_single_rename(const file_info & p_source,t_size p_index,const char * p_new_name) {copy_meta_single_rename_ex(p_source,p_index,p_new_name,pfc_infinite);}
|
||||
void overwrite_info(const file_info & p_source);
|
||||
void overwrite_meta(const file_info & p_source);
|
||||
|
||||
@@ -216,7 +215,6 @@ public:
|
||||
uint32_t info_get_wfx_chanMask() const;
|
||||
|
||||
bool is_encoding_lossy() const;
|
||||
bool is_encoding_overkill() const;
|
||||
|
||||
|
||||
void info_calculate_bitrate(t_filesize p_filesize,double p_length);
|
||||
@@ -243,16 +241,16 @@ public:
|
||||
//! Unsafe - does not check whether the field already exists and will result in duplicates if it does - call only when appropriate checks have been applied externally.
|
||||
t_size __meta_add_unsafe_ex(const char * p_name,t_size p_name_length,const char * p_value,t_size p_value_length) {return meta_set_nocheck_ex(p_name,p_name_length,p_value,p_value_length);}
|
||||
//! Unsafe - does not check whether the field already exists and will result in duplicates if it does - call only when appropriate checks have been applied externally.
|
||||
t_size __meta_add_unsafe(const char * p_name,const char * p_value) {return meta_set_nocheck_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
t_size __meta_add_unsafe(const char * p_name,const char * p_value) {return meta_set_nocheck_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
|
||||
//! Unsafe - does not check whether the field already exists and will result in duplicates if it does - call only when appropriate checks have been applied externally.
|
||||
t_size __info_add_unsafe_ex(const char * p_name,t_size p_name_length,const char * p_value,t_size p_value_length) {return info_set_nocheck_ex(p_name,p_name_length,p_value,p_value_length);}
|
||||
//! Unsafe - does not check whether the field already exists and will result in duplicates if it does - call only when appropriate checks have been applied externally.
|
||||
t_size __info_add_unsafe(const char * p_name,const char * p_value) {return info_set_nocheck_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
t_size __info_add_unsafe(const char * p_name,const char * p_value) {return info_set_nocheck_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
|
||||
void _copy_meta_single_nocheck(const file_info & p_source,t_size p_index) {copy_meta_single_nocheck(p_source, p_index);}
|
||||
|
||||
static bool g_is_valid_field_name(const char * p_name,t_size p_length = SIZE_MAX);
|
||||
static bool g_is_valid_field_name(const char * p_name,t_size p_length = pfc_infinite);
|
||||
//typedef pfc::comparator_stricmp_ascii field_name_comparator;
|
||||
typedef pfc::string::comparatorCaseInsensitiveASCII field_name_comparator;
|
||||
|
||||
@@ -277,11 +275,11 @@ protected:
|
||||
void copy_info_single_nocheck(const file_info & p_source,t_size p_index);
|
||||
void copy_meta_single_by_name_nocheck_ex(const file_info & p_source,const char * p_name,t_size p_name_length);
|
||||
void copy_info_single_by_name_nocheck_ex(const file_info & p_source,const char * p_name,t_size p_name_length);
|
||||
inline void copy_meta_single_by_name_nocheck(const file_info & p_source,const char * p_name) {copy_meta_single_by_name_nocheck_ex(p_source,p_name,SIZE_MAX);}
|
||||
inline void copy_info_single_by_name_nocheck(const file_info & p_source,const char * p_name) {copy_info_single_by_name_nocheck_ex(p_source,p_name,SIZE_MAX);}
|
||||
inline void copy_meta_single_by_name_nocheck(const file_info & p_source,const char * p_name) {copy_meta_single_by_name_nocheck_ex(p_source,p_name,pfc_infinite);}
|
||||
inline void copy_info_single_by_name_nocheck(const file_info & p_source,const char * p_name) {copy_info_single_by_name_nocheck_ex(p_source,p_name,pfc_infinite);}
|
||||
|
||||
virtual t_size meta_set_nocheck_ex(const char * p_name,t_size p_name_length,const char * p_value,t_size p_value_length) = 0;
|
||||
virtual t_size info_set_nocheck_ex(const char * p_name,t_size p_name_length,const char * p_value,t_size p_value_length) = 0;
|
||||
inline t_size meta_set_nocheck(const char * p_name,const char * p_value) {return meta_set_nocheck_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline t_size info_set_nocheck(const char * p_name,const char * p_value) {return info_set_nocheck_ex(p_name,SIZE_MAX,p_value,SIZE_MAX);}
|
||||
inline t_size meta_set_nocheck(const char * p_name,const char * p_value) {return meta_set_nocheck_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
inline t_size info_set_nocheck(const char * p_name,const char * p_value) {return info_set_nocheck_ex(p_name,pfc_infinite,p_value,pfc_infinite);}
|
||||
};
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
#include "tracks.h"
|
||||
|
||||
//! Implementing this class gives you direct control over which part of file_info gets altered during a tag update uperation. To be used with metadb_io_v2::update_info_async().
|
||||
class NOVTABLE file_info_filter : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(file_info_filter, service_base);
|
||||
public:
|
||||
//! Alters specified file_info entry; called as a part of tag update process. Specified file_info has been read from a file, and will be written back.\n
|
||||
//! WARNING: This will be typically called from another thread than main app thread (precisely, from thread created by tag updater). You should copy all relevant data to members of your file_info_filter instance in constructor and reference only member data in apply_filter() implementation.
|
||||
//! @returns True when you have altered file_info and changes need to be written back to the file; false if no changes have been made.
|
||||
virtual bool apply_filter(trackRef p_location, t_filestats p_stats, file_info & p_info) = 0;
|
||||
|
||||
typedef std::function< bool (trackRef, t_filestats, file_info & ) > func_t;
|
||||
static file_info_filter::ptr create( func_t f );
|
||||
};
|
||||
|
||||
//! Extended file_info_filter allowing the caller to do their own manipulation of the file before and after the metadata update takes place. \n
|
||||
//! Respected by foobar2000 v1.5 and up; if metadb_io_v4 is supported, then file_info_filter_v2 is understood.
|
||||
class NOVTABLE file_info_filter_v2 : public file_info_filter {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(file_info_filter_v2, file_info_filter);
|
||||
public:
|
||||
|
||||
enum filterStatus_t {
|
||||
filterNoUpdate = 0,
|
||||
filterProceed,
|
||||
filterAlreadyUpdated
|
||||
};
|
||||
//! Called after just before rewriting metadata. The file is not yet opened for writing, but a file_lock has already been granted (so don't call it on your own). \n
|
||||
//! You can use this method to perform album art updates (via album_art_editor API) alongside metadata updates. \n
|
||||
//! Return value can be used to stop fb2k from proceeding with metadata update on this file. \n
|
||||
//! If your own operations on this file fail, just pass the exceptions to the caller and they will be reported just as other tag update errors.
|
||||
//! @param fileIfAlreadyOpened Reference to an already opened file object, if already opened by the caller. May be null.
|
||||
virtual filterStatus_t before_tag_update(const char * location, file::ptr fileIfAlreadyOpened, abort_callback & aborter) = 0;
|
||||
|
||||
//! Called after metadata has been updated. \n
|
||||
//! If you wish to alter the file on your own, use before_tag_update() for this instead. \n
|
||||
//! If your own operations on this file fail, just pass the exceptions to the caller and they will be reported just as other tag update errors. \n
|
||||
//! The passed reader object can be used to read the properties of the updated file back. In most cases it will be the writer that was used to update the tags. Do not call tag writing methods on it from this function.
|
||||
virtual void after_tag_update(const char * location, service_ptr_t<class input_info_reader> reader, abort_callback & aborter) = 0;
|
||||
|
||||
virtual void after_all_tag_updates(abort_callback & aborter) = 0;
|
||||
|
||||
//! Allows you to do your own error logging.
|
||||
//! @returns True if the error has been noted by your code and does not need to be shown to the user.
|
||||
virtual bool filter_error(const char * location, const char * msg) = 0;
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//! Generic implementation of file_info_filter_impl.
|
||||
class file_info_filter_impl : public file_info_filter {
|
||||
public:
|
||||
file_info_filter_impl(const pfc::list_base_const_t<metadb_handle_ptr> & p_list, const pfc::list_base_const_t<const file_info*> & p_new_info) {
|
||||
FB2K_DYNAMIC_ASSERT(p_list.get_count() == p_new_info.get_count());
|
||||
pfc::array_t<t_size> order;
|
||||
order.set_size(p_list.get_count());
|
||||
order_helper::g_fill(order.get_ptr(), order.get_size());
|
||||
p_list.sort_get_permutation_t(pfc::compare_t<metadb_handle_ptr, metadb_handle_ptr>, order.get_ptr());
|
||||
m_handles.set_count(order.get_size());
|
||||
m_infos.set_size(order.get_size());
|
||||
for (t_size n = 0; n < order.get_size(); n++) {
|
||||
m_handles[n] = p_list[order[n]];
|
||||
m_infos[n] = *p_new_info[order[n]];
|
||||
}
|
||||
}
|
||||
|
||||
bool apply_filter(metadb_handle_ptr p_location, t_filestats p_stats, file_info & p_info) {
|
||||
t_size index;
|
||||
if (m_handles.bsearch_t(pfc::compare_t<metadb_handle_ptr, metadb_handle_ptr>, p_location, index)) {
|
||||
p_info = m_infos[index];
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private:
|
||||
metadb_handle_list m_handles;
|
||||
pfc::array_t<file_info_impl> m_infos;
|
||||
};
|
||||
@@ -30,23 +30,6 @@ public:
|
||||
|
||||
typedef service_ptr_t<file_lock> file_lock_ptr;
|
||||
|
||||
//! \since 1.5
|
||||
//! Modern version of file locking. \n
|
||||
//! A read lock can be interrupted by a write lock request, from the thread that requested writing. \n
|
||||
class NOVTABLE file_lock_interrupt : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(file_lock_interrupt, service_base);
|
||||
public:
|
||||
//! Please note that interrupt() is called outside any sync scopes and may be called after lock reference has been released. \n
|
||||
//! It is implementer's responsibility to safeguard against such. \n
|
||||
//! The interrupt() function must *never* fail, unless aborted by calling context - which means that whoever asked for write access is aborting whatever they're doing. \n
|
||||
//! This function may block for as long as it takes to release the owned resources, but must be able to abort cleanly if doing so. \n
|
||||
//! If the function was aborted, it may be called again on the same object. \n
|
||||
//! If the function succeeded, it will not be called again on the same object; the object will be released immediately after.
|
||||
virtual void interrupt( abort_callback & aborter ) = 0;
|
||||
|
||||
static file_lock_interrupt::ptr create( std::function< void (abort_callback&)> );
|
||||
};
|
||||
|
||||
//! Entry point class for obtaining file_lock objects.
|
||||
class NOVTABLE file_lock_manager : public service_base {
|
||||
public:
|
||||
@@ -68,10 +51,3 @@ public:
|
||||
|
||||
FB2K_MAKE_SERVICE_COREAPI(file_lock_manager);
|
||||
};
|
||||
|
||||
// \since 1.5
|
||||
class NOVTABLE file_lock_manager_v2 : public file_lock_manager {
|
||||
FB2K_MAKE_SERVICE_COREAPI_EXTENSION( file_lock_manager_v2, file_lock_manager );
|
||||
public:
|
||||
virtual fb2k::objRef acquire_read_v2(const char * p_path, file_lock_interrupt::ptr interruptHandler, abort_callback & p_abort) = 0;
|
||||
};
|
||||
|
||||
@@ -210,7 +210,7 @@ void filesystem::g_move(const char * src,const char * dst,abort_callback & p_abo
|
||||
|
||||
void filesystem::g_link(const char * p_src,const char * p_dst,abort_callback & p_abort) {
|
||||
if (!foobar2000_io::_extract_native_path_ptr(p_src) || !foobar2000_io::_extract_native_path_ptr(p_dst)) throw exception_io_no_handler_for_path();
|
||||
WIN32_IO_OP( CreateHardLink( pfc::stringcvt::string_os_from_utf8( pfc::winPrefixPath( p_dst ) ), pfc::stringcvt::string_os_from_utf8( pfc::winPrefixPath( p_src ) ), NULL) );
|
||||
WIN32_IO_OP( CreateHardLink( pfc::stringcvt::string_os_from_utf8( p_dst ), pfc::stringcvt::string_os_from_utf8( p_src ), NULL) );
|
||||
}
|
||||
|
||||
void filesystem::g_link_timeout(const char * p_src,const char * p_dst,double p_timeout,abort_callback & p_abort) {
|
||||
@@ -300,11 +300,7 @@ bool filesystem::g_relative_path_parse(const char * relative_path,const char * p
|
||||
return false;
|
||||
}
|
||||
|
||||
bool archive::is_our_archive( const char * path ) {
|
||||
archive_v2::ptr v2;
|
||||
if ( v2 &= this ) return v2->is_our_archive( path );
|
||||
return true; // accept all files
|
||||
}
|
||||
|
||||
|
||||
bool archive_impl::get_canonical_path(const char * path,pfc::string_base & out)
|
||||
{
|
||||
@@ -551,7 +547,8 @@ namespace {
|
||||
file::g_transfer_object(r_src,r_dst,size,p_abort);
|
||||
} catch(...) {
|
||||
r_dst.release();
|
||||
try {m_fs->remove(dst,fb2k::noAbort);} catch(...) {}
|
||||
abort_callback_dummy dummy;
|
||||
try {m_fs->remove(dst,dummy);} catch(...) {}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -612,14 +609,11 @@ void filesystem::g_copy(const char * src,const char * dst,abort_callback & p_abo
|
||||
file::g_transfer_object(r_src,r_dst,size,p_abort);
|
||||
} catch(...) {
|
||||
r_dst.release();
|
||||
try {g_remove(dst,fb2k::noAbort);} catch(...) {}
|
||||
abort_callback_dummy dummy;
|
||||
try {g_remove(dst,dummy);} catch(...) {}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
file::g_copy_timestamps(r_src, r_dst, p_abort);
|
||||
} catch (exception_io) {}
|
||||
}
|
||||
|
||||
void stream_reader::read_object(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {
|
||||
@@ -679,7 +673,8 @@ void filesystem::g_open_tempmem(service_ptr_t<file> & p_out,abort_callback & p_a
|
||||
}
|
||||
|
||||
file::ptr filesystem::g_open_tempmem() {
|
||||
file::ptr f; g_open_tempmem(f, fb2k::noAbort); return f;
|
||||
abort_callback_dummy aborter;
|
||||
file::ptr f; g_open_tempmem(f, aborter); return f;
|
||||
}
|
||||
|
||||
void archive_impl::list_directory(const char * p_path,directory_callback & p_out,abort_callback & p_abort) {
|
||||
@@ -715,21 +710,6 @@ bool file::is_eof(abort_callback & p_abort) {
|
||||
}
|
||||
|
||||
|
||||
t_filetimestamp foobar2000_io::import_DOS_time(uint32_t v) {
|
||||
#ifdef _WIN32
|
||||
FILETIME ft = {};
|
||||
if (DosDateTimeToFileTime(HIWORD(v), LOWORD(v), &ft)) {
|
||||
FILETIME ft2 = {};
|
||||
if (LocalFileTimeToFileTime(&ft, &ft2)) {
|
||||
return ((uint64_t)ft2.dwHighDateTime << 32) | (uint64_t)ft2.dwLowDateTime;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
// FIX ME
|
||||
return filetimestamp_invalid;
|
||||
}
|
||||
|
||||
t_filetimestamp foobar2000_io::filetimestamp_from_system_timer()
|
||||
{
|
||||
return pfc::fileTimeNow();
|
||||
@@ -803,18 +783,7 @@ namespace {
|
||||
|
||||
class exception_io_win32_ex : public exception_io_win32 {
|
||||
public:
|
||||
static pfc::string8 format(DWORD code) {
|
||||
pfc::string8 ret;
|
||||
ret << "I/O error (win32 ";
|
||||
if (code & 0x80000000) {
|
||||
ret << "0x" << pfc::format_hex(code, 8);
|
||||
} else {
|
||||
ret << "#" << (uint32_t)code;
|
||||
}
|
||||
ret << ")";
|
||||
return ret;
|
||||
}
|
||||
exception_io_win32_ex(DWORD p_code) : m_msg(format(p_code)) {}
|
||||
exception_io_win32_ex(DWORD p_code) : m_msg(pfc::string_formatter() << "I/O error (win32 #" << (t_uint32)p_code << ")") {}
|
||||
exception_io_win32_ex(const exception_io_win32_ex & p_other) {*this = p_other;}
|
||||
const char * what() const throw() {return m_msg;}
|
||||
private:
|
||||
@@ -832,7 +801,7 @@ PFC_NORETURN void foobar2000_io::win32_file_write_failure(DWORD p_code, const ch
|
||||
|
||||
PFC_NORETURN void foobar2000_io::exception_io_from_win32(DWORD p_code) {
|
||||
#if PFC_DEBUG
|
||||
PFC_DEBUGLOG << "exception_io_from_win32: " << p_code;
|
||||
pfc::debugLog() << "exception_io_from_win32: " << p_code;
|
||||
#endif
|
||||
//pfc::string_fixed_t<32> debugMsg; debugMsg << "Win32 I/O error #" << (t_uint32)p_code;
|
||||
//TRACK_CALL_TEXT(debugMsg);
|
||||
@@ -890,7 +859,6 @@ PFC_NORETURN void foobar2000_io::exception_io_from_win32(DWORD p_code) {
|
||||
case ERROR_BAD_NETPATH:
|
||||
// known to be inflicted by momentary net connectivity issues - NOT the same as exception_io_not_found
|
||||
throw exception_io("Network path not found");
|
||||
#if FB2K_SUPPORT_TRANSACTED_FILESYSTEM
|
||||
case ERROR_TRANSACTIONAL_OPEN_NOT_ALLOWED:
|
||||
case ERROR_TRANSACTIONS_UNSUPPORTED_REMOTE:
|
||||
case ERROR_RM_NOT_ACTIVE:
|
||||
@@ -901,16 +869,9 @@ PFC_NORETURN void foobar2000_io::exception_io_from_win32(DWORD p_code) {
|
||||
throw exception_io_transactional_conflict();
|
||||
case ERROR_TRANSACTION_ALREADY_ABORTED:
|
||||
throw exception_io_transaction_aborted();
|
||||
case ERROR_EFS_NOT_ALLOWED_IN_TRANSACTION:
|
||||
throw exception_io("Transacted updates of encrypted content are not supported");
|
||||
#endif // FB2K_SUPPORT_TRANSACTED_FILESYSTEM
|
||||
case ERROR_UNEXP_NET_ERR:
|
||||
// QNAP threw this when messing with very long file paths and concurrent conversion, probably SMB daemon crashed
|
||||
throw exception_io("Unexpected network error");
|
||||
case ERROR_NOT_SAME_DEVICE:
|
||||
throw exception_io("Source and destination must be on the same device");
|
||||
case 0x80310000:
|
||||
throw exception_io("Drive locked by BitLocker");
|
||||
throw exception_io("Unexpected netwrok error");
|
||||
default:
|
||||
throw exception_io_win32_ex(p_code);
|
||||
}
|
||||
@@ -1049,17 +1010,6 @@ bool foobar2000_io::extract_native_path_ex(const char * p_fspath, pfc::string_ba
|
||||
return true;
|
||||
}
|
||||
|
||||
bool foobar2000_io::extract_native_path_archive_aware(const char * in, pfc::string_base & out) {
|
||||
if (foobar2000_io::extract_native_path(in, out)) return true;
|
||||
if (archive_impl::g_is_unpack_path(in)) {
|
||||
pfc::string8 arc, dummy;
|
||||
if (archive_impl::g_parse_unpack_path(in, arc, dummy)) {
|
||||
return foobar2000_io::extract_native_path(arc, out);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pfc::string stream_reader::read_string(abort_callback & p_abort) {
|
||||
t_uint32 len;
|
||||
read_lendian_t(len,p_abort);
|
||||
@@ -1169,8 +1119,7 @@ bool foobar2000_io::matchContentType(const char * fullString, const char * ourTy
|
||||
const char * foobar2000_io::contentTypeFromExtension( const char * ext ) {
|
||||
if ( pfc::stringEqualsI_ascii( ext, "mp3" ) ) return "audio/mpeg";
|
||||
if ( pfc::stringEqualsI_ascii( ext, "flac" ) ) return "audio/flac";
|
||||
if ( pfc::stringEqualsI_ascii( ext, "mp4" ) ) return "application/mp4"; // We don't know if it's audio-only or other.
|
||||
if ( pfc::stringEqualsI_ascii( ext, "m4a" ) ) return "audio/mp4";
|
||||
if ( pfc::stringEqualsI_ascii( ext, "mp4" ) || pfc::stringEqualsI_ascii( ext, "m4a" ) ) return "audio/mp4";
|
||||
if ( pfc::stringEqualsI_ascii( ext, "mpc" ) ) return "audio/musepack";
|
||||
if ( pfc::stringEqualsI_ascii( ext, "ogg" ) ) return "audio/ogg";
|
||||
if ( pfc::stringEqualsI_ascii( ext, "opus" ) ) return "audio/opus";
|
||||
@@ -1183,7 +1132,6 @@ const char * foobar2000_io::contentTypeFromExtension( const char * ext ) {
|
||||
const char * foobar2000_io::extensionFromContentType( const char * contentType ) {
|
||||
if (matchContentType_MP3( contentType )) return "mp3";
|
||||
if (matchContentType_FLAC( contentType )) return "flac";
|
||||
if (matchContentType_MP4audio( contentType)) return "m4a";
|
||||
if (matchContentType_MP4( contentType)) return "mp4";
|
||||
if (matchContentType_Musepack( contentType )) return "mpc";
|
||||
if (matchContentType_Ogg( contentType )) return "ogg";
|
||||
@@ -1199,12 +1147,6 @@ bool foobar2000_io::matchContentType_MP3( const char * type) {
|
||||
return matchContentType(type,"audio/mp3") || matchContentType(type,"audio/mpeg") || matchContentType(type,"audio/mpg") || matchContentType(type,"audio/x-mp3") || matchContentType(type,"audio/x-mpeg") || matchContentType(type,"audio/x-mpg");
|
||||
}
|
||||
bool foobar2000_io::matchContentType_MP4( const char * type ) {
|
||||
return matchContentType(type, "audio/mp4") || matchContentType(type, "audio/x-mp4")
|
||||
|| matchContentType(type, "video/mp4") || matchContentType(type, "video/x-mp4")
|
||||
|| matchContentType(type, "application/mp4") || matchContentType(type, "application/x-mp4");
|
||||
|
||||
}
|
||||
bool foobar2000_io::matchContentType_MP4audio( const char * type ) {
|
||||
return matchContentType(type, "audio/mp4") || matchContentType(type, "audio/x-mp4");
|
||||
}
|
||||
bool foobar2000_io::matchContentType_Ogg( const char * type) {
|
||||
@@ -1414,7 +1356,6 @@ void filesystem::rewrite_file(const char * path, abort_callback & abort, double
|
||||
{
|
||||
auto f = this->openWriteNew( temp, abort, opTimeout );
|
||||
worker(f);
|
||||
f->flushFileBuffers_( abort );
|
||||
}
|
||||
|
||||
retryOnSharingViolation(opTimeout, abort, [&] {
|
||||
@@ -1423,7 +1364,8 @@ void filesystem::rewrite_file(const char * path, abort_callback & abort, double
|
||||
|
||||
} catch(...) {
|
||||
try {
|
||||
retryOnSharingViolation(opTimeout, abort, [&] { this->remove(temp, fb2k::noAbort); } );
|
||||
abort_callback_dummy noAbort;
|
||||
retryOnSharingViolation(opTimeout, abort, [&] { this->remove(temp, noAbort); } );
|
||||
} catch(...) {}
|
||||
throw;
|
||||
}
|
||||
@@ -1528,72 +1470,7 @@ bool filesystem::commit_if_transacted(abort_callback &abort) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
t_filestats filesystem::get_stats(const char * path, abort_callback & abort) {
|
||||
t_filestats s; bool dummy;
|
||||
this->get_stats(path, s, dummy, abort);
|
||||
return s;
|
||||
}
|
||||
|
||||
bool file_dynamicinfo_v2::get_dynamic_info(class file_info & p_out) {
|
||||
t_filesize dummy = 0;
|
||||
return this->get_dynamic_info_v2(p_out, dummy);
|
||||
}
|
||||
|
||||
void file::flushFileBuffers_(abort_callback&a) {
|
||||
file_lowLevelIO::ptr f;
|
||||
if ( f &= this ) f->flushFileBuffers(a);
|
||||
}
|
||||
|
||||
size_t file::lowLevelIO_(const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort) {
|
||||
size_t retval = 0;
|
||||
file_lowLevelIO::ptr f;
|
||||
if (f &= this) retval = f->lowLevelIO(guid, arg1, arg2, arg2size, abort);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool file_lowLevelIO::flushFileBuffers(abort_callback & abort) {
|
||||
return this->lowLevelIO( guid_flushFileBuffers, 0, nullptr, 0, abort) != 0;
|
||||
}
|
||||
|
||||
bool file_lowLevelIO::getFileTimes(filetimes_t & out, abort_callback & a) {
|
||||
return this->lowLevelIO(guid_getFileTimes, 0, &out, sizeof(out), a) != 0;
|
||||
}
|
||||
|
||||
bool file_lowLevelIO::setFileTimes(filetimes_t const & in, abort_callback & a) {
|
||||
return this->lowLevelIO(guid_setFileTimes, 0, (void*)&in, sizeof(in), a) != 0;
|
||||
}
|
||||
|
||||
bool file::g_copy_creation_time(service_ptr_t<file> from, service_ptr_t<file> to, abort_callback& a) {
|
||||
file_lowLevelIO::ptr llFrom, llTo;
|
||||
bool rv = false;
|
||||
if (llTo &= to) {
|
||||
if (llFrom &= from) {
|
||||
file_lowLevelIO::filetimes_t filetimes;
|
||||
if (llFrom->getFileTimes(filetimes, a)) {
|
||||
if (filetimes.creation != filetimestamp_invalid) {
|
||||
file_lowLevelIO::filetimes_t ft2;
|
||||
ft2.creation = filetimes.creation;
|
||||
rv = llTo->setFileTimes(ft2, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
bool file::g_copy_timestamps(file::ptr from, file::ptr to, abort_callback& a) {
|
||||
file_lowLevelIO::ptr llFrom, llTo;
|
||||
if ( llTo &= to ) {
|
||||
if (llFrom &= from) {
|
||||
file_lowLevelIO::filetimes_t filetimes = {};
|
||||
if (llFrom->getFileTimes(filetimes, a)) {
|
||||
return llTo->setFileTimes(filetimes, a);
|
||||
}
|
||||
}
|
||||
file_lowLevelIO::filetimes_t filetimes = {};
|
||||
filetimes.lastWrite = from->get_timestamp(a);
|
||||
if ( filetimes.lastWrite != filetimestamp_invalid ) {
|
||||
return llTo->setFileTimes(filetimes, a);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ namespace foobar2000_io
|
||||
PFC_DECLARE_EXCEPTION(exception_io, pfc::exception,"I/O error");
|
||||
//! Object not found.
|
||||
PFC_DECLARE_EXCEPTION(exception_io_not_found, exception_io,"Object not found");
|
||||
//! Access denied. \n
|
||||
//! Special Windows note: this MAY be thrown instead of exception_io_sharing_violation by operations that rename/move files due to Win32 MoveFile() bugs.
|
||||
//! Access denied.
|
||||
PFC_DECLARE_EXCEPTION(exception_io_denied, exception_io,"Access denied");
|
||||
//! Access denied.
|
||||
PFC_DECLARE_EXCEPTION(exception_io_denied_readonly, exception_io_denied,"File is read-only");
|
||||
@@ -269,12 +268,12 @@ namespace foobar2000_io
|
||||
|
||||
//! Optional, called by owner thread before sleeping.
|
||||
//! @param p_abort abort_callback object signaling user aborting the operation.
|
||||
virtual void on_idle(abort_callback & p_abort) {(void)p_abort;}
|
||||
virtual void on_idle(abort_callback & p_abort) {}
|
||||
|
||||
//! Retrieves last modification time of the file.
|
||||
//! @param p_abort abort_callback object signaling user aborting the operation.
|
||||
//! @returns Last modification time o fthe file; filetimestamp_invalid if N/A.
|
||||
virtual t_filetimestamp get_timestamp(abort_callback & p_abort) {(void)p_abort;return filetimestamp_invalid;}
|
||||
virtual t_filetimestamp get_timestamp(abort_callback & p_abort) {return filetimestamp_invalid;}
|
||||
|
||||
//! Resets non-seekable stream, or seeks to zero on seekable file.
|
||||
//! @param p_abort abort_callback object signaling user aborting the operation.
|
||||
@@ -318,19 +317,12 @@ namespace foobar2000_io
|
||||
static void g_transfer_object(stream_reader * src,stream_writer * dst,t_filesize bytes,abort_callback & p_abort);
|
||||
//! Helper; transfers entire file content from one file to another, erasing previous content.
|
||||
static void g_transfer_file(const service_ptr_t<file> & p_from,const service_ptr_t<file> & p_to,abort_callback & p_abort);
|
||||
//! Helper; transfers file modification times from one file to another, if supported by underlying objects. Returns true on success, false if the operation doesn't appear to be supported.
|
||||
static bool g_copy_timestamps(service_ptr_t<file> from, service_ptr_t<file> to, abort_callback& abort);
|
||||
static bool g_copy_creation_time(service_ptr_t<file> from, service_ptr_t<file> to, abort_callback& abort);
|
||||
|
||||
//! Helper; improved performance over g_transfer on streams (avoids disk fragmentation when transferring large blocks).
|
||||
static t_filesize g_transfer(service_ptr_t<file> p_src,service_ptr_t<file> p_dst,t_filesize p_bytes,abort_callback & p_abort);
|
||||
//! Helper; improved performance over g_transfer_file on streams (avoids disk fragmentation when transferring large blocks).
|
||||
static void g_transfer_object(service_ptr_t<file> p_src,service_ptr_t<file> p_dst,t_filesize p_bytes,abort_callback & p_abort);
|
||||
|
||||
//! file_lowLevelIO wrapper
|
||||
void flushFileBuffers_(abort_callback &);
|
||||
//! file_lowLevelIO wrapper
|
||||
size_t lowLevelIO_(const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort);
|
||||
|
||||
t_filesize skip(t_filesize p_bytes,abort_callback & p_abort);
|
||||
t_filesize skip_seek(t_filesize p_bytes,abort_callback & p_abort);
|
||||
@@ -376,46 +368,6 @@ namespace foobar2000_io
|
||||
static void g_decodeInitCache(file::ptr & theFile, abort_callback & abort, size_t blockSize);
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
//! Additional service implemented by standard file object providing access to low level OS specific APIs.
|
||||
class file_lowLevelIO : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(file_lowLevelIO, service_base );
|
||||
public:
|
||||
//! @returns 0 if the command was not recognized, a command-defined non zero value otherwise.
|
||||
virtual size_t lowLevelIO( const GUID & guid, size_t arg1, void * arg2, size_t arg2size, abort_callback & abort ) = 0;
|
||||
|
||||
//! Win32 FlushFileBuffers() wrapper. \n
|
||||
//! Throws exception_io_denied on a file opened for reading. \n
|
||||
//! No arguments are defined. \n
|
||||
//! Returns 1 if handled, 0 if unsupported.
|
||||
static const GUID guid_flushFileBuffers;
|
||||
//! Retrieves file creation / last access / last write times. \n
|
||||
//! Parameters: arg2 points to a filetimes_t struct to receive the data; arg2size must be set to sizeof(filetimes_t). \n
|
||||
//! If the filesystem does not support a specific portion of the information, relevant struct member will be set to filetimestamp_invalid. \n
|
||||
//! Returns 1 if handled, 0 if unsupported.
|
||||
static const GUID guid_getFileTimes;
|
||||
//! Sets file creation / last access / last write times. \n
|
||||
//! Parameters: arg2 points to a filetimes_t struct holding the new data; arg2size must be set to sizeof(filetimes_t). \n
|
||||
//! Individual members of the filetimes_t struct can be set to filetimestamp_invalid, if not all of the values are to be altered on the file. \n
|
||||
//! Returns 1 if handled, 0 if unsupported.
|
||||
static const GUID guid_setFileTimes;
|
||||
|
||||
//! Struct to be used with guid_getFileTimes / guid_setFileTimes.
|
||||
struct filetimes_t {
|
||||
t_filetimestamp creation = filetimestamp_invalid;
|
||||
t_filetimestamp lastAccess = filetimestamp_invalid;
|
||||
t_filetimestamp lastWrite = filetimestamp_invalid;
|
||||
};
|
||||
|
||||
//! Helper
|
||||
bool flushFileBuffers(abort_callback &);
|
||||
//! Helper
|
||||
bool getFileTimes( filetimes_t & out, abort_callback &);
|
||||
//! Helper
|
||||
bool setFileTimes( filetimes_t const & in, abort_callback &);
|
||||
|
||||
};
|
||||
|
||||
//! Implementation helper - contains dummy implementations of methods that modify the file
|
||||
template<typename t_base> class file_readonly_t : public t_base {
|
||||
public:
|
||||
@@ -426,13 +378,13 @@ namespace foobar2000_io
|
||||
|
||||
class file_streamstub : public file_readonly {
|
||||
public:
|
||||
t_size read(void *,t_size,abort_callback &) {return 0;}
|
||||
t_filesize get_size(abort_callback &) {return filesize_invalid;}
|
||||
t_filesize get_position(abort_callback &) {return 0;}
|
||||
bool get_content_type(pfc::string_base &) {return false;}
|
||||
t_size read(void * p_buffer,t_size p_bytes,abort_callback & p_abort) {return 0;}
|
||||
t_filesize get_size(abort_callback & p_abort) {return filesize_invalid;}
|
||||
t_filesize get_position(abort_callback & p_abort) {return 0;}
|
||||
bool get_content_type(pfc::string_base & p_out) {return false;}
|
||||
bool is_remote() {return true;}
|
||||
void reopen(abort_callback&) {}
|
||||
void seek(t_filesize,abort_callback &) {throw exception_io_object_not_seekable();}
|
||||
void seek(t_filesize p_position,abort_callback & p_abort) {throw exception_io_object_not_seekable();}
|
||||
bool can_seek() {return false;}
|
||||
};
|
||||
|
||||
@@ -470,16 +422,12 @@ namespace foobar2000_io
|
||||
|
||||
virtual void open(service_ptr_t<file> & p_out,const char * p_path, t_open_mode p_mode,abort_callback & p_abort)=0;
|
||||
virtual void remove(const char * p_path,abort_callback & p_abort)=0;
|
||||
//! Moves/renames a file. Will fail if the destination file already exists. \n
|
||||
//! Note that this function may throw exception_io_denied instead of exception_io_sharing_violation when the file is momentarily in use, due to bugs in Windows MoveFile() API. There is no legitimate way for us to distinguish between the two scenarios.
|
||||
virtual void move(const char * p_src,const char * p_dst,abort_callback & p_abort)=0;
|
||||
//! Queries whether a file at specified path belonging to this filesystem is a remote object or not.
|
||||
//! Queries whether a file at specified path belonging to this filesystem is a remove object or not.
|
||||
virtual bool is_remote(const char * p_src) = 0;
|
||||
|
||||
//! Retrieves stats of a file at specified path.
|
||||
virtual void get_stats(const char * p_path,t_filestats & p_stats,bool & p_is_writeable,abort_callback & p_abort) = 0;
|
||||
//! Helper
|
||||
t_filestats get_stats( const char * path, abort_callback & abort );
|
||||
|
||||
virtual bool relative_path_create(const char * file_path,const char * playlist_path,pfc::string_base & out) {return false;}
|
||||
virtual bool relative_path_parse(const char * relative_path,const char * playlist_path,pfc::string_base & out) {return false;}
|
||||
@@ -522,7 +470,6 @@ namespace foobar2000_io
|
||||
//! Attempts to remove file at specified path; if the operation fails with a sharing violation error, keeps retrying (with short sleep period between retries) for specified amount of time.
|
||||
static void g_remove_timeout(const char * p_path,double p_timeout,abort_callback & p_abort);
|
||||
//! Moves file from one path to another.
|
||||
//! Note that this function may throw exception_io_denied instead of exception_io_sharing_violation when the file is momentarily in use, due to bugs in Windows MoveFile() API. There is no legitimate way for us to distinguish between the two scenarios.
|
||||
static void g_move(const char * p_src,const char * p_dst,abort_callback & p_abort);
|
||||
//! Attempts to move file from one path to another; if the operation fails with a sharing violation error, keeps retrying (with short sleep period between retries) for specified amount of time.
|
||||
static void g_move_timeout(const char * p_src,const char * p_dst,double p_timeout,abort_callback & p_abort);
|
||||
@@ -559,12 +506,10 @@ namespace foobar2000_io
|
||||
// Presumes both source and destination belong to this filesystem.
|
||||
void copy_directory(const char * p_src, const char * p_dst, abort_callback & p_abort);
|
||||
|
||||
//! Moves/renames a file, overwriting the destination atomically if exists. \n
|
||||
//! Note that this function may throw exception_io_denied instead of exception_io_sharing_violation when the file is momentarily in use, due to bugs in Windows MoveFile() API. There is no legitimate way for us to distinguish between the two scenarios.
|
||||
//! Move file overwriting an existing one. Regular move() will fail if the file exists.
|
||||
void move_overwrite( const char * src, const char * dst, abort_callback & abort);
|
||||
//! Moves/renames a file, overwriting the destination atomically if exists. \n
|
||||
//! Meant to retain destination attributes if feasible. Otherwise identical to move_overwrite(). \n
|
||||
//! Note that this function may throw exception_io_denied instead of exception_io_sharing_violation when the file is momentarily in use, due to bugs in Windows MoveFile() API. There is no legitimate way for us to distinguish between the two scenarios.
|
||||
//! Same as move_overwrite(). \n
|
||||
//! This used to call win32 ReplaceFile() but that was pulled due to extreme stupidity of ReplaceFile() implementation.
|
||||
void replace_file(const char * src, const char * dst, abort_callback & abort);
|
||||
//! Create a directory, without throwing an exception if it already exists.
|
||||
//! @param didCreate bool flag indicating whether a new directory was created or not. \n
|
||||
@@ -598,12 +543,6 @@ namespace foobar2000_io
|
||||
//! See also: filesystem_transacted. \n
|
||||
//! In order to perform transacted operations, you must obtain a transacted filesystem explicitly, or get one passed down from a higher level context (example: in config_io_callback_v3).
|
||||
void rewrite_file( const char * path, abort_callback & abort, double opTimeout, std::function<void (file::ptr) > worker );
|
||||
//! Full directory rewrite helper that automatically does the right thing to ensure atomic update. \n
|
||||
//! If this is a transacted filesystem, a simple in-place rewrite is performed. \n
|
||||
//! If this is not a transacted filesystem, your content first goes to a temporary folder, which then replaces the original. \n
|
||||
//! It is encouraged to perform flushFileBuffers on all files accessed from within. \n
|
||||
//! See also: filesystem_transacted. \n
|
||||
//! In order to perform transacted operations, you must obtain a transacted filesystem explicitly, or get one passed down from a higher level context (example: in config_io_callback_v3).
|
||||
void rewrite_directory(const char * path, abort_callback & abort, double opTimeout, std::function<void(const char *) > worker);
|
||||
protected:
|
||||
static bool get_parent_helper(const char * path, char separator, pfc::string_base & out);
|
||||
@@ -628,16 +567,8 @@ namespace foobar2000_io
|
||||
class filesystem_v2 : public filesystem {
|
||||
FB2K_MAKE_SERVICE_INTERFACE( filesystem_v2, filesystem )
|
||||
public:
|
||||
//! Moves/renames a file, overwriting the destination atomically if exists. \n
|
||||
//! Note that this function may throw exception_io_denied instead of exception_io_sharing_violation when the file is momentarily in use, due to bugs in Windows MoveFile() API. There is no legitimate way for us to distinguish between the two scenarios.
|
||||
virtual void move_overwrite(const char * src, const char * dst, abort_callback & abort) = 0;
|
||||
//! Moves/renames a file, overwriting the destination atomically if exists. \n
|
||||
//! Meant to retain destination attributes if feasible. Otherwise identical to move_overwrite(). \n
|
||||
//! Note that this function may throw exception_io_denied instead of exception_io_sharing_violation when the file is momentarily in use, due to bugs in Windows MoveFile() API. There is no legitimate way for us to distinguish between the two scenarios.
|
||||
virtual void replace_file(const char * src, const char * dst, abort_callback & abort);
|
||||
//! Create a directory, without throwing an exception if it already exists.
|
||||
//! @param didCreate bool flag indicating whether a new directory was created or not. \n
|
||||
//! This should be a retval, but because it's messy to obtain this information with certain APIs, the caller can opt out of receiving this information,.
|
||||
virtual void make_directory(const char * path, abort_callback & abort, bool * didCreate = nullptr) = 0;
|
||||
virtual bool directory_exists(const char * path, abort_callback & abort) = 0;
|
||||
virtual bool file_exists(const char * path, abort_callback & abort) = 0;
|
||||
@@ -678,9 +609,61 @@ namespace foobar2000_io
|
||||
void sort() {m_data.sort_t(sortfunc);}
|
||||
};
|
||||
|
||||
class archive;
|
||||
|
||||
class NOVTABLE archive_callback : public abort_callback {
|
||||
public:
|
||||
virtual bool on_entry(archive * owner,const char * url,const t_filestats & p_stats,const service_ptr_t<file> & p_reader) = 0;
|
||||
};
|
||||
|
||||
//! Interface for archive reader services. When implementing, derive from archive_impl rather than from deriving from archive directly.
|
||||
class NOVTABLE archive : public filesystem {
|
||||
public:
|
||||
virtual void archive_list(const char * p_path,const service_ptr_t<file> & p_reader,archive_callback & p_callback,bool p_want_readers) = 0;
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE(archive,filesystem);
|
||||
};
|
||||
|
||||
//! Root class for archive implementations. Derive from this instead of from archive directly.
|
||||
class NOVTABLE archive_impl : public archive {
|
||||
private:
|
||||
//do not override these
|
||||
bool get_canonical_path(const char * path,pfc::string_base & out);
|
||||
bool is_our_path(const char * path);
|
||||
bool get_display_path(const char * path,pfc::string_base & out);
|
||||
void remove(const char * path,abort_callback & p_abort);
|
||||
void move(const char * src,const char * dst,abort_callback & p_abort);
|
||||
bool is_remote(const char * src);
|
||||
bool relative_path_create(const char * file_path,const char * playlist_path,pfc::string_base & out);
|
||||
bool relative_path_parse(const char * relative_path,const char * playlist_path,pfc::string_base & out);
|
||||
void open(service_ptr_t<file> & p_out,const char * path, t_open_mode mode,abort_callback & p_abort);
|
||||
void create_directory(const char * path,abort_callback &);
|
||||
void list_directory(const char * p_path,directory_callback & p_out,abort_callback & p_abort);
|
||||
void get_stats(const char * p_path,t_filestats & p_stats,bool & p_is_writeable,abort_callback & p_abort);
|
||||
protected:
|
||||
//override these
|
||||
virtual const char * get_archive_type()=0;//eg. "zip", must be lowercase
|
||||
virtual t_filestats get_stats_in_archive(const char * p_archive,const char * p_file,abort_callback & p_abort) = 0;
|
||||
virtual void open_archive(service_ptr_t<file> & p_out,const char * archive,const char * file, abort_callback & p_abort)=0;//opens for reading
|
||||
public:
|
||||
//override these
|
||||
virtual void archive_list(const char * path,const service_ptr_t<file> & p_reader,archive_callback & p_out,bool p_want_readers)=0;
|
||||
|
||||
|
||||
static bool g_is_unpack_path(const char * path);
|
||||
static bool g_parse_unpack_path(const char * path,pfc::string_base & archive,pfc::string_base & file);
|
||||
static bool g_parse_unpack_path_ex(const char * path,pfc::string_base & archive,pfc::string_base & file, pfc::string_base & type);
|
||||
static void g_make_unpack_path(pfc::string_base & path,const char * archive,const char * file,const char * type);
|
||||
void make_unpack_path(pfc::string_base & path,const char * archive,const char * file);
|
||||
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class archive_factory_t : public service_factory_single_t<T> {};
|
||||
|
||||
|
||||
t_filetimestamp filetimestamp_from_system_timer();
|
||||
t_filetimestamp import_DOS_time(uint32_t);
|
||||
|
||||
#ifdef _WIN32
|
||||
inline t_filetimestamp import_filetimestamp(FILETIME ft) {
|
||||
@@ -776,8 +759,6 @@ namespace foobar2000_io
|
||||
bool is_native_filesystem( const char * p_fspath );
|
||||
bool extract_native_path_ex(const char * p_fspath, pfc::string_base & p_native);//prepends \\?\ where needed
|
||||
|
||||
bool extract_native_path_archive_aware( const char * fspatch, pfc::string_base & out );
|
||||
|
||||
template<typename T>
|
||||
pfc::string getPathDisplay(const T& source) {
|
||||
const char * c = pfc::stringToPtr(source);
|
||||
@@ -802,7 +783,6 @@ namespace foobar2000_io
|
||||
void substituteProtocol(pfc::string_base & out, const char * fullString, const char * protocolName);
|
||||
|
||||
bool matchContentType_MP3( const char * fullString);
|
||||
bool matchContentType_MP4audio( const char * fullString);
|
||||
bool matchContentType_MP4( const char * fullString);
|
||||
bool matchContentType_Ogg( const char * fullString);
|
||||
bool matchContentType_Opus( const char * fullString);
|
||||
@@ -814,22 +794,6 @@ namespace foobar2000_io
|
||||
const char * contentTypeFromExtension( const char * ext );
|
||||
|
||||
void purgeOldFiles(const char * directory, t_filetimestamp period, abort_callback & abort);
|
||||
|
||||
//! \since 1.6
|
||||
class read_ahead_tools : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(read_ahead_tools);
|
||||
public:
|
||||
//! Turn any file object into asynchronous read-ahead-buffered file.
|
||||
//! @param f File object to wrap. Do not call this object's method after a successful call to add_read_ahead; new file object takes over the ownership of it.
|
||||
//! @param size Requested read-ahead bytes. Pass 0 to use user settings for local/remote playback.
|
||||
virtual file::ptr add_read_ahead(file::ptr f, size_t size, abort_callback & aborter) = 0;
|
||||
|
||||
//! A helper method to use prior to opening decoders. \n
|
||||
//! May open the file if needed or leave it blank for the decoder to open.
|
||||
//! @param f File object to open if needed (buffering mandated by user settings). May be valid or null prior to call. May be valid or null (no buffering) after call.
|
||||
//! @param path Path to open. May be null if f is not null. At least one of f and path must be valid prior to call.
|
||||
virtual void open_file_helper(file::ptr & f, const char * path, abort_callback & aborter) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
using namespace foobar2000_io;
|
||||
|
||||
@@ -244,6 +244,8 @@ namespace foobar2000_io {
|
||||
}
|
||||
|
||||
DWORD winVolumeFlags( const char * fb2kPath ) {
|
||||
PFC_ASSERT( matchProtocol( fb2kPath, "file" ) );
|
||||
|
||||
PFC_ASSERT(matchProtocol(fb2kPath, "file"));
|
||||
pfc::string8 native;
|
||||
if (!filesystem::g_get_native_path(fb2kPath, native)) throw pfc::exception_invalid_params();
|
||||
|
||||
@@ -470,20 +470,21 @@ FB2K_STREAM_READER_OVERLOAD(bool) {
|
||||
template<bool BE = false>
|
||||
class stream_writer_formatter_simple : public stream_writer_formatter<BE> {
|
||||
public:
|
||||
stream_writer_formatter_simple() : stream_writer_formatter<BE>(_m_stream,fb2k::noAbort), m_buffer(_m_stream.m_buffer) {}
|
||||
stream_writer_formatter_simple() : stream_writer_formatter<BE>(_m_stream,_m_abort), m_buffer(_m_stream.m_buffer) {}
|
||||
|
||||
typedef stream_writer_buffer_simple::t_buffer t_buffer;
|
||||
t_buffer & m_buffer;
|
||||
private:
|
||||
stream_writer_buffer_simple _m_stream;
|
||||
abort_callback_dummy _m_abort;
|
||||
};
|
||||
|
||||
template<bool BE = false>
|
||||
class stream_reader_formatter_simple_ref : public stream_reader_formatter<BE> {
|
||||
public:
|
||||
stream_reader_formatter_simple_ref(const void * source, t_size sourceSize) : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort), _m_stream(source,sourceSize) {}
|
||||
template<typename TSource> stream_reader_formatter_simple_ref(const TSource& source) : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort), _m_stream(source) {}
|
||||
stream_reader_formatter_simple_ref() : stream_reader_formatter<BE>(_m_stream,fb2k::noAbort) {}
|
||||
stream_reader_formatter_simple_ref(const void * source, t_size sourceSize) : stream_reader_formatter<BE>(_m_stream,_m_abort), _m_stream(source,sourceSize) {}
|
||||
template<typename TSource> stream_reader_formatter_simple_ref(const TSource& source) : stream_reader_formatter<BE>(_m_stream,_m_abort), _m_stream(source) {}
|
||||
stream_reader_formatter_simple_ref() : stream_reader_formatter<BE>(_m_stream,_m_abort) {}
|
||||
|
||||
void set_data(const void * source, t_size sourceSize) {_m_stream.set_data(source,sourceSize);}
|
||||
template<typename TSource> void set_data(const TSource & source) {_m_stream.set_data(source);}
|
||||
@@ -494,6 +495,7 @@ public:
|
||||
const void * get_ptr_() const {return _m_stream.get_ptr_();}
|
||||
private:
|
||||
stream_reader_memblock_ref _m_stream;
|
||||
abort_callback_dummy _m_abort;
|
||||
};
|
||||
|
||||
template<bool BE = false>
|
||||
|
||||
@@ -38,7 +38,5 @@ public:
|
||||
virtual bool is_our_path( const char * path ) = 0;
|
||||
};
|
||||
|
||||
// Since 1.5, transacted filesystem is no longer supported
|
||||
// as it adds extra complexity without actually solving any problems.
|
||||
// Even Microsoft recommends not to use this API.
|
||||
#define FB2K_SUPPORT_TRANSACTED_FILESYSTEM 0
|
||||
// Defined but not actually provided for MS Store target - because it plainly doesn't work there
|
||||
#define FB2K_SUPPORT_TRANSACTED_FILESYSTEM (!FB2K_TARGET_MICROSOFT_STORE)
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef UNICODE
|
||||
#error Only UNICODE environment supported.
|
||||
#endif
|
||||
|
||||
|
||||
#define FOOBAR2000_DESKTOP
|
||||
#define FOOBAR2000_DESKTOP_WINDOWS
|
||||
#define FOOBAR2000_DESKTOP_WINDOWS_OR_BOOM
|
||||
|
||||
// Set target versions to Windows XP as that's what foobar2000 supports, unless overridden before #including us
|
||||
#if !defined(_WIN32_WINNT) && !defined(WINVER)
|
||||
#define _WIN32_WINNT 0x501
|
||||
#define WINVER 0x501
|
||||
#endif
|
||||
|
||||
#define FOOBAR2000_HAVE_FILE_FORMAT_SANITIZER
|
||||
#define FOOBAR2000_HAVE_CHAPTERIZER
|
||||
#define FOOBAR2000_HAVE_ALBUM_ART
|
||||
#define FOOBAR2000_DECLARE_FILE_TYPES
|
||||
#define FOOBAR2000_HAVE_DSP
|
||||
#define FOOBAR2000_HAVE_CONSOLE
|
||||
#define FOOBAR2000_INTERACTIVE
|
||||
#define FOOBAR2000_WINAPI_CLASSIC
|
||||
#define FOOBAR2000_HAVE_METADB
|
||||
@@ -7,13 +7,12 @@
|
||||
|
||||
// #define FOOBAR2000_TARGET_VERSION 75 // 0.9.6
|
||||
// #define FOOBAR2000_TARGET_VERSION 76 // 1.0
|
||||
// #define FOOBAR2000_TARGET_VERSION 77 // 1.1, 1.2
|
||||
// #define FOOBAR2000_TARGET_VERSION 78 // 1.3
|
||||
#define FOOBAR2000_TARGET_VERSION 79 // 1.4
|
||||
// #define FOOBAR2000_TARGET_VERSION 80 // 1.5, 1.6
|
||||
// #define FOOBAR2000_TARGET_VERSION 77 // 1.1
|
||||
#define FOOBAR2000_TARGET_VERSION 78 // 1.3
|
||||
// #define FOOBAR2000_TARGET_VERSION 79 // 1.4
|
||||
|
||||
// Use this to determine what foobar2000 SDK version is in use, undefined for releases older than 2018
|
||||
#define FOOBAR2000_SDK_VERSION 20210223
|
||||
#define FOOBAR2000_SDK_VERSION 20190617
|
||||
|
||||
|
||||
#include "foobar2000-pfc.h"
|
||||
@@ -34,8 +33,8 @@ typedef const char * pcchar;
|
||||
#include "core_api.h"
|
||||
#include "service.h"
|
||||
#include "service_impl.h"
|
||||
#include "service_factory.h"
|
||||
#include "service_by_guid.h"
|
||||
#include "service_compat.h"
|
||||
|
||||
#include "completion_notify.h"
|
||||
#include "abort_callback.h"
|
||||
@@ -44,7 +43,6 @@ typedef const char * pcchar;
|
||||
#include "coreversion.h"
|
||||
#include "filesystem.h"
|
||||
#include "filesystem_transacted.h"
|
||||
#include "archive.h"
|
||||
#include "audio_chunk.h"
|
||||
#include "cfg_var.h"
|
||||
#include "mem_block_container.h"
|
||||
@@ -55,7 +53,6 @@ typedef const char * pcchar;
|
||||
#include "hasher_md5.h"
|
||||
#include "metadb_handle.h"
|
||||
#include "metadb.h"
|
||||
#include "file_info_filter.h"
|
||||
#include "console.h"
|
||||
#include "dsp.h"
|
||||
#include "dsp_manager.h"
|
||||
@@ -124,7 +121,5 @@ typedef const char * pcchar;
|
||||
#include "commonObjects.h"
|
||||
|
||||
#include "file_lock_manager.h"
|
||||
#include "imageLoaderLite.h"
|
||||
#include "imageViewer.h"
|
||||
|
||||
#endif //_FOOBAR2000_H_
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{E8091321-D79D-4575-86EF-064EA1A4A20D}</ProjectGuid>
|
||||
<RootNamespace>foobar2000_SDK</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>7.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
@@ -20,13 +21,13 @@
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v141_xp</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<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>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -44,6 +45,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>foobar2000.h</PrecompiledHeaderFile>
|
||||
@@ -53,6 +55,7 @@
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@@ -65,6 +68,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>
|
||||
@@ -75,12 +79,11 @@
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions>/d2notypeopt %(AdditionalOptions)</AdditionalOptions>
|
||||
<TreatSpecificWarningsAsErrors>4715</TreatSpecificWarningsAsErrors>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@@ -96,7 +99,6 @@
|
||||
<ClInclude Include="album_art.h" />
|
||||
<ClInclude Include="album_art_helpers.h" />
|
||||
<ClInclude Include="app_close_blocker.h" />
|
||||
<ClInclude Include="archive.h" />
|
||||
<ClInclude Include="audio_chunk.h" />
|
||||
<ClInclude Include="audio_chunk_impl.h" />
|
||||
<ClInclude Include="audio_postprocessor.h" />
|
||||
@@ -124,8 +126,6 @@
|
||||
<ClInclude Include="filesystem_transacted.h" />
|
||||
<ClInclude Include="file_format_sanitizer.h" />
|
||||
<ClInclude Include="file_info.h" />
|
||||
<ClInclude Include="file_info_filter.h" />
|
||||
<ClInclude Include="file_info_filter_impl.h" />
|
||||
<ClInclude Include="file_info_impl.h" />
|
||||
<ClInclude Include="file_lock_manager.h" />
|
||||
<ClInclude Include="file_operation_callback.h" />
|
||||
@@ -140,8 +140,6 @@
|
||||
<ClInclude Include="hasher_md5.h" />
|
||||
<ClInclude Include="http_client.h" />
|
||||
<ClInclude Include="icon_remap.h" />
|
||||
<ClInclude Include="imageLoaderLite.h" />
|
||||
<ClInclude Include="imageViewer.h" />
|
||||
<ClInclude Include="info_lookup_handler.h" />
|
||||
<ClInclude Include="initquit.h" />
|
||||
<ClInclude Include="input.h" />
|
||||
@@ -175,18 +173,16 @@
|
||||
<ClInclude Include="search_tools.h" />
|
||||
<ClInclude Include="service.h" />
|
||||
<ClInclude Include="service_by_guid.h" />
|
||||
<ClInclude Include="service_compat.h" />
|
||||
<ClInclude Include="service_factory.h" />
|
||||
<ClInclude Include="service_impl.h" />
|
||||
<ClInclude Include="system_time_keeper.h" />
|
||||
<ClInclude Include="tag_processor.h" />
|
||||
<ClInclude Include="threaded_process.h" />
|
||||
<ClInclude Include="titleformat.h" />
|
||||
<ClInclude Include="tracks.h" />
|
||||
<ClInclude Include="track_property.h" />
|
||||
<ClInclude Include="ui.h" />
|
||||
<ClInclude Include="ui_edit_context.h" />
|
||||
<ClInclude Include="ui_element.h" />
|
||||
<ClInclude Include="ui_element_typable_window_manager.h" />
|
||||
<ClInclude Include="unpack.h" />
|
||||
<ClInclude Include="vis.h" />
|
||||
</ItemGroup>
|
||||
@@ -280,7 +276,6 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="titleformat.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="track_property.cpp" />
|
||||
<ClCompile Include="ui.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="ui_element.cpp" />
|
||||
|
||||
@@ -269,6 +269,9 @@
|
||||
<ClInclude Include="service_by_guid.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="service_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="foobar2000-pfc.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -281,30 +284,6 @@
|
||||
<ClInclude Include="file_lock_manager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="archive.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ui_element_typable_window_manager.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="service_compat.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="tracks.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="file_info_filter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="file_info_filter_impl.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="imageLoaderLite.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="imageViewer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="abort_callback.cpp">
|
||||
@@ -481,8 +460,5 @@
|
||||
<ClCompile Include="foosort.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="track_property.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -55,8 +55,6 @@ FOOGUIDDECL const GUID resampler_entry::class_guid = { 0x3feed4fc, 0xa400, 0x4a3
|
||||
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
FOOGUIDDECL const GUID resampler_manager::class_guid = { 0x9a7e5f01, 0x975b, 0x4373,{ 0xb8, 0x55, 0x3e, 0xf4, 0x16, 0x17, 0x43, 0x3 } };
|
||||
FOOGUIDDECL const GUID resampler_manager_v2::class_guid = { 0xfe4d7f2e, 0xb705, 0x48fa, { 0x89, 0xce, 0xd7, 0x34, 0xa6, 0x3b, 0x9f, 0x15 } };
|
||||
FOOGUIDDECL const GUID resampler_manager_v3::class_guid = { 0x262bfb95, 0x7f3c, 0x45c2, { 0xb0, 0x64, 0x9b, 0x6a, 0xd8, 0x3d, 0x34, 0xdd } };
|
||||
#endif
|
||||
|
||||
#endif // FOOBAR2000_HAVE_DSP
|
||||
@@ -99,10 +97,6 @@ FOOGUIDDECL const GUID packet_decoder_streamparse::class_guid =
|
||||
FOOGUIDDECL const GUID packet_decoder_entry::class_guid =
|
||||
{ 0x53006a71, 0xc03c, 0x4c38, { 0x82, 0x2f, 0x9a, 0x34, 0xa5, 0x65, 0x50, 0x95 } };
|
||||
|
||||
// {C20940DC-8632-414A-BCF5-415DB77CF9BA}
|
||||
FOOGUIDDECL const GUID packet_decoder_entry_v2::class_guid =
|
||||
{ 0xc20940dc, 0x8632, 0x414a, { 0xbc, 0xf5, 0x41, 0x5d, 0xb7, 0x7c, 0xf9, 0xba } };
|
||||
|
||||
// {D3BD5F53-A6D6-4346-991F-CF14DFAD2B3A}
|
||||
FOOGUIDDECL const GUID contextmenu_manager::class_guid =
|
||||
{ 0xd3bd5f53, 0xa6d6, 0x4346, { 0x99, 0x1f, 0xcf, 0x14, 0xdf, 0xad, 0x2b, 0x3a } };
|
||||
@@ -119,11 +113,6 @@ FOOGUIDDECL const GUID input_file_type_v2::class_guid =
|
||||
FOOGUIDDECL const GUID ui_control::class_guid =
|
||||
{ 0x2dc57ff7, 0x476d, 0x42f5, { 0xa0, 0x5a, 0x60, 0x49, 0x98, 0x96, 0x13, 0x4a } };
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 80
|
||||
FOOGUIDDECL const GUID ui_control_v2::class_guid = { 0x6fc5d5c3, 0x13aa, 0x4f7f, { 0x97, 0x8d, 0x89, 0x6b, 0x9b, 0x90, 0x4a, 0x5 } };
|
||||
#endif // FOOBAR2000_TARGET_VERSION >= 80
|
||||
|
||||
|
||||
// {392B88DE-50FC-43b0-9F03-2D79B071CAF6}
|
||||
FOOGUIDDECL const GUID ui_status_text_override::class_guid =
|
||||
{ 0x392b88de, 0x50fc, 0x43b0, { 0x9f, 0x3, 0x2d, 0x79, 0xb0, 0x71, 0xca, 0xf6 } };
|
||||
@@ -176,14 +165,6 @@ FOOGUIDDECL const GUID unpacker::class_guid =
|
||||
FOOGUIDDECL const GUID archive::class_guid =
|
||||
{ 0xec707440, 0xfa3e, 0x4d12, { 0x98, 0x76, 0xfc, 0x36, 0x9f, 0x4, 0xd4, 0xa4 } };
|
||||
|
||||
// {69897890-90CB-4F7D-9969-1A9DCC5D9DDB}
|
||||
FOOGUIDDECL const GUID archive_v2::class_guid =
|
||||
{ 0x69897890, 0x90cb, 0x4f7d, { 0x99, 0x69, 0x1a, 0x9d, 0xcc, 0x5d, 0x9d, 0xdb } };
|
||||
|
||||
// {8D3F8B2D-A866-4F1F-9A4F-FF23929ED6DA}
|
||||
FOOGUIDDECL const GUID archive_v3::class_guid =
|
||||
{ 0x8d3f8b2d, 0xa866, 0x4f1f, { 0x9a, 0x4f, 0xff, 0x23, 0x92, 0x9e, 0xd6, 0xda } };
|
||||
|
||||
// {B2F9FC40-3E55-4b23-A2C9-22BAAD8795B1}
|
||||
FOOGUIDDECL const GUID file::class_guid =
|
||||
{ 0xb2f9fc40, 0x3e55, 0x4b23, { 0xa2, 0xc9, 0x22, 0xba, 0xad, 0x87, 0x95, 0xb1 } };
|
||||
@@ -270,11 +251,6 @@ FOOGUIDDECL const GUID packet_decoder::owner_MP4_ALAC =
|
||||
FOOGUIDDECL const GUID packet_decoder::owner_MP4_AC3 =
|
||||
{ 0x62263199, 0x48c2, 0x4777, { 0xb3, 0x3a, 0x87, 0xed, 0x16, 0x1a, 0x16, 0x30 } };
|
||||
|
||||
// {AE6325BE-6C65-4B8F-B60D-5C803763BFB5}
|
||||
FOOGUIDDECL const GUID packet_decoder::owner_MP4_EAC3 =
|
||||
{ 0xae6325be, 0x6c65, 0x4b8f, { 0xb6, 0xd, 0x5c, 0x80, 0x37, 0x63, 0xbf, 0xb5 } };
|
||||
|
||||
|
||||
// {40017871-50A9-48b6-BF60-BD181A227F9B}
|
||||
FOOGUIDDECL const GUID packet_decoder::owner_MP4_AMR =
|
||||
{ 0x40017871, 0x50a9, 0x48b6, { 0xbf, 0x60, 0xbd, 0x18, 0x1a, 0x22, 0x7f, 0x9b } };
|
||||
@@ -347,14 +323,6 @@ FOOGUIDDECL const GUID packet_decoder::property_eventlogger =
|
||||
FOOGUIDDECL const GUID packet_decoder::property_mp3_delayless =
|
||||
{ 0x7ad5e747, 0xcbf3, 0x487d, { 0x88, 0x4f, 0xd9, 0x3c, 0xf8, 0xb8, 0x6e, 0xb0 } };
|
||||
|
||||
// {CC2741CF-B57F-43A7-83C9-01C8628E6FDC}
|
||||
FOOGUIDDECL const GUID packet_decoder::property_query_delay_samples =
|
||||
{ 0xcc2741cf, 0xb57f, 0x43a7, { 0x83, 0xc9, 0x1, 0xc8, 0x62, 0x8e, 0x6f, 0xdc } };
|
||||
|
||||
// {69DD756A-1396-4D69-A08D-82B34B033141}
|
||||
FOOGUIDDECL const GUID packet_decoder::property_query_mp4_use_elst =
|
||||
{ 0x69dd756a, 0x1396, 0x4d69, { 0xa0, 0x8d, 0x82, 0xb3, 0x4b, 0x3, 0x31, 0x41 } };
|
||||
|
||||
// {6F441057-1D18-4a58-9AC4-8F409CDA7DFD}
|
||||
FOOGUIDDECL const GUID standard_commands::guid_context_file_properties =
|
||||
{ 0x6f441057, 0x1d18, 0x4a58, { 0x9a, 0xc4, 0x8f, 0x40, 0x9c, 0xda, 0x7d, 0xfd } };
|
||||
@@ -634,7 +602,7 @@ FOOGUIDDECL const GUID standard_commands::guid_main_playlist_redo =
|
||||
|
||||
// {02D89A8A-5F7D-41c3-A215-6731D8621036}
|
||||
FOOGUIDDECL const GUID standard_commands::guid_main_show_console =
|
||||
{ 0x5b652d25, 0xce44, 0x4737, { 0x99, 0xbb, 0xa3, 0xcf, 0x2a, 0xeb, 0x35, 0xcc } };
|
||||
{ 0x2d89a8a, 0x5f7d, 0x41c3, { 0xa2, 0x15, 0x67, 0x31, 0xd8, 0x62, 0x10, 0x36 } };
|
||||
|
||||
// {E6970E91-33BE-4288-AC01-4B02F07B5D38}
|
||||
FOOGUIDDECL const GUID standard_commands::guid_main_play_cd =
|
||||
@@ -819,9 +787,6 @@ FOOGUIDDECL const GUID preferences_page::guid_tag_writing=
|
||||
FOOGUIDDECL const GUID preferences_page::guid_media_library=
|
||||
{ 0x2d269fa9, 0x6f78, 0x4cec, { 0x9f, 0x1f, 0xa, 0x17, 0x6e, 0xfc, 0xe7, 0x7a } };
|
||||
|
||||
FOOGUIDDECL const GUID preferences_page::guid_output = { 0xa9038870, 0xdc08, 0x431d, { 0x8c, 0x91, 0x3b, 0x4e, 0x41, 0xd2, 0x43, 0x6d } };
|
||||
FOOGUIDDECL const GUID preferences_page::guid_advanced = { 0x56eb56ab, 0xedfe, 0x4853, { 0x90, 0xf7, 0xc6, 0xb8, 0x34, 0xfa, 0x2f, 0x6b } };
|
||||
FOOGUIDDECL const GUID preferences_page::guid_components = { 0xe966267, 0x7dfb, 0x433b, { 0xa0, 0x7c, 0x3f, 0x8c, 0xdd, 0x31, 0xa2, 0x58 } };
|
||||
// {B8C5CEEA-A5F4-4278-AA2D-798E4403001F}
|
||||
FOOGUIDDECL const GUID library_viewer::class_guid=
|
||||
{ 0xb8c5ceea, 0xa5f4, 0x4278, { 0xaa, 0x2d, 0x79, 0x8e, 0x44, 0x3, 0x0, 0x1f } };
|
||||
@@ -836,9 +801,6 @@ FOOGUIDDECL const GUID input_decoder_v3::class_guid = { 0x953c71ed, 0xd3a2, 0x43
|
||||
FOOGUIDDECL const GUID input_decoder_v4::class_guid = { 0x7bbf10b5, 0x2064, 0x4da2,{ 0x8a, 0x72, 0xeb, 0x4c, 0xe9, 0xb3, 0xa0, 0xb4 } };
|
||||
FOOGUIDDECL const GUID input_params::seeking_expensive = { 0x10973582, 0x1aae, 0x4169, { 0xb9, 0x76, 0xb5, 0xbe, 0xf9, 0x4b, 0x7a, 0x71 } };
|
||||
FOOGUIDDECL const GUID input_params::set_preferred_sample_rate = { 0x7dc7f926, 0x9b9f, 0x4a73, { 0xa2, 0x37, 0x83, 0xe9, 0x93, 0x38, 0x41, 0x75 } };
|
||||
FOOGUIDDECL const GUID input_params::query_position = { 0xa9d79933, 0x5438, 0x480f, { 0x85, 0xf3, 0xb6, 0xb8, 0xee, 0x1e, 0xfa, 0xe9 } };
|
||||
FOOGUIDDECL const GUID input_params::continue_stream = { 0x673050df, 0x11f6, 0x4039, { 0x8b, 0x4, 0x6c, 0x31, 0x98, 0x91, 0x4b, 0x89 } };
|
||||
|
||||
|
||||
// {8E9BB1D4-A52B-4df6-A929-1AAE4075388A}
|
||||
FOOGUIDDECL const GUID input_info_reader::class_guid =
|
||||
@@ -856,23 +818,13 @@ FOOGUIDDECL const GUID input_entry::class_guid =
|
||||
FOOGUIDDECL const GUID input_entry_v2::class_guid =
|
||||
{ 0x7037df35, 0xd246, 0x4cdd,{ 0xb2, 0x24, 0x8, 0xcf, 0x44, 0x4c, 0xe4, 0xce } };
|
||||
|
||||
FOOGUIDDECL const GUID input_entry_v3::class_guid = { 0x7099fac8, 0x10ee, 0x4906, { 0xa3, 0x13, 0xb9, 0x8c, 0x69, 0x74, 0xe7, 0xb9 } };
|
||||
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
FOOGUIDDECL const GUID input_manager::class_guid = { 0x291fccfd, 0xe7de, 0x4e08,{ 0x8d, 0xef, 0x6b, 0xe5, 0xf8, 0x23, 0xc4, 0x9a } };
|
||||
FOOGUIDDECL const GUID input_manager_v2::class_guid = { 0x81b36d9e, 0x4fb6, 0x4788, { 0xa1, 0x2c, 0x40, 0xc0, 0x64, 0x82, 0xe5, 0xc6 } };
|
||||
FOOGUIDDECL const GUID input_manager_v3::class_guid = { 0xc1c414be, 0x74f, 0x41f0, { 0xb8, 0x7f, 0x29, 0x2e, 0xad, 0x50, 0xc0, 0x76 } };
|
||||
FOOGUIDDECL const GUID input_stream_selector::class_guid = { 0x2a511979, 0x2e57, 0x438a,{ 0x9a, 0x6e, 0x37, 0x3c, 0x25, 0x2, 0xd7, 0x63 } };
|
||||
FOOGUIDDECL const GUID input_stream_info_reader::class_guid = { 0x934f1361, 0x9c18, 0x497a,{ 0xb6, 0xef, 0xc0, 0x4e, 0xa1, 0x66, 0x3e, 0xab } };
|
||||
FOOGUIDDECL const GUID input_stream_info_reader_entry::class_guid = { 0xb53b66f2, 0x7cd3, 0x4ce2,{ 0xbf, 0x63, 0x7b, 0xcf, 0xcf, 0xa1, 0xb2, 0x8f } };
|
||||
FOOGUIDDECL const GUID input_stream_manipulator::class_guid = { 0xc258aa65, 0x961a, 0x4e8e,{ 0x83, 0x98, 0xcc, 0x88, 0xd2, 0xba, 0xe0, 0x5d } };
|
||||
FOOGUIDDECL const GUID input_stream_manipulator_callback::class_guid = { 0x88ce44b6, 0x8721, 0x440f,{ 0x9e, 0xab, 0xbd, 0xc6, 0x63, 0xed, 0xe1, 0x73 } };
|
||||
FOOGUIDDECL const GUID input_info_filter::class_guid = { 0xd64af9aa, 0x1c0d, 0x4dd9, { 0xa3, 0x49, 0x2, 0xa3, 0x77, 0xbd, 0x7a, 0xf2 } };
|
||||
FOOGUIDDECL const GUID input_info_filter_v2::class_guid = { 0x7a2023cb, 0xfb7c, 0x4f04, { 0xa5, 0x45, 0x7f, 0xbd, 0x52, 0x92, 0x31, 0x92 } };
|
||||
FOOGUIDDECL const GUID input_stream_info_filter::class_guid = { 0xfd056bba, 0xc6a1, 0x40b5, { 0xa6, 0x17, 0x37, 0xb9, 0x71, 0x34, 0xb4, 0xc6 } };
|
||||
FOOGUIDDECL const GUID input_playback_shim::class_guid = { 0xaf8aea76, 0x144f, 0x4770, { 0x8d, 0x84, 0x7, 0x27, 0x22, 0x8f, 0x29, 0x8 } };
|
||||
|
||||
FOOGUIDDECL const GUID preferences_page::guid_input_info_filter = { 0xf9de8ee1, 0x9a5b, 0x4bbd, { 0xb4, 0x62, 0xef, 0x7b, 0x3a, 0xa5, 0x0, 0x24 } };
|
||||
#endif
|
||||
|
||||
// {3296219B-EBA5-4c32-A193-C9BB174801DA}
|
||||
@@ -1094,8 +1046,6 @@ FOOGUIDDECL const GUID advconfig_entry::guid_branch_tools = { 0x35365484, 0xcc58
|
||||
FOOGUIDDECL const GUID advconfig_entry::guid_branch_playback = { 0xc48d430d, 0x112, 0x4922, { 0x97, 0x23, 0x28, 0x38, 0xc7, 0xd9, 0x7d, 0xd7 } };
|
||||
FOOGUIDDECL const GUID advconfig_entry::guid_branch_display = { 0x6c4bc1c8, 0xbaf4, 0x40c3, { 0x9d, 0xb1, 0x9, 0x50, 0x7f, 0xc, 0xc, 0xb9 } };
|
||||
FOOGUIDDECL const GUID advconfig_entry::guid_branch_debug = { 0xc375447d, 0x58e6, 0x4fd5, { 0xbd, 0xd8, 0x99, 0xfa, 0xef, 0x7b, 0x30, 0x9f } };
|
||||
FOOGUIDDECL const GUID advconfig_entry::guid_branch_tagging_general = { 0x1a7757de, 0x55bd, 0x4c25, { 0xb2, 0xf9, 0xc6, 0x3a, 0x85, 0x0, 0xba, 0xed } };
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1106,7 +1056,6 @@ FOOGUIDDECL const GUID preferences_branch_v2::class_guid = { 0x167ebeb9, 0x8334,
|
||||
|
||||
|
||||
|
||||
|
||||
FOOGUIDDECL const GUID advconfig_entry_enum::class_guid = { 0xb1451540, 0x98ec, 0x4d36, { 0x9f, 0x19, 0xe3, 0x10, 0xfb, 0xa7, 0xab, 0x5a } };
|
||||
|
||||
FOOGUIDDECL const GUID info_lookup_handler::class_guid = { 0x4fcfdab7, 0x55b5, 0x47d6, { 0xb1, 0x9d, 0xa4, 0xdc, 0x9f, 0xd7, 0x69, 0x4c } };
|
||||
@@ -1117,9 +1066,6 @@ FOOGUIDDECL const GUID completion_notify::class_guid = { 0xdf26d586, 0xf7ec, 0x4
|
||||
FOOGUIDDECL const GUID metadb_io_v2::class_guid = { 0x265c4ece, 0xffb2, 0x4299, { 0x91, 0xb5, 0x6c, 0xa6, 0x60, 0x3d, 0xa1, 0x53 } };
|
||||
|
||||
FOOGUIDDECL const GUID file_info_filter::class_guid = { 0x45d0b800, 0xde83, 0x4a77, { 0xad, 0x34, 0x3f, 0x84, 0x2d, 0x40, 0xe7, 0x95 } };
|
||||
FOOGUIDDECL const GUID file_info_filter_v2::class_guid = { 0x9bb4da92, 0xd334, 0x4f18, { 0x91, 0xc1, 0x91, 0x9f, 0xb2, 0xf4, 0x4f, 0x30 } };
|
||||
|
||||
|
||||
|
||||
FOOGUIDDECL const GUID metadb_hint_list::class_guid = { 0x719dc072, 0x8d4d, 0x4aa6, { 0xa6, 0xf3, 0x90, 0x73, 0x7, 0xe5, 0xbc, 0xee } };
|
||||
|
||||
@@ -1241,8 +1187,6 @@ FOOGUIDDECL const GUID library_file_move_manager::class_guid = { 0xc4cd4818, 0xe
|
||||
FOOGUIDDECL const GUID library_file_move_notify::class_guid = { 0xd8ae7613, 0x7577, 0x4192, { 0x8f, 0xa4, 0x2d, 0x8f, 0x7e, 0xc6, 0x6, 0x38 } };
|
||||
|
||||
FOOGUIDDECL const GUID library_meta_autocomplete::class_guid = { 0x4b976e34, 0xf05a, 0x4da4, { 0xad, 0x65, 0x71, 0x9c, 0xdf, 0xd, 0xed, 0xae } };
|
||||
FOOGUIDDECL const GUID library_meta_autocomplete_v2::class_guid = { 0xd60d585e, 0xb42e, 0x4a6e, { 0x8f, 0x24, 0x27, 0x50, 0x71, 0x1a, 0x87, 0x30 } };
|
||||
|
||||
|
||||
FOOGUIDDECL const GUID input_protocol_type::class_guid = { 0x6a03c4ee, 0xf87b, 0x49d7, { 0x81, 0xdb, 0x66, 0xb, 0xe8, 0xc1, 0x0, 0x7e } };
|
||||
|
||||
@@ -1263,7 +1207,6 @@ FOOGUIDDECL const GUID album_art_fallback::class_guid = { 0x45481581, 0x40b3, 0x
|
||||
FOOGUIDDECL const GUID preferences_page_callback::class_guid = { 0x3d26e08e, 0x861c, 0x4599, { 0x9c, 0x89, 0xaa, 0xa7, 0x19, 0xaf, 0x50, 0x70 } };
|
||||
FOOGUIDDECL const GUID preferences_page_instance::class_guid = { 0x6893a996, 0xa816, 0x49fe, { 0x82, 0xce, 0xc, 0xb8, 0x4, 0xa4, 0xcf, 0xec } };
|
||||
FOOGUIDDECL const GUID preferences_page_v3::class_guid = { 0xd6d0f741, 0x9f17, 0x4df8, { 0x9d, 0x5c, 0x87, 0xf2, 0x8b, 0x1f, 0xe, 0x64 } };
|
||||
FOOGUIDDECL const GUID preferences_page_v4::class_guid = { 0x76227dab, 0xc740, 0x4d49, { 0xa2, 0xe2, 0x50, 0x80, 0x13, 0xe, 0xf6, 0xba } };
|
||||
|
||||
FOOGUIDDECL const GUID advconfig_entry_string_v2::class_guid = { 0x2ec9b1fa, 0xe1e4, 0x42f0, { 0x87, 0x97, 0x4a, 0x63, 0x16, 0x94, 0x86, 0xbc } };
|
||||
FOOGUIDDECL const GUID advconfig_entry_checkbox_v2::class_guid = { 0xe29b37d0, 0xa957, 0x4a85, { 0x82, 0x40, 0x1e, 0x96, 0xc7, 0x29, 0xb6, 0x69 } };
|
||||
@@ -1298,7 +1241,6 @@ FOOGUIDDECL const GUID playback_stream_capture::class_guid = { 0x9423439e, 0x8cd
|
||||
|
||||
FOOGUIDDECL const GUID http_request::class_guid = { 0x48580056, 0x2c5f, 0x45a8, { 0xb8, 0x6e, 0x5, 0x83, 0x55, 0x3e, 0xaa, 0x4f } };
|
||||
FOOGUIDDECL const GUID http_request_post::class_guid = { 0xe254b804, 0xeac5, 0x4be0, { 0x99, 0x4d, 0x53, 0x1c, 0x17, 0xea, 0xfd, 0x37 } };
|
||||
FOOGUIDDECL const GUID http_request_post_v2::class_guid = { 0x95309335, 0xd301, 0x4946, { 0x99, 0x27, 0x2d, 0xb7, 0x9b, 0x22, 0x46, 0x9c } };
|
||||
FOOGUIDDECL const GUID http_client::class_guid = { 0x3b5ffe0c, 0xd75a, 0x491e, { 0xbb, 0x6f, 0x10, 0x3f, 0x73, 0x1e, 0x81, 0x84 } };
|
||||
FOOGUIDDECL const GUID http_reply::class_guid = { 0x7f02bf78, 0x5c98, 0x4d6d, { 0x83, 0x6b, 0xb7, 0x77, 0xa4, 0xa3, 0x3e, 0xe5 } };
|
||||
|
||||
@@ -1331,14 +1273,9 @@ FOOGUIDDECL const GUID track_property_provider_v3::class_guid = { 0xdbbce29c, 0x
|
||||
FOOGUIDDECL const GUID output::class_guid = { 0xb9632c4f, 0x596e, 0x43ee, { 0xb2, 0x14, 0x71, 0x4, 0x48, 0x4b, 0x65, 0xf1 } };
|
||||
FOOGUIDDECL const GUID output_entry::class_guid = { 0xe7480b4f, 0x4941, 0x4dd2, { 0xad, 0xbf, 0x96, 0x6c, 0x76, 0x63, 0x43, 0x92 } };
|
||||
FOOGUIDDECL const GUID output_entry_v2::class_guid = { 0xaacc17f3, 0xb7cc, 0x48c2, { 0x95, 0x6e, 0x52, 0xba, 0x72, 0x89, 0x42, 0xe5 } };
|
||||
FOOGUIDDECL const GUID output_entry_v3::class_guid = { 0xfa18060e, 0xfc84, 0x4f53, { 0xa8, 0xe3, 0x60, 0xc5, 0xf5, 0x22, 0x50, 0x7a } };
|
||||
FOOGUIDDECL const GUID volume_control::class_guid = { 0x39f9fc0c, 0x4dc9, 0x4a7a, { 0xb9, 0xad, 0x75, 0x8b, 0x78, 0x57, 0x78, 0xad } };
|
||||
FOOGUIDDECL const GUID output_v2::class_guid = { 0x4f679e4b, 0x79e0, 0x4fc9, { 0x90, 0x27, 0x55, 0x49, 0x85, 0x72, 0x26, 0xbf } };
|
||||
FOOGUIDDECL const GUID output_v3::class_guid = { 0x3b764d8e, 0x6c1c, 0x40bd, { 0x9a, 0x7e, 0xac, 0x3, 0x2a, 0x3e, 0x25, 0xf1 } };
|
||||
FOOGUIDDECL const GUID output_v4::class_guid = { 0x2c7a21a, 0xcc12, 0x48f3, { 0x89, 0x2e, 0xa7, 0x98, 0xb0, 0xc8, 0xaa, 0x49 } };
|
||||
FOOGUIDDECL const GUID output_v5::class_guid = { 0x735e6e3f, 0x95d8, 0x45ca, { 0xad, 0x1a, 0xca, 0x88, 0x1f, 0x1d, 0x5c, 0xfa } };
|
||||
|
||||
|
||||
|
||||
FOOGUIDDECL const GUID output_manager::class_guid = { 0x6cc5827e, 0x2c89, 0x42ff, { 0x83, 0x51, 0x76, 0xa9, 0x2e, 0x2f, 0x34, 0x50 } };
|
||||
FOOGUIDDECL const GUID output_manager_v2::class_guid = { 0xcc8aa352, 0x7af1, 0x41d2, { 0x94, 0x7e, 0xa2, 0x65, 0x17, 0x5b, 0x68, 0x96 } };
|
||||
@@ -1357,6 +1294,7 @@ FOOGUIDDECL const GUID ui_element_replace_dialog_notify::class_guid = { 0x95f925
|
||||
FOOGUIDDECL const GUID ui_element_popup_host::class_guid = { 0xfcc381e9, 0xe527, 0x4887,{ 0xae, 0x63, 0x27, 0xc0, 0x3f, 0x4, 0xd, 0x1 } };
|
||||
FOOGUIDDECL const GUID ui_element_popup_host_callback::class_guid = { 0x2993a043, 0x2e70, 0x4d8f,{ 0x81, 0xb, 0x41, 0x3, 0x37, 0x73, 0x97, 0xcd } };
|
||||
FOOGUIDDECL const GUID ui_element_config::class_guid = { 0xd34bba46, 0x1bad, 0x4547,{ 0xba, 0xb4, 0x17, 0xe2, 0x44, 0xd5, 0xeb, 0x94 } };
|
||||
FOOGUIDDECL const GUID ui_element_typable_window_manager::class_guid = { 0xbaa99ee2, 0xf770, 0x4981,{ 0x9e, 0x50, 0xf3, 0x4c, 0x5c, 0x6d, 0x98, 0x81 } };
|
||||
FOOGUIDDECL const GUID ui_element_instance_callback_v3::class_guid = { 0x6d15c0c6, 0x90b6, 0x4c7e,{ 0xbf, 0x39, 0xe9, 0x39, 0xf2, 0xdf, 0x9b, 0x91 } };
|
||||
FOOGUIDDECL const GUID ui_element_popup_host_v2::class_guid = { 0x8caac11e, 0x52b6, 0x47f7,{ 0x97, 0xc9, 0x2c, 0x87, 0xdb, 0xdb, 0x2e, 0x5b } };
|
||||
|
||||
@@ -1382,32 +1320,5 @@ FOOGUIDDECL const GUID playlist_incoming_item_filter_v4::class_guid = { 0x9bd438
|
||||
|
||||
FOOGUIDDECL const GUID file_lock::class_guid = { 0xb2f0b2f8, 0x1ccf, 0x438e, { 0xb9, 0x75, 0x9f, 0x5b, 0x75, 0x5a, 0xa3, 0x2e } };
|
||||
FOOGUIDDECL const GUID file_lock_manager::class_guid = { 0xa808fe53, 0xd36, 0x42fb, { 0xac, 0x2, 0x6a, 0xc8, 0x9c, 0xcb, 0x24, 0xbe } };
|
||||
FOOGUIDDECL const GUID file_lock_manager_v2::class_guid = { 0x30c07c6a, 0xde51, 0x4094, { 0x82, 0xe6, 0xf1, 0x57, 0xd1, 0xb2, 0x3a, 0xe8 } };
|
||||
FOOGUIDDECL const GUID file_lock_interrupt::class_guid = { 0x73ebabc8, 0xfe66, 0x4dae, { 0x95, 0x90, 0x3d, 0xa3, 0x9f, 0x17, 0x19, 0x15 } };
|
||||
|
||||
FOOGUIDDECL const GUID track_property_provider_v4::class_guid = { 0x707abb57, 0x35f7, 0x41c3, { 0xac, 0x43, 0xc9, 0xb6, 0xc, 0xc6, 0xa9, 0xae } };
|
||||
|
||||
|
||||
FOOGUIDDECL const GUID album_art_extractor_v2::class_guid = { 0x3aa31001, 0xaf5b, 0x497a, { 0xbd, 0xdc, 0xa9, 0x3f, 0x23, 0xb2, 0x1b, 0xc2 } };
|
||||
FOOGUIDDECL const GUID album_art_editor_v2::class_guid = { 0xf16827d3, 0xca20, 0x44fe, { 0x94, 0xe0, 0x56, 0xd7, 0x2d, 0x35, 0x81, 0x6 } };
|
||||
|
||||
|
||||
FOOGUIDDECL const GUID replaygain_scanner_config::class_guid = { 0x210970e1, 0xa478, 0x4d76, { 0xa5, 0x7c, 0x95, 0x9, 0xae, 0x82, 0xae, 0x41 } };
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 80
|
||||
FOOGUIDDECL const GUID metadb_io_v4::class_guid = { 0x6ec07034, 0xd5c2, 0x4fb5, { 0xb7, 0x59, 0x7d, 0x12, 0x4f, 0xa7, 0x27, 0xf4 } };
|
||||
FOOGUIDDECL const GUID popup_message_v3::class_guid = { 0xef3c83fd, 0x1144, 0x4edb, { 0xb8, 0x43, 0xcf, 0xba, 0xc6, 0xe5, 0xe1, 0x8b } };
|
||||
#endif // FOOBAR2000_TARGET_VERSION >= 80
|
||||
|
||||
FOOGUIDDECL const GUID file_lowLevelIO::class_guid = { 0xbcacb272, 0x8e6c, 0x4d23, { 0x91, 0x9a, 0xe, 0xaa, 0x55, 0x2e, 0xc9, 0x70 } };
|
||||
FOOGUIDDECL const GUID file_lowLevelIO::guid_flushFileBuffers = { 0xace5356b, 0x8c72, 0x408a, { 0x8e, 0x32, 0x78, 0x1e, 0x7d, 0x7f, 0xe9, 0x97 } };
|
||||
FOOGUIDDECL const GUID file_lowLevelIO::guid_getFileTimes = { 0xb5a3cd80, 0x23ae, 0x4c51, { 0x83, 0x3b, 0xa5, 0x98, 0x6b, 0xe1, 0xec, 0x59 } };
|
||||
FOOGUIDDECL const GUID file_lowLevelIO::guid_setFileTimes = { 0x46501e0d, 0x644d, 0x4d00, { 0xaf, 0x8c, 0xc5, 0xc1, 0xb, 0x34, 0xae, 0x37 } };
|
||||
|
||||
|
||||
FOOGUIDDECL const GUID async_task_manager::class_guid = { 0xea055f49, 0x7c6d, 0x4695, { 0x8c, 0xf, 0xeb, 0xbd, 0x92, 0xdb, 0xa7, 0xa7 } };
|
||||
|
||||
FOOGUIDDECL const GUID read_ahead_tools::class_guid = { 0x709671bf, 0x449a, 0x4dc8, { 0x9f, 0xcf, 0x84, 0xb3, 0xbc, 0xec, 0x98, 0x4d } };
|
||||
|
||||
FOOGUIDDECL const GUID fb2k::imageLoaderLite::class_guid = { 0xbe06ead9, 0x1c9, 0x42e0, { 0x9f, 0x9f, 0x12, 0xb2, 0xde, 0x95, 0xca, 0x96 } };
|
||||
FOOGUIDDECL const GUID fb2k::imageViewer::class_guid = { 0xdbdaaa24, 0x2f90, 0x426c, { 0x86, 0x3, 0x1c, 0x5a, 0xe5, 0xf9, 0x82, 0x21 } };
|
||||
|
||||
@@ -14,13 +14,6 @@ struct hasher_md5_result {
|
||||
static hasher_md5_result null() {hasher_md5_result h = {}; return h;}
|
||||
};
|
||||
|
||||
FB2K_STREAM_READER_OVERLOAD(hasher_md5_result) {
|
||||
stream.read_raw(&value, sizeof(value)); return stream;
|
||||
}
|
||||
FB2K_STREAM_WRITER_OVERLOAD(hasher_md5_result) {
|
||||
stream.write_raw(&value, sizeof(value)); return stream;
|
||||
}
|
||||
|
||||
inline bool operator==(const hasher_md5_result & p_item1,const hasher_md5_result & p_item2) {return memcmp(&p_item1,&p_item2,sizeof(hasher_md5_result)) == 0;}
|
||||
inline bool operator!=(const hasher_md5_result & p_item1,const hasher_md5_result & p_item2) {return memcmp(&p_item1,&p_item2,sizeof(hasher_md5_result)) != 0;}
|
||||
|
||||
@@ -81,7 +74,7 @@ private:
|
||||
template<bool isBigEndian = false>
|
||||
class stream_formatter_hasher_md5 : public stream_writer_formatter<isBigEndian> {
|
||||
public:
|
||||
stream_formatter_hasher_md5() : stream_writer_formatter<isBigEndian>(_m_stream,fb2k::noAbort) {}
|
||||
stream_formatter_hasher_md5() : stream_writer_formatter<isBigEndian>(_m_stream,_m_abort) {}
|
||||
|
||||
hasher_md5_result result() const {
|
||||
return _m_stream.result();
|
||||
@@ -90,5 +83,6 @@ public:
|
||||
return hasher_md5::guid_from_result(result());
|
||||
}
|
||||
private:
|
||||
abort_callback_dummy _m_abort;
|
||||
stream_writer_hasher_md5 _m_stream;
|
||||
};
|
||||
|
||||
@@ -41,13 +41,6 @@ public:
|
||||
void add_post_data(const char * name, const char * value) { add_post_data(name, value, strlen(value), "", ""); }
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class NOVTABLE http_request_post_v2 : public http_request_post {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(http_request_post_v2, http_request_post);
|
||||
public:
|
||||
virtual void set_post_data(const void* blob, size_t bytes, const char* contentType) = 0;
|
||||
};
|
||||
|
||||
class NOVTABLE http_client : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(http_client)
|
||||
public:
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace Gdiplus {
|
||||
class Image;
|
||||
}
|
||||
#endif
|
||||
namespace fb2k {
|
||||
#ifdef _WIN32
|
||||
typedef Gdiplus::Image * nativeImage_t;
|
||||
#else
|
||||
typedef void * nativeImage_t;
|
||||
#endif
|
||||
|
||||
struct imageInfo_t {
|
||||
uint32_t width, height, bitDepth;
|
||||
bool haveAlpha;
|
||||
const char * formatName;
|
||||
const char * mime;
|
||||
};
|
||||
|
||||
//! \since 1.6
|
||||
//! Interface to common image loader routines that turn a bytestream into a image that can be drawn in a window. \n
|
||||
//! Windows: Using imageLoaderLite methods initializes gdiplus if necessary, leaving gdiplus initialized for the rest of app lifetime. \n
|
||||
//! If your component supports running on foobar2000 older than 1.6, use tryGet() to gracefully fall back to your own image loader: \n
|
||||
//! auto api = fb2k::imageLoaderLite::tryGet(); if (api.is_valid()) { do stuff with api; } else { use fallbacks; }
|
||||
class imageLoaderLite : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(imageLoaderLite);
|
||||
public:
|
||||
//! Throws excpetions on failure, returns valid image otherwise.\n
|
||||
//! Caller takes ownership of the returned object. \n
|
||||
//! @param outInfo Optional struct to receive information about the loaded image.
|
||||
virtual nativeImage_t load(const void * data, size_t bytes, imageInfo_t * outInfo = nullptr, abort_callback & aborter = fb2k::noAbort) = 0;
|
||||
|
||||
//! Parses the image data just enough to hand over basic info about what's inside. \n
|
||||
//! Much faster than load(). \n
|
||||
//! Supports all formats recognized by load().
|
||||
virtual imageInfo_t getInfo(const void * data, size_t bytes, abort_callback & aborter = fb2k::noAbort) = 0;
|
||||
|
||||
//! Helper - made virtual so it can be possibly specialized in the future
|
||||
virtual nativeImage_t load(album_art_data_ptr data, imageInfo_t * outInfo = nullptr, abort_callback & aborter = fb2k::noAbort) {
|
||||
return load(data->get_ptr(), data->get_size(), outInfo, aborter);
|
||||
}
|
||||
//! Helper - made virtual so it can be possibly specialized in the future
|
||||
virtual imageInfo_t getInfo(album_art_data_ptr data, abort_callback & aborter = fb2k::noAbort) {
|
||||
return getInfo(data->get_ptr(), data->get_size(), aborter);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define FB2K_GETOPENFILENAME_PICTUREFILES "Picture files|*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.webp"
|
||||
#define FB2K_GETOPENFILENAME_PICTUREFILES_ALL FB2K_GETOPENFILENAME_PICTUREFILES "|All files|*.*"
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace fb2k {
|
||||
//! \since 1.6.2
|
||||
class imageViewer : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(imageViewer);
|
||||
public:
|
||||
//! Spawns an image viewer window, showing the specified picture already loaded into application memory.
|
||||
virtual void show(HWND parent, fb2k::memBlockRef data) = 0;
|
||||
//! Spawns an image viewer window, showing album art from the specified list of items.
|
||||
//! @param aaType Type of picture to load, front cover, back cover or other.
|
||||
//! @param pageno Reserved for future use, set to 0.
|
||||
virtual void load_and_show(HWND parent, metadb_handle_list_cref items, const GUID & aaType, unsigned pageno = 0) = 0;
|
||||
};
|
||||
}
|
||||
@@ -24,7 +24,6 @@ namespace init_stages {
|
||||
before_config_read = 10,
|
||||
after_config_read = 20,
|
||||
before_library_init = 30,
|
||||
// Since foobar2000 v2.0, after_library_init is fired OUT OF ORDER with the rest, after ASYNCHRONOUS library init has completed.
|
||||
after_library_init = 40,
|
||||
before_ui_init = 50,
|
||||
after_ui_init = 60,
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#include "foobar2000.h" // PCH
|
||||
#ifdef FOOBAR2000_MODERN
|
||||
#include "foobar2000-input.h"
|
||||
#include <pfc/list.h>
|
||||
#include <pfc/timers.h>
|
||||
#endif
|
||||
#include <exception>
|
||||
#include "album_art.h"
|
||||
#include "file_info_impl.h"
|
||||
|
||||
service_ptr input_entry::open(const GUID & whatFor, file::ptr hint, const char * path, event_logger::ptr logger, abort_callback & aborter) {
|
||||
#include "foobar2000.h"
|
||||
|
||||
service_ptr input_entry::open(const GUID & whatFor, file::ptr hint, const char * path, abort_callback & aborter) {
|
||||
if (whatFor == input_decoder::class_guid) {
|
||||
input_decoder::ptr obj;
|
||||
open(obj, hint, path, aborter);
|
||||
return obj;
|
||||
}
|
||||
if (whatFor == input_info_reader::class_guid) {
|
||||
input_info_reader::ptr obj;
|
||||
open(obj, hint, path, aborter);
|
||||
return obj;
|
||||
}
|
||||
if (whatFor == input_info_writer::class_guid) {
|
||||
input_info_writer::ptr obj;
|
||||
open(obj, hint, path, aborter);
|
||||
return obj;
|
||||
}
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
if ( whatFor == input_stream_info_reader::class_guid ) {
|
||||
input_entry_v2::ptr v2;
|
||||
@@ -26,62 +32,7 @@ service_ptr input_entry::open(const GUID & whatFor, file::ptr hint, const char *
|
||||
throw exception_io_unsupported_format();
|
||||
}
|
||||
#endif
|
||||
if ( whatFor == album_art_extractor_instance::class_guid ) {
|
||||
input_entry_v2::ptr v2;
|
||||
if (v2 &= this) {
|
||||
GUID g = v2->get_guid();
|
||||
service_enum_t<album_art_extractor> e;
|
||||
album_art_extractor_v2::ptr p;
|
||||
while( e.next(p) ) {
|
||||
if ( p->get_guid() == g ) {
|
||||
return p->open( hint, path, aborter );
|
||||
}
|
||||
}
|
||||
}
|
||||
throw exception_io_unsupported_format();
|
||||
}
|
||||
if ( whatFor == album_art_editor_instance::class_guid ) {
|
||||
input_entry_v2::ptr v2;
|
||||
if (v2 &= this) {
|
||||
GUID g = v2->get_guid();
|
||||
service_enum_t<album_art_editor> e;
|
||||
album_art_editor_v2::ptr p;
|
||||
while( e.next(p) ) {
|
||||
if ( p->get_guid() == g ) {
|
||||
return p->open( hint, path, aborter );
|
||||
}
|
||||
}
|
||||
}
|
||||
throw exception_io_unsupported_format();
|
||||
}
|
||||
|
||||
input_entry_v3::ptr v3;
|
||||
|
||||
if (v3 &= this) {
|
||||
return v3->open_v3( whatFor, hint, path, logger, aborter );
|
||||
} else {
|
||||
if (whatFor == input_decoder::class_guid) {
|
||||
input_decoder::ptr obj;
|
||||
open(obj, hint, path, aborter);
|
||||
if ( logger.is_valid() ) {
|
||||
input_decoder_v2::ptr v2;
|
||||
if (v2 &= obj) v2->set_logger(logger);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
if (whatFor == input_info_reader::class_guid) {
|
||||
input_info_reader::ptr obj;
|
||||
open(obj, hint, path, aborter);
|
||||
return obj;
|
||||
}
|
||||
if (whatFor == input_info_writer::class_guid) {
|
||||
input_info_writer::ptr obj;
|
||||
open(obj, hint, path, aborter);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
throw pfc::exception_not_implemented();
|
||||
uBugCheck();
|
||||
}
|
||||
|
||||
bool input_entry::g_find_service_by_path(service_ptr_t<input_entry> & p_out,const char * p_path)
|
||||
@@ -155,41 +106,24 @@ static void prepare_for_open(service_ptr_t<input_entry> & p_service,service_ptr_
|
||||
#endif
|
||||
|
||||
bool input_entry::g_find_inputs_by_content_type(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_content_type, bool p_from_redirect) {
|
||||
auto filter = [=] (input_entry::ptr p) {
|
||||
return !(p_from_redirect && p->is_redirect());
|
||||
};
|
||||
return g_find_inputs_by_content_type_ex(p_out, p_content_type, filter );
|
||||
}
|
||||
|
||||
bool input_entry::g_find_inputs_by_path(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_path, bool p_from_redirect) {
|
||||
auto filter = [=] (input_entry::ptr p) {
|
||||
return !(p_from_redirect && p->is_redirect());
|
||||
};
|
||||
return g_find_inputs_by_path_ex(p_out, p_path, filter);
|
||||
}
|
||||
|
||||
bool input_entry::g_find_inputs_by_content_type_ex(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_content_type, input_filter_t filter ) {
|
||||
service_enum_t<input_entry> e;
|
||||
service_ptr_t<input_entry> ptr;
|
||||
bool ret = false;
|
||||
while (e.next(ptr)) {
|
||||
if ( filter(ptr) ) {
|
||||
if (!(p_from_redirect && ptr->is_redirect())) {
|
||||
if (ptr->is_our_content_type(p_content_type)) { p_out.add_item(ptr); ret = true; }
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool input_entry::g_find_inputs_by_path_ex(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_path, input_filter_t filter ) {
|
||||
bool input_entry::g_find_inputs_by_path(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_path, bool p_from_redirect) {
|
||||
service_enum_t<input_entry> e;
|
||||
service_ptr_t<input_entry> ptr;
|
||||
auto extension = pfc::string_extension(p_path);
|
||||
bool ret = false;
|
||||
while (e.next(ptr)) {
|
||||
GUID guid = pfc::guid_null;
|
||||
input_entry_v3::ptr ex;
|
||||
if ( ex &= ptr ) guid = ex->get_guid();
|
||||
if ( filter(ptr) ) {
|
||||
if (!(p_from_redirect && ptr->is_redirect())) {
|
||||
if (ptr->is_our_path(p_path, extension)) { p_out.add_item(ptr); ret = true; }
|
||||
}
|
||||
}
|
||||
@@ -204,82 +138,50 @@ static GUID input_get_guid( input_entry::ptr e ) {
|
||||
return pfc::guid_null;
|
||||
}
|
||||
|
||||
service_ptr input_entry::g_open_from_list(input_entry_list_t const & p_list, const GUID & whatFor, service_ptr_t<file> p_filehint, const char * p_path, event_logger::ptr logger, abort_callback & p_abort, GUID * outGUID) {
|
||||
service_ptr input_entry::g_open_from_list(input_entry_list_t const & p_list, const GUID & whatFor, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort, GUID * outGUID) {
|
||||
const t_size count = p_list.get_count();
|
||||
if ( count == 0 ) {
|
||||
// sanity
|
||||
throw exception_io_unsupported_format();
|
||||
} else if (count == 1) {
|
||||
auto ret = p_list[0]->open(whatFor, p_filehint, p_path, logger, p_abort);
|
||||
auto ret = p_list[0]->open(whatFor, p_filehint, p_path, p_abort);
|
||||
if ( outGUID != nullptr ) * outGUID = input_get_guid( p_list[0] );
|
||||
return ret;
|
||||
} else {
|
||||
std::exception_ptr errData, errUnsupported;
|
||||
unsigned bad_data_count = 0;
|
||||
pfc::string8 bad_data_message;
|
||||
for (t_size n = 0; n < count; n++) {
|
||||
try {
|
||||
auto ret = p_list[n]->open(whatFor, p_filehint, p_path, logger, p_abort);
|
||||
auto ret = p_list[n]->open(whatFor, p_filehint, p_path, p_abort);
|
||||
if (outGUID != nullptr) * outGUID = input_get_guid(p_list[n]);
|
||||
return ret;
|
||||
} catch (exception_io_no_handler_for_path) {
|
||||
//do nothing, skip over
|
||||
} catch (exception_io_unsupported_format) {
|
||||
if (!errUnsupported) errUnsupported = std::current_exception();
|
||||
} catch (exception_io_data) {
|
||||
if (!errData) errData = std::current_exception();
|
||||
//do nothing, skip over
|
||||
} catch (exception_io_data const & e) {
|
||||
if (bad_data_count++ == 0) bad_data_message = e.what();
|
||||
}
|
||||
}
|
||||
if (errData) std::rethrow_exception(errData);
|
||||
if (errUnsupported) std::rethrow_exception(errUnsupported);
|
||||
throw exception_io_unsupported_format();
|
||||
if (bad_data_count > 1) throw exception_io_data();
|
||||
else if (bad_data_count == 0) pfc::throw_exception_with_message<exception_io_data>(bad_data_message);
|
||||
else throw exception_io_unsupported_format();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
service_ptr input_manager::open_v2(const GUID & whatFor, file::ptr hint, const char * path, bool fromRedirect, event_logger::ptr logger, abort_callback & aborter, GUID * outUsedEntry) {
|
||||
// We're wrapping open_v2() on top of old open().
|
||||
// Assert on GUIDs that old open() is known to recognize.
|
||||
PFC_ASSERT(whatFor == input_decoder::class_guid || whatFor == input_info_reader::class_guid || whatFor == input_info_writer::class_guid || whatFor == input_stream_selector::class_guid);
|
||||
|
||||
{
|
||||
input_manager_v2::ptr v2;
|
||||
if ( v2 &= this ) {
|
||||
return v2->open_v2( whatFor, hint, path, fromRedirect, logger, aborter, outUsedEntry );
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = open( whatFor, hint, path, fromRedirect, aborter, outUsedEntry );
|
||||
|
||||
#ifdef FB2K_HAVE_EVENT_LOGGER
|
||||
if ( logger.is_valid() ) {
|
||||
input_decoder_v2::ptr dec;
|
||||
if (dec &= ret) {
|
||||
dec->set_logger(logger);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
service_ptr input_entry::g_open(const GUID & whatFor, file::ptr p_filehint, const char * p_path, event_logger::ptr logger, abort_callback & p_abort, bool p_from_redirect) {
|
||||
service_ptr input_entry::g_open(const GUID & whatFor, file::ptr p_filehint, const char * p_path, abort_callback & p_abort, bool p_from_redirect) {
|
||||
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
|
||||
// #define rationale: not all FOOBAR2000_MODERN flavours come with input_manager implementation, but classic 1.4+ does
|
||||
#if !defined(FOOBAR2000_MODERN) && FOOBAR2000_TARGET_VERSION >= 79
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION > 79
|
||||
return input_manager_v2::get()->open_v2(whatFor, p_filehint, p_path, p_from_redirect, logger, p_abort);
|
||||
#else
|
||||
return input_manager::get()->open_v2(whatFor, p_filehint, p_path, p_from_redirect, logger, p_abort);
|
||||
#if FOOBAR2000_TARGET_VERSION >= 79
|
||||
return input_manager::get()->open(whatFor, p_filehint, p_path, p_from_redirect, p_abort);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
{
|
||||
input_manager::ptr m;
|
||||
service_enum_t<input_manager> e;
|
||||
if (e.next(m)) {
|
||||
return m->open_v2(whatFor, p_filehint, p_path, p_from_redirect, logger, p_abort);
|
||||
return m->open(whatFor, p_filehint, p_path, p_from_redirect, p_abort);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -305,7 +207,7 @@ service_ptr input_entry::g_open(const GUID & whatFor, file::ptr p_filehint, cons
|
||||
#endif
|
||||
if (g_find_inputs_by_content_type(list, content_type, p_from_redirect)) {
|
||||
try {
|
||||
return g_open_from_list(list, whatFor, l_file, p_path, logger, p_abort);
|
||||
return g_open_from_list(list, whatFor, l_file, p_path, p_abort);
|
||||
} catch (exception_io_unsupported_format) {
|
||||
#if PFC_DEBUG
|
||||
FB2K_DebugLog() << "Failed to open by content type, using fallback";
|
||||
@@ -321,7 +223,7 @@ service_ptr input_entry::g_open(const GUID & whatFor, file::ptr p_filehint, cons
|
||||
{
|
||||
pfc::list_t< input_entry::ptr > list;
|
||||
if (g_find_inputs_by_path(list, p_path, p_from_redirect)) {
|
||||
return g_open_from_list(list, whatFor, l_file, p_path, logger, p_abort);
|
||||
return g_open_from_list(list, whatFor, l_file, p_path, p_abort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,17 +232,17 @@ service_ptr input_entry::g_open(const GUID & whatFor, file::ptr p_filehint, cons
|
||||
|
||||
void input_entry::g_open_for_decoding(service_ptr_t<input_decoder> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect) {
|
||||
TRACK_CALL_TEXT("input_entry::g_open_for_decoding");
|
||||
p_instance ^= g_open(input_decoder::class_guid, p_filehint, p_path, nullptr, p_abort, p_from_redirect);
|
||||
p_instance ^= g_open(input_decoder::class_guid, p_filehint, p_path, p_abort, p_from_redirect);
|
||||
}
|
||||
|
||||
void input_entry::g_open_for_info_read(service_ptr_t<input_info_reader> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect) {
|
||||
TRACK_CALL_TEXT("input_entry::g_open_for_info_read");
|
||||
p_instance ^= g_open(input_info_reader::class_guid, p_filehint, p_path, nullptr, p_abort, p_from_redirect);
|
||||
p_instance ^= g_open(input_info_reader::class_guid, p_filehint, p_path, p_abort, p_from_redirect);
|
||||
}
|
||||
|
||||
void input_entry::g_open_for_info_write(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect) {
|
||||
TRACK_CALL_TEXT("input_entry::g_open_for_info_write");
|
||||
p_instance ^= g_open(input_info_writer::class_guid, p_filehint, p_path, nullptr, p_abort, p_from_redirect);
|
||||
p_instance ^= g_open(input_info_writer::class_guid, p_filehint, p_path, p_abort, p_from_redirect);
|
||||
}
|
||||
|
||||
void input_entry::g_open_for_info_write_timeout(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,double p_timeout,bool p_from_redirect) {
|
||||
@@ -390,61 +292,12 @@ void input_open_file_helper(service_ptr_t<file> & p_file,const char * p_path,t_i
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t input_entry::g_flags_for_path( const char * path, uint32_t mask ) {
|
||||
#if FOOBAR2000_TARGET_VERSION >= 80
|
||||
return input_manager_v3::get()->flags_for_path(path, mask);
|
||||
#else
|
||||
input_manager_v3::ptr api;
|
||||
if ( input_manager_v3::tryGet(api) ) {
|
||||
return api->flags_for_path(path, mask);
|
||||
}
|
||||
uint32_t ret = 0;
|
||||
service_enum_t<input_entry> e; input_entry::ptr p;
|
||||
auto ext = pfc::string_extension(path);
|
||||
while(e.next(p)) {
|
||||
uint32_t f = p->get_flags() & mask;
|
||||
if ( f != 0 && p->is_our_path( path, ext ) ) ret |= f;;
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
uint32_t input_entry::g_flags_for_content_type( const char * ct, uint32_t mask ) {
|
||||
#if FOOBAR2000_TARGET_VERSION >= 80
|
||||
return input_manager_v3::get()->flags_for_content_type(ct, mask);
|
||||
#else
|
||||
input_manager_v3::ptr api;
|
||||
if ( input_manager_v3::tryGet(api) ) {
|
||||
return api->flags_for_content_type( ct, mask );
|
||||
}
|
||||
uint32_t ret = 0;
|
||||
service_enum_t<input_entry> e; input_entry::ptr p;
|
||||
while(e.next(p)) {
|
||||
uint32_t f = p->get_flags() & mask;
|
||||
if ( f != 0 && p->is_our_content_type(ct) ) ret |= f;
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool input_entry::g_are_parallel_reads_slow(const char * path) {
|
||||
return g_flags_for_path(path, flag_parallel_reads_slow) != 0;
|
||||
auto ext = pfc::string_extension(path);
|
||||
input_entry::ptr svc;
|
||||
service_enum_t<input_entry> e;
|
||||
while (e.next(svc)) {
|
||||
if (svc->is_our_path(path, ext) && svc->are_parallel_reads_slow()) return true;
|
||||
}
|
||||
|
||||
void input_entry_v3::open_for_decoding(service_ptr_t<input_decoder> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort) {
|
||||
p_instance ^= open_v3( input_decoder::class_guid, p_filehint, p_path, nullptr, p_abort );
|
||||
}
|
||||
void input_entry_v3::open_for_info_read(service_ptr_t<input_info_reader> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort) {
|
||||
p_instance ^= open_v3(input_info_reader::class_guid, p_filehint, p_path, nullptr, p_abort);
|
||||
}
|
||||
void input_entry_v3::open_for_info_write(service_ptr_t<input_info_writer> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort) {
|
||||
p_instance ^= open_v3(input_info_writer::class_guid, p_filehint, p_path, nullptr, p_abort);
|
||||
}
|
||||
|
||||
void input_info_writer::remove_tags_fallback(abort_callback & abort) {
|
||||
uint32_t total = this->get_subsong_count();
|
||||
file_info_impl blank;
|
||||
for( uint32_t walk = 0; walk < total; ++ walk ) {
|
||||
this->set_info( this->get_subsong(walk), blank, abort );
|
||||
}
|
||||
this->commit( abort );
|
||||
return false;
|
||||
}
|
||||
@@ -1,8 +1,3 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "event_logger.h"
|
||||
#include "audio_chunk.h"
|
||||
|
||||
PFC_DECLARE_EXCEPTION(exception_tagging_unsupported, exception_io_data, "Tagging of this file format is not supported")
|
||||
|
||||
enum {
|
||||
@@ -107,8 +102,8 @@ public:
|
||||
//! Returned raw data should be possible to cut into individual samples; size in bytes should be divisible by audio_chunk's sample count for splitting in case partial output is needed (with cuesheets etc).
|
||||
virtual bool run_raw(audio_chunk & out, mem_block_container & outRaw, abort_callback & abort) = 0;
|
||||
|
||||
//! OBSOLETE since 1.5 \n
|
||||
//! Specify logger when opening to reliably get info generated during input open operation.
|
||||
//! OPTIONAL, the call is ignored if this implementation doesn't support status logging. \n
|
||||
//! Mainly used to generate logs when ripping CDs etc.
|
||||
virtual void set_logger(event_logger::ptr ptr) = 0;
|
||||
};
|
||||
|
||||
@@ -139,21 +134,6 @@ public:
|
||||
//! Tells the decoder to output at this sample rate if the decoder's sample rate is adjustable. \n
|
||||
//! Sample rate signaled in arg1.
|
||||
static const GUID set_preferred_sample_rate;
|
||||
|
||||
//! Retrieves logical decode position from the decoder. Implemented only in some rare cases where logical position does not match duration of returned data so far.
|
||||
//! arg2 points to double position in seconds.
|
||||
//! Return 1 if position was written to arg2, 0 if n/a.
|
||||
static const GUID query_position;
|
||||
|
||||
struct continue_stream_t {
|
||||
file::ptr reader;
|
||||
const char * path;
|
||||
};
|
||||
//! Tells the decoder to continue decoding from another URL, without flushing etc. Mainly used by HLS streams.
|
||||
//! arg2: continue_stream_t
|
||||
//! Return 1 to acknowledge, 0 if unsupported.
|
||||
//! A call to decode_initialize() will follow if you return 1; perform actual file open from there.
|
||||
static const GUID continue_stream;
|
||||
};
|
||||
|
||||
//! Class providing interface for writing metadata and replaygain info to files. Also see: file_info. \n
|
||||
@@ -172,9 +152,6 @@ public:
|
||||
//! @param p_abort abort_callback object signaling user aborting the operation. WARNING: abort_callback object is provided for consistency; if writing tags actually gets aborted, user will be likely left with corrupted file. Anything calling this should make sure that aborting is either impossible, or gives appropriate warning to the user first.
|
||||
virtual void commit(abort_callback & p_abort) = 0;
|
||||
|
||||
//! Helper for writers not implementing input_info_writer_v2::remove_tags().
|
||||
void remove_tags_fallback(abort_callback & abort);
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE(input_info_writer,input_info_reader);
|
||||
};
|
||||
|
||||
@@ -245,25 +222,20 @@ public:
|
||||
static void g_open_for_info_write(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,bool p_from_redirect = false);
|
||||
static void g_open_for_info_write_timeout(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort,double p_timeout,bool p_from_redirect = false);
|
||||
static bool g_is_supported_path(const char * p_path);
|
||||
typedef std::function<bool ( input_entry::ptr ) > input_filter_t;
|
||||
static bool g_find_inputs_by_content_type(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_content_type, bool p_from_redirect);
|
||||
static bool g_find_inputs_by_path(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_path, bool p_from_redirect);
|
||||
static bool g_find_inputs_by_content_type_ex(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_content_type, input_filter_t filter );
|
||||
static bool g_find_inputs_by_path_ex(pfc::list_base_t<service_ptr_t<input_entry> > & p_out, const char * p_path, input_filter_t filter );
|
||||
static service_ptr g_open(const GUID & whatFor, file::ptr hint, const char * path, event_logger::ptr logger, abort_callback & aborter, bool fromRedirect = false);
|
||||
static service_ptr g_open(const GUID & whatFor, file::ptr hint, const char * path, abort_callback & aborter, bool fromRedirect = false);
|
||||
|
||||
void open(service_ptr_t<input_decoder> & p_instance,service_ptr_t<file> const & p_filehint,const char * p_path,abort_callback & p_abort) {open_for_decoding(p_instance,p_filehint,p_path,p_abort);}
|
||||
void open(service_ptr_t<input_info_reader> & p_instance,service_ptr_t<file> const & p_filehint,const char * p_path,abort_callback & p_abort) {open_for_info_read(p_instance,p_filehint,p_path,p_abort);}
|
||||
void open(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> const & p_filehint,const char * p_path,abort_callback & p_abort) {open_for_info_write(p_instance,p_filehint,p_path,p_abort);}
|
||||
service_ptr open(const GUID & whatFor, file::ptr hint, const char * path, event_logger::ptr logger, abort_callback & aborter);
|
||||
service_ptr open(const GUID & whatFor, file::ptr hint, const char * path, abort_callback & aborter);
|
||||
|
||||
typedef pfc::list_base_const_t< input_entry::ptr > input_entry_list_t;
|
||||
|
||||
static service_ptr g_open_from_list(input_entry_list_t const & list, const GUID & whatFor, file::ptr hint, const char * path, event_logger::ptr logger, abort_callback & aborter, GUID * outGUID = nullptr);
|
||||
static service_ptr g_open_from_list(input_entry_list_t const & list, const GUID & whatFor, file::ptr hint, const char * path, abort_callback & aborter, GUID * outGUID = nullptr);
|
||||
static bool g_are_parallel_reads_slow( const char * path );
|
||||
|
||||
static uint32_t g_flags_for_path( const char * pathFor, uint32_t mask = UINT32_MAX );
|
||||
static uint32_t g_flags_for_content_type( const char * ct, uint32_t mask = UINT32_MAX );
|
||||
};
|
||||
|
||||
//! \since 1.4
|
||||
@@ -272,7 +244,8 @@ public:
|
||||
class input_entry_v2 : public input_entry {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(input_entry_v2, input_entry);
|
||||
public:
|
||||
//! @returns GUID used to identify us among other decoders in the decoder priority table.
|
||||
#ifdef FOOBAR2000_DESKTOP // none of this is used in fb2k mobile
|
||||
//! @returns GUID used to identify us among other deciders in the decoder priority table.
|
||||
virtual GUID get_guid() = 0;
|
||||
//! @returns Name to present to the user in the decoder priority table.
|
||||
virtual const char * get_name() = 0;
|
||||
@@ -280,22 +253,7 @@ public:
|
||||
virtual GUID get_preferences_guid() = 0;
|
||||
//! @returns true if the decoder should be put at the end of the list when it's first sighted, false otherwise (will be put at the beginning of the list).
|
||||
virtual bool is_low_merit() = 0;
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class input_entry_v3 : public input_entry_v2 {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(input_entry_v3, input_entry_v2);
|
||||
public:
|
||||
//! New unified open() function for all supported interfaces. Supports any future interfaces via alternate GUIDs, as well as allows the event logger to be set prior to the open() call.
|
||||
//! @param whatFor The class GUID of the service we want. \n
|
||||
//! Currently allowed are: input_decoder::class_guid, input_info_reader::class_guid, input_info_writer::class_guid. \n
|
||||
//! This method must throw pfc::exception_not_implemented for any GUIDs it does not recognize.
|
||||
virtual service_ptr open_v3( const GUID & whatFor, file::ptr hint, const char * path, event_logger::ptr logger, abort_callback & aborter ) = 0;
|
||||
|
||||
|
||||
void open_for_decoding(service_ptr_t<input_decoder> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort) ;
|
||||
void open_for_info_read(service_ptr_t<input_info_reader> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort);
|
||||
void open_for_info_write(service_ptr_t<input_info_writer> & p_instance, service_ptr_t<file> p_filehint, const char * p_path, abort_callback & p_abort);
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef FOOBAR2000_DESKTOP
|
||||
@@ -306,40 +264,6 @@ class input_manager : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(input_manager);
|
||||
public:
|
||||
virtual service_ptr open(const GUID & whatFor, file::ptr hint, const char * path, bool fromRedirect, abort_callback & aborter, GUID * outUsedEntry = nullptr) = 0;
|
||||
|
||||
//! input_manager_v2 wrapper.
|
||||
service_ptr open_v2(const GUID & whatFor, file::ptr hint, const char * path, bool fromRedirect, event_logger::ptr logger, abort_callback & aborter, GUID * outUsedEntry = nullptr);
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
//! Extension of input_manager. \n
|
||||
//! Extended open_v2() supports album_art_extractor and album_art_editor. It reliably throws pfc::exception_not_implemented() for unsupported GUIDs (old version would bugcheck). \n
|
||||
//! It also allows event_logger to be specified in advance so open() implementation can already use it.
|
||||
class input_manager_v2 : public input_manager {
|
||||
FB2K_MAKE_SERVICE_COREAPI_EXTENSION(input_manager_v2, input_manager)
|
||||
public:
|
||||
virtual service_ptr open_v2(const GUID & whatFor, file::ptr hint, const char * path, bool fromRedirect, event_logger::ptr logger, abort_callback & aborter, GUID * outUsedEntry = nullptr) = 0;
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class input_manager_v3 : public input_manager_v2 {
|
||||
FB2K_MAKE_SERVICE_COREAPI_EXTENSION(input_manager_v3, input_manager_v2);
|
||||
public:
|
||||
//! Retrieves list of enabled inputs, in user-specified order. \n
|
||||
//! This is rarely needed. If you need this function, consider redesigning your code to call input_manager open methods instead.
|
||||
virtual void get_enabled_inputs( pfc::list_base_t<input_entry::ptr> & out ) = 0;
|
||||
//! Returns input_entry get_flags() values for this path, as returned by enabled inputs.
|
||||
virtual uint32_t flags_for_path( const char * pathFor, uint32_t mask = UINT32_MAX ) = 0;
|
||||
//! Returns input_entry get_flags() values for this content type, as returned by enabled inputs.
|
||||
virtual uint32_t flags_for_content_type( const char * ct, uint32_t mask = UINT32_MAX ) = 0;
|
||||
|
||||
|
||||
enum {
|
||||
flagFromRedirect = 1 << 0,
|
||||
flagSuppressFilters = 1 << 1,
|
||||
};
|
||||
|
||||
virtual service_ptr open_v3(const GUID & whatFor, file::ptr hint, const char * path, uint32_t flags, event_logger::ptr logger, abort_callback & aborter, GUID * outUsedEntry = nullptr) = 0;
|
||||
};
|
||||
|
||||
//! \since 1.4
|
||||
@@ -385,8 +309,6 @@ public:
|
||||
};
|
||||
|
||||
//! \since 1.4
|
||||
//! Callback for input_stream_manipulator \n
|
||||
//! Used for applying ReplayGain to encoded audio streams.
|
||||
class input_stream_manipulator_callback : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(input_stream_manipulator_callback, service_base);
|
||||
public:
|
||||
@@ -402,8 +324,6 @@ public:
|
||||
};
|
||||
|
||||
//! \since 1.4
|
||||
//! Manipulate audio stream payload in files. \n
|
||||
//! Used for applying ReplayGain to encoded audio streams.
|
||||
class input_stream_manipulator : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(input_stream_manipulator);
|
||||
public:
|
||||
@@ -425,121 +345,4 @@ public:
|
||||
//! Return GUID of the matching input_entry.
|
||||
virtual GUID get_guid() = 0;
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
//! An input_info_filter lets you hook into all performed tag read & write operations. \n
|
||||
//! Your tag manipulations will be transparent to all fb2k components, as if the tags were read/written by relevant inputs. \n
|
||||
//! Your input_info_filter needs to be enabled in Preferences in order to become active. Newly added ones are inactive by default.
|
||||
class input_info_filter : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT( input_info_filter );
|
||||
public:
|
||||
//! Tags are being read from a file.
|
||||
virtual void filter_info_read( const playable_location & loc,file_info & info,abort_callback & abort ) = 0;
|
||||
//! Tags are being written to a file. \n
|
||||
//! Return true to continue, false to suppress writing of tags.
|
||||
virtual bool filter_info_write( const playable_location & loc, file_info & info, abort_callback & abort ) = 0;
|
||||
//! Tags are being removed from a file.
|
||||
virtual void on_info_remove( const char * path, abort_callback & abort ) = 0;
|
||||
//! Return GUID of your filter.
|
||||
virtual GUID get_guid() = 0;
|
||||
//! Return preferences page or advconfig branch GUID of your filter.
|
||||
virtual GUID get_preferences_guid() = 0;
|
||||
//! Return user-friendly name of your filter to be shown in preferences.
|
||||
virtual const char * get_name() = 0;
|
||||
//! Optional backwards compatibility method. \n
|
||||
//! If you also provide input services for old foobar2000 versions which don't recognize input_info_filter, report their GUIDs here so they can be ignored. \n
|
||||
//! @param outGUIDs empty on entry, contains GUIDs of ignored inputs (if any) on return.
|
||||
virtual void get_suppressed_inputs( pfc::list_base_t<GUID> & outGUIDs ) {outGUIDs.remove_all();}
|
||||
//! write_fallback() supported or not? \n
|
||||
//! Used if your filter can store tags for untaggable files.
|
||||
virtual bool supports_fallback() = 0;
|
||||
//! Optional; called when user attempted to tag an untaggable/readonly file. \n
|
||||
//! Used if your filter can store tags for untaggable files.
|
||||
virtual bool write_fallback( const playable_location & loc, file_info const & info, abort_callback & abort ) = 0;
|
||||
//! Optional; called when user attempted to remove tags from an untaggable/readonly file.\ n
|
||||
//! Used if your filter can store tags for untaggable files.
|
||||
virtual void remove_tags_fallback( const char * path, abort_callback & abort ) = 0;
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class input_stream_info_filter : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE( input_stream_info_filter, service_base );
|
||||
public:
|
||||
virtual void filter_dynamic_info( file_info & info ) = 0;
|
||||
virtual void filter_dynamic_info_track( file_info & info ) = 0;
|
||||
};
|
||||
|
||||
class album_art_data;
|
||||
|
||||
//! \since 1.5
|
||||
//! Extended input_info_filter.
|
||||
class input_info_filter_v2 : public input_info_filter {
|
||||
FB2K_MAKE_SERVICE_INTERFACE( input_info_filter_v2, input_info_filter );
|
||||
public:
|
||||
//! Creates an object which then can work with dynamic track titles etc of a decoded track. \n
|
||||
//! Returning null to filter the info is allowed.
|
||||
virtual input_stream_info_filter::ptr open_stream(playable_location const & loc, abort_callback & abort) = 0;
|
||||
|
||||
|
||||
typedef service_ptr_t<album_art_data> aaptr_t;
|
||||
|
||||
//! Album art is being read from the file. \n
|
||||
//! info may be null if file had no such picture. \n
|
||||
//! Return passed info, altered info or null.
|
||||
virtual aaptr_t filter_album_art_read( const char * path, const GUID & type, aaptr_t info, abort_callback & aborter ) = 0;
|
||||
//! Album art is being written to the file. \n
|
||||
//! Return passed info, altered info or null to suppress writing.
|
||||
virtual aaptr_t filter_album_art_write( const char * path, const GUID & type, aaptr_t info, abort_callback & aborter ) = 0;
|
||||
//! Specific album art is being removed from the file. \n
|
||||
//! Return true to go on, false to suppress file update.
|
||||
virtual bool filter_album_art_remove( const char * path, const GUID & type, abort_callback & aborter ) = 0;
|
||||
//! All album art is being removed from the file. \n
|
||||
//! Return true to go on, false to suppress file update.
|
||||
virtual bool filter_album_art_remove_all( const char * path, abort_callback & aborter ) = 0;
|
||||
|
||||
//! Valid with supports_fallback() = true \n
|
||||
//! Album art is being written to an untaggable file.
|
||||
virtual void write_album_art_fallback( const char * path, const GUID & type, aaptr_t info, abort_callback & aborter ) = 0;
|
||||
//! Valid with supports_fallback() = true \n
|
||||
//! Specific album art is being removed from an untaggable file.
|
||||
virtual void remove_album_art_fallback( const char * path, const GUID & type, abort_callback & aborter ) = 0;
|
||||
//! Valid with supports_fallback() = true \n
|
||||
//! All album art is being removed from an untaggable file.
|
||||
virtual void remove_all_album_art_fallback( const char * path, abort_callback & aborter ) = 0;
|
||||
};
|
||||
|
||||
class dsp_preset;
|
||||
|
||||
//! \since 1.5
|
||||
//! An input_playback_shim adds additional functionality to a DSP, allowing full control of the decoder. \n
|
||||
//! Currently, input_playback_shim can only exist alongside a DSP, must have the same GUID as a DSP. \n
|
||||
//! It will only be used in supported scenarios when the user has put your DSP in the chain. \n
|
||||
//! Your DSP will be deactivated in such case when your input_playback_shim is active. \n
|
||||
//! input_playback_shim is specifically intended to be instantiated for playback. Do not call this service from your component. \n/
|
||||
//! Implement this service ONLY IF NECESSARY. Very few tasks really need it, primarily DSPs that manipulate logical playback time & seeking.
|
||||
class input_playback_shim : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT( input_playback_shim );
|
||||
public:
|
||||
//! Same GUID as your DSP.
|
||||
virtual GUID get_guid() = 0;
|
||||
//! Preferences page / advconfig branch GUID of your shim, pfc::guid_null if none. \n
|
||||
//! This is currently unused / reserved for future use.
|
||||
virtual GUID get_preferences_guid() = 0;
|
||||
//! Same as your DSP. \n
|
||||
//! This is currently unused / reserved for future use.
|
||||
virtual const char * get_name() = 0;
|
||||
//! Instantiates your shim on top of existing input_decoder. \n
|
||||
//! If you don't want to do anything with this specific decoder, just return the passed decoder.
|
||||
virtual input_decoder::ptr shim( input_decoder::ptr dec, const char * path, dsp_preset const & preset, abort_callback & aborter ) = 0;
|
||||
//! Optional backwards compatibility method. \n
|
||||
//! If you also provide input services for old versions of foobar2000 which don't recognize input_playback_shim, report their GUIDs here so they can be ignored. \n
|
||||
//! @param outGUIDs empty on entry, contains GUIDs of ignored inputs (if any) on return.
|
||||
virtual void get_suppressed_inputs( pfc::list_base_t<GUID> & outGUIDs ) {outGUIDs.remove_all();}
|
||||
};
|
||||
|
||||
#endif // #ifdef FOOBAR2000_DESKTOP
|
||||
|
||||
|
||||
typedef input_info_writer_v2 input_info_writer_vhighest;
|
||||
typedef input_decoder_v4 input_decoder_vhighest;
|
||||
typedef input_info_reader input_info_reader_vhighest;
|
||||
#endif
|
||||
|
||||
@@ -66,10 +66,9 @@ void input_file_type::make_extension_support_fingerprint(pfc::string_base & str)
|
||||
}
|
||||
str = out;
|
||||
}
|
||||
|
||||
void input_file_type::build_openfile_mask(pfc::string_base & out, bool b_include_playlists, bool b_include_archives)
|
||||
void input_file_type::build_openfile_mask(pfc::string_base & out, bool b_include_playlists)
|
||||
{
|
||||
t_fnList extensionsAll, extensionsPl, extensionsArc;
|
||||
t_fnList extensionsAll, extensionsPl;;
|
||||
|
||||
if (b_include_playlists) {
|
||||
service_enum_t<playlist_loader> e; service_ptr_t<playlist_loader> ptr;
|
||||
@@ -81,19 +80,6 @@ void input_file_type::build_openfile_mask(pfc::string_base & out, bool b_include
|
||||
}
|
||||
}
|
||||
}
|
||||
if (b_include_archives) {
|
||||
service_enum_t<filesystem> e;
|
||||
archive_v3::ptr p;
|
||||
pfc::string_formatter temp;
|
||||
while (e.next(p)) {
|
||||
p->list_extensions(temp);
|
||||
pfc::chain_list_v2_t<pfc::string8> lst;
|
||||
pfc::splitStringByChar(lst, temp, ',');
|
||||
for (auto iter = lst.first(); iter.is_valid(); ++iter) {
|
||||
extensionsArc += PFC_string_formatter() << "*." << *iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef pfc::map_t<pfc::string8,t_fnList,pfc::string::comparatorCaseInsensitive> t_masks;
|
||||
t_masks masks;
|
||||
@@ -117,11 +103,8 @@ void input_file_type::build_openfile_mask(pfc::string_base & out, bool b_include
|
||||
}
|
||||
pfc::string_formatter outBuf;
|
||||
outBuf << "All files|*.*|";
|
||||
formatMaskList(outBuf, extensionsAll, "All supported media types");
|
||||
formatMaskList(outBuf, extensionsAll, "All supported types");
|
||||
formatMaskList(outBuf, extensionsPl, "Playlists");
|
||||
formatMaskList(outBuf, extensionsArc, "Archives");
|
||||
|
||||
|
||||
for(auto walk = masks.cfirst(); walk.is_valid(); ++walk) {
|
||||
formatMaskList(outBuf,walk->m_value,walk->m_key);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ public:
|
||||
virtual bool is_associatable(unsigned idx) = 0;
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 76
|
||||
static void build_openfile_mask(pfc::string_base & out,bool b_include_playlists=true, bool b_include_archives = false);
|
||||
static void build_openfile_mask(pfc::string_base & out,bool b_include_playlists=true);
|
||||
static void make_extension_support_fingerprint(pfc::string_base & str);
|
||||
static void make_filetype_support_fingerprint(pfc::string_base & str);
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "input.h"
|
||||
#include "mem_block_container.h"
|
||||
|
||||
enum t_input_open_reason {
|
||||
input_open_info_read,
|
||||
input_open_decode,
|
||||
@@ -78,8 +73,7 @@ public:
|
||||
//! See: input_decoder_v2::run_raw(). Relevant only when implementing input_decoder_v2. Valid after decode_initialize().
|
||||
bool decode_run_raw(audio_chunk & p_chunk, mem_block_container & p_raw, abort_callback & p_abort);
|
||||
|
||||
//! OLD way: may be called at any time from input_decoder_v2 \n
|
||||
//! NEW way (1.5): called prior to open().
|
||||
//! See: input_decoder::set_logger(). Relevant only when implementing input_decoder_v2. Valid after any open().
|
||||
void set_logger(event_logger::ptr ptr);
|
||||
protected:
|
||||
input_impl() {}
|
||||
@@ -98,11 +92,6 @@ public:
|
||||
static GUID g_get_preferences_guid() {return pfc::guid_null;}
|
||||
static bool g_is_low_merit() { return false; }
|
||||
|
||||
bool decode_get_dynamic_info(file_info & p_out, double & p_timestamp_delta) { return false; }
|
||||
bool decode_get_dynamic_info_track(file_info & p_out, double & p_timestamp_delta) { return false; }
|
||||
void decode_on_idle(abort_callback & p_abort) { }
|
||||
|
||||
|
||||
//! These typedefs indicate which interfaces your class actually supports. You can override them to support non default input API interfaces without specifying input_factory parameters.
|
||||
typedef input_decoder_v4 interface_decoder_t;
|
||||
typedef input_info_reader interface_info_reader_t;
|
||||
@@ -165,9 +154,8 @@ template<typename I, typename interface_t>
|
||||
class input_impl_interface_wrapper_t : public interface_t
|
||||
{
|
||||
public:
|
||||
template<typename ... args_t>
|
||||
void open( args_t && ... args) {
|
||||
m_instance.open(std::forward<args_t>(args) ... );
|
||||
void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
|
||||
m_instance.open(p_filehint,p_path,p_reason,p_abort);
|
||||
}
|
||||
|
||||
// input_info_reader methods
|
||||
@@ -307,9 +295,8 @@ class input_wrapper_singletrack_t : public input_forward_static_methods<I>
|
||||
public:
|
||||
input_wrapper_singletrack_t() {}
|
||||
|
||||
template<typename ... args_t>
|
||||
void open( args_t && ... args) {
|
||||
m_instance.open(std::forward<args_t>(args) ... );
|
||||
void open(service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort) {
|
||||
m_instance.open(p_filehint,p_path,p_reason,p_abort);
|
||||
}
|
||||
|
||||
void get_info(t_uint32 p_subsong,file_info & p_info,abort_callback & p_abort) {
|
||||
@@ -373,40 +360,32 @@ private:
|
||||
|
||||
//! Helper; standard input_entry implementation. Do not instantiate this directly, use input_factory_t or one of other input_*_factory_t helpers instead.
|
||||
template<typename I,unsigned t_flags, typename t_decoder = typename I::interface_decoder_t, typename t_inforeader = typename I::interface_info_reader_t, typename t_infowriter = typename I::interface_info_writer_t>
|
||||
class input_entry_impl_t : public input_entry_v3
|
||||
class input_entry_impl_t : public input_entry_v2
|
||||
{
|
||||
private:
|
||||
|
||||
template<typename T, typename out>
|
||||
void instantiate_t(service_ptr_t<out> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,t_input_open_reason p_reason,abort_callback & p_abort)
|
||||
{
|
||||
service_ptr_t< service_impl_t<input_impl_interface_wrapper_t<I,T> > > temp;
|
||||
temp = new service_impl_t<input_impl_interface_wrapper_t<I,T> >();
|
||||
temp->open(p_filehint,p_path,p_reason,p_abort);
|
||||
p_instance = temp.get_ptr();
|
||||
}
|
||||
public:
|
||||
bool is_our_content_type(const char * p_type) {return I::g_is_our_content_type(p_type);}
|
||||
bool is_our_path(const char * p_full_path,const char * p_extension) {return I::g_is_our_path(p_full_path,p_extension);}
|
||||
|
||||
template<typename interface_t, typename outInterace_t, typename ... args_t>
|
||||
void open_ex(service_ptr_t<outInterace_t> & p_instance,args_t && ... args)
|
||||
{
|
||||
auto temp = fb2k::service_new<input_impl_interface_wrapper_t<I,interface_t> >();
|
||||
temp->open(std::forward<args_t>(args) ... );
|
||||
p_instance = temp.get_ptr();
|
||||
void open_for_decoding(service_ptr_t<input_decoder> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort) {
|
||||
instantiate_t<t_decoder>(p_instance,p_filehint,p_path,input_open_decode,p_abort);
|
||||
}
|
||||
|
||||
service_ptr open_v3(const GUID & whatFor, file::ptr hint, const char * path, event_logger::ptr logger, abort_callback & aborter) {
|
||||
if ( whatFor == input_decoder::class_guid ) {
|
||||
auto obj = fb2k::service_new< input_impl_interface_wrapper_t<I,t_decoder> > ();
|
||||
if ( logger.is_valid() ) obj->set_logger(logger);
|
||||
obj->open( hint, path, input_open_decode, aborter );
|
||||
return obj;
|
||||
void open_for_info_read(service_ptr_t<input_info_reader> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort) {
|
||||
instantiate_t<t_inforeader>(p_instance,p_filehint,p_path,input_open_info_read,p_abort);
|
||||
}
|
||||
if ( whatFor == input_info_reader::class_guid ) {
|
||||
auto obj = fb2k::service_new < input_impl_interface_wrapper_t<I, t_inforeader> >();
|
||||
if (logger.is_valid()) obj->set_logger(logger);
|
||||
obj->open(hint, path, input_open_info_read, aborter);
|
||||
return obj;
|
||||
}
|
||||
if ( whatFor == input_info_writer::class_guid ) {
|
||||
auto obj = fb2k::service_new < input_impl_interface_wrapper_t<I, t_infowriter> >();
|
||||
if (logger.is_valid()) obj->set_logger(logger);
|
||||
obj->open(hint, path, input_open_info_write, aborter);
|
||||
return obj;
|
||||
}
|
||||
throw pfc::exception_not_implemented();
|
||||
|
||||
void open_for_info_write(service_ptr_t<input_info_writer> & p_instance,service_ptr_t<file> p_filehint,const char * p_path,abort_callback & p_abort) {
|
||||
instantiate_t<t_infowriter>(p_instance,p_filehint,p_path,input_open_info_write,p_abort);
|
||||
}
|
||||
|
||||
void get_extended_data(service_ptr_t<file> p_filehint,const playable_location & p_location,const GUID & p_guid,mem_block_container & p_out,abort_callback & p_abort) {
|
||||
|
||||
@@ -195,12 +195,3 @@ class NOVTABLE library_meta_autocomplete : public service_base {
|
||||
public:
|
||||
virtual bool get_value_list(const char * metaName, pfc::com_ptr_t<IUnknown> & out) = 0;
|
||||
};
|
||||
|
||||
//! \since 1.6.1
|
||||
//! Caching & asynchronous version. \n
|
||||
//! Keep a reference to your library_meta_autocomplete_v2 object in your dialog class to cache the looked up values & speed up the operation.
|
||||
class NOVTABLE library_meta_autocomplete_v2 : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(library_meta_autocomplete_v2)
|
||||
public:
|
||||
virtual bool get_value_list_async(const char* metaName, pfc::com_ptr_t<IUnknown>& out) = 0;
|
||||
};
|
||||
|
||||
@@ -1,27 +1,6 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
|
||||
// ======================================================================================================
|
||||
// Most of main_thread_callback.h declares API internals and obsolete helpers.
|
||||
// In modern code, simply use fb2k::inMainThread() declared below and disregard the rest.
|
||||
// ======================================================================================================
|
||||
namespace fb2k {
|
||||
//! Queue a call in main thread. Returns immediately. \n
|
||||
//! You can call this from any thread, including main thread - to execute some code outside the current call stack / global fb2k callbacks / etc.
|
||||
void inMainThread(std::function<void() > f);
|
||||
//! Call f synchronously if called from main thread, queue call if called from another.
|
||||
void inMainThread2(std::function<void() > f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ======================================================================================================
|
||||
// API declarations
|
||||
// ======================================================================================================
|
||||
|
||||
//! Callback object class for main_thread_callback_manager service. \n
|
||||
//! You do not need to implement this directly - simply use fb2k::inMainThread() with a lambda.
|
||||
//! Callback object class for main_thread_callback_manager service.
|
||||
class NOVTABLE main_thread_callback : public service_base {
|
||||
public:
|
||||
//! Gets called from main app thread. See main_thread_callback_manager description for more info.
|
||||
@@ -34,8 +13,7 @@ public:
|
||||
|
||||
/*!
|
||||
Allows you to queue a callback object to be called from main app thread. This is commonly used to trigger main-thread-only API calls from worker threads.\n
|
||||
This can be also used from main app thread, to avoid race conditions when trying to use APIs that dispatch global callbacks from inside some other global callback, or using message loops / modal dialogs inside global callbacks. \n
|
||||
There is no need to use this API directly - use fb2k::inMainThread() with a lambda.
|
||||
This can be also used from main app thread, to avoid race conditions when trying to use APIs that dispatch global callbacks from inside some other global callback, or using message loops / modal dialogs inside global callbacks.
|
||||
*/
|
||||
class NOVTABLE main_thread_callback_manager : public service_base {
|
||||
public:
|
||||
@@ -46,11 +24,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
// ======================================================================================================
|
||||
// Obsolete helpers - they still work, but it's easier to just use fb2k::inMainThread().
|
||||
// ======================================================================================================
|
||||
|
||||
//! Helper, equivalent to main_thread_callback_manager::get()->add_callback(ptr)
|
||||
void main_thread_callback_add(main_thread_callback::ptr ptr);
|
||||
|
||||
template<typename t_class> static void main_thread_callback_spawn() {
|
||||
@@ -107,8 +80,7 @@ static void callInMainThreadSvc(myservice_t * host, param_t const & param) {
|
||||
//! Have this as a member of your class, then use m_mthelper.add( this, somearg ) ; to defer a call to this->inMainThread(somearg). \n
|
||||
//! If your class becomes invalid before inMainThread is executed, the pending callback is discarded. \n
|
||||
//! You can optionally call shutdown() to invalidate all pending callbacks early (in a destructor of your class - without waiting for callInMainThreadHelper destructor to do the job. \n
|
||||
//! In order to let callInMainThreadHelper access your private methods, declare friend class callInMainThread. \n
|
||||
//! Note that callInMainThreadHelper is expected to be created and destroyed in main thread.
|
||||
//! In order to let callInMainThreadHelper access your private methods, declare friend class callInMainThread.
|
||||
class callInMainThreadHelper {
|
||||
public:
|
||||
|
||||
@@ -185,3 +157,11 @@ private:
|
||||
killswitch_t m_ks;
|
||||
|
||||
};
|
||||
|
||||
// Modern helper
|
||||
namespace fb2k {
|
||||
// Queue call in main thread
|
||||
void inMainThread( std::function<void () > f );
|
||||
// Call f synchronously if called from main thread, queue call if called from another
|
||||
void inMainThread2( std::function<void () > f );
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include "ui_element_typable_window_manager.h"
|
||||
|
||||
static void fix_ampersand(const char * src,pfc::string_base & out)
|
||||
{
|
||||
out.reset();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "foobar2000.h"
|
||||
#include "file_info_filter_impl.h"
|
||||
|
||||
|
||||
void metadb::handle_create_replace_path_canonical(metadb_handle_ptr & p_out,const metadb_handle_ptr & p_source,const char * p_new_path) {
|
||||
handle_create(p_out,make_playable_location(p_new_path,p_source->get_subsong_index()));
|
||||
@@ -93,20 +93,3 @@ void metadb_io_v2::on_files_rechaptered( metadb_handle_list_cref newHandles ) {
|
||||
|
||||
load_info_async( newHandles, metadb_io::load_info_force, core_api::get_main_window(), metadb_io_v3::op_flag_delay_ui, notify );
|
||||
}
|
||||
|
||||
|
||||
metadb_hint_list::ptr metadb_hint_list::create() {
|
||||
return metadb_io_v2::get()->create_hint_list();
|
||||
}
|
||||
|
||||
metadb_hint_list_v2::ptr metadb_hint_list_v2::create() {
|
||||
metadb_hint_list_v2::ptr ret;
|
||||
ret ^= metadb_hint_list::create();
|
||||
return ret;
|
||||
}
|
||||
|
||||
metadb_hint_list_v3::ptr metadb_hint_list_v3::create() {
|
||||
metadb_hint_list_v3::ptr ret;
|
||||
ret ^= metadb_hint_list::create();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
class file_info_filter; // forward decl; file_info_filter moved to file_info_filter.h
|
||||
|
||||
|
||||
//! API for tag read/write operations. Legal to call from main thread only, except for hint_multi_async() / hint_async() / hint_reader().\n
|
||||
//! Implemented only by core, do not reimplement.\n
|
||||
//! Use static_api_ptr_t template to access metadb_io methods.\n
|
||||
@@ -68,72 +62,49 @@ public:
|
||||
FB2K_MAKE_SERVICE_COREAPI(metadb_io);
|
||||
};
|
||||
|
||||
//! Advanced interface for passing infos read from files to metadb backend. Use metadb_io_v2::create_hint_list() to instantiate. \n
|
||||
//! Thread safety: all methods other than on_done() are intended for worker threads. Instantiate and use the object in a worker thread, call on_done() in main thread to finalize. \n
|
||||
//! Typical usage pattern: create a hint list (in any thread), hand infos to it from files that you work with (in a worker thread), call on_done() in main thread. \n
|
||||
//! Implementing this class gives you direct control over which part of file_info gets altered during a tag update uperation. To be used with metadb_io_v2::update_info_async().
|
||||
class NOVTABLE file_info_filter : public service_base {
|
||||
public:
|
||||
//! Alters specified file_info entry; called as a part of tag update process. Specified file_info has been read from a file, and will be written back.\n
|
||||
//! WARNING: This will be typically called from another thread than main app thread (precisely, from thread created by tag updater). You should copy all relevant data to members of your file_info_filter instance in constructor and reference only member data in apply_filter() implementation.
|
||||
//! @returns True when you have altered file_info and changes need to be written back to the file; false if no changes have been made.
|
||||
virtual bool apply_filter(metadb_handle_ptr p_location,t_filestats p_stats,file_info & p_info) = 0;
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE(file_info_filter,service_base);
|
||||
};
|
||||
|
||||
//! Advanced interface for passing infos read from files to metadb backend. Use metadb_io_v2::create_hint_list() to instantiate.
|
||||
class NOVTABLE metadb_hint_list : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(metadb_hint_list,service_base);
|
||||
public:
|
||||
//! Helper.
|
||||
static metadb_hint_list::ptr create();
|
||||
//! Adds a hint to the list.
|
||||
//! @param p_location Location of the item the hint applies to.
|
||||
//! @param p_info file_info object describing the item.
|
||||
//! @param p_stats Information about the file containing item the hint applies to.
|
||||
//! @param p_freshflag Set to true if the info has been directly read from the file, false if it comes from another source such as a playlist file.
|
||||
virtual void add_hint(metadb_handle_ptr const & p_location,const file_info & p_info,const t_filestats & p_stats,bool p_freshflag) = 0;
|
||||
//! Reads info from specified info reader instance and adds hints. May throw an exception in case info read has failed. \n
|
||||
//! If the file has multiple subsongs, info from all the subsongs will be read and pssed to add_hint(). \n
|
||||
//! Note that an input_info_writer is a subclass of input_info_reader - so any input_info_reader OR input_info_writer is a valid argument for add_hint_reader(). \n
|
||||
//! This method is often called with your input_info_writer instance after committing tag updates, to notify metadb about altered tags.
|
||||
//! Reads info from specified info reader instance and adds hints. May throw an exception in case info read has failed.
|
||||
virtual void add_hint_reader(const char * p_path,service_ptr_t<input_info_reader> const & p_reader,abort_callback & p_abort) = 0;
|
||||
//! Call this when you're done working with this metadb_hint_list instance, to apply hints and dispatch callbacks. \n
|
||||
//! If you don't call this, all added hints will be ignored. \n
|
||||
//! As a general rule, you should add as many infos as possible - such as all the tracks involved in some operation that you perform - then call on_done() once. \n
|
||||
//! on_done() is expensive because it not only updates the metadb, but tells all components about the changes made - refreshes playlists/autoplaylists, library viewers, etc. \n
|
||||
//! Calling on_done() repeatedly is inefficient and should be avoided.
|
||||
//! Call this when you're done working with this metadb_hint_list instance, to apply hints and dispatch callbacks. If you don't call this, all added hints will be ignored.
|
||||
virtual void on_done() = 0;
|
||||
};
|
||||
|
||||
//! \since 1.0
|
||||
//! To obtain metadb_hint_list_v2, use service_query on a metadb_hint_list object. \n
|
||||
//! Simplified: metadb_hint_list_v2::ptr v2; v2 ^= myHintList; ( causes bugcheck if old fb2k / no interface ).
|
||||
class NOVTABLE metadb_hint_list_v2 : public metadb_hint_list {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(metadb_hint_list_v2, metadb_hint_list);
|
||||
public:
|
||||
//! Helper.
|
||||
static metadb_hint_list_v2::ptr create();
|
||||
//! Hint with browse info. \n
|
||||
//! See: metadb_handle::get_browse_info() for browse info rationale.
|
||||
//! @param p_location Location for which we're providing browse info.
|
||||
//! @param p_info Browse info for this location.
|
||||
//! @param browseTS timestamp of the browse info - such as last-modified time of the playlist file providing browse info.
|
||||
virtual void add_hint_browse(metadb_handle_ptr const & p_location,const file_info & p_info, t_filetimestamp browseTS) = 0;
|
||||
};
|
||||
|
||||
//! \since 1.3
|
||||
//! To obtain metadb_hint_list_v3, use service_query on a metadb_hint_list object. \n
|
||||
//! Simplified: metadb_hint_list_v3::ptr v3; v3 ^= myHintList; ( causes bugcheck if old fb2k / no interface ).
|
||||
class NOVTABLE metadb_hint_list_v3 : public metadb_hint_list_v2 {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(metadb_hint_list_v3, metadb_hint_list_v2);
|
||||
public:
|
||||
//! Helper.
|
||||
static metadb_hint_list_v3::ptr create();
|
||||
//! Hint primary info with a metadb_info_container.
|
||||
virtual void add_hint_v3(metadb_handle_ptr const & p_location, metadb_info_container::ptr info,bool p_freshflag) = 0;
|
||||
//! Hint browse info with a metadb_info_container.
|
||||
virtual void add_hint_browse_v3(metadb_handle_ptr const & p_location,metadb_info_container::ptr info) = 0;
|
||||
|
||||
//! Add a forced hint.\n
|
||||
//! A normal hint may or may not cause metadb update - metadb is not updated if the file has not changed according to last modified time. \n
|
||||
//! A forced hint always updates metadb regardless of timestamps.
|
||||
virtual void add_hint_forced(metadb_handle_ptr const & p_location, const file_info & p_info,const t_filestats & p_stats,bool p_freshflag) = 0;
|
||||
//! Add a forced hint, with metadb_info_container. \n
|
||||
//! Forced hint rationale - see add_hint_forced().
|
||||
virtual void add_hint_forced_v3(metadb_handle_ptr const & p_location, metadb_info_container::ptr info,bool p_freshflag) = 0;
|
||||
//! Adds a forced hint, with an input_info_reader. \n
|
||||
//! Forced hint rationale - see add_hint_forced(). \n
|
||||
//! Info reader use rationale - see add_hint_reader().
|
||||
virtual void add_hint_forced_reader(const char * p_path,service_ptr_t<input_info_reader> const & p_reader,abort_callback & p_abort) = 0;
|
||||
};
|
||||
|
||||
@@ -178,8 +149,7 @@ public:
|
||||
//! @param p_notify Called when the task is completed. Status code is one of t_update_info values. Can be null if caller doesn't care.
|
||||
virtual void remove_info_async(metadb_handle_list_cref p_list,HWND p_parent_window,t_uint32 p_op_flags,completion_notify_ptr p_notify) = 0;
|
||||
|
||||
//! Creates a metadb_hint_list object. \n
|
||||
//! Contrary to other metadb_io methods, this can be safely called in a worker thread. You only need to call the hint list's on_done() method in main thread to finalize.
|
||||
//! Creates a metadb_hint_list object.
|
||||
virtual metadb_hint_list::ptr create_hint_list() = 0;
|
||||
|
||||
//! Updates tags of the specified tracks. Helper; uses update_info_async internally.
|
||||
@@ -192,8 +162,6 @@ public:
|
||||
//! Helper to be called after a file has been rechaptered. \n
|
||||
//! Forcibly reloads info then tells playlist_manager to update all affected playlists.
|
||||
void on_file_rechaptered( const char * path, metadb_handle_list_cref newItems );
|
||||
//! Helper to be called after a file has been rechaptered. \n
|
||||
//! Forcibly reloads info then tells playlist_manager to update all affected playlists.
|
||||
void on_files_rechaptered( metadb_handle_list_cref newHandles );
|
||||
|
||||
FB2K_MAKE_SERVICE_COREAPI_EXTENSION(metadb_io_v2,metadb_io);
|
||||
@@ -206,7 +174,7 @@ public:
|
||||
virtual void on_changed_sorted(metadb_handle_list_cref p_items_sorted, bool p_fromhook) = 0;
|
||||
};
|
||||
|
||||
//! \since 0.9.5
|
||||
//! New (0.9.5)
|
||||
class NOVTABLE metadb_io_v3 : public metadb_io_v2 {
|
||||
public:
|
||||
virtual void register_callback(metadb_io_callback_dynamic * p_callback) = 0;
|
||||
@@ -215,29 +183,6 @@ public:
|
||||
FB2K_MAKE_SERVICE_COREAPI_EXTENSION(metadb_io_v3,metadb_io_v2);
|
||||
};
|
||||
|
||||
|
||||
class threaded_process_callback;
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 80
|
||||
//! \since 1.5
|
||||
class NOVTABLE metadb_io_v4 : public metadb_io_v3 {
|
||||
FB2K_MAKE_SERVICE_COREAPI_EXTENSION(metadb_io_v4, metadb_io_v3);
|
||||
public:
|
||||
//! Creates an update-info task, that can be either fed to threaded_process API, or invoked by yourself respecting threaded_process semantics. \n
|
||||
//! May return null pointer if the operation has been refused (by user settings or such). \n
|
||||
//! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup.
|
||||
virtual service_ptr_t<threaded_process_callback> spawn_update_info( metadb_handle_list_cref items, service_ptr_t<file_info_filter> p_filter, uint32_t opFlags, completion_notify_ptr reply ) = 0;
|
||||
//! Creates an remove-info task, that can be either fed to threaded_process API, or invoked by yourself respecting threaded_process semantics. \n
|
||||
//! May return null pointer if the operation has been refused (by user settings or such). \n
|
||||
//! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup.
|
||||
virtual service_ptr_t<threaded_process_callback> spawn_remove_info( metadb_handle_list_cref items, uint32_t opFlags, completion_notify_ptr reply) = 0;
|
||||
//! Creates an load-info task, that can be either fed to threaded_process API, or invoked by yourself respecting threaded_process semantics. \n
|
||||
//! May return null pointer if the operation has been refused (for an example no loading is needed for these items). \n
|
||||
//! Useful for performing the operation with your own in-dialog progress display instead of the generic progress popup.
|
||||
virtual service_ptr_t<threaded_process_callback> spawn_load_info( metadb_handle_list_cref items, t_load_info_type opType, uint32_t opFlags, completion_notify_ptr reply) = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
//! metadb_io_callback_dynamic implementation helper.
|
||||
class metadb_io_callback_dynamic_impl_base : public metadb_io_callback_dynamic {
|
||||
public:
|
||||
@@ -297,8 +242,8 @@ public:
|
||||
//! @returns True on success; false on failure (no known playable locations).
|
||||
static bool g_get_random_handle(metadb_handle_ptr & p_out);
|
||||
|
||||
enum {case_sensitive = playable_location::case_sensitive};
|
||||
typedef playable_location::path_comparator path_comparator;
|
||||
enum {case_sensitive = true};
|
||||
typedef pfc::comparator_strcmp path_comparator;
|
||||
|
||||
inline static int path_compare_ex(const char * p1,t_size len1,const char * p2,t_size len2) {return case_sensitive ? pfc::strcmp_ex(p1,len1,p2,len2) : stricmp_utf8_ex(p1,len1,p2,len2);}
|
||||
inline static int path_compare_nc(const char * p1, size_t len1, const char * p2, size_t len2) {return case_sensitive ? pfc::strcmp_nc(p1,len1,p2,len2) : stricmp_utf8_ex(p1,len1,p2,len2);}
|
||||
@@ -349,6 +294,38 @@ public:
|
||||
|
||||
|
||||
|
||||
//! Helper implementation of file_info_filter_impl.
|
||||
class file_info_filter_impl : public file_info_filter {
|
||||
public:
|
||||
file_info_filter_impl(const pfc::list_base_const_t<metadb_handle_ptr> & p_list,const pfc::list_base_const_t<const file_info*> & p_new_info) {
|
||||
FB2K_DYNAMIC_ASSERT(p_list.get_count() == p_new_info.get_count());
|
||||
pfc::array_t<t_size> order;
|
||||
order.set_size(p_list.get_count());
|
||||
order_helper::g_fill(order.get_ptr(),order.get_size());
|
||||
p_list.sort_get_permutation_t(pfc::compare_t<metadb_handle_ptr,metadb_handle_ptr>,order.get_ptr());
|
||||
m_handles.set_count(order.get_size());
|
||||
m_infos.set_size(order.get_size());
|
||||
for(t_size n = 0; n < order.get_size(); n++) {
|
||||
m_handles[n] = p_list[order[n]];
|
||||
m_infos[n] = *p_new_info[order[n]];
|
||||
}
|
||||
}
|
||||
|
||||
bool apply_filter(metadb_handle_ptr p_location,t_filestats p_stats,file_info & p_info) {
|
||||
t_size index;
|
||||
if (m_handles.bsearch_t(pfc::compare_t<metadb_handle_ptr,metadb_handle_ptr>,p_location,index)) {
|
||||
p_info = m_infos[index];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private:
|
||||
metadb_handle_list m_handles;
|
||||
pfc::array_t<file_info_impl> m_infos;
|
||||
};
|
||||
|
||||
|
||||
//! \since 1.1
|
||||
//! metadb_index_manager hash, currently a 64bit int, typically made from halving MD5 hash.
|
||||
typedef t_uint64 metadb_index_hash;
|
||||
|
||||
@@ -99,30 +99,3 @@ metadb_info_container::ptr metadb_handle::get_full_info_ref( abort_callback & ab
|
||||
reader->get_info( this->get_subsong_index(), obj->m_info, aborter );
|
||||
return obj;
|
||||
}
|
||||
|
||||
namespace fb2k {
|
||||
pfc::string_formatter formatTrackList( metadb_handle_list_cref lst ) {
|
||||
pfc::string_formatter ret;
|
||||
auto cnt = lst.get_count();
|
||||
if ( cnt == 0 ) ret << "[Empty track list]\n";
|
||||
else {
|
||||
if (cnt == 1) ret << "[Track list: 1 track]\n";
|
||||
else ret << "[Track list: " << cnt << " tracks]\n";
|
||||
for( size_t walk = 0; walk < cnt; ++ walk ) {
|
||||
ret << " " << lst[walk]->get_location() << "\n";
|
||||
}
|
||||
ret << "[Track list end]";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
pfc::string_formatter formatTrackTitle(metadb_handle_ptr item, const char * script ) {
|
||||
pfc::string_formatter ret;
|
||||
item->format_title_legacy(NULL,ret,script,NULL);
|
||||
return ret;
|
||||
}
|
||||
pfc::string_formatter formatTrackTitle(metadb_handle_ptr item,service_ptr_t<class titleformat_object> script) {
|
||||
pfc::string_formatter ret;
|
||||
item->format_title(NULL,ret,script,NULL);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
class titleformat_hook;
|
||||
class titleformat_text_filter;
|
||||
class titleformat_object;
|
||||
@@ -17,6 +15,7 @@ public:
|
||||
//! To obtain a metadb_handle to specific location, use metadb::handle_create(). To obtain a list of metadb_handle objects corresponding to specific path (directory, playlist, multitrack file, etc), use relevant playlist_incoming_item_filter methods (recommended), or call playlist_loader methods directly.\n
|
||||
//! A metadb_handle is also the most efficient way of passing playable object locations around because it provides fast access to both location and infos, and is reference counted so duplicating it is as fast as possible.\n
|
||||
//! To retrieve a path of a file from a metadb_handle, use metadb_handle::get_path() function. Note that metadb_handle is NOT just file path, some formats support multiple subsongs per physical file, which are signaled using subsong indexes.
|
||||
|
||||
class NOVTABLE metadb_handle : public service_base
|
||||
{
|
||||
public:
|
||||
@@ -40,11 +39,9 @@ public:
|
||||
//! Returns last seen file stats, filestats_invalid if unknown.
|
||||
virtual t_filestats get_filestats() const = 0;
|
||||
|
||||
//! Obsolete, use get_info_ref() family of methods instead. \n
|
||||
//! Queries whether cached info about item referenced by this metadb_handle object is already available. Note that this function causes the metadb to be temporarily locked; you can not use it in context that where locking is forbidden.\n
|
||||
//! Note that state of cached info changes only inside main thread, so you can safely assume that it doesn't change while some block of your code inside main thread is being executed.
|
||||
virtual bool is_info_loaded() const = 0;
|
||||
//! Obsolete, use get_info_ref() instead. \n
|
||||
//! Queries cached info about item referenced by this metadb_handle object. Returns true on success, false when info is not yet known. Note that this function causes the metadb to be temporarily locked; you can not use it in context that where locking is forbidden. \n
|
||||
//! Note that state of cached info changes only inside main thread, so you can safely assume that it doesn't change while some block of your code inside main thread is being executed.
|
||||
virtual bool get_info(file_info & p_info) const = 0;
|
||||
@@ -52,11 +49,9 @@ public:
|
||||
//! OBSOLETE, DO NOT CALL
|
||||
__declspec(deprecated) virtual bool get_info_locked(const file_info * & p_info) const = 0;
|
||||
|
||||
//! Obsolete, use get_info_ref() family of methods instead. \n
|
||||
//! Queries whether cached info about item referenced by this metadb_handle object is already available.\n
|
||||
//! This is intended for use in special cases when you need to immediately retrieve info sent by metadb_io hint from another thread; state of returned data can be altered by any thread, as opposed to non-async methods.
|
||||
virtual bool is_info_loaded_async() const = 0;
|
||||
//! Obsolete, use get_info_ref() family of methods instead. \n
|
||||
//! Queries cached info about item referenced by this metadb_handle object. Returns true on success, false when info is not yet known. Note that this function causes the metadb to be temporarily locked; you can not use it in context that where locking is forbidden.\n
|
||||
//! This is intended for use in special cases when you need to immediately retrieve info sent by metadb_io hint from another thread; state of returned data can be altered by any thread, as opposed to non-async methods.
|
||||
virtual bool get_info_async(file_info & p_info) const = 0;
|
||||
@@ -73,40 +68,34 @@ public:
|
||||
__declspec(deprecated) virtual void format_title_from_external_info_nonlocking(const file_info & p_info,titleformat_hook * p_hook,pfc::string_base & p_out,const service_ptr_t<titleformat_object> & p_script,titleformat_text_filter * p_filter) = 0;
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 76
|
||||
//! \since 1.0
|
||||
//! Returns browse info for this track. \n
|
||||
//! Browse info comes from an external source - such as internet playlist metadata - not from the media file itself, and is maintained separately. \n
|
||||
//! When title formatting calls are invoked on for a track having browse info present, data for title formatting is sourced from both primary and browse info. \n
|
||||
//! Example: internet radio stream provides no metadata but its playlist XML has title (radio station name), %title% resolves to the radio station name from the playlist.
|
||||
//! New in 1.0
|
||||
virtual bool get_browse_info(file_info & info, t_filetimestamp & ts) const = 0;
|
||||
|
||||
//! \since 1.0
|
||||
//! OBSOLETE, DO NOT CALL
|
||||
__declspec(deprecated) virtual bool get_browse_info_locked(const file_info * & p_info, t_filetimestamp & ts) const = 0;
|
||||
#endif
|
||||
#if FOOBAR2000_TARGET_VERSION >= 78
|
||||
//! \since 1.3
|
||||
//! Retrieve a reference to the primary info. \n
|
||||
//! You can hold the reference to the info as long as you like, call the method in any context you like with no lock semantics involved. The info held by the returned reference will remain constant even if the metadb content changes. \n
|
||||
//! You can hold the reference to the info as long as you like, call the method in any context you like with no lock semantics involved. The info held by the returned reference will remain constant even if the metadb content changes.
|
||||
//! Returns true and sets outInfo to a reference to this item's primary info on success, returns false on failure (no info known at this time).
|
||||
virtual bool get_info_ref(metadb_info_container::ptr & outInfo) const = 0;
|
||||
|
||||
//! \since 1.3
|
||||
//! Retrieve a reference to the async info (pending info update). If async info isn't set, a reference to the primary info is returned.\n
|
||||
//! You can hold the reference to the info as long as you like, call the method in any context you like with no lock semantics involved. The info held by the returned reference will remain constant even if the metadb content changes. \n
|
||||
//! You can hold the reference to the info as long as you like, call the method in any context you like with no lock semantics involved. The info held by the returned reference will remain constant even if the metadb content changes.
|
||||
//! Returns true and sets outInfo to a reference to this item's async or primary info on success, returns false on failure (no info known at this time).
|
||||
virtual bool get_async_info_ref(metadb_info_container::ptr & outInfo) const = 0;
|
||||
|
||||
//! \since 1.3
|
||||
//! Retrieve references to the item's primary and browse infos. If no info is set, NULL pointers are returned. For most local files, browse info is not available and you get a NULL for it.\n
|
||||
//! Since browse info is usually used along with the primary info (as a fallback for missing metas), you can get the two with one call for better performance. \n
|
||||
//! You can hold the reference to the info as long as you like, call the method in any context you like with no lock semantics involved. The info held by the returned reference will remain constant even if the metadb content changes. \n
|
||||
//! See also: get_browse_info(), for browse info rationale.
|
||||
//! Since browse info is usually used along with the primary info (as a fallback for missing metas), you can get the two with one call for better performance.
|
||||
//! You can hold the reference to the info as long as you like, call the method in any context you like with no lock semantics involved. The info held by the returned reference will remain constant even if the metadb content changes.
|
||||
virtual void get_browse_info_ref(metadb_info_container::ptr & outInfo, metadb_info_container::ptr & outBrowse) const = 0;
|
||||
|
||||
//! Simplified method, always returns non-null, dummy info if nothing to return.
|
||||
//! Simplified method, always returns non-null, dummy info if nothing to return
|
||||
virtual metadb_info_container::ptr get_info_ref() const = 0;
|
||||
//! Simplified method, always returns non-null, dummy info if nothing to return.
|
||||
//! Simplified method, always returns non-null, dummy info if nothing to return
|
||||
virtual metadb_info_container::ptr get_async_info_ref() const = 0;
|
||||
|
||||
//! \since 1.3
|
||||
@@ -116,11 +105,10 @@ public:
|
||||
#endif
|
||||
|
||||
//! \since 1.3
|
||||
//! Helper using get_browse_info_ref(). \n
|
||||
//! Retrieves primary info + browse info merged together. \n
|
||||
//! Returns true on success, false if neither info is available. \n
|
||||
//! If neither info is avaialble, output data structure is emptied. \n
|
||||
//! See also: get_browse_info() for browse info rationale.
|
||||
//! Helper using get_browse_info_ref()
|
||||
//! Retrieves primary info + browse info merged together.
|
||||
//! Returns true on success, false if neither info is available.
|
||||
//! If neither info is avaialble, output data structure is emptied.
|
||||
bool get_browse_info_merged(file_info & infoMerged) const;
|
||||
|
||||
|
||||
@@ -143,9 +131,6 @@ public:
|
||||
t_filetimestamp get_filetimestamp();
|
||||
t_filesize get_filesize();
|
||||
|
||||
//! Internal method, do not use
|
||||
inline const char * _get_path() const { return get_path(); }
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE(metadb_handle,service_base);
|
||||
};
|
||||
|
||||
@@ -253,10 +238,17 @@ inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const metadb_handl
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace fb2k {
|
||||
pfc::string_formatter formatTrackList( metadb_handle_list_cref );
|
||||
pfc::string_formatter formatTrackTitle(metadb_handle_ptr item, const char * script = "%title%" );
|
||||
pfc::string_formatter formatTrackTitle(metadb_handle_ptr item,service_ptr_t<class titleformat_object> script);
|
||||
class string_format_title {
|
||||
public:
|
||||
string_format_title(metadb_handle_ptr p_item,const char * p_script) {
|
||||
p_item->format_title_legacy(NULL,m_data,p_script,NULL);
|
||||
}
|
||||
string_format_title(metadb_handle_ptr p_item,service_ptr_t<class titleformat_object> p_script) {
|
||||
p_item->format_title(NULL,m_data,p_script,NULL);
|
||||
}
|
||||
|
||||
const char * get_ptr() const {return m_data.get_ptr();}
|
||||
operator const char * () const {return m_data.get_ptr();}
|
||||
private:
|
||||
pfc::string8_fastalloc m_data;
|
||||
};
|
||||
|
||||
@@ -89,7 +89,8 @@ namespace {
|
||||
|
||||
void metadb_handle_list_helper::sort_by_format_get_order(metadb_handle_list_cref p_list,t_size* order,const service_ptr_t<titleformat_object> & p_script,titleformat_hook * p_hook,int p_direction)
|
||||
{
|
||||
sort_by_format_get_order_v2(p_list, order, p_script, p_hook, p_direction, fb2k::noAbort );
|
||||
abort_callback_dummy noAbort;
|
||||
sort_by_format_get_order_v2(p_list, order, p_script, p_hook, p_direction, noAbort );
|
||||
}
|
||||
|
||||
void metadb_handle_list_helper::sort_by_relative_path(metadb_handle_list_ref p_list)
|
||||
|
||||
@@ -44,11 +44,12 @@ public:
|
||||
set_entry_count(total);
|
||||
t_size done = 0;
|
||||
pfc::string8 name; metadb_handle_list content;
|
||||
abort_callback_dummy abort;
|
||||
for(t_size walk = 0; walk < pltotal; ++walk) if (mask[walk]) {
|
||||
pfc::dynamic_assert( done < total );
|
||||
api->playlist_get_name(walk,name); api->playlist_get_all_items(walk,content);
|
||||
set_entry_name(done,name); set_entry_content(done,content);
|
||||
stream_writer_buffer_simple sideData; api->playlist_get_sideinfo(walk, &sideData, fb2k::noAbort);
|
||||
stream_writer_buffer_simple sideData; api->playlist_get_sideinfo(walk, &sideData, abort);
|
||||
set_side_data(done,sideData.m_buffer.get_ptr(), sideData.m_buffer.get_size());
|
||||
++done;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
#include "foobar2000.h"
|
||||
#include "output.h"
|
||||
#include "audio_chunk_impl.h"
|
||||
#include "dsp.h"
|
||||
#include "resampler.h"
|
||||
|
||||
pfc::string8 output_entry::get_device_name( const GUID & deviceID ) {
|
||||
pfc::string8 temp;
|
||||
if (!get_device_name(deviceID, temp)) temp = "[unknown device]";
|
||||
return temp;
|
||||
return std::move(temp);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -50,25 +46,8 @@ output_entry::ptr output_entry::g_find( const GUID & outputID ) {
|
||||
}
|
||||
|
||||
|
||||
bool output::is_progressing_() {
|
||||
output_v4::ptr v4;
|
||||
if ( v4 &= this ) return v4->is_progressing();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t output::update_v2_() {
|
||||
output_v4::ptr v4;
|
||||
if ( v4 &= this ) return v4->update_v2();
|
||||
bool bReady = false;
|
||||
this->update(bReady);
|
||||
return bReady ? SIZE_MAX : 0;
|
||||
}
|
||||
|
||||
pfc::eventHandle_t output::get_trigger_event_() {
|
||||
output_v4::ptr v4;
|
||||
if ( v4 &= this ) return v4->get_trigger_event();
|
||||
return pfc::eventInvalid;
|
||||
}
|
||||
|
||||
void output_impl::flush() {
|
||||
m_incoming_ptr = 0;
|
||||
@@ -80,11 +59,7 @@ void output_impl::flush_changing_track() {
|
||||
m_incoming.set_size(0);
|
||||
on_flush_changing_track();
|
||||
}
|
||||
|
||||
void output_impl::update(bool & p_ready) {
|
||||
p_ready = update_v2() > 0;
|
||||
}
|
||||
size_t output_impl::update_v2() {
|
||||
on_update();
|
||||
if (m_incoming_spec != m_active_spec && m_incoming_ptr < m_incoming.get_size()) {
|
||||
if (get_latency_samples() == 0) {
|
||||
@@ -94,7 +69,6 @@ size_t output_impl::update_v2() {
|
||||
force_play();
|
||||
}
|
||||
}
|
||||
size_t retCanWriteSamples = 0;
|
||||
if (m_incoming_spec == m_active_spec && m_incoming_ptr < m_incoming.get_size()) {
|
||||
t_size cw = can_write_samples() * m_incoming_spec.m_channels;
|
||||
t_size delta = pfc::min_t(m_incoming.get_size() - m_incoming_ptr,cw);
|
||||
@@ -102,13 +76,9 @@ size_t output_impl::update_v2() {
|
||||
write(audio_chunk_temp_impl(m_incoming.get_ptr()+m_incoming_ptr,delta / m_incoming_spec.m_channels,m_incoming_spec.m_sample_rate,m_incoming_spec.m_channels,m_incoming_spec.m_channel_config));
|
||||
m_incoming_ptr += delta;
|
||||
}
|
||||
retCanWriteSamples = (cw - delta) / m_incoming_spec.m_channels;
|
||||
} else if ( m_incoming_ptr == m_incoming.get_size() ) {
|
||||
retCanWriteSamples = SIZE_MAX;
|
||||
}
|
||||
return retCanWriteSamples;
|
||||
p_ready = (m_incoming_ptr == m_incoming.get_size());
|
||||
}
|
||||
|
||||
double output_impl::get_latency() {
|
||||
double ret = 0;
|
||||
if (m_incoming_spec.is_valid()) {
|
||||
@@ -131,50 +101,18 @@ void output_impl::process_samples(const audio_chunk & p_chunk) {
|
||||
}
|
||||
|
||||
void output_v3::get_injected_dsps( dsp_chain_config & dsps ) {
|
||||
#ifdef FOOBAR2000_HAVE_DSP
|
||||
dsps.remove_all();
|
||||
#if 0 // DEPRECATED
|
||||
unsigned rate = this->get_forced_sample_rate();
|
||||
if (rate != 0) {
|
||||
#if PFC_DEBUG
|
||||
FB2K_console_formatter() << "output_v3::get_injected_dsps() : requesting resampling to " << rate << " Hz";
|
||||
#endif
|
||||
dsp_preset_impl temp;
|
||||
if (resampler_entry::g_create_preset( temp, 0, rate, 0 )) {
|
||||
dsps.insert_item( temp, dsps.get_count() );
|
||||
} else {
|
||||
#if PFC_DEBUG
|
||||
FB2K_console_formatter() << "output_v3::get_injected_dsps() : resampler could not be created";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // FOOBAR2000_HAVE_DSP
|
||||
}
|
||||
|
||||
size_t output_v4::update_v2() {
|
||||
bool bReady = false;
|
||||
update(bReady);
|
||||
return bReady ? SIZE_MAX : 0;
|
||||
}
|
||||
|
||||
uint32_t output_entry::get_config_flags_compat() {
|
||||
uint32_t ret = get_config_flags();
|
||||
if ((ret & (flag_low_latency | flag_high_latency)) == 0) {
|
||||
// output predating flag_high_latency + flag_low_latency
|
||||
// if it's old foo_out_upnp, report high latency, otherwise low latency.
|
||||
static const GUID guid_foo_out_upnp = { 0x9900b4f6, 0x8431, 0x4b0a, { 0x95, 0x56, 0xa7, 0xfc, 0xb9, 0x5b, 0x74, 0x3 } };
|
||||
if (this->get_guid() == guid_foo_out_upnp) ret |= flag_high_latency;
|
||||
else ret |= flag_low_latency;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool output_entry::is_high_latency() {
|
||||
return (this->get_config_flags_compat() & flag_high_latency) != 0;
|
||||
}
|
||||
|
||||
bool output_entry::is_low_latency() {
|
||||
return (this->get_config_flags_compat() & flag_low_latency) != 0;
|
||||
}
|
||||
|
||||
// {EEEB07DE-C2C8-44c2-985C-C85856D96DA1}
|
||||
const GUID output_id_null =
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
PFC_DECLARE_EXCEPTION(exception_output_device_not_found, pfc::exception, "Audio device not found")
|
||||
PFC_DECLARE_EXCEPTION(exception_output_module_not_found, exception_output_device_not_found, "Output module not found")
|
||||
PFC_DECLARE_EXCEPTION(exception_output_invalidated, pfc::exception, "Audio device invalidated")
|
||||
PFC_DECLARE_EXCEPTION(exception_output_device_in_use, pfc::exception, "Audio device in use")
|
||||
PFC_DECLARE_EXCEPTION(exception_output_unsupported_stream_format, pfc::exception, "Unsupported audio stream format")
|
||||
|
||||
|
||||
// =======================================================
|
||||
@@ -20,15 +17,17 @@ PFC_DECLARE_EXCEPTION(exception_output_unsupported_stream_format, pfc::exception
|
||||
//! Structure describing PCM audio data format, with basic helper functions.
|
||||
struct t_pcmspec
|
||||
{
|
||||
unsigned m_sample_rate = 0;
|
||||
unsigned m_bits_per_sample = 0;
|
||||
unsigned m_channels = 0,m_channel_config = 0;
|
||||
bool m_float = false;
|
||||
inline t_pcmspec() {reset();}
|
||||
inline t_pcmspec(const t_pcmspec & p_source) {*this = p_source;}
|
||||
unsigned m_sample_rate;
|
||||
unsigned m_bits_per_sample;
|
||||
unsigned m_channels,m_channel_config;
|
||||
bool m_float;
|
||||
|
||||
inline unsigned align() const {return (m_bits_per_sample / 8) * m_channels;}
|
||||
|
||||
uint64_t time_to_bytes(double p_time) const {return audio_math::time_to_samples(p_time,m_sample_rate) * (m_bits_per_sample / 8) * m_channels;}
|
||||
double bytes_to_time(uint64_t p_bytes) const {return (double) (p_bytes / ((m_bits_per_sample / 8) * m_channels)) / (double) m_sample_rate;}
|
||||
t_size time_to_bytes(double p_time) const {return (t_size)audio_math::time_to_samples(p_time,m_sample_rate) * (m_bits_per_sample / 8) * m_channels;}
|
||||
double bytes_to_time(t_size p_bytes) const {return (double) (p_bytes / ((m_bits_per_sample / 8) * m_channels)) / (double) m_sample_rate;}
|
||||
|
||||
inline bool operator==(/*const t_pcmspec & p_spec1,*/const t_pcmspec & p_spec2) const
|
||||
{
|
||||
@@ -44,7 +43,7 @@ struct t_pcmspec
|
||||
return !(*this == p_spec2);
|
||||
}
|
||||
|
||||
inline void reset() { *this = t_pcmspec(); }
|
||||
inline void reset() {m_sample_rate = 0; m_bits_per_sample = 0; m_channels = 0; m_channel_config = 0; m_float = false;}
|
||||
inline bool is_valid() const
|
||||
{
|
||||
return m_sample_rate >= 1000 && m_sample_rate <= 1000000 &&
|
||||
@@ -115,71 +114,26 @@ public:
|
||||
//! @p_val Volume level in dB. Value of 0 indicates full ("100%") volume, negative values indciate different attenuation levels.
|
||||
virtual void volume_set(double p_val) = 0;
|
||||
|
||||
//! Helper, see output_v4::is_progressing().
|
||||
bool is_progressing_();
|
||||
//! Helper, see output_v4::update_v2()
|
||||
size_t update_v2_();
|
||||
//! Helper, see output_v4::get_event_trigger()
|
||||
pfc::eventHandle_t get_trigger_event_();
|
||||
|
||||
//! Helper for output_entry implementation.
|
||||
static uint32_t g_extra_flags() { return 0; }
|
||||
|
||||
};
|
||||
|
||||
class NOVTABLE output_v2 : public output {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(output_v2, output);
|
||||
public:
|
||||
//! Obsolete, do not use.
|
||||
virtual bool want_track_marks() {return false;}
|
||||
//! Obsolete, do not use.
|
||||
virtual void on_track_mark() {}
|
||||
//! Obsolete, do not use.
|
||||
virtual void enable_fading(bool state) {}
|
||||
//! Called when flushing due to manual track change rather than seek-within-track
|
||||
virtual void flush_changing_track() {flush();}
|
||||
};
|
||||
|
||||
class dsp_chain_config;
|
||||
|
||||
//! \since 1.4
|
||||
class NOVTABLE output_v3 : public output_v2 {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(output_v3, output_v2);
|
||||
public:
|
||||
//! Does this output require a specific sample rate? If yes, return the value, otherwise return zero. \n
|
||||
//! Returning a nonzero will cause a resampler DSP to be injected.
|
||||
virtual unsigned get_forced_sample_rate() { return 0; }
|
||||
//! Allows the output to inject specific DSPs at the end of the used chain. \n
|
||||
//! Default implementation queries get_forced_sample_rate() and injects a resampler.
|
||||
virtual void get_injected_dsps( dsp_chain_config & );
|
||||
};
|
||||
|
||||
//! \since 1.6
|
||||
class NOVTABLE output_v4 : public output_v3 {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(output_v4, output_v3);
|
||||
public:
|
||||
//! Returns an event handle that becomes signaled once the output wants an update() call and possibly process_samples(). \n
|
||||
//! Optional; may return pfc::eventInvalid if not available at this time or not supported. \n
|
||||
//! If implemented, calling update() should clear the event each time.
|
||||
virtual pfc::eventHandle_t get_trigger_event() {return pfc::eventInvalid;}
|
||||
//! Returns whether the audio stream is currently being played or not. \n
|
||||
//! Typically, for a short period of time, initially send data is not played until a sufficient amount is queued to initiate playback without glitches. \n
|
||||
//! For old outputs that do not implement this, the value can be assumed to be true.
|
||||
virtual bool is_progressing() {return true;}
|
||||
|
||||
//! Improved version of update(); returns 0 if the output isn't ready to receive any new data, otherwise an advisory number of samples - at the current stream format - that the output expects to take now. \n
|
||||
//! If the caller changes the stream format, the value is irrelevant. \n
|
||||
//! The output may return SIZE_MAX to indicate that it can take data but does not currently have any hints to tell how much.
|
||||
virtual size_t update_v2();
|
||||
};
|
||||
|
||||
//! \since 1.6
|
||||
class output_v5 : public output_v4 {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(output_v5, output_v4);
|
||||
public:
|
||||
virtual unsigned get_forced_channel_mask() { return 0; }
|
||||
};
|
||||
|
||||
class NOVTABLE output_entry : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(output_entry);
|
||||
public:
|
||||
@@ -192,34 +146,20 @@ public:
|
||||
//! For internal use by backend. Retrieves human-readable name of this output_entry implementation.
|
||||
virtual const char * get_name() = 0;
|
||||
|
||||
//! Obsolete, do not use.
|
||||
//! Pops up advanced settings dialog. This method is optional and not supported if get_config_flag() return value does not have flag_needs_advanced_config set.
|
||||
//! @param p_parent Parent window for the dialog.
|
||||
//! @param p_menupoint Point in screen coordinates - can be used to display a simple popup menu with options to be checked instead of a full dialog.
|
||||
virtual void advanced_settings_popup(HWND p_parent,POINT p_menupoint) = 0;
|
||||
|
||||
enum {
|
||||
flag_needs_bitdepth_config = 1 << 0,
|
||||
flag_needs_dither_config = 1 << 1,
|
||||
//! Obsolete, do not use.
|
||||
flag_needs_advanced_config = 1 << 2,
|
||||
flag_needs_device_list_prefixes = 1 << 3,
|
||||
|
||||
//! Supports playing multiple simultaneous audio streams thru one device?
|
||||
flag_supports_multiple_streams = 1 << 4,
|
||||
|
||||
//! High latency operation (such as remote network playback), mutually exclusive with flag_low_latency
|
||||
flag_high_latency = 1 << 5,
|
||||
//! Low latency operation (local playback), mutually exclusive with flag_high_latency
|
||||
flag_low_latency = 1 << 6,
|
||||
//! When set, the output will be used in special compatibility mode: guaranteed regular update() calls, injected padding (silence) at the end of stream.
|
||||
flag_needs_shims = 1 << 7,
|
||||
};
|
||||
|
||||
virtual t_uint32 get_config_flags() = 0;
|
||||
|
||||
uint32_t get_config_flags_compat();
|
||||
|
||||
bool is_high_latency();
|
||||
bool is_low_latency();
|
||||
|
||||
pfc::string8 get_device_name( const GUID & deviceID);
|
||||
bool get_device_name( const GUID & deviceID, pfc::string_base & out );
|
||||
|
||||
@@ -246,10 +186,6 @@ public:
|
||||
if (T::g_needs_bitdepth_config()) flags |= output_entry::flag_needs_bitdepth_config;
|
||||
if (T::g_needs_dither_config()) flags |= output_entry::flag_needs_dither_config;
|
||||
if (T::g_needs_device_list_prefixes()) flags |= output_entry::flag_needs_device_list_prefixes ;
|
||||
if (T::g_supports_multiple_streams()) flags |= output_entry::flag_supports_multiple_streams;
|
||||
if (T::g_is_high_latency()) flags |= output_entry::flag_high_latency;
|
||||
else flags |= output_entry::flag_low_latency;
|
||||
flags |= T::g_extra_flags();
|
||||
return flags;
|
||||
}
|
||||
};
|
||||
@@ -259,7 +195,7 @@ public:
|
||||
template<class T>
|
||||
class output_factory_t : public service_factory_single_t<output_entry_impl_t<T> > {};
|
||||
|
||||
class output_impl : public output_v5 {
|
||||
class output_impl : public output_v3 {
|
||||
protected:
|
||||
output_impl() : m_incoming_ptr(0) {}
|
||||
virtual void on_update() = 0;
|
||||
@@ -280,7 +216,6 @@ private:
|
||||
void flush();
|
||||
void flush_changing_track();
|
||||
void update(bool & p_ready);
|
||||
size_t update_v2();
|
||||
double get_latency();
|
||||
void process_samples(const audio_chunk & p_chunk);
|
||||
|
||||
@@ -328,31 +263,6 @@ public:
|
||||
virtual bool hasVisualisation() = 0;
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class NOVTABLE output_devices_notify {
|
||||
public:
|
||||
virtual void output_devices_changed() = 0;
|
||||
protected:
|
||||
output_devices_notify() {}
|
||||
private:
|
||||
output_devices_notify(const output_devices_notify &) = delete;
|
||||
void operator=(const output_devices_notify &) = delete;
|
||||
};
|
||||
|
||||
//! \since 1.5
|
||||
class NOVTABLE output_entry_v3 : public output_entry_v2 {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(output_entry_v3, output_entry_v2)
|
||||
public:
|
||||
|
||||
//! Main thread only!
|
||||
virtual void add_notify(output_devices_notify *) = 0;
|
||||
//! Main thread only!
|
||||
virtual void remove_notify(output_devices_notify *) = 0;
|
||||
|
||||
//! Main thread only!
|
||||
virtual void set_pinned_device(const GUID & guid) = 0;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
//! \since 1.3.5
|
||||
struct outputCoreConfig_t {
|
||||
|
||||
@@ -1,35 +1,16 @@
|
||||
#include "foobar2000.h"
|
||||
#include <exception>
|
||||
|
||||
void packet_decoder::g_open(service_ptr_t<packet_decoder> & p_out,bool p_decode,const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort)
|
||||
{
|
||||
std::exception_ptr rethrow;
|
||||
bool havePartial = false, tryingPartial = false;
|
||||
for ( ;; ) {
|
||||
service_enum_t<packet_decoder_entry> e;
|
||||
service_ptr_t<packet_decoder_entry> ptr;
|
||||
|
||||
while(e.next(ptr)) {
|
||||
p_abort.check();
|
||||
if (ptr->is_our_setup(p_owner,p_param1,p_param2,p_param2size)) {
|
||||
if (!tryingPartial && ptr->is_supported_partially_(p_owner, p_param1, p_param2, p_param2size)) {
|
||||
havePartial = true;
|
||||
} else {
|
||||
try {
|
||||
ptr->open(p_out,p_decode,p_owner,p_param1,p_param2,p_param2size,p_abort);
|
||||
return;
|
||||
} catch (exception_io_data) {
|
||||
rethrow = std::current_exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!havePartial || tryingPartial) break;
|
||||
tryingPartial = true;
|
||||
}
|
||||
|
||||
if (rethrow) std::rethrow_exception(rethrow);
|
||||
throw exception_io_data();
|
||||
}
|
||||
|
||||
@@ -53,11 +34,3 @@ void packet_decoder::setAllowDelayed( bool bAllow ) {
|
||||
this->set_stream_property( property_allow_delayed_output, bAllow ? 1 : 0, NULL, 0);
|
||||
}
|
||||
|
||||
bool packet_decoder_entry::is_supported_partially_(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size) {
|
||||
bool ret = false;
|
||||
packet_decoder_entry_v2::ptr v2;
|
||||
if (v2 &= this) {
|
||||
ret = v2->is_supported_partially(p_owner, p_param1, p_param2, p_param2size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -13,10 +13,6 @@ protected:
|
||||
void open(const GUID & p_owner,bool p_decode,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort) {throw exception_io_data();}
|
||||
public:
|
||||
|
||||
//! Prototype of function that must be implemented by packet_decoder implementation but is not accessible through packet_decoder interface itself.
|
||||
//! Returns true if this is not the preferred decoder for this format, another one should be used if found.
|
||||
static bool g_is_supported_partially(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size) { return false; }
|
||||
|
||||
|
||||
//! Forwards additional information about stream being decoded. \n
|
||||
//! Calling: this must be called immediately after packet_decoder object is created, before any other methods are called.\n
|
||||
@@ -38,7 +34,6 @@ public:
|
||||
|
||||
//! Decodes a block of audio data.\n
|
||||
//! It may return empty chunk even when successful (caused by encoder+decoder delay for an example), caller must check for it and handle it appropriately.
|
||||
//! Called with 0 bytes at the end of stream - if the decoder introduces a delay between input/output, any buffered data should be passed back then.
|
||||
virtual void decode(const void * p_buffer,t_size p_bytes,audio_chunk & p_chunk,abort_callback & p_abort)=0;
|
||||
|
||||
//! Returns whether this packet decoder supports analyze_first_frame() function.
|
||||
@@ -49,7 +44,7 @@ public:
|
||||
//! Static helper, creates a packet_decoder instance and initializes it with specific decoder setup data.
|
||||
static void g_open(service_ptr_t<packet_decoder> & p_out,bool p_decode,const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort);
|
||||
|
||||
static const GUID owner_MP4,owner_matroska,owner_MP3,owner_MP2,owner_MP1,owner_MP4_ALAC,owner_ADTS,owner_ADIF, owner_Ogg, owner_MP4_AMR, owner_MP4_AMR_WB, owner_MP4_AC3, owner_MP4_EAC3;
|
||||
static const GUID owner_MP4,owner_matroska,owner_MP3,owner_MP2,owner_MP1,owner_MP4_ALAC,owner_ADTS,owner_ADIF, owner_Ogg, owner_MP4_AMR, owner_MP4_AMR_WB, owner_MP4_AC3;
|
||||
|
||||
struct matroska_setup
|
||||
{
|
||||
@@ -87,21 +82,12 @@ public:
|
||||
//property_mp4_esds : p_param2 = MP4 ESDS chunk content as needed by some decoders
|
||||
static const GUID property_mp4_esds;
|
||||
|
||||
// DEPRECATED
|
||||
//property_allow_delayed_output : p_param1 = bool flag indicating whether the decoder than delay outputting audio data at will; essential for Apple AQ decoder
|
||||
static const GUID property_allow_delayed_output;
|
||||
|
||||
// property_mp3_delayless : return non-zero if this codec drops MP3 delay by itself
|
||||
static const GUID property_mp3_delayless;
|
||||
|
||||
// property_query_delay_samples :
|
||||
// Return non-zero if this codec has a decoder delay that the caller should deal with.
|
||||
// Param1 signals sample rate used by input - should always match decoder's sample rate - return zero if it does not match.
|
||||
static const GUID property_query_delay_samples;
|
||||
|
||||
// property_query_mp4_use_elst :
|
||||
// Return non-zero if MP4 elst should be used with this codec.
|
||||
static const GUID property_query_mp4_use_elst;
|
||||
|
||||
size_t initPadding();
|
||||
void setEventLogger(event_logger::ptr logger);
|
||||
void setCheckingIntegrity(bool checkingIntegrity);
|
||||
@@ -119,40 +105,29 @@ public:
|
||||
FB2K_MAKE_SERVICE_INTERFACE(packet_decoder_streamparse,packet_decoder);
|
||||
};
|
||||
|
||||
class NOVTABLE packet_decoder_entry : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(packet_decoder_entry);
|
||||
class NOVTABLE packet_decoder_entry : public service_base
|
||||
{
|
||||
public:
|
||||
virtual bool is_our_setup(const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size) = 0;
|
||||
virtual void open(service_ptr_t<packet_decoder> & p_out,bool p_decode,const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort) = 0;
|
||||
|
||||
//! Returns true if this is not the preferred decoder for this format, another one should be used if found.
|
||||
bool is_supported_partially_(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size);
|
||||
};
|
||||
|
||||
class NOVTABLE packet_decoder_entry_v2 : public packet_decoder_entry {
|
||||
FB2K_MAKE_SERVICE_INTERFACE(packet_decoder_entry_v2, packet_decoder_entry);
|
||||
public:
|
||||
//! Returns true if this is not the preferred decoder for this format, another one should be used if found.
|
||||
virtual bool is_supported_partially(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size) = 0;
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(packet_decoder_entry);
|
||||
};
|
||||
|
||||
|
||||
template<class T>
|
||||
class packet_decoder_entry_impl_t : public packet_decoder_entry_v2
|
||||
class packet_decoder_entry_impl_t : public packet_decoder_entry
|
||||
{
|
||||
public:
|
||||
bool is_our_setup(const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size) override {
|
||||
bool is_our_setup(const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size) {
|
||||
return T::g_is_our_setup(p_owner,p_param1,p_param2,p_param2size);
|
||||
}
|
||||
void open(service_ptr_t<packet_decoder> & p_out,bool p_decode,const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort) override {
|
||||
void open(service_ptr_t<packet_decoder> & p_out,bool p_decode,const GUID & p_owner,t_size p_param1,const void * p_param2,t_size p_param2size,abort_callback & p_abort) {
|
||||
PFC_ASSERT(is_our_setup(p_owner,p_param1,p_param2,p_param2size));
|
||||
service_ptr_t<T> instance = new service_impl_t<T>();
|
||||
instance->open(p_owner,p_decode,p_param1,p_param2,p_param2size,p_abort);
|
||||
p_out = instance.get_ptr();
|
||||
}
|
||||
bool is_supported_partially(const GUID& p_owner, t_size p_param1, const void* p_param2, t_size p_param2size) override {
|
||||
return T::g_is_supported_partially(p_owner, p_param1, p_param2, p_param2size);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -5,26 +5,26 @@ Use play_callback_manager to register your dynamically created instances. Static
|
||||
class NOVTABLE play_callback {
|
||||
public:
|
||||
//! Playback process is being initialized. on_playback_new_track() should be called soon after this when first file is successfully opened for decoding.
|
||||
virtual void on_playback_starting(play_control::t_track_command p_command,bool p_paused) = 0;
|
||||
virtual void FB2KAPI on_playback_starting(play_control::t_track_command p_command,bool p_paused) = 0;
|
||||
//! Playback advanced to new track.
|
||||
virtual void on_playback_new_track(metadb_handle_ptr p_track) = 0;
|
||||
virtual void FB2KAPI on_playback_new_track(metadb_handle_ptr p_track) = 0;
|
||||
//! Playback stopped.
|
||||
virtual void on_playback_stop(play_control::t_stop_reason p_reason) = 0;
|
||||
virtual void FB2KAPI on_playback_stop(play_control::t_stop_reason p_reason) = 0;
|
||||
//! User has seeked to specific time.
|
||||
virtual void on_playback_seek(double p_time) = 0;
|
||||
virtual void FB2KAPI on_playback_seek(double p_time) = 0;
|
||||
//! Called on pause/unpause.
|
||||
virtual void on_playback_pause(bool p_state) = 0;
|
||||
virtual void FB2KAPI on_playback_pause(bool p_state) = 0;
|
||||
//! Called when currently played file gets edited.
|
||||
virtual void on_playback_edited(metadb_handle_ptr p_track) = 0;
|
||||
virtual void FB2KAPI on_playback_edited(metadb_handle_ptr p_track) = 0;
|
||||
//! Dynamic info (VBR bitrate etc) change.
|
||||
virtual void on_playback_dynamic_info(const file_info & p_info) = 0;
|
||||
virtual void FB2KAPI on_playback_dynamic_info(const file_info & p_info) = 0;
|
||||
//! Per-track dynamic info (stream track titles etc) change. Happens less often than on_playback_dynamic_info().
|
||||
virtual void on_playback_dynamic_info_track(const file_info & p_info) = 0;
|
||||
virtual void FB2KAPI on_playback_dynamic_info_track(const file_info & p_info) = 0;
|
||||
//! Called every second, for time display
|
||||
virtual void on_playback_time(double p_time) = 0;
|
||||
virtual void FB2KAPI on_playback_time(double p_time) = 0;
|
||||
//! User changed volume settings. Possibly called when not playing.
|
||||
//! @param p_new_val new volume level in dB; 0 for full volume.
|
||||
virtual void on_volume_change(float p_new_val) = 0;
|
||||
virtual void FB2KAPI on_volume_change(float p_new_val) = 0;
|
||||
|
||||
enum {
|
||||
flag_on_playback_starting = 1 << 0,
|
||||
@@ -51,23 +51,25 @@ protected:
|
||||
//! Standard API (always present); manages registrations of dynamic play_callbacks. \n
|
||||
//! Usage: use play_callback_manager::get() to obtain on instance. \n
|
||||
//! Do not reimplement.
|
||||
class NOVTABLE play_callback_manager : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(play_callback_manager);
|
||||
class NOVTABLE play_callback_manager : public service_base
|
||||
{
|
||||
public:
|
||||
//! Registers a play_callback object.
|
||||
//! @param p_callback Interface to register.
|
||||
//! @param p_flags Indicates which notifications are requested.
|
||||
//! @param p_forward_status_on_register Set to true to have the callback immediately receive current playback status as notifications if playback is active (eg. to receive info about playback process that started before our callback was registered).
|
||||
virtual void register_callback(play_callback * p_callback,unsigned p_flags,bool p_forward_status_on_register) = 0;
|
||||
virtual void FB2KAPI register_callback(play_callback * p_callback,unsigned p_flags,bool p_forward_status_on_register) = 0;
|
||||
//! Unregisters a play_callback object.
|
||||
//! @p_callback Previously registered interface to unregister.
|
||||
virtual void unregister_callback(play_callback * p_callback) = 0;
|
||||
virtual void FB2KAPI unregister_callback(play_callback * p_callback) = 0;
|
||||
|
||||
FB2K_MAKE_SERVICE_COREAPI(play_callback_manager);
|
||||
};
|
||||
|
||||
//! Implementation helper.
|
||||
class play_callback_impl_base : public play_callback {
|
||||
public:
|
||||
play_callback_impl_base(unsigned p_flags = UINT_MAX) {
|
||||
play_callback_impl_base(unsigned p_flags = ~0) {
|
||||
play_callback_manager::get()->register_callback(this,p_flags,false);
|
||||
}
|
||||
~play_callback_impl_base() {
|
||||
@@ -94,10 +96,11 @@ public:
|
||||
|
||||
//! Static (autoregistered) version of play_callback. Use play_callback_static_factory_t to register.
|
||||
class play_callback_static : public service_base, public play_callback {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(play_callback_static);
|
||||
public:
|
||||
//! Controls which methods your callback wants called; returned value should not change in run time, you should expect it to be queried only once (on startup). See play_callback::flag_* constants.
|
||||
virtual unsigned get_flags() = 0;
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(play_callback_static);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -107,9 +110,10 @@ class play_callback_static_factory_t : public service_factory_single_t<T> {};
|
||||
//! Gets notified about tracks being played. Notification occurs when at least 60s of the track has been played, or the track has reached its end after at least 1/3 of it has been played through.
|
||||
//! Use playback_statistics_collector_factory_t to register.
|
||||
class NOVTABLE playback_statistics_collector : public service_base {
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(playback_statistics_collector);
|
||||
public:
|
||||
virtual void on_item_played(metadb_handle_ptr p_item) = 0;
|
||||
|
||||
FB2K_MAKE_SERVICE_INTERFACE_ENTRYPOINT(playback_statistics_collector);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -36,9 +36,6 @@ public:
|
||||
bool is_valid() const;
|
||||
|
||||
|
||||
enum {case_sensitive = true};
|
||||
typedef pfc::comparator_strcmp path_comparator;
|
||||
|
||||
class comparator {
|
||||
public:
|
||||
static int compare(const playable_location & v1, const playable_location & v2) {return g_compare(v1,v2);}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
//! \since 1.0
|
||||
//! Implemented by components - register with playback_stream_capture methods.
|
||||
class NOVTABLE playback_stream_capture_callback {
|
||||
public:
|
||||
//! Delivers a real-time chunk of audio data. \n
|
||||
@@ -14,12 +11,13 @@ protected:
|
||||
};
|
||||
|
||||
//! \since 1.0
|
||||
//! Implemented by core.
|
||||
class NOVTABLE playback_stream_capture : public service_base {
|
||||
FB2K_MAKE_SERVICE_COREAPI(playback_stream_capture)
|
||||
public:
|
||||
//! Possible to call only from the main thread.
|
||||
virtual void add_callback(playback_stream_capture_callback * ) = 0;
|
||||
//! Possible to call only from the main thread.
|
||||
virtual void remove_callback(playback_stream_capture_callback * ) = 0;
|
||||
|
||||
|
||||
FB2K_MAKE_SERVICE_COREAPI(playback_stream_capture)
|
||||
};
|
||||
|
||||
@@ -90,14 +90,12 @@ void playlist_manager::playlist_get_all_items(t_size p_playlist,pfc::list_base_t
|
||||
|
||||
void playlist_manager::playlist_get_selected_items(t_size p_playlist,pfc::list_base_t<metadb_handle_ptr> & out)
|
||||
{
|
||||
enum_items_callback_retrieve_selected_items cb(out);
|
||||
playlist_enum_items(p_playlist,cb,pfc::bit_array_true());
|
||||
playlist_enum_items(p_playlist,enum_items_callback_retrieve_selected_items(out),pfc::bit_array_true());
|
||||
}
|
||||
|
||||
void playlist_manager::playlist_get_selection_mask(t_size p_playlist,bit_array_var & out)
|
||||
{
|
||||
enum_items_callback_retrieve_selection_mask cb(out);
|
||||
playlist_enum_items(p_playlist,cb,pfc::bit_array_true());
|
||||
playlist_enum_items(p_playlist,enum_items_callback_retrieve_selection_mask(out),pfc::bit_array_true());
|
||||
}
|
||||
|
||||
bool playlist_manager::playlist_is_item_selected(t_size p_playlist,t_size p_item)
|
||||
@@ -134,9 +132,8 @@ bool playlist_manager::playlist_move_selection(t_size p_playlist,int p_delta) {
|
||||
pfc::array_t<t_size> order; order.set_size(count);
|
||||
pfc::array_t<bool> selection; selection.set_size(count);
|
||||
|
||||
pfc::bit_array_var_table mask(selection.get_ptr(),selection.get_size());
|
||||
playlist_get_selection_mask(p_playlist, mask);
|
||||
g_make_selection_move_permutation(order.get_ptr(),count,mask,p_delta);
|
||||
playlist_get_selection_mask(p_playlist, pfc::bit_array_var_table(selection.get_ptr(),selection.get_size()));
|
||||
g_make_selection_move_permutation(order.get_ptr(),count, pfc::bit_array_table(selection.get_ptr(),selection.get_size()),p_delta);
|
||||
return playlist_reorder_items(p_playlist,order.get_ptr(),count);
|
||||
}
|
||||
|
||||
@@ -552,8 +549,7 @@ bool playlist_manager::highlight_playing_item()
|
||||
|
||||
void playlist_manager::playlist_get_items(t_size p_playlist,pfc::list_base_t<metadb_handle_ptr> & out,const bit_array & p_mask)
|
||||
{
|
||||
enum_items_callback_retrieve_all_items cb(out);
|
||||
playlist_enum_items(p_playlist,cb,p_mask);
|
||||
playlist_enum_items(p_playlist,enum_items_callback_retrieve_all_items(out),p_mask);
|
||||
}
|
||||
|
||||
void playlist_manager::activeplaylist_get_items(pfc::list_base_t<metadb_handle_ptr> & out,const bit_array & p_mask)
|
||||
|
||||
@@ -441,8 +441,7 @@ public:
|
||||
//! \param p_data array that contains the data that will be associated with the property
|
||||
template<typename t_array> void playlist_set_property(t_size p_playlist,const GUID & p_property,const t_array & p_data) {
|
||||
PFC_STATIC_ASSERT( sizeof(p_data[0]) == 1 );
|
||||
stream_reader_memblock_ref reader(p_data);
|
||||
playlist_set_property(p_playlist,p_property,&reader,p_data.get_size(),fb2k::noAbort);
|
||||
playlist_set_property(p_playlist,p_property,&stream_reader_memblock_ref(p_data),p_data.get_size(),abort_callback_dummy());
|
||||
}
|
||||
//! Read a persistent playlist property.
|
||||
//! \param p_playlist Index of the playlist
|
||||
@@ -453,10 +452,7 @@ public:
|
||||
PFC_STATIC_ASSERT( sizeof(p_data[0]) == 1 );
|
||||
typedef pfc::array_t<t_uint8,pfc::alloc_fast_aggressive> t_temp;
|
||||
t_temp temp;
|
||||
{
|
||||
stream_writer_buffer_append_ref_t<t_temp> reader(temp);
|
||||
if (!playlist_get_property(p_playlist,p_property,&reader,fb2k::noAbort)) return false;
|
||||
}
|
||||
if (!playlist_get_property(p_playlist,p_property,&stream_writer_buffer_append_ref_t<t_temp>(temp),abort_callback_dummy())) return false;
|
||||
p_data = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ static void track_indexer__g_get_tracks_wrap(const char * p_path,const service_p
|
||||
fail = true;
|
||||
} catch(std::exception const & e) {
|
||||
fail = true;
|
||||
FB2K_console_formatter() << "could not enumerate tracks (" << e << ") on:\n" << file_path_display(p_path);
|
||||
console::formatter() << "could not enumerate tracks (" << e << ") on:\n" << file_path_display(p_path);
|
||||
}
|
||||
if (fail) {
|
||||
if (!got_input && !p_abort.is_aborting()) {
|
||||
@@ -276,7 +276,7 @@ static void process_path_internal(const char * p_path,const service_ptr_t<file>
|
||||
while(e.next(f)) {
|
||||
abort.check();
|
||||
service_ptr_t<archive> arch;
|
||||
if (f->service_query_t(arch) && arch->is_our_archive(p_path)) {
|
||||
if (f->service_query_t(arch)) {
|
||||
if (p_reader.is_valid()) p_reader->reopen(abort);
|
||||
|
||||
try {
|
||||
|
||||
@@ -22,16 +22,6 @@ void popup_message::g_complain(const char * p_whatFailed, const char * msg) {
|
||||
g_complain( PFC_string_formatter() << p_whatFailed << ": " << msg );
|
||||
}
|
||||
|
||||
#if FOOBAR2000_TARGET_VERSION >= 80
|
||||
void popup_message_v3::show_query( const char * title, const char * msg, unsigned buttons, completion_notify::ptr reply) {
|
||||
query_t q;
|
||||
q.title = title; q.msg = msg; q.buttons = buttons; q.reply = reply;
|
||||
this->show_query( q );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef FOOBAR2000_DESKTOP_WINDOWS
|
||||
void popup_message_v2::g_show(HWND parent, const char * msg, const char * title) {
|
||||
service_enum_t< popup_message_v2 > e;
|
||||
service_ptr_t< popup_message_v2 > m;
|
||||
@@ -47,91 +37,3 @@ void popup_message_v2::g_complain(HWND parent, const char * whatFailed, const ch
|
||||
void popup_message_v2::g_complain(HWND parent, const char * whatFailed, const std::exception & e) {
|
||||
g_complain(parent, whatFailed, e.what());
|
||||
}
|
||||
#endif // FOOBAR2000_DESKTOP_WINDOWS
|
||||
|
||||
#ifdef FOOBAR2000_MODERN
|
||||
void fb2k::showToast( const char * msg ) {
|
||||
fb2k::popup_toast::arg_t arg;
|
||||
fb2k::popup_toast::get()->show_toast(msg, arg);
|
||||
}
|
||||
|
||||
void fb2k::showToastLongDuration( const char * msg ) {
|
||||
fb2k::popup_toast::arg_t arg;
|
||||
arg.longDuration = true;
|
||||
fb2k::popup_toast::get()->show_toast(msg, arg);
|
||||
}
|
||||
void popup_message::g_showToast(const char * msg) {
|
||||
fb2k::showToast( msg );
|
||||
}
|
||||
void popup_message::g_showToastLongDuration(const char * msg) {
|
||||
fb2k::showToastLongDuration( msg );
|
||||
}
|
||||
|
||||
#endif // FOOBAR2000_MODERN
|
||||
|
||||
#if defined(FOOBAR2000_DESKTOP_WINDOWS) && FOOBAR2000_TARGET_VERSION >= 80
|
||||
int popup_message_v3::messageBox(HWND parent, const char* msg, const char* title, UINT flags) {
|
||||
query_t q = {};
|
||||
q.title = title;
|
||||
q.msg = msg;
|
||||
q.wndParent = parent;
|
||||
|
||||
switch (flags & 0xF) {
|
||||
default:
|
||||
case MB_OK:
|
||||
q.buttons = buttonOK;
|
||||
q.defButton = buttonOK;
|
||||
break;
|
||||
case MB_OKCANCEL:
|
||||
q.buttons = buttonOK | buttonCancel;
|
||||
q.defButton = (flags & MB_DEFBUTTON2) ? buttonCancel : buttonOK;
|
||||
break;
|
||||
case MB_ABORTRETRYIGNORE:
|
||||
q.buttons = buttonAbort | buttonRetry | buttonIgnore;
|
||||
if (flags & MB_DEFBUTTON3) q.defButton = buttonIgnore;
|
||||
else if (flags & MB_DEFBUTTON2) q.defButton = buttonRetry;
|
||||
else q.defButton = buttonAbort;
|
||||
break;
|
||||
case MB_YESNOCANCEL:
|
||||
q.buttons = buttonYes | buttonNo | buttonCancel;
|
||||
if (flags & MB_DEFBUTTON3) q.defButton = buttonCancel;
|
||||
else if (flags & MB_DEFBUTTON2) q.defButton = buttonNo;
|
||||
else q.defButton = buttonYes;
|
||||
break;
|
||||
case MB_YESNO:
|
||||
q.buttons = buttonYes | buttonNo;
|
||||
q.defButton = (flags & MB_DEFBUTTON2) ? buttonNo : buttonYes;
|
||||
break;
|
||||
case MB_RETRYCANCEL:
|
||||
q.buttons = buttonRetry | buttonCancel;
|
||||
q.defButton = (flags & MB_DEFBUTTON2) ? buttonCancel : buttonRetry;
|
||||
break;
|
||||
}
|
||||
switch (flags & 0xF0) {
|
||||
case MB_ICONHAND:
|
||||
q.icon = iconWarning;
|
||||
break;
|
||||
case MB_ICONQUESTION:
|
||||
q.icon = iconQuestion;
|
||||
break;
|
||||
case MB_ICONEXCLAMATION:
|
||||
q.icon = iconError;
|
||||
break;
|
||||
case MB_ICONASTERISK:
|
||||
q.icon = iconInformation;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t status = this->show_query_modal(q);
|
||||
|
||||
if (status & buttonOK) return IDOK;
|
||||
if (status & buttonCancel) return IDCANCEL;
|
||||
if (status & buttonYes) return IDYES;
|
||||
if (status & buttonNo) return IDNO;
|
||||
if (status & buttonRetry) return IDRETRY;
|
||||
if (status & buttonAbort) return IDABORT;
|
||||
if (status & buttonIgnore) return IDIGNORE;
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user