aboutsummaryrefslogtreecommitdiff
path: root/Src/omBrowser/mlNavigationHelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/omBrowser/mlNavigationHelper.cpp')
-rw-r--r--Src/omBrowser/mlNavigationHelper.cpp599
1 files changed, 599 insertions, 0 deletions
diff --git a/Src/omBrowser/mlNavigationHelper.cpp b/Src/omBrowser/mlNavigationHelper.cpp
new file mode 100644
index 00000000..cfa032b1
--- /dev/null
+++ b/Src/omBrowser/mlNavigationHelper.cpp
@@ -0,0 +1,599 @@
+#include "main.h"
+#include "./mlNavigationHelper.h"
+#include "./ifc_omcachemanager.h"
+#include "./ifc_omcachegroup.h"
+#include "./ifc_omcacherecord.h"
+#include "./ifc_mlnavigationcallback.h"
+#include "./resource.h"
+#include "./ifc_wasabihelper.h"
+
+#include "../Plugins/General/gen_ml/ml.h"
+
+#define _ML_HEADER_IMPMLEMENT
+#include "../Plugins/General/gen_ml/ml_ipc_0313.h"
+#undef _ML_HEADER_IMPMLEMENT
+
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <algorithm>
+
+MlNavigationHelper::MlNavigationHelper(HWND hLibraryWnd, ifc_omcachegroup *cacheGroup)
+ : ref(1), hLibrary(hLibraryWnd), defaultIndex(-1), cacheGroup(cacheGroup), lastCookie(0)
+{
+ InitializeCriticalSection(&lock);
+
+ if (NULL != cacheGroup)
+ cacheGroup->AddRef();
+}
+
+MlNavigationHelper::~MlNavigationHelper()
+{
+ EnterCriticalSection(&lock);
+
+ for(CallbackMap::iterator iter = callbackMap.begin(); iter != callbackMap.end(); iter++)
+ {
+ ifc_mlnavigationcallback *callback = iter->second;
+ if (NULL != callback) callback->Release();
+ }
+
+ size_t index = recordList.size();
+ while(index--)
+ {
+ Plugin_FreeString(recordList[index].name);
+ }
+
+ if (NULL != cacheGroup)
+ cacheGroup->Release();
+
+ LeaveCriticalSection(&lock);
+ DeleteCriticalSection(&lock);
+}
+
+HRESULT MlNavigationHelper::CreateInstance(HWND hLibrary, ifc_omcachemanager *cacheManager, MlNavigationHelper **instance)
+{
+ if (NULL == instance) return E_POINTER;
+
+ if (NULL == hLibrary || FALSE == IsWindow(hLibrary) || NULL == cacheManager)
+ {
+ *instance = NULL;
+ return E_INVALIDARG;
+ }
+
+ ifc_omcachegroup *group = NULL;
+ HRESULT hr = cacheManager->Find(L"icons", TRUE, &group, NULL);
+ if (SUCCEEDED(hr) && group != NULL)
+ {
+ *instance = new MlNavigationHelper(hLibrary, group);
+ if (NULL == *instance)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ group->Release();
+ }
+
+ return hr;
+}
+
+size_t MlNavigationHelper::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t MlNavigationHelper::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int MlNavigationHelper::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_MlNavigationHelper))
+ *object = static_cast<ifc_mlnavigationhelper*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+HRESULT MlNavigationHelper::GetDefaultIndex(int *index)
+{
+ if (NULL == index)
+ return E_POINTER;
+
+ EnterCriticalSection(&lock);
+
+ if (-1 == defaultIndex)
+ {
+ WCHAR szPath[MAX_PATH] = {0};
+ HRESULT hr = Plugin_MakeResourcePath(szPath, ARRAYSIZE(szPath), Plugin_GetInstance(),
+ RT_RCDATA, MAKEINTRESOURCE(IDR_SERVICEICON_IMAGE), RESPATH_COMPACT);
+
+ if (SUCCEEDED(hr))
+ {
+ UpdateImageList(szPath, &defaultIndex);
+ }
+ }
+ *index = defaultIndex;
+
+ LeaveCriticalSection(&lock);
+
+ return S_OK;
+}
+
+
+HRESULT MlNavigationHelper::UpdateImageList(LPCWSTR address, INT *index)
+{
+ if (NULL == address || NULL == index)
+ {
+ if (NULL != index) *index = -1;
+ return E_INVALIDARG;
+ }
+
+ HMLIMGLST hmlil = MLNavCtrl_GetImageList(hLibrary);
+ if (NULL == hmlil) return E_UNEXPECTED;
+
+ MLIMAGESOURCE source = {0};
+ source.cbSize = sizeof(source);
+ source.lpszName = address;
+ source.type = SRC_TYPE_PNG;
+ source.bpp = 24;
+ source.flags = ISF_LOADFROMFILE | ISF_FORCE_BPP | ISF_PREMULTIPLY;
+
+ MLIMAGELISTIMAGESIZE imageSize = {0};
+ imageSize.hmlil = hmlil;
+ if (FALSE != MLImageList_GetImageSize(hLibrary, &imageSize))
+ {
+ source.cxDst = imageSize.cx;
+ source.cyDst = imageSize.cy;
+ source.flags |= ISF_FORCE_SIZE | ISF_SCALE;
+ }
+
+ if (FALSE == MLImageLoader_CheckExist(hLibrary, &source))
+ return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+
+ MLIMAGELISTITEM item = {0};
+ item.cbSize = sizeof(item);
+ item.hmlil = hmlil;
+ item.filterUID = MLIF_FILTER3_UID;
+ item.pmlImgSource = &source;
+
+ HRESULT hr = S_OK;
+ INT iImage = *index;
+ if (iImage >= 0)
+ {
+ INT imageCount = MLImageList_GetImageCount(hLibrary, item.hmlil);
+ if (iImage >= imageCount) iImage = -1;
+ }
+
+ if (iImage >=0)
+ {
+ item.mlilIndex = iImage;
+ if (FALSE == MLImageList_Replace(hLibrary, &item))
+ hr = E_FAIL;
+ }
+ else
+ {
+ iImage = MLImageList_Add(hLibrary, &item);
+ if (-1 == iImage) hr = E_FAIL;
+ }
+
+ *index = iImage;
+ return hr;
+}
+
+HRESULT MlNavigationHelper::ImageLocator(LPCWSTR address, INT *index)
+{
+ if (NULL == address) return E_INVALIDARG;
+
+ HRESULT hr;
+ BOOL runtimeOnly = TRUE;
+
+ if (PathIsURL(address) &&
+ CSTR_EQUAL != CompareString(CSTR_INVARIANT, NORM_IGNORECASE, address, 6, L"res://", -1))
+ {
+ runtimeOnly = FALSE;
+ }
+ else
+ {
+ hr = UpdateImageList(address, index);
+ if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) != hr)
+ return hr;
+ }
+
+ BOOL fCreated = FALSE;
+ ifc_omcacherecord *cacheRecord = NULL;
+ hr = cacheGroup->Find(address, TRUE, &cacheRecord, &fCreated);
+ if (SUCCEEDED(hr) && cacheRecord != NULL)
+ {
+ cacheRecord->RegisterCallback(this);
+
+ if (FALSE != fCreated && FALSE != runtimeOnly)
+ {
+ cacheRecord->SetFlags(ifc_omcacherecord::flagNoStore, ifc_omcacherecord::flagNoStore);
+ }
+
+ WCHAR szBuffer[MAX_PATH * 2] = {0};
+ if (SUCCEEDED(cacheRecord->GetPath(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ hr = UpdateImageList(szBuffer, index);
+ }
+
+ cacheRecord->Release();
+ }
+ return hr;
+}
+
+HRESULT MlNavigationHelper::QueryIndex(LPCWSTR pszName, INT *index, BOOL *defaultUsed)
+{
+ if (NULL == cacheGroup)
+ return E_UNEXPECTED;
+
+ if (NULL == index)
+ {
+ if (NULL != defaultUsed) *defaultUsed = FALSE;
+ return E_POINTER;
+ }
+
+ if (NULL == pszName || L'\0' == *pszName)
+ {
+ if (SUCCEEDED(GetDefaultIndex(index)))
+ {
+ if (NULL != defaultUsed) *defaultUsed = TRUE;
+ }
+ else
+ {
+ *index = 0;
+ if (NULL != defaultUsed) *defaultUsed = FALSE;
+ }
+ return S_OK;
+ }
+
+ EnterCriticalSection(&lock);
+
+ RECORD *record = Find(pszName);
+ if (NULL == record)
+ { // create new
+ RECORD rec = {0};
+
+ size_t index = recycledList.size();
+ if (0 != index)
+ {
+ rec.index = recycledList[index - 1];
+ recycledList.pop_back();
+ }
+ else
+ {
+ rec.index = -1;
+ }
+
+ rec.ref = 1;
+ rec.name = Plugin_CopyString(pszName);
+ recordList.push_back(rec);
+ Sort();
+ // locate record again;
+ record = Find(pszName);
+ ImageLocator(record->name, &record->index);
+ }
+ else
+ {
+ record->ref++;
+ if (-1 == record->index)
+ ImageLocator(record->name, &record->index);
+ }
+
+ if(-1 == record->index)
+ {
+ if (SUCCEEDED(GetDefaultIndex(index)) && NULL != defaultUsed)
+ *defaultUsed = TRUE;
+ }
+ else
+ {
+ *index = record->index;
+ }
+ LeaveCriticalSection(&lock);
+
+ return S_OK;
+}
+
+HRESULT MlNavigationHelper::ReleaseIndex(LPCWSTR pszName)
+{
+ if(NULL == pszName || L'\0' == *pszName)
+ return E_INVALIDARG;
+
+ EnterCriticalSection(&lock);
+
+ RECORD *record = Find(pszName);
+ if (NULL != record)
+ {
+ record->ref--;
+ if (0 == record->ref)
+ {
+ if (-1 != record->index)
+ recycledList.push_back(record->index);
+
+ Plugin_FreeString(record->name);
+ record->name = NULL;
+
+ size_t index = recordList.size();
+ while(index--)
+ {
+ if (&recordList[index] == record)
+ {
+ recordList.erase(recordList.begin() + index);
+ break;
+ }
+ }
+ }
+ }
+
+ LeaveCriticalSection(&lock);
+ return (NULL != record) ? S_OK : S_FALSE;
+}
+
+HRESULT MlNavigationHelper::RegisterAlias(LPCWSTR pszName, LPCWSTR pszAddress)
+{
+ if (NULL == cacheGroup)
+ return E_UNEXPECTED;
+
+ BOOL fCreated = FALSE;
+ ifc_omcacherecord *record = NULL;
+ HRESULT hr = cacheGroup->Find(pszName, TRUE, &record, &fCreated);
+ if (SUCCEEDED(hr) && record != NULL)
+ {
+ if (FALSE != fCreated)
+ {
+ record->SetFlags(ifc_omcacherecord::flagNoStore, ifc_omcacherecord::flagNoStore);
+ }
+
+ hr = record->SetPath(pszAddress);
+ record->RegisterCallback(this);
+ record->Release();
+ }
+ return hr;
+}
+
+HWND MlNavigationHelper::GetLibrary()
+{
+ return hLibrary;
+}
+
+typedef struct __CACHECHANGEAPCPARAM
+{
+ MlNavigationHelper *instance;
+ ifc_omcacherecord *record;
+} CACHECHANGEAPCPARAM;
+
+static void CALLBACK MlNavigationHelper_CacheRecordPathChangedApc(ULONG_PTR data)
+{
+ CACHECHANGEAPCPARAM *param = (CACHECHANGEAPCPARAM*)data;
+ if (NULL != param)
+ {
+ if (NULL != param->instance)
+ {
+ param->instance->CacheRecordPathChangedApc(param->record);
+ param->instance->Release();
+ }
+ if (NULL != param->record)
+ param->record->Release();
+
+ free(param);
+ }
+}
+
+void MlNavigationHelper::CacheRecordPathChanged(ifc_omcacherecord *cacheRecord)
+{
+ if (NULL == cacheRecord)
+ return;
+
+ HRESULT hr = E_FAIL;
+ CACHECHANGEAPCPARAM *param = (CACHECHANGEAPCPARAM*)calloc(1, sizeof(CACHECHANGEAPCPARAM));
+ if (NULL != param)
+ {
+ param->instance = this;
+ param->instance->AddRef();
+ param->record = cacheRecord;
+ param->record->AddRef();
+
+ ifc_wasabihelper *wasabi = NULL;
+ if (SUCCEEDED(Plugin_GetWasabiHelper(&wasabi)) && wasabi != NULL)
+ {
+ api_application *application = NULL;
+ if (SUCCEEDED(wasabi->GetApplicationApi(&application)) && application != NULL)
+ {
+ HANDLE hThread = application->main_getMainThreadHandle();
+ if (NULL != hThread &&
+ 0 != QueueUserAPC(MlNavigationHelper_CacheRecordPathChangedApc, hThread, (ULONG_PTR)param))
+ {
+ hr = S_OK;
+ }
+ application->Release();
+ }
+ wasabi->Release();
+ }
+
+ if (FAILED(hr))
+ {
+ if (NULL != param->instance) param->instance->Release();
+ if (NULL != param->record) param->record->Release();
+ free(param);
+ param = NULL;
+ }
+ }
+}
+
+void MlNavigationHelper::CacheRecordPathChangedApc(ifc_omcacherecord *cacheRecord)
+{
+ if (NULL == cacheRecord)
+ return;
+
+ WCHAR szBuffer[1024] = {0};
+ if (FAILED(cacheRecord->GetName(szBuffer, ARRAYSIZE(szBuffer))))
+ return;
+
+ EnterCriticalSection(&lock);
+
+ RECORD *record = Find(szBuffer);
+ if (NULL != record &&
+ SUCCEEDED(cacheRecord->GetPath(szBuffer, ARRAYSIZE(szBuffer))) &&
+ SUCCEEDED(UpdateImageList(szBuffer, &record->index)))
+ {
+ for(CallbackMap::iterator iter = callbackMap.begin(); iter != callbackMap.end(); iter++)
+ {
+ ifc_mlnavigationcallback *callback = iter->second;
+ callback->ImageChanged(record->name, record->index);
+ }
+ }
+
+ LeaveCriticalSection(&lock);
+}
+
+HRESULT MlNavigationHelper::RegisterCallback(ifc_mlnavigationcallback *callback, UINT *cookie)
+{
+ if (NULL == cookie) return E_POINTER;
+ *cookie = 0;
+
+ if (NULL == callback)
+ return E_INVALIDARG;
+
+ EnterCriticalSection(&lock);
+
+ *cookie = ++lastCookie;
+
+ callbackMap.insert({ *cookie, callback });
+ callback->AddRef();
+
+ LeaveCriticalSection(&lock);
+
+ return S_OK;
+}
+
+HRESULT MlNavigationHelper::UnregisterCallback(UINT cookie)
+{
+ if (0 == cookie) return E_INVALIDARG;
+
+ ifc_mlnavigationcallback *callback = NULL;
+ EnterCriticalSection(&lock);
+
+ for(CallbackMap::iterator iter = callbackMap.begin(); iter != callbackMap.end(); iter++)
+ {
+ if (cookie == iter->first)
+ {
+ callback = iter->second;
+ callbackMap.erase(iter);
+ break;
+ }
+ }
+
+ LeaveCriticalSection(&lock);
+
+ if (NULL != callback)
+ {
+ callback->Release();
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+__inline static int __cdecl MlNavigationHelper_RecordComparer(const void *elem1, const void *elem2)
+{
+ LPCWSTR s1 = ((MlNavigationHelper::RECORD*)elem1)->name;
+ LPCWSTR s2 = ((MlNavigationHelper::RECORD*)elem2)->name;
+
+ if (NULL == s1 || NULL == s2) return ((INT)(ULONG_PTR)(s1 - s2));
+ return CompareString(CSTR_INVARIANT, NORM_IGNORECASE, s1, -1, s2, -1) - 2;
+}
+__inline static int __cdecl MlNavigationHelper_RecordComparer_V2(const void* elem1, const void* elem2)
+{
+ return MlNavigationHelper_RecordComparer(elem1, elem2) < 0;
+}
+
+__inline static int __cdecl MlNavigationHelper_RecordSearch(const void *elem1, const void *elem2)
+{
+ LPCWSTR s1 = (LPCWSTR)elem1;
+ LPCWSTR s2 = ((MlNavigationHelper::RECORD*)elem2)->name;
+
+ if (NULL == s1 || NULL == s2) return ((INT)(ULONG_PTR)(s1 - s2));
+ INT r = CompareString(CSTR_INVARIANT, NORM_IGNORECASE, s1, -1, s2, -1) - 2;
+ return r;
+}
+
+HRESULT MlNavigationHelper::Sort()
+{
+ HRESULT hr;
+ if (recordList.size() < 2)
+ {
+ hr = S_FALSE;
+ }
+ else
+ {
+ //qsort(recordList.begin(), recordList.size(), sizeof(RECORD), MlNavigationHelper_RecordComparer);
+ std::sort(recordList.begin(), recordList.end(),
+ [&](RECORD lhs, RECORD rhs) -> bool
+ {
+ return MlNavigationHelper_RecordComparer_V2(&lhs, &rhs);
+ }
+ );
+
+ hr = S_OK;
+ }
+ return hr;
+}
+
+MlNavigationHelper::RECORD *MlNavigationHelper::Find(LPCWSTR pszName)
+{
+ if (0 == recordList.size())
+ return NULL;
+
+ //return (RECORD*)bsearch(pszName, recordList.begin(), recordList.size(),
+ // sizeof(RECORD), MlNavigationHelper_RecordSearch);
+
+ auto it = std::find_if(recordList.begin(), recordList.end(),
+ [&](RECORD upT) -> bool
+ {
+ return MlNavigationHelper_RecordSearch(pszName, &upT) == 0;
+ }
+ );
+ if (it == recordList.end())
+ {
+ return NULL;
+ }
+
+ return &(* it);
+}
+
+#define CBCLASS MlNavigationHelper
+START_MULTIPATCH;
+ START_PATCH(MPIID_MLNAVIGATIONHELPER)
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, ADDREF, AddRef);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, RELEASE, Release);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, QUERYINTERFACE, QueryInterface);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_GETDEFAULTINDEX, GetDefaultIndex);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_QUERYINDEX, QueryIndex);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_RELEASEINDEX, ReleaseIndex);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_REGISTERALIAS, RegisterAlias);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_REGISTERCALLBACK, RegisterCallback);
+ M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_UNREGISTERCALLBACK, UnregisterCallback);
+ NEXT_PATCH(MPIID_OMCACHECALLBACK)
+ M_CB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, ADDREF, AddRef);
+ M_CB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, RELEASE, Release);
+ M_CB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, QUERYINTERFACE, QueryInterface);
+ M_VCB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, API_PATHCHANGED, CacheRecordPathChanged);
+ END_PATCH
+END_MULTIPATCH;
+#undef CBCLASS \ No newline at end of file