diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_history | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/Plugins/Library/ml_history')
30 files changed, 5356 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_history/HistoryAPI.cpp b/Src/Plugins/Library/ml_history/HistoryAPI.cpp new file mode 100644 index 00000000..58301aef --- /dev/null +++ b/Src/Plugins/Library/ml_history/HistoryAPI.cpp @@ -0,0 +1,93 @@ +#include "HistoryAPI.h" +#include "ml_history.h" + +static void saveQueryToList(nde_scanner_t s, historyRecordList *obj) +{ + emptyRecentRecordList(obj); + + NDE_Scanner_First(s); + + int r; + do + { + nde_field_t f = NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_FILENAME); + if (f) + { + allocRecentRecordList(obj, obj->Size + 1); + if (!obj->Alloc) break; + + wchar_t *strval = NDE_StringField_GetString(f); + ndestring_retain(strval); + obj->Items[obj->Size].filename = strval; + recentScannerRefToObjCacheNFN(s, obj); + } + + r = NDE_Scanner_Next(s); + } + while (r && !NDE_Scanner_EOF(s)); + + if (obj->Size && obj->Size < obj->Alloc - 1024) + { + size_t old_Alloc = obj->Alloc; + obj->Alloc = obj->Size; + historyRecord *data = (historyRecord*)realloc(obj->Items, sizeof(historyRecord) * obj->Alloc); + if (data) + { + obj->Items=data; + } + else + { + data=(historyRecord*)malloc(sizeof(historyRecord)*obj->Alloc); + if (data) + { + memcpy(data, obj->Items, sizeof(historyRecord)*old_Alloc); + free(obj->Items); + obj->Items=data; + } + else obj->Alloc = (int)old_Alloc; + } + } +} + +historyRecordList *HistoryAPI::Query(const wchar_t *query) +{ + if (!openDb()) + return 0; + + // run query + EnterCriticalSection(&g_db_cs); + nde_scanner_t s=NDE_Table_CreateScanner(g_table); + NDE_Scanner_Query(s, query); + + historyRecordList obj; + + obj.Alloc = 0; + obj.Items = NULL; + obj.Size = 0; + saveQueryToList(s, &obj); + NDE_Table_DestroyScanner(g_table, s); + LeaveCriticalSection(&g_db_cs); + if (obj.Size) + { + historyRecordList *result = (historyRecordList *)malloc(sizeof(historyRecordList)); + memcpy(result, &obj, sizeof(historyRecordList)); + return result; + } + else + { + freeRecentRecordList(&obj); + return 0; + } +} + +void HistoryAPI::FreeHistoryList(historyRecordList *historyList) +{ + freeRecentRecordList(historyList); +} + +#define CBCLASS HistoryAPI +START_DISPATCH; +CB(API_HISTORY_QUERY, Query) +VCB(API_HISTORY_FREEHISTORYLIST, FreeHistoryList) +END_DISPATCH; +#undef CBCLASS diff --git a/Src/Plugins/Library/ml_history/HistoryAPI.h b/Src/Plugins/Library/ml_history/HistoryAPI.h new file mode 100644 index 00000000..cd5dfc8d --- /dev/null +++ b/Src/Plugins/Library/ml_history/HistoryAPI.h @@ -0,0 +1,12 @@ +#pragma once +#include "api_history.h" + +class HistoryAPI : public api_history +{ +public: + historyRecordList *Query(const wchar_t *query); + void FreeHistoryList(historyRecordList *historyList); + +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/HistoryAPIFactory.cpp b/Src/Plugins/Library/ml_history/HistoryAPIFactory.cpp new file mode 100644 index 00000000..a938f6c5 --- /dev/null +++ b/Src/Plugins/Library/ml_history/HistoryAPIFactory.cpp @@ -0,0 +1,62 @@ +#include "HistoryAPIFactory.h" +#include "api__ml_history.h" +#include "HistoryAPI.h" + +HistoryAPI historyAPI; +static const char serviceName[] = "History API"; + +FOURCC HistoryAPIFactory::GetServiceType() +{ + return WaSvc::UNIQUE; +} + +const char *HistoryAPIFactory::GetServiceName() +{ + return serviceName; +} + +GUID HistoryAPIFactory::GetGUID() +{ + return HistoryApiGuid; +} + +void *HistoryAPIFactory::GetInterface(int global_lock) +{ +// if (global_lock) +// plugin.service->service_lock(this, (void *)ifc); + return &historyAPI; +} + +int HistoryAPIFactory::SupportNonLockingInterface() +{ + return 1; +} + +int HistoryAPIFactory::ReleaseInterface(void *ifc) +{ + //plugin.service->service_unlock(ifc); + return 1; +} + +const char *HistoryAPIFactory::GetTestString() +{ + return 0; +} + +int HistoryAPIFactory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#define CBCLASS HistoryAPIFactory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/HistoryAPIFactory.h b/Src/Plugins/Library/ml_history/HistoryAPIFactory.h new file mode 100644 index 00000000..a07e063f --- /dev/null +++ b/Src/Plugins/Library/ml_history/HistoryAPIFactory.h @@ -0,0 +1,21 @@ +#pragma once + +#include "api__ml_history.h" +#include <api/service/waservicefactory.h> +#include <api/service/services.h> + +class HistoryAPIFactory : public waServiceFactory +{ +public: + FOURCC GetServiceType(); + const char *GetServiceName(); + GUID GetGUID(); + void *GetInterface(int global_lock); + int SupportNonLockingInterface(); + int ReleaseInterface(void *ifc); + const char *GetTestString(); + int ServiceNotify(int msg, int param1, int param2); + +protected: + RECVS_DISPATCH; +};
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/JSAPI2_Creator.cpp b/Src/Plugins/Library/ml_history/JSAPI2_Creator.cpp new file mode 100644 index 00000000..d3619745 --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_Creator.cpp @@ -0,0 +1,95 @@ +#include "api__ml_history.h" +#include "JSAPI2_Creator.h" +#include "JSAPI2_HistoryAPI.h" +#include "resource.h" + + +IDispatch *JSAPI2_Creator::CreateAPI(const wchar_t *name, const wchar_t *key, JSAPI::ifc_info *info) +{ + if (!_wcsicmp(name, L"History")) + return new JSAPI2::HistoryAPI(key, info); + else + return 0; +} + +int JSAPI2_Creator::PromptForAuthorization(HWND parent, const wchar_t *group, const wchar_t *action, const wchar_t *authorization_key, JSAPI2::api_security::AuthorizationData *data) +{ + if (group && !_wcsicmp(group, L"history")) + { + const wchar_t *title_str = AGAVE_API_JSAPI2_SECURITY->GetAssociatedName(authorization_key); + return AGAVE_API_JSAPI2_SECURITY->SecurityPrompt(parent, title_str, L"This service is requesting access to your playback history.", JSAPI2::svc_apicreator::AUTHORIZATION_FLAG_GROUP_ONLY); + } + else + return JSAPI2::svc_apicreator::AUTHORIZATION_UNDEFINED; +} + +#define CBCLASS JSAPI2_Creator +START_DISPATCH; +CB(JSAPI2_SVC_APICREATOR_CREATEAPI, CreateAPI); +CB(JSAPI2_SVC_APICREATOR_PROMPTFORAUTHORIZATION, PromptForAuthorization); +END_DISPATCH; +#undef CBCLASS + +static JSAPI2_Creator jsapi2_svc; +static const char serviceName[] = "History Javascript Objects"; + +// {D9F59F89-82F3-446d-8CD8-6D4445094D50} +static const GUID jsapi2_factory_guid = +{ 0xd9f59f89, 0x82f3, 0x446d, { 0x8c, 0xd8, 0x6d, 0x44, 0x45, 0x9, 0x4d, 0x50 } }; + + +FOURCC JSAPI2Factory::GetServiceType() +{ + return jsapi2_svc.getServiceType(); +} + +const char *JSAPI2Factory::GetServiceName() +{ + return serviceName; +} + +GUID JSAPI2Factory::GetGUID() +{ + return jsapi2_factory_guid; +} + +void *JSAPI2Factory::GetInterface(int global_lock) +{ +// if (global_lock) +// plugin.service->service_lock(this, (void *)ifc); + return &jsapi2_svc; +} + +int JSAPI2Factory::SupportNonLockingInterface() +{ + return 1; +} + +int JSAPI2Factory::ReleaseInterface(void *ifc) +{ + //plugin.service->service_unlock(ifc); + return 1; +} + +const char *JSAPI2Factory::GetTestString() +{ + return 0; +} + +int JSAPI2Factory::ServiceNotify(int msg, int param1, int param2) +{ + return 1; +} + +#define CBCLASS JSAPI2Factory +START_DISPATCH; +CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType) +CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName) +CB(WASERVICEFACTORY_GETGUID, GetGUID) +CB(WASERVICEFACTORY_GETINTERFACE, GetInterface) +CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface) +CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface) +CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString) +CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify) +END_DISPATCH; +#undef CBCLASS
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/JSAPI2_Creator.h b/Src/Plugins/Library/ml_history/JSAPI2_Creator.h new file mode 100644 index 00000000..ddcc9aee --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_Creator.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../Winamp/JSAPI2_svc_apicreator.h" + +#include <api/service/waservicefactory.h> +#include <api/service/services.h> + +class JSAPI2Factory : public waServiceFactory +{ +public: + FOURCC GetServiceType(); + const char *GetServiceName(); + GUID GetGUID(); + void *GetInterface(int global_lock); + int SupportNonLockingInterface(); + int ReleaseInterface(void *ifc); + const char *GetTestString(); + int ServiceNotify(int msg, int param1, int param2); + +protected: + RECVS_DISPATCH; +}; + + +class JSAPI2_Creator : public JSAPI2::svc_apicreator +{ + IDispatch *CreateAPI(const wchar_t *name, const wchar_t *key, JSAPI::ifc_info *info); + int PromptForAuthorization(HWND parent, const wchar_t *group, const wchar_t *action, const wchar_t *authorization_key, JSAPI2::api_security::AuthorizationData *data); +protected: + RECVS_DISPATCH; +}; diff --git a/Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.cpp b/Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.cpp new file mode 100644 index 00000000..32bea983 --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.cpp @@ -0,0 +1,123 @@ +#include "api__ml_history.h" +#include "JSAPI2_HistoryAPI.h" +#include "../Winamp/JSAPI.h" +#include "JSAPI2_HistoryRecordList.h" +#include "HistoryAPI.h" + +extern HistoryAPI historyAPI; + +JSAPI2::HistoryAPI::HistoryAPI(const wchar_t *_key, JSAPI::ifc_info *_info) +{ + info = _info; + key = _key; + refCount = 1; +} + +#define DISP_TABLE \ + CHECK_ID(Query)\ + + +#define CHECK_ID(str) JSAPI_DISP_ENUMIFY(str), +enum { + DISP_TABLE +}; + +#undef CHECK_ID +#define CHECK_ID(str) if (wcscmp(rgszNames[i], L## #str) == 0) { rgdispid[i] = JSAPI_DISP_ENUMIFY(str); continue; } +HRESULT JSAPI2::HistoryAPI::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + DISP_TABLE + + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT JSAPI2::HistoryAPI::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryAPI::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryAPI::Query(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr) +{ + JSAPI_VERIFY_METHOD(wFlags); + JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1); + JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_BSTR, puArgErr); + JSAPI_INIT_RESULT(pvarResult, VT_DISPATCH); + + if (AGAVE_API_JSAPI2_SECURITY->GetActionAuthorization(L"history", L"query", key, info, JSAPI2::api_security::ACTION_PROMPT) == JSAPI2::api_security::ACTION_ALLOWED) + { + if (pvarResult) // no sense in running the query if they don't care about the return value! + { + historyRecordList *query_results = historyAPI.Query(JSAPI_PARAM(pdispparams, 1).bstrVal); + if (query_results) + V_DISPATCH(pvarResult) = new JSAPI2::HistoryRecordList(query_results); + else + V_DISPATCH(pvarResult) = 0; + } + return S_OK; + } + else + { + V_DISPATCH(pvarResult) = 0; + return S_OK; + } + + return E_FAIL; +} + +#undef CHECK_ID +#define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): return str(wFlags, pdispparams, pvarResult, puArgErr); +HRESULT JSAPI2::HistoryAPI::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch (dispid) + { + DISP_TABLE + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP JSAPI2::HistoryAPI::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG JSAPI2::HistoryAPI::AddRef(void) +{ + return InterlockedIncrement(&refCount); +} + + +ULONG JSAPI2::HistoryAPI::Release(void) +{ + LONG lRef = InterlockedDecrement(&refCount); + if (lRef == 0) delete this; + return lRef; +} diff --git a/Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.h b/Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.h new file mode 100644 index 00000000..a880ef4d --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.h @@ -0,0 +1,27 @@ +#pragma once + +#include <ocidl.h> +#include "../Winamp/JSAPI_Info.h" + +namespace JSAPI2 +{ + class HistoryAPI : public IDispatch + { + public: + HistoryAPI(const wchar_t *_key, JSAPI::ifc_info *info); + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); + private: + const wchar_t *key; + volatile LONG refCount; + JSAPI::ifc_info *info; + + STDMETHOD (Query)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr); + }; +} diff --git a/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.cpp b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.cpp new file mode 100644 index 00000000..6b9a9ac6 --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.cpp @@ -0,0 +1,260 @@ +#include "JSAPI2_HistoryRecord.h" +#include "../Winamp/JSAPI.h" + +#define DISP_TABLE \ + CHECK_ID(filename)\ + CHECK_ID(title)\ + CHECK_ID(length)\ + CHECK_ID(playcount)\ + CHECK_ID(lastplay)\ + CHECK_ID(offset)\ + + +#define CHECK_ID(str) JSAPI_DISP_ENUMIFY(str), +enum { + DISP_TABLE + DISP_TABLE_NUM_ENTRIES, +}; + +JSAPI2::HistoryRecord::HistoryRecord(IUnknown *_parent, const historyRecord *_record) +{ + parent = _parent; + parent->AddRef(); + record = _record; + refCount = 1; +} + +JSAPI2::HistoryRecord::~HistoryRecord() +{ + if (parent) + parent->Release(); +} + +HRESULT JSAPI2::HistoryRecord::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + if (GetDispID(rgszNames[i], fdexNameCaseSensitive, &rgdispid[i]) == DISPID_UNKNOWN) + unknowns=true; + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT JSAPI2::HistoryRecord::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + return InvokeEx(dispid, lcid, wFlags, pdispparams, pvarResult, pexecinfo, 0); +} + +HRESULT JSAPI2::HistoryRecord::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecord::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +STDMETHODIMP JSAPI2::HistoryRecord::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else if (IsEqualIID(riid, IID_IDispatchEx)) + *ppvObject = (IDispatchEx *)this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG JSAPI2::HistoryRecord::AddRef(void) +{ + return InterlockedIncrement(&refCount); +} + +ULONG JSAPI2::HistoryRecord::Release(void) +{ + LONG lRef = InterlockedDecrement(&refCount); + if (lRef == 0) delete this; + return lRef; +} + +#undef CHECK_ID +#define CHECK_ID(str) if (wcscmp(bstrName, L## #str) == 0) { *pid = JSAPI_DISP_ENUMIFY(str); return S_OK; } +HRESULT JSAPI2::HistoryRecord::GetDispID(BSTR bstrName, DWORD grfdex, DISPID *pid) +{ + DISP_TABLE + + return DISP_E_MEMBERNOTFOUND; +} + +#undef CHECK_ID +#define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): return str(wFlags, pdp, pvarRes); +HRESULT JSAPI2::HistoryRecord::InvokeEx(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + if (wFlags & DISPATCH_PROPERTYGET) + { + switch(id) + { + DISP_TABLE + } + } + + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT JSAPI2::HistoryRecord::DeleteMemberByName(BSTR bstrName, DWORD grfdex) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecord::DeleteMemberByDispID(DISPID id) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecord::GetMemberProperties(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) +{ + return E_NOTIMPL; +} + +#undef CHECK_ID +#define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): *pbstrName = SysAllocString(L ## #str); return S_OK; +HRESULT JSAPI2::HistoryRecord::GetMemberName(DISPID id, BSTR *pbstrName) +{ + switch(id) + { + DISP_TABLE + } + + return E_NOTIMPL; +} + +#undef CHECK_ID +#define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): *pid = JSAPI_DISP_ENUMIFY(str) + 1; break; +HRESULT JSAPI2::HistoryRecord::GetNextDispID(DWORD grfdex, DISPID id, DISPID *pid) +{ + if (grfdex == fdexEnumDefault) + { + switch(id) + { + case DISPID_UNKNOWN: + *pid = 0; + break; + DISP_TABLE + + } + if (*pid == DISP_TABLE_NUM_ENTRIES) + return S_FALSE; + else + return S_OK; + } + + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecord::GetNameSpaceParent(IUnknown **ppunk) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecord::filename(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult) +{ + if (wFlags & DISPATCH_PROPERTYGET) + { + JSAPI_INIT_RESULT(pvarResult, VT_BSTR); + JSAPI_SET_RESULT(pvarResult, bstrVal, SysAllocString(record->filename)); + return S_OK; + } + else + return DISP_E_MEMBERNOTFOUND; +} + + +HRESULT JSAPI2::HistoryRecord::title(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult) +{ + if (wFlags & DISPATCH_PROPERTYGET) + { + JSAPI_INIT_RESULT(pvarResult, VT_BSTR); + JSAPI_SET_RESULT(pvarResult, bstrVal, SysAllocString(record->title)); + return S_OK; + } + else + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT JSAPI2::HistoryRecord::length(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult) +{ + if (wFlags & DISPATCH_PROPERTYGET) + { + JSAPI_INIT_RESULT(pvarResult, VT_I4); + JSAPI_SET_RESULT(pvarResult, lVal, record->length); + return S_OK; + } + else + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT JSAPI2::HistoryRecord::offset(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult) +{ + if (wFlags & DISPATCH_PROPERTYGET) + { + JSAPI_INIT_RESULT(pvarResult, VT_I4); + JSAPI_SET_RESULT(pvarResult, lVal, record->offset); + return S_OK; + } + else + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT JSAPI2::HistoryRecord::playcount(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult) +{ + if (wFlags & DISPATCH_PROPERTYGET) + { + JSAPI_INIT_RESULT(pvarResult, VT_I4); + JSAPI_SET_RESULT(pvarResult, lVal, record->playcnt); + return S_OK; + } + else + return DISP_E_MEMBERNOTFOUND; +} + +HRESULT JSAPI2::HistoryRecord::lastplay(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult) +{ + if (wFlags & DISPATCH_PROPERTYGET) + { + JSAPI_INIT_RESULT(pvarResult, VT_DATE); + if (pvarResult) + { + __time64_t convertTime = record->lastplayed; + SYSTEMTIME sysTime = {0}; + tm *newtime = _localtime64(&convertTime); + + sysTime.wYear = newtime->tm_year + 1900; + sysTime.wMonth = newtime->tm_mon + 1; + sysTime.wDayOfWeek = newtime->tm_wday; + sysTime.wDay = newtime->tm_mday; + sysTime.wHour = newtime->tm_hour; + sysTime.wMinute = newtime->tm_min; + sysTime.wSecond = newtime->tm_sec; + + SystemTimeToVariantTime(&sysTime, &pvarResult->date); + } + return S_OK; + } + else + return DISP_E_MEMBERNOTFOUND; +} diff --git a/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.h b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.h new file mode 100644 index 00000000..065b4edd --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.h @@ -0,0 +1,46 @@ +#pragma once +#include <dispex.h> +#include "history.h" +namespace JSAPI2 +{ + class HistoryRecord : public IDispatchEx + { + public: + HistoryRecord(IUnknown *_parent, const historyRecord *_record); + ~HistoryRecord(); + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + void AddObject(IDispatch *obj); + private: + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); + + // *** IDispatchEx Methods *** + STDMETHOD (GetDispID)(BSTR bstrName, DWORD grfdex, DISPID *pid); + STDMETHOD (InvokeEx)(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller); + STDMETHOD (DeleteMemberByName)(BSTR bstrName, DWORD grfdex) ; + STDMETHOD (DeleteMemberByDispID)(DISPID id); + STDMETHOD (GetMemberProperties)(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex); + STDMETHOD (GetMemberName)(DISPID id, BSTR *pbstrName); + STDMETHOD (GetNextDispID)(DWORD grfdex, DISPID id, DISPID *pid); + STDMETHOD (GetNameSpaceParent)(IUnknown **ppunk); + private: + const historyRecord *record; + IUnknown *parent; + volatile LONG refCount; + + STDMETHOD (filename)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult); + STDMETHOD (title)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult); + STDMETHOD (length)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult); + STDMETHOD (playcount)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult); + STDMETHOD (lastplay)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult); + STDMETHOD (offset)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult); + + }; + +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.cpp b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.cpp new file mode 100644 index 00000000..ee949c48 --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.cpp @@ -0,0 +1,198 @@ +#include "JSAPI2_HistoryRecordList.h" +#include "JSAPI2_HistoryRecord.h" +#include "ml_history.h" +#include "../Winamp/JSAPI.h" +#include <strsafe.h> + +JSAPI2::HistoryRecordList::HistoryRecordList(historyRecordList *_record) +{ + recordList = _record; + refCount = 1; +} + +JSAPI2::HistoryRecordList::~HistoryRecordList() +{ + freeRecentRecordList(recordList); +} + +enum +{ + OBJ_ARRAY_DISP_LENGTH, + OBJ_ARRAY_NUM_DISP, +}; + +HRESULT JSAPI2::HistoryRecordList::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + if (GetDispID(rgszNames[i], fdexNameCaseSensitive, &rgdispid[i]) == DISPID_UNKNOWN) + unknowns=true; + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT JSAPI2::HistoryRecordList::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + return InvokeEx(dispid, lcid, wFlags, pdispparams, pvarResult, pexecinfo, 0); +} + +HRESULT JSAPI2::HistoryRecordList::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecordList::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +STDMETHODIMP JSAPI2::HistoryRecordList::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else if (IsEqualIID(riid, IID_IDispatchEx)) + *ppvObject = (IDispatchEx *)this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG JSAPI2::HistoryRecordList::AddRef(void) +{ + return InterlockedIncrement(&refCount); +} + + +ULONG JSAPI2::HistoryRecordList::Release(void) +{ + LONG lRef = InterlockedDecrement(&refCount); + if (lRef == 0) delete this; + return lRef; +} + +HRESULT JSAPI2::HistoryRecordList::GetDispID(BSTR bstrName, DWORD grfdex, DISPID *pid) +{ + if (!_wcsicmp(bstrName, L"length")) + *pid=OBJ_ARRAY_DISP_LENGTH; + else + { + if (bstrName[0] >= L'0' && bstrName[0] <= L'9') + *pid=_wtoi(bstrName) + OBJ_ARRAY_NUM_DISP; + else + return DISP_E_MEMBERNOTFOUND; + + } + return S_OK; +} + +HRESULT JSAPI2::HistoryRecordList::InvokeEx(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + JSAPI_VERIFY_PARAMCOUNT(pdp, 0); + + switch(id) + { + case OBJ_ARRAY_DISP_LENGTH: + { + JSAPI_INIT_RESULT(pvarRes, VT_I4); + JSAPI_SET_RESULT(pvarRes, lVal, recordList->Size); + return S_OK; + } + default: + { + if (id < OBJ_ARRAY_NUM_DISP) + return DISP_E_MEMBERNOTFOUND; + + int index = id - OBJ_ARRAY_NUM_DISP; + if (index>=recordList->Size) + return DISP_E_MEMBERNOTFOUND; + + JSAPI_INIT_RESULT(pvarRes, VT_DISPATCH); + JSAPI_SET_RESULT(pvarRes, pdispVal, new JSAPI2::HistoryRecord(this, &recordList->Items[index])); + return S_OK; + } + } + +} + +HRESULT JSAPI2::HistoryRecordList::DeleteMemberByName(BSTR bstrName, DWORD grfdex) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecordList::DeleteMemberByDispID(DISPID id) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecordList::GetMemberProperties(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) +{ + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecordList::GetMemberName(DISPID id, BSTR *pbstrName) +{ + if (id >= OBJ_ARRAY_NUM_DISP) + { + wchar_t temp[64]; + StringCbPrintfW(temp, sizeof(temp), L"%d", id-OBJ_ARRAY_NUM_DISP); + *pbstrName = SysAllocString(temp); + return S_OK; + } + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecordList::GetNextDispID(DWORD grfdex, DISPID id, DISPID *pid) +{ + if (grfdex == fdexEnumDefault) + { + if (id == DISPID_UNKNOWN) + { + if (recordList->Size == 0) + return S_FALSE; + else + { + *pid = OBJ_ARRAY_NUM_DISP; + return S_OK; + } + } + else if (id < OBJ_ARRAY_NUM_DISP) + { + return S_FALSE; + } + else + { + int index = (id - OBJ_ARRAY_NUM_DISP) + 1; + if (index >= recordList->Size) + { + return S_FALSE; + } + else + { + *pid = OBJ_ARRAY_NUM_DISP + index; + return S_OK; + } + + } + } + + return E_NOTIMPL; +} + +HRESULT JSAPI2::HistoryRecordList::GetNameSpaceParent(IUnknown **ppunk) +{ + return E_NOTIMPL; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.h b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.h new file mode 100644 index 00000000..961614ab --- /dev/null +++ b/Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.h @@ -0,0 +1,37 @@ +#pragma once +#include <dispex.h> +#include "history.h" +namespace JSAPI2 +{ + class HistoryRecordList : public IDispatchEx + { + public: + HistoryRecordList(historyRecordList *_record); + ~HistoryRecordList(); + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + void AddObject(IDispatch *obj); + private: + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); + + // *** IDispatchEx Methods *** + STDMETHOD (GetDispID)(BSTR bstrName, DWORD grfdex, DISPID *pid); + STDMETHOD (InvokeEx)(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller); + STDMETHOD (DeleteMemberByName)(BSTR bstrName, DWORD grfdex) ; + STDMETHOD (DeleteMemberByDispID)(DISPID id); + STDMETHOD (GetMemberProperties)(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex); + STDMETHOD (GetMemberName)(DISPID id, BSTR *pbstrName); + STDMETHOD (GetNextDispID)(DWORD grfdex, DISPID id, DISPID *pid); + STDMETHOD (GetNameSpaceParent)(IUnknown **ppunk); + private: + historyRecordList *recordList; + volatile LONG refCount; + }; + +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/Main.cpp b/Src/Plugins/Library/ml_history/Main.cpp new file mode 100644 index 00000000..7f8391d4 --- /dev/null +++ b/Src/Plugins/Library/ml_history/Main.cpp @@ -0,0 +1,349 @@ +#include "api__ml_history.h" +#include "main.h" +#include "..\..\General\gen_ml/config.h" +#include "HistoryAPIFactory.h" +#include "JSAPI2_Creator.h" +#include "..\..\General\gen_ml/ml_ipc_0313.h" +#include "../nu/AutoCharFn.h" +#include <strsafe.h> + +#define LOCAL_WRITE_VER L"2.03" + +// wasabi based services for localisation support +api_language *WASABI_API_LNG = 0; +api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = 0; +JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = 0; +api_application *WASABI_API_APP=0; + +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +static HistoryAPIFactory historyAPIFactory; +static JSAPI2Factory jsapi2Factory; + +static int Init(); +static void Quit(); +static INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3); + +prefsDlgRecW preferences = {0}; +wchar_t g_tableDir[MAX_PATH] = {0}; + +extern "C" winampMediaLibraryPlugin plugin = +{ + MLHDR_VER, + "nullsoft(ml_history.dll)", + Init, + Quit, + MessageProc, + 0, + 0, + 0, +}; + +// Delay load library control << begin >> +#include <delayimp.h> +#pragma comment(lib, "delayimp") + +bool nde_error = false; + +FARPROC WINAPI FailHook(unsigned dliNotify, PDelayLoadInfo pdli) +{ + nde_error = true; + return 0; +} +/* +extern "C" +{ + PfnDliHook __pfnDliFailureHook2 = FailHook; +} +*/ +// Delay load library control << end >> + +int Init() +{ + InitializeCriticalSection(&g_db_cs); + + g_db = NULL; + g_table = NULL; + + mediaLibrary.library = plugin.hwndLibraryParent; + mediaLibrary.winamp = plugin.hwndWinampParent; + mediaLibrary.instance = plugin.hDllInstance; + + wchar_t configName[ MAX_PATH ] = { 0 }; + const wchar_t *dir = mediaLibrary.GetIniDirectoryW(); + + if ( (INT_PTR)( dir ) < 65536 ) + return ML_INIT_FAILURE; + + + PathCombineW( g_tableDir, dir, L"Plugins" ); + PathCombineW( configName, g_tableDir, L"gen_ml.ini" ); + + g_config = new C_Config( configName ); + + CreateDirectoryW( g_tableDir, NULL ); + PathCombineW( g_tableDir, g_tableDir, L"ml" ); + CreateDirectoryW( g_tableDir, NULL ); + + // loader so that we can get the localisation service api for use + + waServiceFactory *sf = plugin.service->service_getServiceByGuid( languageApiGUID ); + if ( sf ) + WASABI_API_LNG = reinterpret_cast<api_language *>( sf->getInterface() ); + + sf = plugin.service->service_getServiceByGuid( JSAPI2::api_securityGUID ); + if ( sf ) + AGAVE_API_JSAPI2_SECURITY = reinterpret_cast<JSAPI2::api_security *>( sf->getInterface() ); + + sf = plugin.service->service_getServiceByGuid( applicationApiServiceGuid ); + if ( sf ) + WASABI_API_APP = reinterpret_cast<api_application *>( sf->getInterface() ); + + sf = plugin.service->service_getServiceByGuid( ExplorerFindFileApiGUID ); + if ( sf ) + WASABI_API_EXPLORERFINDFILE = reinterpret_cast<api_explorerfindfile *>( sf->getInterface() ); + + plugin.service->service_register( &historyAPIFactory ); + plugin.service->service_register( &jsapi2Factory ); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG( plugin.hDllInstance, MlHistoryLangGUID ); + + g_context_menus2 = WASABI_API_LOADMENUW(IDR_CONTEXTMENUS); + + static wchar_t szDescription[256]; + StringCchPrintfW( szDescription, ARRAYSIZE( szDescription ), WASABI_API_LNGSTRINGW( IDS_NULLSOFT_HISTORY ), LOCAL_WRITE_VER ); + + + plugin.description = (char*)szDescription; + + static wchar_t preferencesName[64] = {0}; + preferences.hInst = WASABI_API_LNG_HINST; + preferences.dlgID = IDD_PREFS; + preferences.proc = (void *)PrefsProc; + preferences.name = WASABI_API_LNGSTRINGW_BUF( IDS_HISTORY, preferencesName, 64 ); + preferences.where = 6; // media library + + SENDWAIPC( plugin.hwndWinampParent, IPC_ADD_PREFS_DLGW, &preferences ); + + if ( !history_init() ) + return ML_INIT_FAILURE; + + return ML_INIT_SUCCESS; +} + +void Quit() +{ + plugin.service->service_deregister(&historyAPIFactory); + plugin.service->service_deregister(&jsapi2Factory); + history_quit(); + delete g_config; + DeleteCriticalSection(&g_db_cs); + waServiceFactory *sf = plugin.service->service_getServiceByGuid(languageApiGUID); + if (sf) sf->releaseInterface(WASABI_API_LNG); + + sf = plugin.service->service_getServiceByGuid(JSAPI2::api_securityGUID); + if (sf) sf->releaseInterface(AGAVE_API_JSAPI2_SECURITY); +} + +static INT_PTR History_OnContextMenu(INT_PTR param1, HWND hHost, POINTS pts) +{ + HNAVITEM hItem = (HNAVITEM)param1; + HNAVITEM myItem = MLNavCtrl_FindItemById(plugin.hwndLibraryParent, ml_history_tree); + if (hItem != myItem) + return FALSE; + + POINT pt; + POINTSTOPOINT(pt, pts); + if (-1 == pt.x || -1 == pt.y) + { + NAVITEMGETRECT itemRect; + itemRect.fItem = FALSE; + itemRect.hItem = hItem; + if (MLNavItem_GetRect(plugin.hwndLibraryParent, &itemRect)) + { + MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2); + pt.x = itemRect.rc.left + 2; + pt.y = itemRect.rc.top + 2; + } + } + + + HMENU hMenu = WASABI_API_LOADMENUW(IDR_CONTEXTMENUS); + HMENU subMenu = (NULL != hMenu) ? GetSubMenu(hMenu, 1) : NULL; + if (NULL != subMenu) + { + + INT r = Menu_TrackPopup(plugin.hwndLibraryParent, subMenu, + TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | + TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, + pt.x, pt.y, hHost, NULL); + + switch(r) + { + case ID_NAVIGATION_PREFERENCES: + SENDWAIPC(plugin.hwndWinampParent, IPC_OPENPREFSTOPAGE, &preferences); + break; + case ID_NAVIGATION_HELP: + SENDWAIPC(plugin.hwndWinampParent, IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8105304048660-The-Winamp-Media-Library"); + break; + } + } + + if (NULL != hMenu) + DestroyMenu(hMenu); + + return TRUE; +} + +void History_StartTracking(const wchar_t *filename, bool resume) +{ + KillTimer(plugin.hwndWinampParent, 8082); + if (!resume) + { + free(history_fn); + history_fn = 0; + history_fn_mode = 0; + if (wcsstr(filename, L"://") && _wcsnicmp(filename, L"cda://", 6) && _wcsnicmp(filename, L"file://", 7)) + { + history_fn_mode = 1; + } + history_fn = _wcsdup(filename); + } + + int timer1 = -1, timer2 = -1, timer3 = -1; + + // wait for x seconds + if(g_config->ReadInt(L"recent_wait_secs",0)) + { + timer1 = g_config->ReadInt(L"recent_wait_secs_lim",5)*1000; + } + + // wait for x percent of the song (approx to a second) + if(g_config->ReadInt(L"recent_wait_percent",0)) + { + basicFileInfoStructW bfiW = {0}; + bfiW.filename = history_fn; + SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&bfiW, IPC_GET_BASIC_FILE_INFOW); + if(bfiW.length > 0) + { + bfiW.length=bfiW.length*1000; + timer2 = (bfiW.length*g_config->ReadInt(L"recent_wait_percent_lim",50))/100; + } + } + + // wait for the end of the item (within the last second of the track hopefully) + if(g_config->ReadInt(L"recent_wait_end",0)) + { + basicFileInfoStructW bfiW = {0}; + bfiW.filename = history_fn; + SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&bfiW, IPC_GET_BASIC_FILE_INFOW); + if(bfiW.length > 0) + { + timer3=(bfiW.length-1)*1000; + } + } + + // decide on which playback option will be the prefered duration (smallest wins) + if(timer1 != -1 && timer2 != -1) + { + if(timer1 > timer2) + { + timer = timer2; + } + if(timer2 > timer1) + { + timer = timer1; + } + } + else if(timer1 == -1 && timer2 != -1) + { + timer = timer2; + } + else if(timer2 == -1 && timer1 != -1) + { + timer = timer1; + } + + // only track on end of file as very last method + if((timer <= 0) && (timer3 > 0)){ timer = timer3; } + + // if no match or something went wrong then try to ensure the default timer value is used + SetTimer(plugin.hwndWinampParent, 8082, ((timer > 0)? timer : 350), NULL); +} + +INT_PTR MessageProc(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3) +{ + switch (message_type) + { + case ML_MSG_TREE_ONCREATEVIEW: // param1 = param of tree item, param2 is HWND of parent. return HWND if it is us + return (param1 == ml_history_tree) ? (INT_PTR)onTreeViewSelectChange((HWND)param2) : 0; + + case ML_MSG_CONFIG: + mediaLibrary.GoToPreferences((int)preferences._id); + return TRUE; + + case ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE: + enqueuedef = (int)param1; + groupBtn = (int)param2; + PostMessage(m_curview_hwnd, WM_APP + 104, param1, param2); + return 0; + + case ML_MSG_NAVIGATION_CONTEXTMENU: + return History_OnContextMenu(param1, (HWND)param2, MAKEPOINTS(param3)); + + case ML_MSG_PLAYING_FILE: + if (param1) + { + int resume = g_config->ReadInt(L"resumeplayback",0); + if(resume) + { + int is_playing = (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_ISPLAYING); + //int play_pos = SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GETOUTPUTTIME); + if(is_playing == 1/* && !(play_pos/1000 > 0)*/) //playing, look up last play offset and send seek message + { + wchar_t genre[256]={0}; + extendedFileInfoStructW efis={ + (wchar_t*)param1, + L"genre", + genre, + ARRAYSIZE(genre), + }; + SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efis,IPC_GET_EXTENDED_FILE_INFOW); + + wchar_t ispodcast[8]={0}; + extendedFileInfoStructW efis1={ + (wchar_t*)param1, + L"ispodcast", + ispodcast, + ARRAYSIZE(ispodcast), + }; + SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&efis1,IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE); + + if (resume == 2 || (ispodcast[0] && _wtoi(ispodcast) > 0) || (genre[0] && !_wcsicmp(genre, L"podcast"))) + { + int offset = retrieve_offset((wchar_t*)param1); + if (offset > 0 && (offset/1000 > 0)) PostMessage(plugin.hwndWinampParent,WM_WA_IPC,offset,IPC_JUMPTOTIME); + } + } + } + + History_StartTracking((const wchar_t *)param1, false); + } + break; + + case ML_MSG_WRITE_CONFIG: + if(param1) + { + closeDb(); + openDb(); + } + break; + } + return 0; +} + +extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin() +{ + return &plugin; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/Main.h b/Src/Plugins/Library/ml_history/Main.h new file mode 100644 index 00000000..685b8ad1 --- /dev/null +++ b/Src/Plugins/Library/ml_history/Main.h @@ -0,0 +1,34 @@ +#ifndef NULLSOFT_MAINH +#define NULLSOFT_MAINH + +#include <windows.h> +#include "..\..\General\gen_ml/ml.h" +#include "resource.h" +#include "../nu/MediaLibraryInterface.h" +#include "..\..\General\gen_ml/menu.h" +#include <commctrl.h> +#include <shlwapi.h> +#include "ml_history.h" +#include <windowsx.h> +#include "..\..\General\gen_ml/ml.h" +#include "..\..\General\gen_ml/ml_ipc.h" +#include "../nde/nde_c.h" +#include "api__ml_history.h" + +#include "../Winamp/JSAPI2_svc_apicreator.h" + +#include <api/service/waservicefactory.h> +#include <api/service/services.h> + +extern winampMediaLibraryPlugin plugin; +extern bool nde_error; +extern int history_fn_mode; +extern wchar_t *history_fn; +extern int timer; +extern HWND m_curview_hwnd; +extern int groupBtn, enqueuedef; +extern HMENU g_context_menus2; + +void History_StartTracking(const wchar_t *filename, bool resume); + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/api__ml_history.h b/Src/Plugins/Library/ml_history/api__ml_history.h new file mode 100644 index 00000000..ae410bf1 --- /dev/null +++ b/Src/Plugins/Library/ml_history/api__ml_history.h @@ -0,0 +1,18 @@ +#ifndef NULLSOFT_ML_HISTORY_API_H +#define NULLSOFT_ML_HISTORY_API_H + +#include <api/application/api_application.h> +extern api_application *applicationApi; +#define WASABI_API_APP applicationApi + +#include "../Winamp/JSAPI2_api_security.h" +extern JSAPI2::api_security *jsapi2_security; +#define AGAVE_API_JSAPI2_SECURITY jsapi2_security + +#include <api/service/waServiceFactory.h> + +#include "../Agave/Language/api_language.h" + +#include "../Agave/ExplorerFindFile/api_explorerfindfile.h" + +#endif // !NULLSOFT_ML_HISTORY_API_H
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/api_history.cpp b/Src/Plugins/Library/ml_history/api_history.cpp new file mode 100644 index 00000000..b13f171d --- /dev/null +++ b/Src/Plugins/Library/ml_history/api_history.cpp @@ -0,0 +1 @@ +#include "api_history.h" diff --git a/Src/Plugins/Library/ml_history/api_history.h b/Src/Plugins/Library/ml_history/api_history.h new file mode 100644 index 00000000..3f987eb3 --- /dev/null +++ b/Src/Plugins/Library/ml_history/api_history.h @@ -0,0 +1,34 @@ +#pragma once + +#include <bfc/dispatch.h> +#include "history.h" + +class api_history : public Dispatchable +{ +protected: + api_history() {} + ~api_history() {} +public: + historyRecordList *Query(const wchar_t *query); + void FreeHistoryList(historyRecordList *historyList); + + enum + { + API_HISTORY_QUERY = 0, + API_HISTORY_FREEHISTORYLIST = 1, + }; +}; + +inline historyRecordList *api_history::Query(const wchar_t *query) +{ + return _call(API_HISTORY_QUERY, (historyRecordList *)0, query); +} + +inline void api_history::FreeHistoryList(historyRecordList *historyList) +{ + _voidcall(API_HISTORY_FREEHISTORYLIST, historyList); +} + +// {F9BF9119-D163-4118-BEA7-5980869DBB2E} +static const GUID HistoryApiGuid = +{ 0xf9bf9119, 0xd163, 0x4118, { 0xbe, 0xa7, 0x59, 0x80, 0x86, 0x9d, 0xbb, 0x2e } }; diff --git a/Src/Plugins/Library/ml_history/db_error.txt b/Src/Plugins/Library/ml_history/db_error.txt new file mode 100644 index 00000000..0705b100 --- /dev/null +++ b/Src/Plugins/Library/ml_history/db_error.txt @@ -0,0 +1,6 @@ +
+There was an error reading the history database. This could be due to the database becoming corrupted or having been removed.
+
+If the database files (recent.dat and recent.idx) have become corrupted then unless you have a back up, you will need to reset the database.
+
+By resetting the database, any playing history will be lost which also will have happened if the database was corrupted.
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/history.h b/Src/Plugins/Library/ml_history/history.h new file mode 100644 index 00000000..6e1e8486 --- /dev/null +++ b/Src/Plugins/Library/ml_history/history.h @@ -0,0 +1,35 @@ +#pragma once +#ifndef NULLSOFT_ML_HISTORY_HISTORY_H +#define NULLSOFT_ML_HISTORY_HISTORY_H + +#include <time.h> +typedef struct +{ + wchar_t *filename; + wchar_t *title; + wchar_t *ext; + int length; + unsigned int playcnt; + __time64_t lastplayed; + int offset; +} historyRecord; + +typedef struct +{ + historyRecord *Items; + int Size; + int Alloc; +} historyRecordList; + +enum +{ + HISTORY_SORT_LASTPLAYED = 0, + HISTORY_SORT_PLAYCOUNT = 1, + HISTORY_SORT_TITLE = 2, + HISTORY_SORT_LENGTH = 3, + HISTORY_SORT_FILENAME = 4, + HISTORY_SORT_OFFSET = 5, +}; + + +#endif
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/ml_history.cpp b/Src/Plugins/Library/ml_history/ml_history.cpp new file mode 100644 index 00000000..24cb7aa1 --- /dev/null +++ b/Src/Plugins/Library/ml_history/ml_history.cpp @@ -0,0 +1,289 @@ +#include "main.h" +#include "..\..\General\gen_ml/config.h" +#include "..\..\General\gen_ml/gaystring.h" +#include "..\..\General\gen_hotkeys/wa_hotkeys.h" +#include "..\..\General\gen_ml/MediaLibraryCOM.h" +#include "..\..\General\gen_ml/ml_ipc_0313.h" +#include <malloc.h> + +int ml_history_tree = -1; +HWND m_curview_hwnd = NULL; +static WNDPROC wa_oldWndProc=0; +wchar_t *history_fn = 0; +int timer = -1; +static wchar_t *last_history_fn; +static int last_timer=-1; +static int last_play_pos=0; +int history_fn_mode = 0; + +nde_database_t g_db = NULL; +nde_table_t g_table = NULL; +int g_table_dirty = 0; +C_Config *g_config; +CRITICAL_SECTION g_db_cs; + +HWND onTreeViewSelectChange(HWND hwnd) +{ + openDb(); + if (m_curview_hwnd) DestroyWindow(m_curview_hwnd); + + if (!g_table || nde_error) + { + m_curview_hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_DB_ERROR, hwnd, view_errorinfoDialogProc, 0); + } + else + { + m_curview_hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_VIEW_RECENTITEMS, hwnd, view_historyDialogProc, 0); + } + return m_curview_hwnd; +} + +static void history_cleanupifnecessary(); +static LRESULT APIENTRY wa_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +int history_init() +{ + if ( !g_config->ReadInt( L"showrecentitems", 1 ) ) + return 1; + + NAVINSERTSTRUCT nis = {0}; + nis.item.cbSize = sizeof(NAVITEM); + nis.item.pszText = WASABI_API_LNGSTRINGW(IDS_HISTORY); + nis.item.pszInvariant = L"History"; + nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL; + nis.item.iSelectedImage = nis.item.iImage = mediaLibrary.AddTreeImageBmp(IDB_TREEITEM_RECENT); + + // map to item id (will probably have to change but is a quick port to support invariant item naming) + NAVITEM nvItem = {sizeof(NAVITEM),0,NIMF_ITEMID,}; + nvItem.hItem = MLNavCtrl_InsertItem(plugin.hwndLibraryParent, &nis); + MLNavItem_GetInfo(plugin.hwndLibraryParent, &nvItem); + ml_history_tree = nvItem.id; + + if (!wa_oldWndProc) // don't double dip (we call history_init() dynamically if the user fiddles with prefs + { + wa_oldWndProc = (WNDPROC)SetWindowLongPtrW(plugin.hwndWinampParent, GWLP_WNDPROC, (LONG_PTR)wa_newWndProc); + } + + + return 1; +} + +void history_quit() +{ + if (last_history_fn) + { + if ((last_timer <= 0) || (last_play_pos > last_timer)) + { + if (last_play_pos) history_onFile(last_history_fn, last_play_pos); + } + } + free(last_history_fn); + last_history_fn = 0; + free(history_fn); + history_fn = 0; + closeDb(); + mediaLibrary.RemoveTreeItem(ml_history_tree); + ml_history_tree=0; +} + +static void RetypeFilename(nde_table_t table) +{ + // TODO: UI + int totalRecords = NDE_Table_GetRecordsCount(g_table); + if (totalRecords == 0) // bail out early so we don't flash a dialog + return; + nde_scanner_t pruneScanner = NDE_Table_CreateScanner(table); + if (pruneScanner) + { + NDE_Scanner_First(pruneScanner); + while (!NDE_Scanner_EOF(pruneScanner)) + { + nde_field_t f = NDE_Scanner_GetFieldByID(pruneScanner, HISTORYVIEW_COL_FILENAME); + if (f && NDE_Field_GetType(f) == FIELD_STRING) + { + wchar_t *s = NDE_StringField_GetString(f); + ndestring_retain(s); + + NDE_Scanner_DeleteField(pruneScanner, f); + + nde_field_t new_f = NDE_Scanner_NewFieldByID(pruneScanner, HISTORYVIEW_COL_FILENAME); + NDE_StringField_SetNDEString(new_f, s); + + ndestring_release(s); + NDE_Scanner_Post(pruneScanner); + } + else if (f) + break; + + NDE_Scanner_Next(pruneScanner); + } + + NDE_Table_DestroyScanner(table, pruneScanner); + NDE_Table_Sync(table); + } +} + +static void CreateFields(nde_table_t table) +{ + // create defaults + NDE_Table_NewColumnW(g_table, HISTORYVIEW_COL_LASTPLAYED, L"lastplay", FIELD_DATETIME); + NDE_Table_NewColumnW(g_table, HISTORYVIEW_COL_PLAYCOUNT, L"playcount", FIELD_INTEGER); + NDE_Table_NewColumnW(g_table, HISTORYVIEW_COL_TITLE, L"title", FIELD_STRING); + NDE_Table_NewColumnW(g_table, HISTORYVIEW_COL_LENGTH, L"length", FIELD_INTEGER); + NDE_Table_NewColumnW(g_table, HISTORYVIEW_COL_FILENAME, L"filename", FIELD_FILENAME); + NDE_Table_NewColumnW(g_table, HISTORYVIEW_COL_OFFSET, L"offset", FIELD_INTEGER); + NDE_Table_PostColumns(g_table); + NDE_Table_AddIndexByIDW(g_table, 0, L"filename");} + +int openDb() +{ + if (g_table) return 1; // need to close first + + EnterCriticalSection(&g_db_cs); + + // benski> i know this looks redundant, but we might have sat and blocked at the above Critical Section for a while + if (g_table) + { + LeaveCriticalSection(&g_db_cs); + return 1; + } + + if (!g_db) + { + __try + { + g_db = NDE_CreateDatabase(plugin.hDllInstance); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + g_db = NULL; + LeaveCriticalSection(&g_db_cs); + return 0; + } + } + + wchar_t tableName[MAX_PATH] = {0}, indexName[MAX_PATH] = {0}; + PathCombineW(tableName, g_tableDir, L"recent.dat"); + PathCombineW(indexName, g_tableDir, L"recent.idx"); + + if (!g_db) + { + LeaveCriticalSection(&g_db_cs); + return 0; + } + g_table = NDE_Database_OpenTable(g_db, tableName, indexName, NDE_OPEN_ALWAYS, NDE_CACHE); + if (g_table) + { + CreateFields(g_table); + RetypeFilename(g_table); + } + LeaveCriticalSection(&g_db_cs); + return (g_table != NULL); +} + +void closeDb(bool clear_dirty) +{ + if (g_table_dirty && g_table) + { + history_bgQuery_Stop(); + NDE_Table_Sync(g_table); + if (clear_dirty) g_table_dirty=0; + } + if (g_db) + { + __try + { + if (g_table) + NDE_Database_CloseTable(g_db, g_table); + + NDE_DestroyDatabase(g_db); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + } + g_db = NULL; + g_table = NULL; +} + +INT_PTR pluginHandleIpcMessage(int msg, INT_PTR param) +{ + return (INT_PTR) SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, param, msg); +} + +static LRESULT APIENTRY wa_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if ((uMsg == WM_SYSKEYDOWN || uMsg == WM_KEYDOWN) && + wParam == 'H' && + !(GetAsyncKeyState(VK_MENU)&0x8000) && + !(GetAsyncKeyState(VK_SHIFT)&0x8000) && + (GetAsyncKeyState(VK_CONTROL)&0x8000) + && ml_history_tree > 0) + { + mediaLibrary.ShowMediaLibrary(); + mediaLibrary.SelectTreeItem(ml_history_tree); + } + else if (history_fn && uMsg == WM_TIMER && wParam == 8082) + { + if (!history_fn_mode || SendMessage(hwndDlg, WM_WA_IPC, 0, IPC_GETOUTPUTTIME) > 350) + { + KillTimer(hwndDlg, 8082); + if (SendMessage(hwndDlg, WM_WA_IPC, 0, IPC_ISPLAYING) == 1) + { + history_onFile(history_fn, -1); + } + free(history_fn); + history_fn = 0; + } + return CallWindowProcW(wa_oldWndProc, hwndDlg, uMsg, wParam, lParam); + } + else if (last_history_fn && uMsg == WM_TIMER && wParam == 8083) + { + KillTimer(hwndDlg, 8083); + history_onFile(last_history_fn, last_play_pos); + return CallWindowProcW(wa_oldWndProc, hwndDlg, uMsg, wParam, lParam); + } + else if (uMsg == WM_WA_IPC) + { + if(lParam == IPC_STOPPLAYING && g_config->ReadInt(L"resumeplayback",0)) + { + KillTimer(hwndDlg, 8082); + if (last_history_fn) + { + free(last_history_fn); + last_history_fn = 0; + } + if (history_fn) last_history_fn = _wcsdup(history_fn); + // copes with stopping after playback was tracked otherwise this aspect will fail! + else last_history_fn = _wcsdup((wchar_t*)SendMessage(hwndDlg,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME)); + last_timer = timer; + + stopPlayingInfoStruct *stopPlayingInfo = (stopPlayingInfoStruct *)wParam; + last_play_pos = stopPlayingInfo->last_time; + + if (!stopPlayingInfo->g_fullstop) + { + if ((last_timer <= 0) || (last_play_pos > last_timer)) + { + if (last_play_pos) SetTimer(hwndDlg, 8083, 150, NULL); + } + } + else // clean up play offset + { + if (last_history_fn) history_onFile(last_history_fn, 0); + } + } + else if(lParam == IPC_CB_MISC) + { + if (wParam == IPC_CB_MISC_PAUSE) + { + KillTimer(hwndDlg, 8082); + } + else if (wParam == IPC_CB_MISC_UNPAUSE) + { + if (history_fn) History_StartTracking(history_fn, true); + } + } + } // wm_wa_ipc + return CallWindowProcW(wa_oldWndProc, hwndDlg, uMsg, wParam, lParam); +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/ml_history.h b/Src/Plugins/Library/ml_history/ml_history.h new file mode 100644 index 00000000..4e8e60f5 --- /dev/null +++ b/Src/Plugins/Library/ml_history/ml_history.h @@ -0,0 +1,64 @@ +#ifndef ML_HISTORY_MAIN_H +#define ML_HISTORY_MAIN_H + +#include "main.h" +#include <windows.h> +#include <commctrl.h> +#include "..\..\General\gen_ml/gaystring.h" +#include "..\..\General\gen_ml/config.h" +#include "../nde/nde_c.h" + +#define HISTORYVIEW_COL_LASTPLAYED 0 +#define HISTORYVIEW_COL_PLAYCOUNT 1 +#define HISTORYVIEW_COL_TITLE 2 +#define HISTORYVIEW_COL_LENGTH 3 +#define HISTORYVIEW_COL_FILENAME 4 +#define HISTORYVIEW_COL_OFFSET 5 + +#define UPDATE_QUERY_TIMER_ID 505 + +extern int ml_history_tree; +HWND onTreeViewSelectChange(HWND hwnd); + +int history_init(); +void history_quit(); + +int openDb(); +void closeDb(bool clear_dirty=true); +extern wchar_t g_tableDir[]; +extern C_Config *g_config; + +extern CRITICAL_SECTION g_db_cs; +extern nde_database_t g_db; +extern nde_table_t g_table; +extern int g_table_dirty; + +inline BOOL WINAPI IsCharSpaceW(wchar_t c) { return (c == L' ' || c == L'\t'); } +inline bool IsTheW(const wchar_t *str) { if (str && (str[0] == L't' || str[0] == L'T') && (str[1] == L'h' || str[1] == L'H') && (str[2] == L'e' || str[2] == L'E') && (str[3] == L' ')) return true; else return false; } +#define SKIP_THE_AND_WHITESPACE(x) { wchar_t *save##x=(wchar_t*)x; while (IsCharSpaceW(*x) && *x) x++; if (IsTheW(x)) x+=4; while (IsCharSpaceW(*x)) x++; if (!*x) x=save##x; } + +void history_bgQuery_Stop(); +void history_onFile(const wchar_t *fn, int offset); +int retrieve_offset(const wchar_t *fn); +BOOL CALLBACK view_historyDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +BOOL CALLBACK view_errorinfoDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +void db_setFieldInt(nde_scanner_t s, unsigned char id, int data); +void db_setFieldString(nde_scanner_t s, unsigned char id, const wchar_t *data); +void makeFilename2(const wchar_t *filename, wchar_t *filename2, int filename2_len); +void queryStrEscape(const char *p, GayString &str); +INT_PTR pluginHandleIpcMessage(int msg, INT_PTR param); + +//prefs.cpp +BOOL CALLBACK PrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +#include "history.h" + +void allocRecentRecordList(historyRecordList *obj, int newsize, int granularity=512); +void emptyRecentRecordList(historyRecordList *obj); +void recentScannerRefToObjCacheNFN(nde_scanner_t s, historyRecordList *obj); +void sortResults(historyRecordList *obj, int column, int dir); +void freeRecentRecordList(historyRecordList *obj); +void saveQueryToList(nde_scanner_t s, historyRecordList *obj); + +#endif ML_HISTORY_MAIN_H
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/ml_history.rc b/Src/Plugins/Library/ml_history/ml_history.rc new file mode 100644 index 00000000..6627b89a --- /dev/null +++ b/Src/Plugins/Library/ml_history/ml_history.rc @@ -0,0 +1,264 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_VIEW_RECENTITEMS DIALOGEX 0, 0, 291, 248 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LTEXT "Search:",IDC_SEARCHCAPTION,1,2,25,8,SS_CENTERIMAGE + EDITTEXT IDC_QUICKSEARCH,28,1,209,10,ES_AUTOHSCROLL | NOT WS_BORDER + CONTROL "Clear Search",IDC_CLEAR,"Button",BS_OWNERDRAW | WS_TABSTOP,240,0,49,11 + CONTROL "List4",IDC_LIST2,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDATA | WS_TABSTOP,0,14,289,220 + CONTROL "Play",IDC_BUTTON_PLAY,"Button",BS_OWNERDRAW | WS_TABSTOP,0,237,35,11 + CONTROL "Enqueue",IDC_BUTTON_ENQUEUE,"Button",BS_OWNERDRAW | WS_TABSTOP,38,237,35,11 + CONTROL "Remove",IDC_REMOVEBOOK,"Button",BS_OWNERDRAW | WS_TABSTOP,76,237,36,11 + CONTROL "",IDC_MEDIASTATUS,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | SS_ENDELLIPSIS | WS_GROUP,113,238,178,10 +END + +IDD_PREFS DIALOGEX 0, 0, 272, 247 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "History",IDC_STATIC,0,0,272,246 + LTEXT "When 'History' is enabled, Winamp will keep track of when and how many times any items are played through Winamp.",IDC_STATIC2,6,12,259,16 + CONTROL "Enable 'History' view in Media Library",IDC_CHECK1, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,34,132,10 + CONTROL "Track when and how many times all files are played",IDC_CHECK2, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,49,177,10 + CONTROL "Track when and how many times all streams are played",IDC_CHECK3, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,62,189,10 + CONTROL "Limit tracking to items played in the last",IDC_CHECK4, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,75,138,10 + EDITTEXT IDC_EDIT1,157,74,24,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "days",IDC_STATIC1,183,76,16,8 + GROUPBOX "Tracking Control",IDC_STATIC,16,89,249,89 + CONTROL "Wait",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,23,103,30,8 + EDITTEXT IDC_EDIT2,53,101,24,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "seconds before tracking items",IDC_STATIC4,81,103,174,8 + CONTROL "Wait",IDC_CHECK6,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,23,119,30,8 + EDITTEXT IDC_EDIT3,53,117,24,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "percent of playback before tracking items",IDC_STATIC5,81,119,174,8 + CONTROL "Wait until the end of complete playback\n(May count before the displayed end due to output buffer sizes)",IDC_CHECK7, + "Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,23,133,232,16 + LTEXT "Note: When the first match against any of the above selected option(s) is met then the currently playing item will be tracked.",IDC_STATIC6,23,156,235,16 + GROUPBOX "Playback Resume",IDC_STATIC,16,184,249,52 + COMBOBOX IDC_COMBO1,22,196,236,39,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Note: This will make Winamp resume playback of tracked files from the last played position (once the first Tracking Control target has been reached).",IDC_STATIC7,22,212,249,16 +END + +IDD_VIEW_DB_ERROR DIALOGEX 0, 0, 194, 166 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CTEXT "",IDC_DB_ERROR,10,10,174,129,0x2000 + CONTROL "Reset Database",IDC_RESET_DB_ON_ERROR,"Button",BS_OWNERDRAW | WS_TABSTOP,61,144,70,12 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONTEXTMENUS MENU +BEGIN + POPUP "RecentWnd" + BEGIN + MENUITEM "Play selection\tEnter", ID_MEDIAWND_PLAYSELECTEDFILES + MENUITEM "Enqueue selection\tShift+Enter", ID_MEDIAWND_ENQUEUESELECTEDFILES + POPUP "Send to:" + BEGIN + MENUITEM "", 40003 + END + MENUITEM SEPARATOR + MENUITEM "Select all\tCtrl+A", ID_MEDIAWND_SELECTALL + MENUITEM SEPARATOR + MENUITEM "View f&ile info...\tAlt+3", ID_PE_ID3 + MENUITEM "Remove from recent list\tDel", ID_MEDIAWND_REMOVEFROMLIBRARY + MENUITEM SEPARATOR + MENUITEM "&Clear playback offset\tShift+Del", ID_MEDIAWND_REMOVEOFFSETFROMLIBRARY + MENUITEM SEPARATOR + MENUITEM "Explore item folder\tCtrl+F", 40007 + END + POPUP "Navigation" + BEGIN + MENUITEM "&Preferences", ID_NAVIGATION_PREFERENCES + MENUITEM SEPARATOR + MENUITEM "Help", ID_NAVIGATION_HELP + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PREFS, DIALOG + BEGIN + BOTTOMMARGIN, 246 + END + + IDD_VIEW_DB_ERROR, DIALOG + BEGIN + LEFTMARGIN, 10 + RIGHTMARGIN, 184 + TOPMARGIN, 10 + BOTTOMMARGIN, 156 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TREEITEM_RECENT BITMAP "resources\\ti_history_items_16x16x16.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// TEXT +// + +IDR_DB_ERROR TEXT ".\\db_error.txt" +IDR_NDE_ERROR TEXT "..\\ml_local\\nde_error.txt" + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_VIEW_ACCELERATORS ACCELERATORS +BEGIN + "3", ID_PE_ID3, VIRTKEY, ALT, NOINVERT + "A", ID_MEDIAWND_SELECTALL, VIRTKEY, CONTROL, NOINVERT + "F", ID_MEDIAWND_EXPLOREFOLDER, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_MEDIAWND_REMOVEFROMLIBRARY, VIRTKEY, NOINVERT + VK_DELETE, ID_MEDIAWND_REMOVEOFFSETFROMLIBRARY, VIRTKEY, SHIFT, NOINVERT + VK_RETURN, ID_MEDIAWND_PLAYSELECTEDFILES, VIRTKEY, NOINVERT + VK_RETURN, ID_MEDIAWND_ENQUEUESELECTEDFILES, VIRTKEY, SHIFT, NOINVERT + VK_RETURN, IDC_BUTTON_CUSTOM, VIRTKEY, SHIFT, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NULLSOFT_HISTORY "Nullsoft History v%s" + 65535 "{F8756C00-11D2-4857-8C50-163AE4A57783}" +END + +STRINGTABLE +BEGIN + IDS_HISTORY "History" + IDS_DELAYLOAD_FAILURE "History was unable to load library ('%s') which is required. \nHistory cannot continue succesfully and will be turned off." + IDS_ERROR "Error" + IDS_SCANNING_ELLIPSE "Scanning..." + IDS_COL_LAST_PLAYED "Last Played" + IDS_COL_PLAY_COUNT "Play Count" + IDS_COL_TITLE "Title" + IDS_COL_LENGTH "Length" + IDS_COL_FILENAME "Filename" + IDS_EST_TIME_NO_SECS "%d %s, %u %s [%u:%02u estimated playtime]" + IDS_EST_TIME_HAS_SECS "%d %s, %u %s [%u:%02u:%02u estimated playtime]" + IDS_SCANNING "Scanning" + IDS_ITEM "item" + IDS_ITEMS "items" +END + +STRINGTABLE +BEGIN + IDS_PLAY "play" + IDS_PLAYS "plays" + IDS_DAY "day" + IDS_DAYS "days" + IDS_REMOVE_ALL_HISTORY "Are you sure you want to reset your playing history?" + IDS_CONFIRMATION "Confirmation" + IDS_DISABLED "Disabled" + IDS_PODCAST_ONLY "Enabled for Podcast files only" + IDS_ANY_APPLICABLE "Enabled for any applicable files" +END + +STRINGTABLE +BEGIN + IDS_COL_OFFSET "Play Offset" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Library/ml_history/ml_history.sln b/Src/Plugins/Library/ml_history/ml_history.sln new file mode 100644 index 00000000..e8f35db5 --- /dev/null +++ b/Src/Plugins/Library/ml_history/ml_history.sln @@ -0,0 +1,94 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_history", "ml_history.vcxproj", "{840EEC3D-03D7-4186-8FC7-763392D13309}" + ProjectSection(ProjectDependencies) = postProject + {57C90706-B25D-4ACA-9B33-95CDB2427C27} = {57C90706-B25D-4ACA-9B33-95CDB2427C27} + {4D25C321-7F8B-424E-9899-D80A364BAF1A} = {4D25C321-7F8B-424E-9899-D80A364BAF1A} + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} = {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915} + {E105A0A2-7391-47C5-86AC-718003524C3D} = {E105A0A2-7391-47C5-86AC-718003524C3D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nde", "..\nde\nde.vcxproj", "{4D25C321-7F8B-424E-9899-D80A364BAF1A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nx", "..\replicant\nx\nx.vcxproj", "{57C90706-B25D-4ACA-9B33-95CDB2427C27}" + ProjectSection(ProjectDependencies) = postProject + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nu", "..\replicant\nu\nu.vcxproj", "{F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jnetlib", "..\replicant\jnetlib\jnetlib.vcxproj", "{E105A0A2-7391-47C5-86AC-718003524C3D}" + ProjectSection(ProjectDependencies) = postProject + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} = {44AEBB50-1331-4F2E-8AEC-56C82DE16C11} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\replicant\zlib\zlib.vcxproj", "{44AEBB50-1331-4F2E-8AEC-56C82DE16C11}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {840EEC3D-03D7-4186-8FC7-763392D13309}.Debug|Win32.ActiveCfg = Debug|Win32 + {840EEC3D-03D7-4186-8FC7-763392D13309}.Debug|Win32.Build.0 = Debug|Win32 + {840EEC3D-03D7-4186-8FC7-763392D13309}.Debug|x64.ActiveCfg = Debug|x64 + {840EEC3D-03D7-4186-8FC7-763392D13309}.Debug|x64.Build.0 = Debug|x64 + {840EEC3D-03D7-4186-8FC7-763392D13309}.Release|Win32.ActiveCfg = Release|Win32 + {840EEC3D-03D7-4186-8FC7-763392D13309}.Release|Win32.Build.0 = Release|Win32 + {840EEC3D-03D7-4186-8FC7-763392D13309}.Release|x64.ActiveCfg = Release|x64 + {840EEC3D-03D7-4186-8FC7-763392D13309}.Release|x64.Build.0 = Release|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|Win32.ActiveCfg = Debug|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|Win32.Build.0 = Debug|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|x64.ActiveCfg = Debug|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Debug|x64.Build.0 = Debug|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|Win32.ActiveCfg = Release|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|Win32.Build.0 = Release|Win32 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|x64.ActiveCfg = Release|x64 + {4D25C321-7F8B-424E-9899-D80A364BAF1A}.Release|x64.Build.0 = Release|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.ActiveCfg = Debug|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|Win32.Build.0 = Debug|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.ActiveCfg = Debug|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Debug|x64.Build.0 = Debug|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.ActiveCfg = Release|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|Win32.Build.0 = Release|Win32 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.ActiveCfg = Release|x64 + {57C90706-B25D-4ACA-9B33-95CDB2427C27}.Release|x64.Build.0 = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.ActiveCfg = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|Win32.Build.0 = Debug|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.ActiveCfg = Debug|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Debug|x64.Build.0 = Debug|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.ActiveCfg = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|Win32.Build.0 = Release|Win32 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.ActiveCfg = Release|x64 + {F1F5CD60-0D5B-4CEA-9EEB-2F87FF9AA915}.Release|x64.Build.0 = Release|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|Win32.Build.0 = Debug|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.ActiveCfg = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Debug|x64.Build.0 = Debug|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.ActiveCfg = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|Win32.Build.0 = Release|Win32 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.ActiveCfg = Release|x64 + {E105A0A2-7391-47C5-86AC-718003524C3D}.Release|x64.Build.0 = Release|x64 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|Win32.ActiveCfg = Debug|Win32 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|Win32.Build.0 = Debug|Win32 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|x64.ActiveCfg = Debug|x64 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Debug|x64.Build.0 = Debug|x64 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|Win32.ActiveCfg = Release|Win32 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|Win32.Build.0 = Release|Win32 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|x64.ActiveCfg = Release|x64 + {44AEBB50-1331-4F2E-8AEC-56C82DE16C11}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C4E799C1-027B-487B-8E1A-31F2D26A1AFE} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Library/ml_history/ml_history.vcxproj b/Src/Plugins/Library/ml_history/ml_history.vcxproj new file mode 100644 index 00000000..0d6cf7cd --- /dev/null +++ b/Src/Plugins/Library/ml_history/ml_history.vcxproj @@ -0,0 +1,341 @@ +<?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="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{840EEC3D-03D7-4186-8FC7-763392D13309}</ProjectGuid> + <RootNamespace>ml_history</RootNamespace> + <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </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> + <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|x64'" 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)'=='Release|x64'" 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 Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <IncludePath>$(IncludePath)</IncludePath> + <LibraryPath>$(LibraryPath)</LibraryPath> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir> + <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir> + <EmbedManifest>true</EmbedManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg"> + <VcpkgEnableManifest>false</VcpkgEnableManifest> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + <VcpkgConfiguration>Debug</VcpkgConfiguration> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgInstalledDir> + </VcpkgInstalledDir> + <VcpkgUseStatic>false</VcpkgUseStatic> + <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;ML_HISTORY_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;ML_HISTORY_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MinimalRebuild>false</MinimalRebuild> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <BufferSecurityCheck>true</BufferSecurityCheck> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;ML_HISTORY_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <TargetMachine>MachineX86</TargetMachine> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <Optimization>MinSpace</Optimization> + <IntrinsicFunctions>true</IntrinsicFunctions> + <FavorSizeOrSpeed>Size</FavorSizeOrSpeed> + <AdditionalIncludeDirectories>.;..\..\..\Wasabi;..\..\..\replicant;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;ML_HISTORY_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <StringPooling>true</StringPooling> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <BufferSecurityCheck>true</BufferSecurityCheck> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <WarningLevel>Level3</WarningLevel> + <DebugInformationFormat>None</DebugInformationFormat> + <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> + </ClCompile> + <Link> + <AdditionalDependencies>comctl32.lib;shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile> + <SubSystem>Windows</SubSystem> + <OptimizeReferences>true</OptimizeReferences> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <RandomizedBaseAddress>false</RandomizedBaseAddress> + <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers> + </Link> + <PostBuildEvent> + <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command> + <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message> + </PostBuildEvent> + <ResourceCompile> + <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ResourceCompile> + <Manifest> + <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile> + </Manifest> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\General\gen_ml\config.cpp" /> + <ClCompile Include="..\..\General\gen_ml\gaystring.cpp" /> + <ClCompile Include="..\..\General\gen_ml\menu.cpp" /> + <ClCompile Include="..\..\..\nu\DialogSkinner.cpp" /> + <ClCompile Include="..\..\..\nu\listview.cpp" /> + <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp" /> + <ClCompile Include="..\..\..\nu\menushortcuts.cpp" /> + <ClCompile Include="..\..\..\nu\sort.cpp" /> + <ClCompile Include="..\..\..\Winamp\strutil.cpp" /> + <ClCompile Include="api_history.cpp" /> + <ClCompile Include="HistoryAPI.cpp" /> + <ClCompile Include="HistoryAPIFactory.cpp" /> + <ClCompile Include="JSAPI2_Creator.cpp" /> + <ClCompile Include="JSAPI2_HistoryAPI.cpp" /> + <ClCompile Include="JSAPI2_HistoryRecord.cpp" /> + <ClCompile Include="JSAPI2_HistoryRecordList.cpp" /> + <ClCompile Include="Main.cpp"> + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader> + <PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">main.h</PrecompiledHeaderFile> + </ClCompile> + <ClCompile Include="ml_history.cpp" /> + <ClCompile Include="prefs.cpp" /> + <ClCompile Include="view_history.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\General\gen_ml\config.h" /> + <ClInclude Include="..\..\General\gen_ml\gaystring.h" /> + <ClInclude Include="..\..\General\gen_ml\menu.h" /> + <ClInclude Include="..\..\..\nu\menushortcuts.h" /> + <ClInclude Include="..\..\..\Winamp\strutil.h" /> + <ClInclude Include="api__ml_history.h" /> + <ClInclude Include="api_history.h" /> + <ClInclude Include="history.h" /> + <ClInclude Include="HistoryAPI.h" /> + <ClInclude Include="HistoryAPIFactory.h" /> + <ClInclude Include="JSAPI2_Creator.h" /> + <ClInclude Include="JSAPI2_HistoryAPI.h" /> + <ClInclude Include="JSAPI2_HistoryRecord.h" /> + <ClInclude Include="JSAPI2_HistoryRecordList.h" /> + <ClInclude Include="Main.h" /> + <ClInclude Include="ml_history.h" /> + <ClInclude Include="resource.h" /> + </ItemGroup> + <ItemGroup> + <Text Include="..\ml_local\nde_error.txt" /> + <Text Include="db_error.txt" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_history.rc" /> + </ItemGroup> + <ItemGroup> + <Image Include="resources\ti_history_items_16x16x16.bmp" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\..\nde\nde.vcxproj"> + <Project>{4d25c321-7f8b-424e-9899-d80a364baf1a}</Project> + </ProjectReference> + <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj"> + <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/ml_history.vcxproj.filters b/Src/Plugins/Library/ml_history/ml_history.vcxproj.filters new file mode 100644 index 00000000..4912c232 --- /dev/null +++ b/Src/Plugins/Library/ml_history/ml_history.vcxproj.filters @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="api_history.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="HistoryAPI.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="HistoryAPIFactory.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JSAPI2_Creator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JSAPI2_HistoryAPI.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JSAPI2_HistoryRecord.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="JSAPI2_HistoryRecordList.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="ml_history.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="prefs.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="view_history.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\General\gen_ml\config.cpp"> + <Filter>Source Files\gen_ml</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\DialogSkinner.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\General\gen_ml\gaystring.cpp"> + <Filter>Source Files\gen_ml</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\listview.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\General\gen_ml\menu.cpp"> + <Filter>Source Files\gen_ml</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\menushortcuts.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\..\nu\sort.cpp"> + <Filter>Source Files\nu</Filter> + </ClCompile> + <ClCompile Include="..\..\..\Winamp\strutil.cpp"> + <Filter>Source Files\Winamp</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="api__ml_history.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="api_history.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="history.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="HistoryAPI.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="HistoryAPIFactory.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="JSAPI2_Creator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="JSAPI2_HistoryAPI.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="JSAPI2_HistoryRecord.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="JSAPI2_HistoryRecordList.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="Main.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="ml_history.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="resource.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\General\gen_ml\config.h"> + <Filter>Header Files\gen_ml</Filter> + </ClInclude> + <ClInclude Include="..\..\General\gen_ml\gaystring.h"> + <Filter>Header Files\gen_ml</Filter> + </ClInclude> + <ClInclude Include="..\..\General\gen_ml\menu.h"> + <Filter>Header Files\gen_ml</Filter> + </ClInclude> + <ClInclude Include="..\..\..\nu\menushortcuts.h"> + <Filter>Header Files\nu</Filter> + </ClInclude> + <ClInclude Include="..\..\..\Winamp\strutil.h"> + <Filter>Header Files\Winamp</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Text Include="..\ml_local\nde_error.txt" /> + <Text Include="db_error.txt" /> + </ItemGroup> + <ItemGroup> + <Filter Include="Header Files"> + <UniqueIdentifier>{c2e8f027-5cef-4e02-911f-4719fc3ae234}</UniqueIdentifier> + </Filter> + <Filter Include="Ressource Files"> + <UniqueIdentifier>{a71fca4c-7b5a-4857-9d5d-ac9a8635df44}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{bd330ba0-d0e2-4ac8-b30d-b936c4549d0c}</UniqueIdentifier> + </Filter> + <Filter Include="Image Files"> + <UniqueIdentifier>{b97ef514-3134-4462-b767-54f001121b05}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\gen_ml"> + <UniqueIdentifier>{73085ff5-df6e-41f0-a474-a193e6007e23}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\nu"> + <UniqueIdentifier>{6091f753-b15a-4842-b822-d511183811d7}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Winamp"> + <UniqueIdentifier>{02146aa6-27b8-4603-89ac-f219ea316867}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\gen_ml"> + <UniqueIdentifier>{91facc1d-0b70-4a53-a029-b731c6a5fc00}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\nu"> + <UniqueIdentifier>{a9cf0eec-25c4-45ad-a116-77cc32baf3fa}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\Winamp"> + <UniqueIdentifier>{b4d6ce84-5fe5-48af-a5b1-f0e01e83d9c5}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="ml_history.rc"> + <Filter>Ressource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <Image Include="resources\ti_history_items_16x16x16.bmp"> + <Filter>Image Files</Filter> + </Image> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/prefs.cpp b/Src/Plugins/Library/ml_history/prefs.cpp new file mode 100644 index 00000000..8fa3832d --- /dev/null +++ b/Src/Plugins/Library/ml_history/prefs.cpp @@ -0,0 +1,178 @@ +#include "main.h" +#include "..\..\General\gen_ml/config.h" +#include "resource.h" + +void hideShowRecentItemsCheckboxes(HWND hwndDlg) +{ + int enabled = IsDlgButtonChecked(hwndDlg, IDC_CHECK1); + int wait_secs = !!g_config->ReadInt(L"recent_wait_secs",0); + int wait_percent = !!g_config->ReadInt(L"recent_wait_percent",0); + int wait_end = !!g_config->ReadInt(L"recent_wait_end",0); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK2), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK3), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK4), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK5), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK6), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK7), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT1), enabled && !!g_config->ReadInt(L"recent_limitd",1)); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC1), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC3), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC4), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC5), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC6), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT2), enabled && wait_secs); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT3), enabled && wait_percent); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC7), enabled && !(wait_end && !wait_secs && !wait_percent)); + EnableWindow(GetDlgItem(hwndDlg, IDC_COMBO1), enabled && !(wait_end && !wait_secs && !wait_percent)); +} + +BOOL CALLBACK PrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + static int need_ref; + switch (uMsg) + { + case WM_INITDIALOG: + { + CheckDlgButton(hwndDlg,IDC_CHECK2,(g_config->ReadInt(L"recent_track",1)&1)?BST_CHECKED:0); + CheckDlgButton(hwndDlg,IDC_CHECK3,(g_config->ReadInt(L"recent_track",1)&2)?0:BST_CHECKED); + CheckDlgButton(hwndDlg,IDC_CHECK1,!!g_config->ReadInt(L"showrecentitems",1)); + CheckDlgButton(hwndDlg,IDC_CHECK4,!!g_config->ReadInt(L"recent_limitd",1)); + CheckDlgButton(hwndDlg,IDC_CHECK5,!!g_config->ReadInt(L"recent_wait_secs",0)); + CheckDlgButton(hwndDlg,IDC_CHECK6,!!g_config->ReadInt(L"recent_wait_percent",0)); + CheckDlgButton(hwndDlg,IDC_CHECK7,!!g_config->ReadInt(L"recent_wait_end",0)); + + int item = (int)SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_DISABLED)); + SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_SETITEMDATA,item,0); + item = (int)SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_PODCAST_ONLY)); + SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_SETITEMDATA,item,1); + item = (int)SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)WASABI_API_LNGSTRINGW(IDS_ANY_APPLICABLE)); + SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_SETITEMDATA,item,2); + SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_SETCURSEL,g_config->ReadInt(L"resumeplayback",0),0); + + SetDlgItemInt(hwndDlg,IDC_EDIT1,g_config->ReadInt(L"recent_limitnd",30),FALSE); + SetDlgItemInt(hwndDlg,IDC_EDIT2,g_config->ReadInt(L"recent_wait_secs_lim",5),FALSE); + SetDlgItemInt(hwndDlg,IDC_EDIT3,g_config->ReadInt(L"recent_wait_percent_lim",50),FALSE); + need_ref=0; + hideShowRecentItemsCheckboxes(hwndDlg); + break; + } + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_CHECK1: + { + int a=!!IsDlgButtonChecked(hwndDlg,IDC_CHECK1); + g_config->WriteInt(L"showrecentitems",a); + if (a) history_init(); + else history_quit(); + hideShowRecentItemsCheckboxes(hwndDlg); + } + break; + + case IDC_EDIT1: + if (HIWORD(wParam) == EN_CHANGE) + { + BOOL t; + int v=GetDlgItemInt(hwndDlg,IDC_EDIT1,&t,FALSE); + if (t) g_config->WriteInt(L"recent_limitnd",v); + need_ref++; + } + break; + + case IDC_CHECK4: + g_config->WriteInt(L"recent_limitd",!!IsDlgButtonChecked(hwndDlg,IDC_CHECK4)); + EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),!!g_config->ReadInt(L"recent_limitd",1)); + need_ref++; + break; + + case IDC_CHECK2: + case IDC_CHECK3: + g_config->WriteInt(L"recent_track",(IsDlgButtonChecked(hwndDlg,IDC_CHECK2)?1:0) | (IsDlgButtonChecked(hwndDlg,IDC_CHECK3)?0:2)); + break; + + case IDC_CHECK5: + g_config->WriteInt(L"recent_wait_secs",!!IsDlgButtonChecked(hwndDlg,IDC_CHECK5)); + hideShowRecentItemsCheckboxes(hwndDlg); + break; + + case IDC_CHECK6: + g_config->WriteInt(L"recent_wait_percent",!!IsDlgButtonChecked(hwndDlg,IDC_CHECK6)); + hideShowRecentItemsCheckboxes(hwndDlg); + break; + + case IDC_CHECK7: + g_config->WriteInt(L"recent_wait_end",!!IsDlgButtonChecked(hwndDlg,IDC_CHECK7)); + hideShowRecentItemsCheckboxes(hwndDlg); + break; + + case IDC_COMBO1: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int item = (int)SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_GETCURSEL,0,0); + if (item != CB_ERR) + { + g_config->WriteInt(L"resumeplayback", (int)SendDlgItemMessageW(hwndDlg,IDC_COMBO1,CB_GETITEMDATA,item,0)); + } + } + break; + + case IDC_EDIT2: + if (HIWORD(wParam) == EN_CHANGE) + { + BOOL t; + int v=GetDlgItemInt(hwndDlg,IDC_EDIT2,&t,FALSE); + if (t) + { + if(v < 0) + { + v = 1; + SetDlgItemInt(hwndDlg, IDC_EDIT2, v, 0); + } + g_config->WriteInt(L"recent_wait_secs_lim",v); + } + need_ref++; + } + break; + + case IDC_EDIT3: + if (HIWORD(wParam) == EN_CHANGE) + { + BOOL t; + int v=GetDlgItemInt(hwndDlg,IDC_EDIT3,&t,FALSE); + if(t) + { + int tweaked = 0; + if(v > 99){ + v = 99; + tweaked = 1; + } + else if(v < 1) + { + v = 1; + tweaked = 1; + } + if(tweaked) + { + SetDlgItemInt(hwndDlg, IDC_EDIT3, v, 0); + } + + g_config->WriteInt(L"recent_wait_percent_lim",v); + } + need_ref++; + } + break; + }; + break; + + case WM_DESTROY: + if (need_ref) + { + g_config->WriteInt(L"recent_limitlt",0); // make sure it gets refreshed + // TODO: only do this if the history view is open + PostMessage(plugin.hwndLibraryParent,WM_USER+30,0,0); + } + break; + } + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/Library/ml_history/resource.h b/Src/Plugins/Library/ml_history/resource.h new file mode 100644 index 00000000..83a2c048 --- /dev/null +++ b/Src/Plugins/Library/ml_history/resource.h @@ -0,0 +1,92 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ml_history.rc +// +#define IDS_HISTORY 1 +#define IDS_DELAYLOAD_FAILURE 2 +#define IDS_ERROR 3 +#define IDS_SCANNING_ELLIPSE 4 +#define IDS_COL_LAST_PLAYED 5 +#define IDS_COL_PLAY_COUNT 6 +#define IDS_COL_TITLE 7 +#define IDS_COL_LENGTH 8 +#define IDS_COL_FILENAME 9 +#define IDS_EST_TIME_NO_SECS 10 +#define IDS_EST_TIME_HAS_SECS 11 +#define IDS_SCANNING 13 +#define IDS_ITEM 14 +#define IDS_ITEMS 15 +#define IDS_PLAY 16 +#define IDS_PLAYS 17 +#define IDS_DAY 18 +#define IDS_DAYS 19 +#define IDS_REMOVE_ALL_HISTORY 20 +#define IDS_CONFIRMATION 21 +#define IDS_DISABLED 22 +#define IDS_PODCAST_ONLY 23 +#define IDS_ANY_APPLICABLE 24 +#define IDR_CONTEXTMENUS 101 +#define IDD_DIALOG1 102 +#define IDB_BITMAP1 103 +#define IDB_TREEITEM_RECENT 103 +#define IDD_PREFS 104 +#define IDS_COL_OFFSET 107 +#define IDR_ACCELERATOR1 109 +#define IDR_VIEW_ACCELERATORS 109 +#define IDR_DB_ERROR 110 +#define IDD_VIEW_DB_ERROR 111 +#define IDR_NDE_ERROR 111 +#define IDD_VIEW_RECENTITEMS 246 +#define IDC_BUTTON_CUSTOM 1000 +#define IDC_LIST2 1001 +#define IDC_STATIC6 1001 +#define IDC_CLEAR 1005 +#define IDC_QUICKSEARCH 1006 +#define IDC_COMBO1 1006 +#define IDC_MEDIASTATUS 1015 +#define IDC_BUTTON_PLAY 1016 +#define IDC_BUTTON_ENQUEUE 1017 +#define IDC_SEARCHCAPTION 1020 +#define IDC_CHECK1 1050 +#define IDC_CHECK2 1051 +#define IDC_CHECK3 1052 +#define IDC_CHECK4 1053 +#define IDC_EDIT1 1054 +#define IDC_CHECK5 1055 +#define IDC_EDIT2 1056 +#define IDC_STATIC1 1057 +#define IDC_STATIC2 1058 +#define IDC_STATIC3 1059 +#define IDC_STATIC4 1060 +#define IDC_CHECK6 1061 +#define IDC_EDIT3 1062 +#define IDC_STATIC5 1063 +#define IDC_CHECK7 1064 +#define IDC_CHECK8 1065 +#define IDC_STATIC7 1066 +#define IDC_REMOVEBOOK 1084 +#define IDC_DB_ERROR 1085 +#define IDC_RESET_DB_ON_ERROR 1086 +#define ID_MEDIAWND_PLAYSELECTEDFILES 40001 +#define ID_MEDIAWND_ENQUEUESELECTEDFILES 40002 +#define ID_MEDIAWND_ADDTOPLAYLIST 40003 +#define ID_MEDIAWND_SELECTALL 40004 +#define ID_PE_ID3 40005 +#define ID_MEDIAWND_REMOVEFROMLIBRARY 40006 +#define ID_MEDIAWND_EXPLOREFOLDER 40007 +#define ID_NAVIGATION_PREFERENCES 40008 +#define ID_NAVIGATION_HELP 40009 +#define ID_MEDIAWND_REMOVEOFFSETFROMLIBRARY 40010 +#define ID_RECENTWND_CLEARPLAYBACKOFFSET 40011 +#define IDS_NULLSOFT_HISTORY 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 113 +#define _APS_NEXT_COMMAND_VALUE 40012 +#define _APS_NEXT_CONTROL_VALUE 1007 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Library/ml_history/resources/ti_history_items_16x16x16.bmp b/Src/Plugins/Library/ml_history/resources/ti_history_items_16x16x16.bmp Binary files differnew file mode 100644 index 00000000..06714c39 --- /dev/null +++ b/Src/Plugins/Library/ml_history/resources/ti_history_items_16x16x16.bmp diff --git a/Src/Plugins/Library/ml_history/version.rc2 b/Src/Plugins/Library/ml_history/version.rc2 new file mode 100644 index 00000000..c0abd21a --- /dev/null +++ b/Src/Plugins/Library/ml_history/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,0,3,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Media Library Plug-in" + VALUE "FileVersion", "2,0,3,0" + VALUE "InternalName", "Nullsoft History" + VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "ml_history.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Library/ml_history/view_history.cpp b/Src/Plugins/Library/ml_history/view_history.cpp new file mode 100644 index 00000000..010cf4e4 --- /dev/null +++ b/Src/Plugins/Library/ml_history/view_history.cpp @@ -0,0 +1,2349 @@ +#include "main.h" +#include "..\..\General\gen_ml/config.h" +#include "resource.h" +#include "../nu/DialogSkinner.h" +#include "../nu/listview.h" +#include <time.h> +#include "..\..\General\gen_ml/gaystring.h" +#include "..\..\General\gen_ml/ml_ipc.h" +#include <malloc.h> +#include <string> +#include "../nu/AutoWide.h" +#include "../nu/AutoCharFn.h" +#include "..\..\General\gen_ml/ml.h" +#include "..\..\General\gen_ml/ml_ipc_0313.h" +#include "../nu/sort.h" +#include "../nu/menushortcuts.h" +#include "../Winamp/strutil.h" +#include "api__ml_history.h" +#include <strsafe.h> + +static INT_PTR IPC_LIBRARY_SENDTOMENU; +static void history_cleanupifnecessary(); +static GayStringW g_q; +static W_ListView resultlist; +static int resultSkin, customAllowed; +static HWND m_hwnd, m_headerhwnd; +static historyRecordList itemCache; +static int history_bgThread_Kill; +static HANDLE history_bgThread_Handle; +static void fileInfoDialogs(HWND hwndParent); +static int m_lv_last_topidx; +int groupBtn = 1, enqueuedef = 0; +HMENU g_context_menus2 = NULL; +static viewButtons view; + +static void MakeDateStringW(__time64_t convertTime, wchar_t *dest, size_t destlen) +{ + SYSTEMTIME sysTime = {0}; + tm *newtime = _localtime64(&convertTime); + + sysTime.wYear = newtime->tm_year + 1900; + sysTime.wMonth = newtime->tm_mon + 1; + sysTime.wDayOfWeek = newtime->tm_wday; + sysTime.wDay = newtime->tm_mday; + sysTime.wHour = newtime->tm_hour; + sysTime.wMinute = newtime->tm_min; + sysTime.wSecond = newtime->tm_sec; + + GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &sysTime, NULL, dest, (int)destlen); + + size_t dateSize = lstrlenW(dest); + dest += dateSize; + destlen -= dateSize; + if (destlen) + { + *dest++ = ' '; + destlen--; + } + + GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, dest, (int)destlen); +} + +void makeFilename2(const wchar_t *filename, wchar_t *filename2, int filename2_len) +{ + filename2[0]=0; + if (wcsstr(filename,L"~")) + { + WIN32_FIND_DATAW d = {0}; + HANDLE h = FindFirstFileW(filename,&d); + if (h != INVALID_HANDLE_VALUE) + { + FindClose(h); + lstrcpynW(filename2,filename,filename2_len); + wchar_t *p=scanstr_backW(filename2,L"\\",filename2-1)+1; + int offs=(int)(p-filename2); + lstrcpynW(filename2+offs,d.cFileName,filename2_len - offs); + if (!_wcsicmp(filename,filename2)) filename2[0]=0; + } + } +} + +void db_setFieldString(nde_scanner_t s, unsigned char id, const wchar_t *data) +{ + nde_field_t f = NDE_Scanner_GetFieldByID(s, id); + if (!f) f = NDE_Scanner_NewFieldByID(s, id); + NDE_StringField_SetString(f, data); +} + +void db_setFieldInt(nde_scanner_t s, unsigned char id, int data) +{ + nde_field_t f = NDE_Scanner_GetFieldByID(s, id); + if (!f) f = NDE_Scanner_NewFieldByID(s, id); + NDE_IntegerField_SetValue(f, data); +} + +void queryStrEscape(const wchar_t *p, GayStringW &str) +{ + if (!p || !*p) return; + size_t l = wcslen(p); + wchar_t *escaped = (wchar_t *)calloc((l*3+1), sizeof(wchar_t)); + if (escaped) + { + wchar_t *d = escaped; + while (p && *p) + { + if (*p == L'%') { *d++ = L'%'; *d++ = L'%'; } + else if (*p == L'\"') { *d++ = L'%'; *d++ = L'2'; *d++ = L'2'; } + else if (*p == L'\'') { *d++ = L'%'; *d++ = L'2'; *d++ = L'7'; } + else if (*p == L'[') { *d++ = L'%'; *d++ = L'5'; *d++ = L'B'; } + else if (*p == L']') { *d++ = L'%'; *d++ = L'5'; *d++ = L'D'; } + else if (*p == L'(') { *d++ = L'%'; *d++ = L'2'; *d++ = L'8'; } + else if (*p == L')') { *d++ = L'%'; *d++ = L'2'; *d++ = L'9'; } + else if (*p == L'#') { *d++ = L'%'; *d++ = L'2'; *d++ = L'3'; } + else *d++ = *p; + p++; + } + *d = 0; + str.Set(escaped); + free(escaped); + } +} + +int STRCMP_NULLOK(const wchar_t *pa, const wchar_t *pb) +{ + if (!pa) pa = L""; + else SKIP_THE_AND_WHITESPACE(pa) + + if (!pb) pb = L""; + else SKIP_THE_AND_WHITESPACE(pb) + + return _wcsicmp(pa, pb); +} + +struct SortRules +{ + int by; + int dir; +}; +static int __fastcall sortFunc(const void *elem1, const void *elem2, const void *context) +{ + historyRecord *a=(historyRecord*)elem1; + historyRecord *b=(historyRecord*)elem2; + + const SortRules *rules = (SortRules *)context; + int use_by = rules->by; + int use_dir = !!rules->dir; + + #define RETIFNZ(v) if ((v)<0) return use_dir?1:-1; if ((v)>0) return use_dir?-1:1; + + // this might be too slow, but it'd be nice + int x; + for (x = 0; x < 4; x ++) + { + if (use_by == HISTORY_SORT_FILENAME) + { + int v=STRCMP_NULLOK(a->filename,b->filename); + RETIFNZ(v) + return 0; + } + else if (use_by == HISTORY_SORT_TITLE) + { + int v=STRCMP_NULLOK(a->title,b->title); + RETIFNZ(v) + return 0; + } + else if (use_by == HISTORY_SORT_LASTPLAYED) + { + __time64_t v1=a->lastplayed; + __time64_t v2=b->lastplayed; + RETIFNZ(v2-v1) + return 0; + } + else if (use_by == HISTORY_SORT_PLAYCOUNT) + { + int v1=a->playcnt; + int v2=b->playcnt; + RETIFNZ(v2-v1) + use_by=HISTORYVIEW_COL_LASTPLAYED; + } + else if (use_by == HISTORY_SORT_LENGTH) // length -> artist -> album -> track + { + int v1=a->length; + int v2=b->length; + if (v1<0)v1=0; + if (v2<0)v2=0; + RETIFNZ(v2-v1) + return 0; + } + else if (use_by == HISTORY_SORT_OFFSET) + { + int v1=a->offset; + int v2=b->offset; + if (v1<0)v1=0; + if (v2<0)v2=0; + RETIFNZ(v2-v1) + return 0; + } + else break; // no sort order? + } + #undef RETIFNZ + return 0; +} + +void sortResults(historyRecordList *obj, int column, int dir) // sorts the results based on the current sort mode +{ + if (obj->Size > 1) + { + SortRules rules = {column, dir}; + nu::qsort(obj->Items,obj->Size,sizeof(historyRecord),&rules, sortFunc); + } +} + +// does not copy filename +void recentScannerRefToObjCacheNFN(nde_scanner_t s, historyRecordList *obj) +{ + nde_field_t f=NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_TITLE); + if (f) + { + wchar_t *strval = NDE_StringField_GetString(f); + ndestring_retain(strval); + obj->Items[obj->Size].title = strval; + } + else + obj->Items[obj->Size].title = 0; + + f=NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_LENGTH); + obj->Items[obj->Size].length = f?NDE_IntegerField_GetValue(f):-1; + f=NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_OFFSET); + obj->Items[obj->Size].offset = f?NDE_IntegerField_GetValue(f):-1; + f=NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_PLAYCOUNT); + obj->Items[obj->Size].playcnt = f?NDE_IntegerField_GetValue(f):0; + f=NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_LASTPLAYED); + obj->Items[obj->Size].lastplayed = f?NDE_IntegerField_GetValue(f):0; + obj->Size++; +} + +static void freeRecentRecord(historyRecord *p) +{ + ndestring_release(p->title); + ndestring_release(p->filename); +} + +void emptyRecentRecordList(historyRecordList *obj) +{ + historyRecord *p=obj->Items; + while (obj->Size-->0) + { + freeRecentRecord(p); + p++; + } + obj->Size=0; +} + +void freeRecentRecordList(historyRecordList *obj) +{ + emptyRecentRecordList(obj); + free(obj->Items); + obj->Items=0; + obj->Alloc=obj->Size=0; +} + +void allocRecentRecordList(historyRecordList *obj, int newsize, int granularity) +{ + if (newsize < obj->Alloc || newsize < obj->Size) return; + + size_t old_Alloc = obj->Alloc; + obj->Alloc=newsize+granularity; + historyRecord *data = (historyRecord*)realloc(obj->Items,sizeof(historyRecord)*obj->Alloc); + if (data) + { + obj->Items=data; + } + else + { + data=(historyRecord*)malloc(sizeof(historyRecord)*obj->Alloc); + if (data) + { + memcpy(data, obj->Items, sizeof(historyRecord)*old_Alloc); + free(obj->Items); + obj->Items=data; + } + else obj->Alloc = (int)old_Alloc; + } + if (!obj->Items) obj->Alloc=0; +} + +static void playFiles(int enqueue, int all) +{ + if (history_bgThread_Handle) return; + + int cnt=0; + int l=itemCache.Size; + + for(int i=0;i<l;i++) + { + if ( all || resultlist.GetSelected( i ) ) + { + if ( !cnt ) + { + if ( !enqueue ) SendMessage( plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_DELETE ); + cnt++; + } + enqueueFileWithMetaStructW s = { 0 }; + s.filename = itemCache.Items[ i ].filename; + s.title = itemCache.Items[ i ].title; + s.ext = NULL; + s.length = itemCache.Items[ i ].length; + SendMessage( plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW ); + } + } + if (cnt) + { + if(!enqueue) SendMessage(plugin.hwndWinampParent, WM_WA_IPC,0,IPC_STARTPLAY); + } +} + +static int history_saveQueryToList(nde_scanner_t s, historyRecordList *obj, int user32, int *killswitch) { + + emptyRecentRecordList(obj); + + NDE_Scanner_First(s, killswitch); + if (killswitch && *killswitch) + { + return 0; + } + + int r; + unsigned int total_length_s=0; + do + { + nde_field_t f = NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_FILENAME); + if (!f) break; + + allocRecentRecordList(obj,obj->Size+1); + if (!obj->Alloc) break; + + wchar_t *strval = NDE_StringField_GetString(f); + ndestring_retain(strval); + obj->Items[obj->Size].filename = strval; + recentScannerRefToObjCacheNFN(s,obj); + + int thisl=obj->Items[obj->Size-1].length; + + if (thisl > 0) total_length_s+=thisl * obj->Items[obj->Size-1].playcnt; + else total_length_s|=(1<<31); + + r=NDE_Scanner_Next(s, killswitch); + if (killswitch && *killswitch) + { + return 0; + } + } while(r); + + if (obj->Size && obj->Size < obj->Alloc - 1024) + { + size_t old_Alloc = obj->Alloc; + obj->Alloc = obj->Size; + historyRecord *data=(historyRecord*)realloc(obj->Items,sizeof(historyRecord)*obj->Alloc); + if (data) + { + obj->Items=data; + } + else + { + data=(historyRecord*)malloc(sizeof(historyRecord)*obj->Alloc); + if (data) + { + memcpy(data, obj->Items, sizeof(historyRecord)*old_Alloc); + free(obj->Items); + obj->Items=data; + } + else obj->Alloc = (int)old_Alloc; + } + } + + if (killswitch && *killswitch) return 0; + + sortResults(obj, + g_config->ReadInt(L"recent_sort_by",HISTORY_SORT_LASTPLAYED), + g_config->ReadInt(L"recent_sort_dir",0)); + + if (killswitch && *killswitch) return 0; + + return total_length_s; +} + +typedef struct +{ + int user32; +} history_bgThreadParms; + +// out can never be bigger than in+1 +static void parsequicksearch(wchar_t *out, const wchar_t *in) // parses a list into a list of terms that we are searching for +{ + int inquotes = 0, neednull = 0; + while (in && *in) + { + wchar_t c = *in++; + if (c != ' ' && c != '\t' && c != '\"') + { + neednull = 1; + *out++ = c; + } + else if (c == '\"') + { + inquotes = !inquotes; + if (!inquotes) + { + *out++ = 0; + neednull = 0; + } + } + else + { + if (inquotes) *out++ = c; + else if (neednull) + { + *out++ = 0; + neednull = 0; + } + } + } + *out++ = 0; + *out++ = 0; +} + +void makeQueryStringFromText(GayStringW *query, const wchar_t *text, int nf) +{ + int ispar = 0; + if (query->Get()[0]) + { + ispar = 1; + query->Append(L"&("); + } + if (!_wcsnicmp(text, L"query:", 6)) query->Append(text + 6); // copy the query as is + else if (text[0] == L'?') query->Append(text + 1); + else // this is ubergay. no wait it isn't anymore. it rocks now due to the GayString + { + int isAny = 0; + if (text && (*text == L'*' && text[1] == L' ')) + { + isAny = 1; + text += 2; + } + int cchText = lstrlenW(text); + wchar_t *tmpbuf = (wchar_t*)calloc((cchText + 2), sizeof(wchar_t)); + parsequicksearch(tmpbuf, text); + + int x; + const wchar_t *fields[5] = + { + L"filename", + L"title", + L"artist", + L"album", + L"genre", + }; + wchar_t *p = tmpbuf; + while (p && *p) + { + size_t lenp = wcslen(p); + + if (p == tmpbuf) query->Append(L"("); + else if (isAny) query->Append(L")|("); + else query->Append(L")&("); + if (p[0] == L'<' && p[wcslen(p) - 1] == L'>' && wcslen(p) > 2) + { + wchar_t *op = p; + while (op && *op) + { + if (op && *op == L'\'') *op = L'\"'; + if (op) op++; + } + p[lenp - 1] = 0; // remove > + query->Append(p + 1); + } + else + { + for (x = 0; x < (int)min(sizeof(fields) / sizeof(fields[0]), nf); x ++) + { + const wchar_t *field = fields[x]; + if (x) query->Append(L"|"); + query->Append(field); + query->Append(L" HAS \""); + GayStringW escaped; + queryStrEscape(p, escaped); + query->Append(escaped.Get()); + query->Append(L"\""); + } + } + p += lenp + 1; + } + query->Append(L")"); + free(tmpbuf); + } + if (ispar) query->Append(L")"); +} + +static DWORD WINAPI history_bgThreadQueryProc(void *tmp) +{ + history_bgThreadParms *p=(history_bgThreadParms*)tmp; + + EnterCriticalSection(&g_db_cs); + nde_scanner_t s = NDE_Table_CreateScanner(g_table); + NDE_Scanner_Query(s, g_q.Get()); + int total_length_s=history_saveQueryToList(s,&itemCache, p->user32, &history_bgThread_Kill); + NDE_Table_DestroyScanner(g_table, s); + LeaveCriticalSection(&g_db_cs); + + if (!history_bgThread_Kill) PostMessage(m_hwnd,WM_APP+3,0x69,total_length_s); + return 0; +} + +void history_bgQuery_Stop() // exported for other people to call since it is useful (eventually +{ // we should have bgQuery pass the new query info along but I'll do that soon) + if (history_bgThread_Handle) + { + history_bgThread_Kill=1; + WaitForSingleObject(history_bgThread_Handle,INFINITE); + CloseHandle(history_bgThread_Handle); + history_bgThread_Handle=0; + } + KillTimer(m_hwnd,123); +} + +static void history_bgQuery(int user32=0) // only internal used +{ + history_bgQuery_Stop(); + + SetDlgItemTextW(m_hwnd,IDC_MEDIASTATUS,WASABI_API_LNGSTRINGW(IDS_SCANNING_ELLIPSE)); + SetTimer(m_hwnd,123,200,NULL); + + DWORD id; + static history_bgThreadParms parms; + parms.user32=user32; + history_bgThread_Kill=0; + history_bgThread_Handle=CreateThread(NULL,0,history_bgThreadQueryProc,(LPVOID)&parms,0,&id); +} + +static void doQuery(HWND hwndDlg, wchar_t *text, int dobg=1) { + history_bgQuery_Stop(); + + GayStringW query; + if (text[0]) makeQueryStringFromText(&query,text,2); + + wchar_t *parent_query=NULL; + SendMessage(GetParent(hwndDlg),WM_APP+2,0,(LONG_PTR)&parent_query); + + g_q.Set(L""); + + if(parent_query && parent_query[0]) + { + g_q.Set(L"("); + g_q.Append(parent_query); + g_q.Append(L")"); + } + + if (query.Get() && query.Get()[0]) + { + if(g_q.Get()[0]) + { + g_q.Append(L" & ("); + g_q.Append(query.Get()); + g_q.Append(L")"); + } + else g_q.Set(query.Get()); + } + + if (dobg) history_bgQuery(); +} + +static WNDPROC search_oldWndProc; +static DWORD WINAPI search_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + if(uMsg == WM_KEYDOWN && wParam == VK_DOWN) + { + HWND hwndList = resultlist.getwnd(); + if (hwndList) + { + PostMessage(GetParent(hwndDlg), WM_NEXTDLGCTL, (WPARAM)hwndList, TRUE); + ListView_SetItemState(hwndList,0,LVIS_FOCUSED|LVIS_SELECTED,LVIS_FOCUSED|LVIS_SELECTED); + } + } + return (DWORD)CallWindowProcW(search_oldWndProc,hwndDlg,uMsg,wParam,lParam); +} + +static void exploreItemFolder(HWND hwndDlg) +{ + if (resultlist.GetSelectionMark() >= 0) + { + int l=resultlist.GetCount(); + for(int i=0;i<l;i++) + { + if (resultlist.GetSelected(i)) + { + WASABI_API_EXPLORERFINDFILE->AddFile(itemCache.Items[i].filename); + } + } + WASABI_API_EXPLORERFINDFILE->ShowFiles(); + } +} + +static void removeSelectedItems(int isAll=0) +{ + int hasdel=0; + history_bgQuery_Stop(); + + EnterCriticalSection(&g_db_cs); + nde_scanner_t s = NDE_Table_CreateScanner(g_table); + + for(int i=0;i<itemCache.Size;i++) + { + if(resultlist.GetSelected(i) || isAll) + { + if(NDE_Scanner_LocateNDEFilename(s, HISTORYVIEW_COL_FILENAME,FIRST_RECORD,itemCache.Items[i].filename)) + { + hasdel=1; + NDE_Scanner_Edit(s); + NDE_Scanner_Delete(s); + NDE_Scanner_Post(s); + } + } + } + + NDE_Table_DestroyScanner(g_table, s); + + if (!hasdel) + { + LeaveCriticalSection(&g_db_cs); + return; + } + + NDE_Table_Sync(g_table); + NDE_Table_Compact(g_table); + g_table_dirty=0; + LeaveCriticalSection(&g_db_cs); + + resultlist.Clear(); + emptyRecentRecordList(&itemCache); + + SendMessage(m_hwnd,WM_APP+1,0,0); //refresh current view +} + +static void removeSelectedItemOffsets(int isAll=0) +{ + int hasdel=0; + history_bgQuery_Stop(); + + EnterCriticalSection(&g_db_cs); + nde_scanner_t s = NDE_Table_CreateScanner(g_table); + + for(int i=0;i<itemCache.Size;i++) + { + if(resultlist.GetSelected(i) || isAll) + { + if(NDE_Scanner_LocateNDEFilename(s, HISTORYVIEW_COL_FILENAME,FIRST_RECORD,itemCache.Items[i].filename)) + { + db_setFieldInt(s, HISTORYVIEW_COL_OFFSET, -1); + NDE_Scanner_Post(s); + hasdel=1; + itemCache.Items[i].offset = -1; + } + } + } + + NDE_Table_DestroyScanner(g_table, s); + + if (!hasdel) + { + LeaveCriticalSection(&g_db_cs); + return; + } + + NDE_Table_Sync(g_table); + NDE_Table_Compact(g_table); + g_table_dirty=0; + LeaveCriticalSection(&g_db_cs); + + resultlist.RefreshAll(); +} + +static void History_SaveLastQuery(HWND hwnd) +{ + LPSTR pszQuery = NULL; + HWND hEditbox = GetDlgItem(hwnd, IDC_QUICKSEARCH); + if (NULL != hEditbox) + { + UINT cchTextMax = GetWindowTextLength(hEditbox); + if (0 != cchTextMax) + { + cchTextMax++; + LPWSTR pszText = (LPWSTR)calloc(cchTextMax, sizeof(WCHAR)); + if (NULL != pszText) + { + UINT cchText = GetWindowTextW(hEditbox, pszText, cchTextMax); + if (0 != cchText) + { + UINT cchQuery = WideCharToMultiByte(CP_UTF8, 0, pszText, cchText, NULL, 0, NULL, NULL); + if (0 != cchQuery) + { + cchQuery++; + pszQuery = (LPSTR)calloc(cchQuery, sizeof(CHAR)); + if (NULL != pszQuery) + { + cchQuery = WideCharToMultiByte(CP_UTF8, 0, pszText, cchText, pszQuery, cchQuery, NULL, NULL); + pszQuery[cchQuery] = '\0'; + } + } + } + free(pszText); + } + } + } + g_config->WriteString("recent_lastquery", pszQuery); + if (NULL != pszQuery) + free(pszQuery); +} + +void SwapPlayEnqueueInMenu(HMENU listMenu) +{ + int playPos=-1, enqueuePos=-1; + MENUITEMINFOW playItem={sizeof(MENUITEMINFOW), 0,}, enqueueItem={sizeof(MENUITEMINFOW), 0,}; + + int numItems = GetMenuItemCount(listMenu); + + for (int i=0;i<numItems;i++) + { + UINT id = GetMenuItemID(listMenu, i); + if (id == ID_MEDIAWND_PLAYSELECTEDFILES) + { + playItem.fMask = MIIM_ID; + playPos = i; + GetMenuItemInfoW(listMenu, i, TRUE, &playItem); + } + else if (id == ID_MEDIAWND_ENQUEUESELECTEDFILES) + { + enqueueItem.fMask = MIIM_ID; + enqueuePos= i; + GetMenuItemInfoW(listMenu, i, TRUE, &enqueueItem); + } + } + + playItem.wID = ID_MEDIAWND_ENQUEUESELECTEDFILES; + enqueueItem.wID = ID_MEDIAWND_PLAYSELECTEDFILES; + SetMenuItemInfoW(listMenu, playPos, TRUE, &playItem); + SetMenuItemInfoW(listMenu, enqueuePos, TRUE, &enqueueItem); +} + +void SyncMenuWithAccelerators(HWND hwndDlg, HMENU menu) +{ + HACCEL szAccel[24] = {0}; + INT c = WASABI_API_APP->app_getAccelerators(hwndDlg, szAccel, sizeof(szAccel)/sizeof(szAccel[0]), FALSE); + AppendMenuShortcuts(menu, szAccel, c, MSF_REPLACE); +} + +static HRGN g_rgnUpdate = NULL; +static int offsetX = 0, offsetY = 0; + +typedef struct _LAYOUT +{ + INT id; + HWND hwnd; + INT x; + INT y; + INT cx; + INT cy; + DWORD flags; + HRGN rgn; +} +LAYOUT, PLAYOUT; + +#define SETLAYOUTPOS(_layout, _x, _y, _cx, _cy) { _layout->x=_x; _layout->y=_y;_layout->cx=_cx;_layout->cy=_cy;_layout->rgn=NULL; } +#define SETLAYOUTFLAGS(_layout, _r) \ + { \ + BOOL fVis; \ + fVis = (WS_VISIBLE & (LONG)GetWindowLongPtr(_layout->hwnd, GWL_STYLE)); \ + if (_layout->x == _r.left && _layout->y == _r.top) _layout->flags |= SWP_NOMOVE; \ + if (_layout->cx == (_r.right - _r.left) && _layout->cy == (_r.bottom - _r.top)) _layout->flags |= SWP_NOSIZE; \ + if ((SWP_HIDEWINDOW & _layout->flags) && !fVis) _layout->flags &= ~SWP_HIDEWINDOW; \ + if ((SWP_SHOWWINDOW & _layout->flags) && fVis) _layout->flags &= ~SWP_SHOWWINDOW; \ + } + +#define LAYOUTNEEEDUPDATE(_layout) ((SWP_NOMOVE | SWP_NOSIZE) != ((SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_SHOWWINDOW) & _layout->flags)) + +#define GROUP_MIN 0x1 +#define GROUP_MAX 0x3 +#define GROUP_SEARCH 0x1 +#define GROUP_STATUSBAR 0x2 +#define GROUP_MAIN 0x3 + + +static void LayoutWindows(HWND hwnd, BOOL fRedraw, BOOL fUpdateAll = FALSE) +{ + static INT controls[] = + { + GROUP_SEARCH, IDC_SEARCHCAPTION, IDC_CLEAR, IDC_QUICKSEARCH, + GROUP_STATUSBAR, IDC_BUTTON_PLAY, IDC_BUTTON_ENQUEUE, IDC_BUTTON_CUSTOM, IDC_REMOVEBOOK, IDC_MEDIASTATUS, + GROUP_MAIN, IDC_LIST2 + }; + + INT index; + RECT rc, rg, ri; + LAYOUT layout[sizeof(controls)/sizeof(controls[0])], *pl; + BOOL skipgroup; + HRGN rgn = NULL; + + GetClientRect(hwnd, &rc); + if (rc.bottom == rc.top || rc.right == rc.left) return; + + SetRect(&rg, rc.left, rc.top, rc.right, rc.bottom); + + pl = layout; + skipgroup = FALSE; + + InvalidateRect(hwnd, NULL, TRUE); + + for (index = 0; index < sizeof(controls) / sizeof(*controls); index++) + { + if (controls[index] >= GROUP_MIN && controls[index] <= GROUP_MAX) // group id + { + skipgroup = FALSE; + switch (controls[index]) + { + case GROUP_SEARCH: + { + wchar_t buffer[128] = {0}; + GetDlgItemTextW(hwnd, IDC_BUTTON_PLAY, buffer, ARRAYSIZE(buffer)); + LRESULT idealSize = MLSkinnedButton_GetIdealSize(GetDlgItem(hwnd, IDC_BUTTON_PLAY), buffer); + + SetRect(&rg, rc.left, rc.top + WASABI_API_APP->getScaleY(2), + rc.right - WASABI_API_APP->getScaleX(2), + rc.top + WASABI_API_APP->getScaleY(HIWORD(idealSize)+1)); + rc.top = rg.bottom + WASABI_API_APP->getScaleY(3); + break; + } + case GROUP_STATUSBAR: + { + wchar_t buffer[128] = {0}; + HWND ctrl = GetDlgItem(hwnd, IDC_BUTTON_PLAY); + GetWindowTextW(ctrl, buffer, ARRAYSIZE(buffer)); + LRESULT idealSize = MLSkinnedButton_GetIdealSize(ctrl, buffer); + + SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1), + rc.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)), + rc.right, rc.bottom); + rc.bottom = rg.top - WASABI_API_APP->getScaleY(3); + break; + } + case GROUP_MAIN: + SetRect(&rg, rc.left + WASABI_API_APP->getScaleX(1), rc.top, rc.right, rc.bottom); + break; + } + continue; + } + if (skipgroup) continue; + + pl->id = controls[index]; + pl->hwnd = GetDlgItem(hwnd, pl->id); + if (!pl->hwnd) continue; + + GetWindowRect(pl->hwnd, &ri); + MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&ri, 2); + pl->flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS; + + switch (pl->id) + { + case IDC_SEARCHCAPTION: + { + wchar_t buffer[128] = {0}; + GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer)); + LRESULT idealSize = MLSkinnedStatic_GetIdealSize(pl->hwnd, buffer); + + SETLAYOUTPOS(pl, rg.left + WASABI_API_APP->getScaleX(2), + rg.top + WASABI_API_APP->getScaleY(1), + WASABI_API_APP->getScaleX(LOWORD(idealSize)), + (rg.bottom - rg.top)); + rg.left += (pl->cx + WASABI_API_APP->getScaleX(4)); + break; + } + case IDC_CLEAR: + { + wchar_t buffer[128] = {0}; + GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer)); + LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer); + LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6); + pl->flags |= (((rg.right - rg.left) - width) > WASABI_API_APP->getScaleX(40)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW ; + SETLAYOUTPOS(pl, rg.right - width, rg.top, width, (rg.bottom - rg.top)); + if (SWP_SHOWWINDOW & pl->flags) rg.right -= (pl->cx + WASABI_API_APP->getScaleX(4)); + break; + } + case IDC_QUICKSEARCH: + pl->flags |= (rg.right > rg.left) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left - WASABI_API_APP->getScaleX(1), + (rg.bottom - rg.top) - WASABI_API_APP->getScaleY(1)); + break; + case IDC_BUTTON_PLAY: + case IDC_BUTTON_ENQUEUE: + case IDC_BUTTON_CUSTOM: + case IDC_REMOVEBOOK: + if (IDC_BUTTON_CUSTOM != pl->id || customAllowed) + { + if (groupBtn && pl->id == IDC_BUTTON_PLAY && enqueuedef == 1) + { + pl->flags |= SWP_HIDEWINDOW; + break; + } + + if (groupBtn && pl->id == IDC_BUTTON_ENQUEUE && enqueuedef != 1) + { + pl->flags |= SWP_HIDEWINDOW; + break; + } + + if (groupBtn && (pl->id == IDC_BUTTON_PLAY || pl->id == IDC_BUTTON_ENQUEUE) && customAllowed) + { + pl->flags |= SWP_HIDEWINDOW; + break; + } + + wchar_t buffer[128] = {0}; + GetWindowTextW(pl->hwnd, buffer, ARRAYSIZE(buffer)); + LRESULT idealSize = MLSkinnedButton_GetIdealSize(pl->hwnd, buffer); + LONG width = LOWORD(idealSize) + WASABI_API_APP->getScaleX(6); + SETLAYOUTPOS(pl, rg.left, rg.bottom - WASABI_API_APP->getScaleY(HIWORD(idealSize)), + width, WASABI_API_APP->getScaleY(HIWORD(idealSize))); + pl->flags |= ((rg.right - rg.left) > width) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + if (SWP_SHOWWINDOW & pl->flags) rg.left += (pl->cx + WASABI_API_APP->getScaleX(4)); + } + else + pl->flags |= SWP_HIDEWINDOW; + break; + case IDC_MEDIASTATUS: + SETLAYOUTPOS(pl, rg.left, rg.top, rg.right - rg.left, (rg.bottom - rg.top)); + pl->flags |= (pl->cx > WASABI_API_APP->getScaleX(16)) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW; + break; + case IDC_LIST2: + SETLAYOUTPOS(pl, rg.left, rg.top + WASABI_API_APP->getScaleY(1), + (rg.right - rg.left) - WASABI_API_APP->getScaleX(3), + (rg.bottom - rg.top) - WASABI_API_APP->getScaleY(2)); + break; + } + + SETLAYOUTFLAGS(pl, ri); + if (LAYOUTNEEEDUPDATE(pl)) + { + if (SWP_NOSIZE == ((SWP_HIDEWINDOW | SWP_SHOWWINDOW | SWP_NOSIZE) & pl->flags) && + ri.left == (pl->x + offsetX) && ri.top == (pl->y + offsetY) && !fUpdateAll && IsWindowVisible(pl->hwnd)) + { + SetRect(&ri, pl->x, pl->y, pl->cx + pl->x, pl->y + pl->cy); + ValidateRect(hwnd, &ri); + } + + pl++; + } + else if (!fUpdateAll && (fRedraw || (!offsetX && !offsetY)) && IsWindowVisible(pl->hwnd)) + { + ValidateRect(hwnd, &ri); + if (GetUpdateRect(pl->hwnd, NULL, FALSE)) + { + if (!rgn) rgn = CreateRectRgn(0,0,0,0); + GetUpdateRgn(pl->hwnd, rgn, FALSE); + OffsetRgn(rgn, pl->x, pl->y); + InvalidateRgn(hwnd, rgn, FALSE); + } + } + } + + if (pl != layout) + { + LAYOUT *pc; + HDWP hdwp = BeginDeferWindowPos((INT)(pl - layout)); + for (pc = layout; pc < pl && hdwp; pc++) + { + hdwp = DeferWindowPos(hdwp, pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags); + } + if (hdwp) EndDeferWindowPos(hdwp); + + if (!rgn) rgn = CreateRectRgn(0, 0, 0, 0); + + if (fRedraw) + { + GetUpdateRgn(hwnd, rgn, FALSE); + for (pc = layout; pc < pl && hdwp; pc++) + { + if (pc->rgn) + { + OffsetRgn(pc->rgn, pc->x, pc->y); + CombineRgn(rgn, rgn, pc->rgn, RGN_OR); + } + } + RedrawWindow(hwnd, NULL, rgn, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN); + } + if (g_rgnUpdate) + { + GetUpdateRgn(hwnd, g_rgnUpdate, FALSE); + for (pc = layout; pc < pl && hdwp; pc++) + { + if (pc->rgn) + { + OffsetRgn(pc->rgn, pc->x, pc->y); + CombineRgn(g_rgnUpdate, g_rgnUpdate, pc->rgn, RGN_OR); + } + } + } + + for (pc = layout; pc < pl && hdwp; pc++) + if (pc->rgn) DeleteObject(pc->rgn); + } + if (rgn) DeleteObject(rgn); + ValidateRgn(hwnd, NULL); +} + +static void LayoutWindows2(HWND hwnd, BOOL fRedraw) +{ + RECT rc, rg; + HRGN rgn = NULL; + + GetClientRect(hwnd, &rc); + SetRect(&rg, 0, 0, 0, 0); + + rc.top += WASABI_API_APP->getScaleY(2); + rc.right -= WASABI_API_APP->getScaleY(2); + + if (rc.bottom <= rc.top || rc.right <= rc.left) return; + + HWND temp = GetDlgItem(hwnd, IDC_DB_ERROR); + GetWindowRect(temp, &rg); + SetWindowPos(temp, NULL, WASABI_API_APP->getScaleX(20), WASABI_API_APP->getScaleY(20), + rc.right - rc.left - WASABI_API_APP->getScaleX(40), + rc.bottom - rc.top - WASABI_API_APP->getScaleY(45), + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); + + temp = GetDlgItem(hwnd, IDC_RESET_DB_ON_ERROR); + GetWindowRect(temp, &rg); + SetWindowPos(temp, NULL, ((rc.right - rc.left) - (rg.right - rg.left)) / WASABI_API_APP->getScaleX(2), + rc.bottom - (rg.bottom - rg.top), + rg.right - rg.left, rg.bottom - rg.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); + + InvalidateRect(hwnd, NULL, TRUE); + + if (fRedraw) + { + UpdateWindow(hwnd); + } + if (g_rgnUpdate) + { + GetUpdateRgn(hwnd, g_rgnUpdate, FALSE); + if (rgn) + { + OffsetRgn(rgn, rc.left, rc.top); + CombineRgn(g_rgnUpdate, g_rgnUpdate, rgn, RGN_OR); + } + } + ValidateRgn(hwnd, NULL); + if (rgn) DeleteObject(rgn); +} + +static void history_ManageButtons(HWND hwndDlg) +{ + int has_selection = resultlist.GetSelectedCount(); + + const int buttonids[] = { IDC_BUTTON_PLAY, IDC_BUTTON_ENQUEUE, IDC_BUTTON_CUSTOM, IDC_REMOVEBOOK}; + for (size_t i = 0; i != sizeof(buttonids)/sizeof(buttonids[0]); i++) + { + HWND controlHWND = GetDlgItem(hwndDlg, buttonids[i]); + EnableWindow(controlHWND, has_selection); + } +} + +void history_UpdateButtonText(HWND hwndDlg, int _enqueuedef) +{ + if (groupBtn) + { + switch(_enqueuedef) + { + case 1: + SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, view.enqueue); + customAllowed = FALSE; + break; + + default: + // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay + // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed + pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK_IN_USE, (INT_PTR)_enqueuedef, 0, 0}; + + wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + if (pszTextW && pszTextW[0] != 0) + { + // set this to be a bit different so we can just use one button and not the + // mixable one as well (leaving that to prevent messing with the resources) + SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, pszTextW); + customAllowed = TRUE; + } + else + { + SetDlgItemTextW(hwndDlg, IDC_BUTTON_PLAY, view.play); + customAllowed = FALSE; + } + break; + } + } +} + +void UpdateMenuItems(HWND hwndDlg, HMENU menu) +{ + bool swapPlayEnqueue=false; + if (g_config->ReadInt(L"enqueuedef", 0) == 1) + { + SwapPlayEnqueueInMenu(menu); + swapPlayEnqueue=true; + } + + SyncMenuWithAccelerators(hwndDlg, menu); + if (swapPlayEnqueue) SwapPlayEnqueueInMenu(menu); +} + +enum +{ + BPM_ECHO_WM_COMMAND=0x1, // send WM_COMMAND and return value + BPM_WM_COMMAND = 0x2, // just send WM_COMMAND +}; + +BOOL history_ButtonPopupMenu(HWND hwndDlg, int buttonId, HMENU menu, int flags=0) +{ + RECT r; + HWND buttonHWND = GetDlgItem(hwndDlg, buttonId); + + GetWindowRect(buttonHWND, &r); + UpdateMenuItems(hwndDlg, menu); + MLSkinnedButton_SetDropDownState(buttonHWND, TRUE); + + UINT tpmFlags = TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN; + if (!(flags & BPM_WM_COMMAND)) + tpmFlags |= TPM_RETURNCMD; + int x = Menu_TrackPopup(plugin.hwndLibraryParent, menu, tpmFlags, r.left, r.top, hwndDlg, NULL); + if ((flags & BPM_ECHO_WM_COMMAND) && x) + SendMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(x, 0), 0); + MLSkinnedButton_SetDropDownState(buttonHWND, FALSE); + return x; +} + +static void history_Play(HWND hwndDlg, HWND from, UINT idFrom) +{ + HMENU listMenu = GetSubMenu(g_context_menus2, 0); + int count = GetMenuItemCount(listMenu); + if (count > 2) + { + for (int i = 2; i < count; i++) + { + DeleteMenu(listMenu, 2, MF_BYPOSITION); + } + } + + history_ButtonPopupMenu(hwndDlg, idFrom, listMenu, BPM_WM_COMMAND); +} + +BOOL CALLBACK view_historyDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + BOOL a = (BOOL)dialogSkinner.Handle(hwndDlg, uMsg, wParam, lParam); if (a) return a; + + static HMENU sendto_hmenu; + static librarySendToMenuStruct s; + + switch(uMsg) + { + case WM_INITMENUPOPUP: + if (wParam && (HMENU)wParam == s.build_hMenu && s.mode==1) + { + if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU)==0xffffffff) + s.mode=2; + } + return 0; + + case WM_DISPLAYCHANGE: + { + ListView_SetTextColor(resultlist.getwnd(), dialogSkinner.Color(WADLG_ITEMFG)); + ListView_SetBkColor(resultlist.getwnd(), dialogSkinner.Color(WADLG_ITEMBG)); + ListView_SetTextBkColor(resultlist.getwnd(), dialogSkinner.Color(WADLG_ITEMBG)); + resultlist.SetFont(dialogSkinner.GetFont()); + UpdateWindow(hwndDlg); + LayoutWindows(hwndDlg, TRUE); + return 0; + } + + case WM_CONTEXTMENU: + { + HWND hwndFrom = (HWND)wParam; + if (hwndFrom != resultlist.getwnd()) + return 0; + + int x = GET_X_LPARAM(lParam), y = GET_Y_LPARAM(lParam); + POINT pt = {x, y}; + + if (x == -1 || y == -1) // x and y are -1 if the user invoked a shift-f10 popup menu + { + RECT itemRect = {0}; + int selected = resultlist.GetNextSelected(); + if (selected != -1) // if something is selected we'll drop the menu from there + { + resultlist.GetItemRect(selected, &itemRect); + ClientToScreen(resultlist.getwnd(), (POINT *)&itemRect); + } + else // otherwise we'll drop it from the top-left corner of the listview, adjusting for the header location + { + GetWindowRect(resultlist.getwnd(), &itemRect); + + HWND hHeader = (HWND)SNDMSG(hwndFrom, LVM_GETHEADER, 0, 0L); + RECT headerRect; + if ((WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) && GetWindowRect(hHeader, &headerRect)) + { + itemRect.top += (headerRect.bottom - headerRect.top); + } + } + x = itemRect.left; + y = itemRect.top; + } + + HWND hHeader = (HWND)SNDMSG(hwndFrom, LVM_GETHEADER, 0, 0L); + RECT headerRect; + if (0 == (WS_VISIBLE & GetWindowLongPtr(hHeader, GWL_STYLE)) || FALSE == GetWindowRect(hHeader, &headerRect)) + { + SetRectEmpty(&headerRect); + } + + if (FALSE != PtInRect(&headerRect, pt)) + { + return 0; + } + + HMENU g_context_menus=WASABI_API_LOADMENU(IDR_CONTEXTMENUS); + HMENU menu=GetSubMenu(g_context_menus,0); + sendto_hmenu=GetSubMenu(menu,2); + + UpdateMenuItems(hwndDlg, menu); + + s.mode = 0; + s.hwnd = 0; + s.build_hMenu = 0; + + IPC_LIBRARY_SENDTOMENU = (INT_PTR)SendMessage(plugin.hwndWinampParent, WM_WA_IPC,(WPARAM)&"LibrarySendToMenu",IPC_REGISTER_WINAMP_IPCMESSAGE); + if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage(plugin.hwndWinampParent, WM_WA_IPC,(WPARAM)0,IPC_LIBRARY_SENDTOMENU)==0xffffffff) + { + s.mode = 1; + s.hwnd = hwndDlg; + s.data_type = ML_TYPE_FILENAMESW; + s.ctx[1] = 1; + s.build_hMenu = sendto_hmenu; + } + + UINT menustate = 0; + int n=resultlist.GetSelectedCount(); + if(n == 0) + { + menustate = MF_BYCOMMAND|MF_GRAYED; + } + else + { + menustate = MF_BYCOMMAND|MF_ENABLED; + } + + EnableMenuItem(menu,ID_PE_ID3,menustate); + EnableMenuItem(menu,ID_MEDIAWND_PLAYSELECTEDFILES,menustate); + EnableMenuItem(menu,ID_MEDIAWND_ENQUEUESELECTEDFILES,menustate); + EnableMenuItem(menu,ID_MEDIAWND_EXPLOREFOLDER,menustate); + EnableMenuItem(menu,ID_MEDIAWND_REMOVEFROMLIBRARY,menustate); + EnableMenuItem(menu,ID_MEDIAWND_REMOVEOFFSETFROMLIBRARY,menustate); + EnableMenuItem(menu, 2, MF_BYPOSITION|(n==0?MF_GRAYED:MF_ENABLED)); + + int r = Menu_TrackPopup(plugin.hwndLibraryParent, menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, x, y, hwndDlg, NULL); + if(!SendMessage(hwndDlg,WM_COMMAND,r,0)) + { + if (s.mode == 2) + { + s.menu_id = r; + if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&s, IPC_LIBRARY_SENDTOMENU) == 0xffffffff) + { + // build my data. + s.mode=3; + s.data_type=ML_TYPE_FILENAMESW; + + //std::vector<wchar_t> sendStr; + std::wstring sendStr; + + int l=resultlist.GetCount(); + for(int i=0;i<l;i++) + { + if (resultlist.GetSelected(i)) + { + // HAKAN: why (len + 1) ? + //sendStr.append(itemCache.Items[i].filename, wcslen(itemCache.Items[i].filename)+1); + sendStr.append(itemCache.Items[i].filename, wcslen(itemCache.Items[i].filename)); + } + } + // HAKAN: No need to add trailing zero + //sendStr.push_back(0); + + s.data = (void*)sendStr.c_str(); + + if(SendMessage(plugin.hwndWinampParent, WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU)!=1) + { + s.mode=3; + s.data_type=ML_TYPE_FILENAMES; + + //std::vector<char> sendStrA; + std::string sendStrA; + + int l=resultlist.GetCount(); + for(int i=0;i<l;i++) + { + if (resultlist.GetSelected(i)) + { + // HAKAN: why (len + 1) ? + //sendStrA.append(AutoCharFn(itemCache.Items[i].filename), strlen(AutoCharFn(itemCache.Items[i].filename))+1); + sendStrA.append(AutoCharFn(itemCache.Items[i].filename), strlen(AutoCharFn(itemCache.Items[i].filename))); + } + } + // HAKAN: No need to add trailing zero + //sendStrA.push_back(0); + + s.data = (void*)sendStrA.c_str(); + + SendMessage(plugin.hwndWinampParent, WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU); + } + } + } + } + + if (s.mode) + { + s.mode=4; + SendMessage(plugin.hwndWinampParent, WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU); // cleanup + } + + sendto_hmenu=0; + DestroyMenu(g_context_menus); + Sleep(100); + MSG msg = {0}; + while(PeekMessage(&msg,NULL,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE)); //eat return + } + return 0; + + case WM_INITDIALOG: + { + m_hwnd=hwndDlg; + g_q.Set(L""); + + HACCEL accel = WASABI_API_LOADACCELERATORSW(IDR_VIEW_ACCELERATORS); + if (accel) + WASABI_API_APP->app_addAccelerators(hwndDlg, &accel, 1, TRANSLATE_MODE_CHILD); + + if (!view.play) + { + SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_GET_VIEW_BUTTON_TEXT, (WPARAM)&view); + } + + history_cleanupifnecessary(); + + itemCache.Items=0; + itemCache.Alloc=0; + itemCache.Size=0; + + resultlist.setwnd(GetDlgItem(hwndDlg,IDC_LIST2)); + resultlist.ForceUnicode(); + resultSkin = (int)(INT_PTR)resultlist.getwnd(); //Might be unsafe + + groupBtn = g_config->ReadInt(L"groupbtn", 1); + enqueuedef = (g_config->ReadInt(L"enqueuedef", 0) == 1); + + // v5.66+ - re-use the old predixis parts so the button can be used functionally via ml_enqplay + // pass the hwnd, button id and plug-in id so the ml plug-in can check things as needed + pluginMessage p = {ML_MSG_VIEW_BUTTON_HOOK, (INT_PTR)hwndDlg, (INT_PTR)MAKELONG(IDC_BUTTON_CUSTOM, IDC_BUTTON_ENQUEUE), (INT_PTR)L"ml_history"}; + wchar_t *pszTextW = (wchar_t *)SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + if (pszTextW && pszTextW[0] != 0) + { + // set this to be a bit different so we can just use one button and not the + // mixable one as well (leaving that to prevent messing with the resources) + customAllowed = TRUE; + SetDlgItemTextW(hwndDlg, IDC_BUTTON_CUSTOM, pszTextW); + } + else + customAllowed = FALSE; + + MLSKINWINDOW m = {0}; + m.skinType = SKINNEDWND_TYPE_LISTVIEW; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS; + m.hwndToSkin = resultlist.getwnd(); + MLSkinWindow(mediaLibrary.library, &m); + + m.skinType = SKINNEDWND_TYPE_BUTTON; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | (groupBtn ? SWBS_SPLITBUTTON : 0); + + FLICKERFIX ff = {0, FFM_ERASEINPAINT}; + const int buttonids[] = {IDC_BUTTON_PLAY, IDC_BUTTON_ENQUEUE, IDC_BUTTON_CUSTOM}; + for (size_t i=0;i!=sizeof(buttonids)/sizeof(buttonids[0]);i++) + { + m.hwndToSkin = ff.hwnd = GetDlgItem(hwndDlg, buttonids[i]); + if (IsWindow(m.hwndToSkin)) + { + MLSkinWindow(plugin.hwndLibraryParent, &m); + SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_FLICKERFIX, (WPARAM)&ff); + } + } + + INT ffcl[] = {IDC_REMOVEBOOK, IDC_CLEAR, IDC_MEDIASTATUS, IDC_QUICKSEARCH, IDC_SEARCHCAPTION}; + m.skinType = SKINNEDWND_TYPE_AUTO; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS; + for (INT index = 0; index < sizeof(ffcl) / sizeof(INT); index++) + { + ff.hwnd = GetDlgItem(hwndDlg, ffcl[index]); + if (IsWindow(ff.hwnd)) + { + SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_FLICKERFIX, (WPARAM)&ff); + m.hwndToSkin = ff.hwnd; + MLSkinWindow(plugin.hwndLibraryParent, &m); + } + } + + ListView_SetTextColor(resultlist.getwnd(),dialogSkinner.Color(WADLG_ITEMFG)); + ListView_SetBkColor(resultlist.getwnd(), dialogSkinner.Color(WADLG_ITEMBG)); + ListView_SetTextBkColor(resultlist.getwnd(),dialogSkinner.Color(WADLG_ITEMBG)); + + resultlist.SetFont(dialogSkinner.GetFont()); + + resultlist.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_LAST_PLAYED), g_config->ReadInt(L"recent_col_lp", 111)); + resultlist.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_PLAY_COUNT), g_config->ReadInt(L"recent_col_count", 70)); + resultlist.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_TITLE), g_config->ReadInt(L"recent_col_title", 238)); + resultlist.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_LENGTH), g_config->ReadInt(L"recent_col_len", 50)); + resultlist.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_FILENAME), g_config->ReadInt(L"recent_col_filename", 285)); + resultlist.AddCol(WASABI_API_LNGSTRINGW(IDS_COL_OFFSET), g_config->ReadInt(L"recent_col_offset", 80)); + + m_headerhwnd=ListView_GetHeader(resultlist.getwnd()); + + { + char *query=""; + if (g_config->ReadInt(L"remembersearch",0)) query = g_config->ReadString("recent_lastquery", ""); + AutoWide queryUnicode(query, CP_UTF8); + SetDlgItemTextW(hwndDlg,IDC_QUICKSEARCH,queryUnicode); + KillTimer(hwndDlg,UPDATE_QUERY_TIMER_ID); + doQuery(hwndDlg,queryUnicode,0); + } + + { + int l_sc=g_config->ReadInt(L"recent_sort_by", HISTORY_SORT_LASTPLAYED); + int l_sd=g_config->ReadInt(L"recent_sort_dir", 0); + mediaLibrary.ListViewSort(resultSkin, l_sc, l_sd); + mediaLibrary.ListViewShowSort(resultSkin, TRUE); + } + + history_ManageButtons(hwndDlg); + history_UpdateButtonText(hwndDlg, enqueuedef == 1); + + search_oldWndProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(GetDlgItem(hwndDlg,IDC_QUICKSEARCH),GWLP_WNDPROC,(LONG_PTR)search_newWndProc); + break; + } + + case WM_WINDOWPOSCHANGED: + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & ((WINDOWPOS*)lParam)->flags) || + (SWP_FRAMECHANGED & ((WINDOWPOS*)lParam)->flags)) + { + LayoutWindows(hwndDlg, !(SWP_NOREDRAW & ((WINDOWPOS*)lParam)->flags)); + } + return 0; + + case WM_USER + 0x201: + offsetX = (short)LOWORD(wParam); + offsetY = (short)HIWORD(wParam); + g_rgnUpdate = (HRGN)lParam; + return TRUE; + + case WM_MOUSEMOVE: + if (GetCapture()==hwndDlg) + { + POINT p; + p.x=GET_X_LPARAM(lParam); + p.y=GET_Y_LPARAM(lParam); + ClientToScreen(hwndDlg,&p); + mlDropItemStruct m={0}; + m.type=ML_TYPE_FILENAMESW; + m.p=p; + + pluginHandleIpcMessage(ML_IPC_HANDLEDRAG,(WPARAM)&m); + break; + } + break; + + case WM_LBUTTONUP: + if (GetCapture()==hwndDlg) + { + ReleaseCapture(); + POINT p; + p.x=GET_X_LPARAM(lParam); + p.y=GET_Y_LPARAM(lParam); + ClientToScreen(hwndDlg,&p); + mlDropItemStruct m={0}; + m.type=ML_TYPE_FILENAMESW; + m.p=p; + m.flags=ML_HANDLEDRAG_FLAG_NOCURSOR; + + pluginHandleIpcMessage(ML_IPC_HANDLEDRAG,(WPARAM)&m); + + if (m.result>0) + { + size_t buf_size=4096; + wchar_t *buf=(wchar_t*)calloc(buf_size, sizeof(wchar_t)); + int buf_pos=0; + + int l=resultlist.GetCount(); + for(int i=0;i<l;i++) + { + if (resultlist.GetSelected(i)) + { + const wchar_t *tbuf = itemCache.Items[i].filename; + size_t cchFilename = wcslen(tbuf) + 1; + size_t newsize=buf_pos + cchFilename; + if (newsize < buf_size) + { + size_t old_buf_size = buf_size; + buf_size=newsize+4096; + wchar_t *reallocated_buf = (wchar_t*)realloc(buf,buf_size*sizeof(wchar_t)); + if (reallocated_buf) + { + buf = reallocated_buf; + } + else + { + wchar_t *newbuf = (wchar_t*)malloc(buf_size*sizeof(wchar_t)); + if (!newbuf) // out of memory? well we can at least send what we've got + break; + memcpy(newbuf, buf, old_buf_size); + free(buf); + buf = newbuf; + } + } + StringCchCopyNW(buf+buf_pos,buf_size-buf_pos,tbuf,cchFilename); + buf_pos=(int)newsize; + } + } + + if (buf_pos) + { + buf[buf_pos]=0; + m.flags=0; + m.result=0; + m.data=(void*)buf; + pluginHandleIpcMessage(ML_IPC_HANDLEDROP,(WPARAM)&m); + } + + free(buf); + } + } + break; + + case WM_COMMAND: + if (GetFocus() != GetDlgItem(hwndDlg, IDC_QUICKSEARCH)) + { + switch(LOWORD(wParam)) + { + case IDC_REMOVEBOOK: + removeSelectedItems(0); + break; + case IDC_CLEAR: + SetDlgItemText(hwndDlg,IDC_QUICKSEARCH,TEXT("")); + break; + case IDC_QUICKSEARCH: + if (HIWORD(wParam) == EN_CHANGE) + { + KillTimer(hwndDlg,UPDATE_QUERY_TIMER_ID); + SetTimer(hwndDlg,UPDATE_QUERY_TIMER_ID,100,NULL); + } + break; + case IDC_BUTTON_PLAY: + case ID_MEDIAWND_PLAYSELECTEDFILES: + case IDC_BUTTON_ENQUEUE: + case ID_MEDIAWND_ENQUEUESELECTEDFILES: + case IDC_BUTTON_CUSTOM: + { + if (HIWORD(wParam) == MLBN_DROPDOWN) + { + history_Play(hwndDlg, (HWND)lParam, LOWORD(wParam)); + } + else + { + int action; + if (LOWORD(wParam) == IDC_BUTTON_PLAY || LOWORD(wParam) == ID_MEDIAWND_PLAYSELECTEDFILES) + { + action = (HIWORD(wParam) == 1) ? g_config->ReadInt(L"enqueuedef", 0) == 1 : 0; + } + else if (LOWORD(wParam) == IDC_BUTTON_ENQUEUE || LOWORD(wParam) == ID_MEDIAWND_ENQUEUESELECTEDFILES) + { + action = (HIWORD(wParam) == 1) ? g_config->ReadInt(L"enqueuedef", 0) != 1 : 1; + } + else + break; + + playFiles(action, 0); + } + break; + } + case ID_MEDIAWND_SELECTALL: + ListView_SetItemState(resultlist.getwnd(), -1, LVIS_SELECTED, LVIS_SELECTED); + break; + case ID_MEDIAWND_REMOVEFROMLIBRARY: + removeSelectedItems(0); + break; + case ID_MEDIAWND_REMOVEOFFSETFROMLIBRARY: + removeSelectedItemOffsets(0); + break; + case ID_MEDIAWND_EXPLOREFOLDER: + exploreItemFolder(hwndDlg); + break; + case ID_PE_ID3: + fileInfoDialogs(hwndDlg); + break; + } + } + else + { + switch(LOWORD(wParam)) + { + case IDC_QUICKSEARCH: + if (HIWORD(wParam) == EN_CHANGE) + { + KillTimer(hwndDlg,UPDATE_QUERY_TIMER_ID); + SetTimer(hwndDlg,UPDATE_QUERY_TIMER_ID,100,NULL); + } + break; + case ID_MEDIAWND_SELECTALL: + SendDlgItemMessageW(hwndDlg, IDC_QUICKSEARCH, EM_SETSEL, 0, -1); + break; + case ID_MEDIAWND_REMOVEFROMLIBRARY: + { + DWORD start = -1, end = -1; + SendDlgItemMessageW(hwndDlg, IDC_QUICKSEARCH, EM_GETSEL, (WPARAM)&start, (LPARAM)&end); + if (start != -1) + { + if (start == end) + { + SendDlgItemMessageW(hwndDlg, IDC_QUICKSEARCH, EM_SETSEL, start, end + 1); + } + SendDlgItemMessageW(hwndDlg, IDC_QUICKSEARCH, EM_REPLACESEL, TRUE, (LPARAM)L""); + SendDlgItemMessageW(hwndDlg, IDC_QUICKSEARCH, EM_SETSEL, start, start); + } + } + break; + } + } + break; + + case WM_TIMER: + if (wParam == 123) + { + if (history_bgThread_Handle) + { + ListView_SetItemCount(resultlist.getwnd(),1); + ListView_RedrawItems(resultlist.getwnd(),0,0); + } + } + else if (wParam == UPDATE_QUERY_TIMER_ID) + { + KillTimer(hwndDlg,UPDATE_QUERY_TIMER_ID); + wchar_t text[512] = {0}; + GetWindowTextW(GetDlgItem(hwndDlg,IDC_QUICKSEARCH),text,511-1); + text[511]=0; + doQuery(hwndDlg,text); + } + return 0; + + case WM_APP+3: // sent by bgthread + if (wParam == 0x69) + { + history_bgQuery_Stop(); + + ListView_SetItemCount(resultlist.getwnd(),0); + ListView_SetItemCount(resultlist.getwnd(),itemCache.Size); + if (itemCache.Size>0) ListView_RedrawItems(resultlist.getwnd(),0,itemCache.Size-1); + + if (m_lv_last_topidx) + { + ListView_EnsureVisible(resultlist.getwnd(),m_lv_last_topidx,FALSE); + m_lv_last_topidx=0; + } + + unsigned int total_plays=0; + int x; + for (x = 0; x < itemCache.Size; x ++) + { + total_plays += itemCache.Items[x].playcnt; + } + + int total_length_s = (int)lParam & 0x7FFFFFFF; + int uncert=(int)(lParam>>31); + wchar_t buf[1024] = {0}, itemStr[16] = {0}, playStr[16] = {0}; + + StringCchPrintfW(buf, 1024, + L"%d %s, %u %s ", + itemCache.Size, + WASABI_API_LNGSTRINGW_BUF(itemCache.Size==1?IDS_ITEM:IDS_ITEMS,itemStr,16), + total_plays, + WASABI_API_LNGSTRINGW_BUF(total_plays==1?IDS_PLAY:IDS_PLAYS,playStr,16)); + + if (total_length_s < 60*60) + { + StringCchPrintfW(buf+wcslen(buf), 64, L"[%s%u:%02u]", + uncert ? L"~" : L"", total_length_s / 60, + total_length_s % 60); + } + else if (total_length_s < 60*60*24) + { + StringCchPrintfW(buf+wcslen(buf), 64, L"[%s%u:%02u:%02u]", + uncert ? L"~" : L"", total_length_s / 60 / 60, + (total_length_s / 60) % 60, total_length_s % 60); + } + else + { + wchar_t days[16] = {0}; + int total_days = total_length_s / (60 * 60 * 24); + total_length_s -= total_days * 60 * 60 * 24; + StringCchPrintfW(buf+wcslen(buf), 64, + L"[%s%u %s+%u:%02u:%02u]", + uncert ? L"~" : L"", total_days, + WASABI_API_LNGSTRINGW_BUF(total_days == 1 ? IDS_DAY : IDS_DAYS, days, 16), + total_length_s / 60 / 60, (total_length_s / 60) % 60, total_length_s % 60); + } + + SetDlgItemTextW(hwndDlg,IDC_MEDIASTATUS,buf); + } + break; + + case WM_DESTROY: + if (resultlist.getwnd()) + { + g_config->WriteInt(L"recent_col_lp", resultlist.GetColumnWidth(0)); + g_config->WriteInt(L"recent_col_count", resultlist.GetColumnWidth(1)); + g_config->WriteInt(L"recent_col_title", resultlist.GetColumnWidth(2)); + g_config->WriteInt(L"recent_col_len", resultlist.GetColumnWidth(3)); + g_config->WriteInt(L"recent_col_filename", resultlist.GetColumnWidth(4)); + g_config->WriteInt(L"recent_col_offset", resultlist.GetColumnWidth(5)); + } + + History_SaveLastQuery(hwndDlg); + + if (g_table_dirty && g_table) + { + EnterCriticalSection(&g_db_cs); + NDE_Table_Sync(g_table); + g_table_dirty=0; + LeaveCriticalSection(&g_db_cs); + } + + emptyRecentRecordList(&itemCache); + free(itemCache.Items); + itemCache.Items=0; + itemCache.Alloc=0; + itemCache.Size=0; + + m_hwnd=0; + g_q.Set(L""); + WASABI_API_APP->app_removeAccelerators(hwndDlg); + break; + + case WM_PAINT: + { + int tab[] = {IDC_QUICKSEARCH|DCW_SUNKENBORDER, IDC_LIST2|DCW_SUNKENBORDER}; + dialogSkinner.Draw(hwndDlg, tab, 1 + !!IsWindowVisible(GetDlgItem(hwndDlg, IDC_QUICKSEARCH))); + } + return 0; + + case WM_APP+1: + history_bgQuery((int)lParam); + break; + + case WM_ML_CHILDIPC: + if(lParam == ML_CHILDIPC_GO_TO_SEARCHBAR) + { + SendDlgItemMessage(hwndDlg, IDC_QUICKSEARCH, EM_SETSEL, 0, -1); + SetFocus(GetDlgItem(hwndDlg,IDC_QUICKSEARCH)); + } + else if (lParam == ML_CHILDIPC_REFRESH_SEARCH) + { + PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_QUICKSEARCH, EN_CHANGE), (LPARAM)GetDlgItem(hwndDlg, IDC_QUICKSEARCH)); + } + break; + + case WM_ERASEBKGND: + return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT + + case WM_NOTIFY: + { + LPNMHDR l=(LPNMHDR)lParam; + if (l->idFrom==IDC_LIST2) // media view + { + if (l->code == NM_DBLCLK) + { + playFiles((!!g_config->ReadInt(L"enqueuedef", 0)) ^ (!!(GetAsyncKeyState(VK_SHIFT)&0x8000)),0); + } + else if (l->code == LVN_ODFINDITEMW) // yay we find an item (for kb shortcuts) + { + if (history_bgThread_Handle) return 0; + NMLVFINDITEMW *t = (NMLVFINDITEMW *)lParam; + int i=t->iStart; + if (i >= itemCache.Size) i=0; + + int cnt=itemCache.Size-i; + if (t->lvfi.flags & LVFI_WRAP) cnt+=i; + + int by = g_config->ReadInt(L"recent_sort_by", HISTORY_SORT_LASTPLAYED); + + while (cnt-->0) + { + historyRecord *thisitem=itemCache.Items + i; + wchar_t tmp[128] = {0}; + const wchar_t *name=0; + + switch (by) + { + case HISTORYVIEW_COL_FILENAME: + name=thisitem->filename; + if (!wcsstr(name,L"://")) + { + while (name && *name) name++; + while (name && name >= thisitem->filename && *name != '/' && *name != '\\') name--; + if (name) name++; + } + break; + case HISTORYVIEW_COL_TITLE: name=thisitem->title; break; + case HISTORYVIEW_COL_LASTPLAYED: + tmp[0]=0; + if (thisitem->lastplayed > 0) + { + __time64_t timev = thisitem->lastplayed; + MakeDateStringW(timev, tmp, ARRAYSIZE(tmp)); + } + name=tmp; + break; + case HISTORYVIEW_COL_PLAYCOUNT: + StringCchPrintfW(tmp,128,L"%u",thisitem->playcnt); + name=tmp; + break; + case HISTORYVIEW_COL_LENGTH: + tmp[0]=0; + if (thisitem->length >= 0) StringCchPrintfW(tmp,128,L"%d:%02d",thisitem->length/60,thisitem->length%60); + name=tmp; + break; + case HISTORYVIEW_COL_OFFSET: + tmp[0]=0; + if (thisitem->offset > 0) StringCchPrintfW(tmp,128,L"%d:%02d",(thisitem->offset/1000)/60,(thisitem->offset/1000)%60); + name=tmp; + break; + } + + if (!name) name=L""; + else SKIP_THE_AND_WHITESPACE(name) + + if (t->lvfi.flags & (4|LVFI_PARTIAL)) + { + if (!_wcsnicmp(name,t->lvfi.psz,lstrlenW(t->lvfi.psz))) + { + SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,i); + return 1; + } + } + else if (t->lvfi.flags & LVFI_STRING) + { + if (!_wcsicmp(name,t->lvfi.psz)) + { + SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,i); + return 1; + } + } + else + { + SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,-1); + return 1; + } + if (++i == itemCache.Size) i=0; + } + SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,-1); + return 1; + } + else if (l->code == LVN_GETDISPINFOW) + { + NMLVDISPINFOW *lpdi = (NMLVDISPINFOW*) lParam; + int item=lpdi->item.iItem; + + if (history_bgThread_Handle) + { + if (!item && lpdi->item.iSubItem == 0 && lpdi->item.mask & LVIF_TEXT) + { + static char bufpos; + static char chars[4]={'/','-','\\','|'}; + StringCchPrintfW(lpdi->item.pszText,lpdi->item.cchTextMax, + L"%s %c",WASABI_API_LNGSTRINGW(IDS_SCANNING), + chars[bufpos++&3]); + return 0; + } + } + + if (item < 0 || item >= itemCache.Size) return 0; + + historyRecord *thisitem = itemCache.Items + item; + + if (lpdi->item.mask & (LVIF_TEXT|/*LVIF_IMAGE*/0)) // we can always do images too :) + { + if (lpdi->item.mask & LVIF_TEXT) + { + wchar_t tmpbuf[128] = {0}; + const wchar_t *nameptr=0; + switch (lpdi->item.iSubItem) + { + case HISTORYVIEW_COL_FILENAME: + nameptr=thisitem->filename; + if (!wcsstr(nameptr,L"://")) + { + while (nameptr && *nameptr) nameptr++; + while (nameptr && nameptr >= thisitem->filename && *nameptr != L'/' && *nameptr != L'\\') nameptr--; + if (nameptr) nameptr++; + } + break; + case HISTORYVIEW_COL_TITLE: nameptr=thisitem->title; break; + case HISTORYVIEW_COL_LASTPLAYED: + if (thisitem->lastplayed > 0) + { + __time64_t timev = thisitem->lastplayed; + MakeDateStringW(timev, tmpbuf, ARRAYSIZE(tmpbuf)); + } + nameptr=tmpbuf; + break; + case HISTORYVIEW_COL_PLAYCOUNT: + StringCchPrintfW(tmpbuf,128,L"%u",thisitem->playcnt); + nameptr=tmpbuf; + break; + case HISTORYVIEW_COL_LENGTH: + if (thisitem->length >= 0) + StringCchPrintfW(tmpbuf,128,L"%d:%02d",thisitem->length/60,thisitem->length%60); + nameptr=tmpbuf; + break; + case HISTORYVIEW_COL_OFFSET: + if (thisitem->offset > 0) + StringCchPrintfW(tmpbuf,128,L"%d:%02d",(thisitem->offset/1000)/60,(thisitem->offset/1000)%60); + nameptr=tmpbuf; + break; + } + if (nameptr) lstrcpynW(lpdi->item.pszText,nameptr,lpdi->item.cchTextMax); + else lpdi->item.pszText[0]=0; + } + // if(lpdi->item.mask & LVIF_IMAGE) + } // bother + return 0; + } // LVN_GETDISPINFO + else if (l->code == LVN_COLUMNCLICK) + { + NMLISTVIEW *p=(NMLISTVIEW*)lParam; + int l_sc=g_config->ReadInt(L"recent_sort_by",HISTORY_SORT_LASTPLAYED); + int l_sd=g_config->ReadInt(L"recent_sort_dir",0); + if (p->iSubItem == l_sc) l_sd=!l_sd; + else { l_sd=0; l_sc=p->iSubItem; } + + g_config->WriteInt(L"recent_sort_by",l_sc); + g_config->WriteInt(L"recent_sort_dir",l_sd); + + mediaLibrary.ListViewSort(resultSkin, l_sc, l_sd); + sortResults(&itemCache, + g_config->ReadInt(L"recent_sort_by",HISTORY_SORT_LASTPLAYED), + g_config->ReadInt(L"recent_sort_dir",0)); + ListView_SetItemCount(resultlist.getwnd(),0); + ListView_SetItemCount(resultlist.getwnd(),itemCache.Size); + ListView_RedrawItems(resultlist.getwnd(),0,itemCache.Size-1); + } + else if (l->code == LVN_BEGINDRAG) + { + SetCapture(hwndDlg); + } + else if (l->code == LVN_ITEMCHANGED) + { + history_ManageButtons(hwndDlg); + } + } + } + break; + + case WM_APP + 104: + { + history_UpdateButtonText(hwndDlg, (int)wParam); + LayoutWindows(hwndDlg, TRUE); + return 0; + } + } + return FALSE; +} + +void nukeHistory(HWND hwndDlg) +{ + wchar_t titleStr[32] = {0}; + if (MessageBoxW(hwndDlg, WASABI_API_LNGSTRINGW(IDS_REMOVE_ALL_HISTORY), + WASABI_API_LNGSTRINGW_BUF(IDS_CONFIRMATION,titleStr,32), + MB_YESNO | MB_ICONQUESTION) == IDYES) + { + closeDb(); + + wchar_t tmp[MAX_PATH] = {0}; + StringCchPrintfW(tmp, MAX_PATH, L"%s\\recent.dat", g_tableDir); + DeleteFileW(tmp); + StringCchPrintfW(tmp, MAX_PATH, L"%s\\recent.idx", g_tableDir); + DeleteFileW(tmp); + + openDb(); + + // trigger a refresh of the current view + PostMessage(plugin.hwndLibraryParent, WM_USER + 30, 0, 0); + } +} + +BOOL CALLBACK view_errorinfoDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + BOOL a= (BOOL)dialogSkinner.Handle(hwndDlg,uMsg,wParam,lParam); if (a) return a; + switch(uMsg) + { + case WM_INITDIALOG: + { + SetWindowText(GetDlgItem(hwndDlg, IDC_DB_ERROR), + (wchar_t*)WASABI_API_LOADRESFROMFILEW(TEXT("TEXT"), MAKEINTRESOURCE((nde_error ? IDR_NDE_ERROR : IDR_DB_ERROR)), 0)); + + if (nde_error) + DestroyWindow(GetDlgItem(hwndDlg, IDC_RESET_DB_ON_ERROR)); + + FLICKERFIX ff; + INT index; + INT ffcl[] = { IDC_DB_ERROR, + IDC_RESET_DB_ON_ERROR, + }; + + ff.mode = FFM_ERASEINPAINT; + for (index = 0; index < (sizeof(ffcl) / sizeof(INT)); index++) + { + ff.hwnd = GetDlgItem(hwndDlg, ffcl[index]); + if (IsWindow(ff.hwnd)) + { + SENDMLIPC(plugin.hwndLibraryParent, ML_IPC_FLICKERFIX, (WPARAM)&ff); + } + } + + MLSKINWINDOW m = {0}; + m.skinType = SKINNEDWND_TYPE_DIALOG; + m.hwndToSkin = hwndDlg; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS; + MLSkinWindow(plugin.hwndLibraryParent, &m); + } + return TRUE; + + case WM_COMMAND: + if(LOWORD(wParam) == IDC_RESET_DB_ON_ERROR) + { + nukeHistory(hwndDlg); + } + break; + + case WM_WINDOWPOSCHANGED: + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & ((WINDOWPOS*)lParam)->flags) || + (SWP_FRAMECHANGED & ((WINDOWPOS*)lParam)->flags)) + { + LayoutWindows2(hwndDlg, !(SWP_NOREDRAW & ((WINDOWPOS*)lParam)->flags)); + } + return 0; + + case WM_USER+66: + if (wParam == -1) + { + LayoutWindows2(hwndDlg, TRUE); + } + return TRUE; + + case WM_USER + 0x201: + offsetX = (short)LOWORD(wParam); + offsetY = (short)HIWORD(wParam); + g_rgnUpdate = (HRGN)lParam; + return TRUE; + + case WM_PAINT: + { + dialogSkinner.Draw(hwndDlg, 0, 0); + } + return 0; + + case WM_ERASEBKGND: + return 1; //handled by WADlg_DrawChildWindowBorders in WM_PAINT + } + return FALSE; +} + +void history_cleanupifnecessary() +{ + if (!g_table) return; + + time_t now=time(NULL); + + // if we've done it in the last 8 hours, don't do it again! + if (now < g_config->ReadInt(L"recent_limitlt",0) + 8*60*60) return; + + // time to cleanup + int limit_d=g_config->ReadInt(L"recent_limitd",1); + int limit_dn=g_config->ReadInt(L"recent_limitnd",30); + + if (!limit_d || limit_dn < 1) return; + + g_config->WriteInt(L"recent_limitlt",(int)now); + + EnterCriticalSection(&g_db_cs); + nde_scanner_t s = NDE_Table_CreateScanner(g_table); + wchar_t str[512] = {0}; + StringCchPrintfW(str,512,L"lastplay < [%d days ago]",limit_dn); + NDE_Scanner_Query(s, str); + NDE_Scanner_First(s); + for (;;) + { + if (!NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_LASTPLAYED)) break; + + NDE_Scanner_Edit(s); + NDE_Scanner_Delete(s); + NDE_Scanner_Post(s); + g_table_dirty++; + } + + NDE_Table_DestroyScanner(g_table, s); + if (g_table_dirty) + { + NDE_Table_Sync(g_table); + g_table_dirty=0; + NDE_Table_Compact(g_table); + } + LeaveCriticalSection(&g_db_cs); +} + +void history_onFile(const wchar_t *fn, int offset) +{ + if (!fn || fn && !*fn) return; + + int isstream=!!wcsstr(fn,L"://"); + if (isstream) + { + if (g_config->ReadInt(L"recent_track",1)&2) return; + } + else + { + if (!(g_config->ReadInt(L"recent_track",1)&1)) return; + } + + if (!g_table && !openDb()) return; + + const wchar_t *filename = fn; + + int was_querying=0; + if (history_bgThread_Handle) + { + history_bgQuery_Stop(); + was_querying=1; + } + KillTimer(m_hwnd,123); + + EnterCriticalSection(&g_db_cs); + nde_scanner_t s = NDE_Table_CreateScanner(g_table); + + wchar_t filename2[2048] = {0}; // full lfn path if set + makeFilename2(filename,filename2,ARRAYSIZE(filename2)); + int found=0; + + if (filename2[0]) + { + if (NDE_Scanner_LocateFilename(s, HISTORYVIEW_COL_FILENAME, FIRST_RECORD, filename2)) found = 2; + } + if (!found) + { + if (NDE_Scanner_LocateFilename(s, HISTORYVIEW_COL_FILENAME, FIRST_RECORD, filename)) found = 1; + } + + int cnt=0; + if (found) + { + NDE_Scanner_Edit(s); + if (found == 1 && filename2[0]) db_setFieldString(s,HISTORYVIEW_COL_FILENAME,filename2); // if we have a better filename, update it + nde_field_t f = NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_PLAYCOUNT); + cnt = f?NDE_IntegerField_GetValue(f):0; + } + else + { + NDE_Scanner_New(s); + db_setFieldString(s, HISTORYVIEW_COL_FILENAME, filename2[0] ? filename2 : filename); + + int plidx= (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GETLISTPOS); + const wchar_t *ft=(const wchar_t*)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, plidx, IPC_GETPLAYLISTTITLEW); + if (!ft || (INT_PTR)ft == 1) ft=fn; + const wchar_t *ftp=ft; + int length= (int)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 1, IPC_GETOUTPUTTIME); + + if (*ftp == '[' && (ftp=wcsstr(ftp,L"]"))) + { + ftp++; + while (ftp && *ftp == ' ') ftp++; + if (ftp && !*ftp) ftp=ft; + } + else ftp=ft; + + db_setFieldInt(s, HISTORYVIEW_COL_LENGTH, length); + db_setFieldString(s, HISTORYVIEW_COL_TITLE, ftp); + } + + if (offset >= 0) + { + db_setFieldInt(s, HISTORYVIEW_COL_OFFSET, (offset > 0 ? offset : -1)); + } + else + { + db_setFieldInt(s, HISTORYVIEW_COL_LASTPLAYED, (int)time(NULL)); + db_setFieldInt(s, HISTORYVIEW_COL_PLAYCOUNT, cnt+1); + } + + NDE_Scanner_Post(s); + + NDE_Table_DestroyScanner(g_table, s); + NDE_Table_Sync(g_table); + g_table_dirty++; + + // changed to save the history when updated to prevent it being + // lost but retains the 8hr cleanup, etc which is otherwise run + if (g_table_dirty > 100) + { + history_cleanupifnecessary(); + } + if (g_table_dirty) + { + // and to keep existing behaviour for the dirty count, we + // ensure that even on saving we maintain the dirty count + closeDb(false); + openDb(); + } + LeaveCriticalSection(&g_db_cs); + + if (was_querying) + history_bgQuery(); + + if (IsWindow(m_hwnd)) + { + m_lv_last_topidx=ListView_GetTopIndex(resultlist.getwnd()); + SendMessage(m_hwnd,WM_APP+1,0,0); + } +} + +int retrieve_offset(const wchar_t *fn) +{ + int offset = -1; + + int isstream=!!wcsstr(fn,L"://"); + if (isstream) + { + if (g_config->ReadInt(L"recent_track",1)&2) return offset; + } + else + { + if (!(g_config->ReadInt(L"recent_track",1)&1)) return offset; + } + + if (!g_table && !openDb()) return offset; + + const wchar_t *filename=fn; + + EnterCriticalSection(&g_db_cs); + nde_scanner_t s = NDE_Table_CreateScanner(g_table); + + wchar_t filename2[2048] = {0}; // full lfn path if set + makeFilename2(filename,filename2,ARRAYSIZE(filename2)); + int found=0; + + if (filename2[0]) + { + if (NDE_Scanner_LocateFilename(s, HISTORYVIEW_COL_FILENAME,FIRST_RECORD,filename2)) found=2; + } + if (!found) + { + if (NDE_Scanner_LocateFilename(s, HISTORYVIEW_COL_FILENAME,FIRST_RECORD,filename)) found=1; + } + + if (found) + { + nde_field_t f = NDE_Scanner_GetFieldByID(s, HISTORYVIEW_COL_OFFSET); + offset = f?NDE_IntegerField_GetValue(f):-1; + } + + NDE_Table_DestroyScanner(g_table, s); + + LeaveCriticalSection(&g_db_cs); + + return offset; +} + +void fileInfoDialogs(HWND hwndParent) +{ + history_bgQuery_Stop(); + int l=resultlist.GetCount(),i; + int needref=0; + for(i=0;i<l;i++) + { + if(!resultlist.GetSelected(i)) continue; + historyRecord *song=(historyRecord *)itemCache.Items + i; + if (!song->filename || !song->filename[0]) continue; + + infoBoxParamW p; + p.filename=song->filename; + p.parent=hwndParent; + if (SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&p,IPC_INFOBOXW)) break; + needref=1; + + EnterCriticalSection(&g_db_cs); + nde_scanner_t s= NDE_Table_CreateScanner(g_table); + if (NDE_Scanner_LocateNDEFilename(s, HISTORYVIEW_COL_FILENAME,FIRST_RECORD,song->filename)) + { + wchar_t ft[1024]={0}; + basicFileInfoStructW bi = {0}; + bi.filename=p.filename; + bi.length=-1; + bi.title=ft; + bi.titlelen=ARRAYSIZE(ft); + SendMessage(plugin.hwndWinampParent,WM_WA_IPC,(WPARAM)&bi,IPC_GET_BASIC_FILE_INFOW); + + db_setFieldInt(s,HISTORYVIEW_COL_LENGTH,bi.length); + db_setFieldString(s,HISTORYVIEW_COL_TITLE,ft); + + NDE_Scanner_Post(s); + } + NDE_Table_DestroyScanner(g_table, s); + g_table_dirty++; + LeaveCriticalSection(&g_db_cs); + } + + if (g_table_dirty && g_table) + { + EnterCriticalSection(&g_db_cs); + NDE_Table_Sync(g_table); + g_table_dirty=0; + LeaveCriticalSection(&g_db_cs); + } + + if (needref) + { + SendMessage(hwndParent,WM_TIMER,UPDATE_QUERY_TIMER_ID,0); + } + + MSG msg; + while(PeekMessage(&msg,NULL,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE)); //eat return +}
\ No newline at end of file |