diff options
Diffstat (limited to 'Src/omBrowser/mlNavigationHelper.cpp')
-rw-r--r-- | Src/omBrowser/mlNavigationHelper.cpp | 599 |
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 |