aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_online/Setup
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Library/ml_online/Setup
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Library/ml_online/Setup')
-rw-r--r--Src/Plugins/Library/ml_online/Setup/SetupGroupFilter.h91
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setup.cpp71
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupDetails.cpp80
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupDetails.h30
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupDetailsGroup.cpp284
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupDetailsService.cpp596
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupGroup.cpp929
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupGroup.h131
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupGroupFilter.cpp226
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupGroupList.cpp190
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupGroupList.h52
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupImage.cpp282
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupImage.h50
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupListbox.cpp878
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupListbox.h84
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupListboxLabel.cpp228
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupListboxLabel.h57
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupLog.cpp395
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupLog.h54
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupPage.cpp648
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupPage.h85
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupPageWnd.cpp145
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupRecord.cpp567
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupRecord.h84
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupServicePanel.cpp797
-rw-r--r--Src/Plugins/Library/ml_online/Setup/setupServicePanel.h79
26 files changed, 7113 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_online/Setup/SetupGroupFilter.h b/Src/Plugins/Library/ml_online/Setup/SetupGroupFilter.h
new file mode 100644
index 00000000..feb4be24
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/SetupGroupFilter.h
@@ -0,0 +1,91 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUPFILTER_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUPFILTER_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include <vector>
+
+class ifc_omservice;
+
+class __declspec(novtable) SetupGroupFilter
+{
+public:
+ typedef enum
+ {
+ serviceIgnore = 0x00000000,
+ serviceInclude = 0x00000001,
+ serviceForceSubscribe = 0x00000002,
+ serviceForceUnsubscribe = 0x00000004,
+ } FilterResult;
+protected:
+ SetupGroupFilter(const GUID *filterId);
+ virtual ~SetupGroupFilter();
+
+public:
+ static HRESULT CreateInstance(const GUID *filterId, SetupGroupFilter **instance);
+
+public:
+ virtual ULONG AddRef();
+ virtual ULONG Release();
+
+ virtual HRESULT GetId(GUID *filterId);
+
+ virtual HRESULT Initialize() = 0;
+ virtual HRESULT ProcessService(ifc_omservice *service, UINT *filterResult) = 0;
+
+protected:
+ typedef std::vector<UINT> ServiceIdList;
+ static BOOL CALLBACK AppendServiceIdCallback(UINT serviceId, void *data);
+
+protected:
+ ULONG ref;
+ GUID id;
+};
+
+
+// {2F45FBDF-4372-4def-B20C-C6F1BAE5AE85}
+static const GUID FUID_SetupFeaturedGroupFilter =
+{ 0x2f45fbdf, 0x4372, 0x4def, { 0xb2, 0xc, 0xc6, 0xf1, 0xba, 0xe5, 0xae, 0x85 } };
+
+
+class SetupFeaturedGroupFilter : public SetupGroupFilter
+{
+protected:
+ SetupFeaturedGroupFilter();
+ ~SetupFeaturedGroupFilter();
+public:
+ static HRESULT CreateInstance(SetupFeaturedGroupFilter **instance);
+
+public:
+ HRESULT Initialize();
+ HRESULT ProcessService(ifc_omservice *service, UINT *filterResult);
+
+protected:
+ ServiceIdList filterList;
+};
+
+// {7CA8722D-8B11-43a0-8F55-533C9DE3D73E}
+static const GUID FUID_SetupKnownGroupFilter =
+{ 0x7ca8722d, 0x8b11, 0x43a0, { 0x8f, 0x55, 0x53, 0x3c, 0x9d, 0xe3, 0xd7, 0x3e } };
+
+
+class SetupKnownGroupFilter : public SetupGroupFilter
+{
+protected:
+ SetupKnownGroupFilter();
+ ~SetupKnownGroupFilter();
+public:
+ static HRESULT CreateInstance(SetupKnownGroupFilter **instance);
+
+public:
+ HRESULT Initialize();
+ HRESULT ProcessService(ifc_omservice *service, UINT *filterResult);
+
+protected:
+ ServiceIdList filterList;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUPFILTER_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setup.cpp b/Src/Plugins/Library/ml_online/Setup/setup.cpp
new file mode 100644
index 00000000..e8497306
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setup.cpp
@@ -0,0 +1,71 @@
+#define GUID_DEFINE
+#include "../../winamp/setup/svc_setup.h"
+#undef GUID_DEFINE
+
+#include "./setupPage.h"
+
+#include "../api__ml_online.h"
+
+
+static HRESULT Setup_RegisterPage()
+{
+ HRESULT hr;
+ svc_setup *setupSvc;
+ SetupPage *page;
+
+
+ if (FAILED(WasabiApi_LoadDefaults()) ||
+ NULL == OMBROWSERMNGR ||
+ NULL == OMSERVICEMNGR ||
+ NULL == OMUTILITY)
+ {
+ return E_UNEXPECTED;
+ }
+
+ setupSvc = QueryWasabiInterface(svc_setup, UID_SVC_SETUP);
+ if (NULL == setupSvc)
+ return E_POINTER;
+
+ page = SetupPage::CreateInstance();
+ if (NULL == page)
+ hr = E_OUTOFMEMORY;
+ else
+ {
+ // try to insert before 'feedback' (if present)
+ // otherwise dump at the end of the pages list.
+ int index = 0xFFFFF;
+ if (FAILED(setupSvc->GetPageCount(&index)))
+ index = 0xFFFFF;
+ else if (index > 0 && index == 3)
+ index--;
+
+ hr = setupSvc->InsertPage(page, &index);
+ if (SUCCEEDED(hr))
+ setupSvc->AddJob((ifc_setupjob*)page);
+
+ page->Release();
+ }
+
+ ReleaseWasabiInterface(UID_SVC_SETUP, setupSvc);
+
+ return hr;
+}
+
+EXTERN_C _declspec(dllexport) BOOL RegisterSetup(HINSTANCE hInstance, api_service *waServices)
+{
+ // check the current date and if past November 30th 2013
+ // then we will prevent the online page from being shown
+ time_t now = time(0);
+ struct tm *tn = localtime(&now);
+ tn->tm_sec = tn->tm_min = tn->tm_hour = 0;
+
+ if (mktime(tn) >= 1387497600)
+ return FALSE;
+
+ if (FAILED(WasabiApi_Initialize(hInstance, waServices)))
+ return FALSE;
+
+ BOOL result = SUCCEEDED(Setup_RegisterPage());
+ WasabiApi_Release();
+ return result;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupDetails.cpp b/Src/Plugins/Library/ml_online/Setup/setupDetails.cpp
new file mode 100644
index 00000000..5e1b7be2
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupDetails.cpp
@@ -0,0 +1,80 @@
+#include "./common.h"
+#include "./setupDetails.h"
+#include "./setupServicePanel.h"
+
+EXTERN_C ATOM DETAILS_PROP = 0;
+HMODULE hEditModule = NULL;
+
+
+BOOL SetupDetails_Initialize()
+{
+ if (0 == DETAILS_PROP)
+ {
+ DETAILS_PROP = GlobalAddAtom(L"omSetupDetailsProp");
+ if (0 == DETAILS_PROP) return FALSE;
+ }
+
+ if (NULL == (hEditModule = LoadLibrary(L"riched20.dll")))
+ return FALSE;
+
+ return TRUE;
+}
+
+void SetupDetails_Uninitialize()
+{
+ if (NULL != hEditModule)
+ {
+ FreeLibrary(hEditModule);
+ hEditModule = NULL;
+ }
+
+ if (0 != DETAILS_PROP)
+ {
+ GlobalDeleteAtom(DETAILS_PROP);
+ DETAILS_PROP = 0;
+ }
+}
+
+void SetupDetails_SetDescription(HWND hEdit, LPCWSTR pszText)
+{
+ SetWindowText(hEdit, pszText);
+
+ DWORD originalStyle = GetWindowStyle(hEdit);
+ DWORD windowStyle = originalStyle & ~WS_VSCROLL;
+
+ INT lineCount = (INT)SendMessage(hEdit, EM_GETLINECOUNT, 0, 0L);
+ if (lineCount > 0)
+ {
+ INT charIndex = (INT)SendMessage(hEdit, EM_LINEINDEX, (WPARAM)(lineCount - 1), 0L);
+ if (-1 != charIndex)
+ {
+ LRESULT result = SendMessage(hEdit, EM_POSFROMCHAR, charIndex, 0L);
+ POINTS pts = MAKEPOINTS(result);
+ RECT clientRect;
+ if (GetClientRect(hEdit, &clientRect) && pts.y > (clientRect.bottom - 14))
+ {
+ windowStyle |= WS_VSCROLL;
+ }
+ }
+ }
+
+ if (windowStyle != originalStyle)
+ {
+ SetWindowLongPtr(hEdit, GWL_STYLE, windowStyle);
+ SetWindowPos(hEdit, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
+ }
+
+ ShowWindow(hEdit, SW_HIDE);
+ if (0 != ShowWindow(hEdit, (L'\0' != *pszText) ? SW_SHOWNA : SW_HIDE))
+ InvalidateRect(hEdit, NULL, TRUE);
+}
+
+HWND SetupDetails_CreateServiceView(HWND hParent, LPCWSTR pszName, ifc_omservice *service)
+{
+ return ServicePanel::CreateInstance(hParent, pszName, service, NULL);
+}
+
+BOOL SetupDetails_GetUniqueName(HWND hwnd, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ return (BOOL)SendMessage(hwnd, NSDM_GETUNIQUENAME, (WPARAM)cchBufferMax, (LPARAM)pszBuffer);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupDetails.h b/Src/Plugins/Library/ml_online/Setup/setupDetails.h
new file mode 100644
index 00000000..2afd1b48
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupDetails.h
@@ -0,0 +1,30 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPDETAILS_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPDETAILS_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+class ifc_omservice;
+class SetupGroup;
+class SetupListboxItem;
+
+#define NSDM_FIRST (WM_APP + 7)
+#define NSDM_GETUNIQUENAME (NSDM_FIRST + 0)
+
+EXTERN_C ATOM DETAILS_PROP;
+
+BOOL SetupDetails_Initialize();
+void SetupDetails_Uninitialize();
+
+
+HWND SetupDetails_CreateServiceView(HWND hParent, LPCWSTR pszName, ifc_omservice *service);
+HWND SetupDetails_CreateGroupView(HWND hParent, LPCWSTR pszName, SetupGroup *group);
+BOOL SetupDetails_GetUniqueName(HWND hwnd, LPWSTR pszBuffer, UINT cchBufferMax);
+
+// internal
+void SetupDetails_SetDescription(HWND hEdit, LPCWSTR pszText);
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPDETAILS_HEADER
diff --git a/Src/Plugins/Library/ml_online/Setup/setupDetailsGroup.cpp b/Src/Plugins/Library/ml_online/Setup/setupDetailsGroup.cpp
new file mode 100644
index 00000000..38ca9943
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupDetailsGroup.cpp
@@ -0,0 +1,284 @@
+#include "./setupDetails.h"
+#include "../common.h"
+#include "../resource.h"
+#include "../api__ml_online.h"
+
+#include "../../winamp/commandLink.h"
+#include "./setupGroup.h"
+
+#include <commctrl.h>
+#include <strsafe.h>
+
+#ifndef IDC_HELPLINK
+#define IDC_HELPLINK 10000
+#endif
+
+struct GROUPDETAILSCREATEPARAM
+{
+ GROUPDETAILSCREATEPARAM() : group(NULL), name(NULL) {}
+
+ SetupGroup *group;
+ LPCWSTR name;
+};
+
+struct GROUPDETAILS
+{
+ GROUPDETAILS() : group(NULL), name(NULL), fontTitle(NULL) {}
+
+ SetupGroup *group;
+ LPWSTR name;
+ HFONT fontTitle;
+};
+
+#define GetDetails(__hwnd) ((GROUPDETAILS*)GetPropW((__hwnd), MAKEINTATOM(DETAILS_PROP)))
+
+static INT_PTR WINAPI GroupDetails_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+HWND SetupDetails_CreateGroupView(HWND hParent, LPCWSTR pszName, SetupGroup *group)
+{
+ GROUPDETAILSCREATEPARAM param;
+ param.group = group;
+ param.name = pszName;
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_SETUP_GROUPDETAILS, hParent, GroupDetails_DialogProc, (LPARAM)&param);
+}
+
+static void GroupDetails_SetTitle(HWND hwnd, SetupGroup *group)
+{
+ HWND hTitle = GetDlgItem(hwnd, IDC_TITLE);
+ if (NULL == hTitle) return;
+
+ WCHAR szBuffer[128] = {0};
+ if (NULL == group ||
+ FAILED(group->GetLongName(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ szBuffer[0] = L'\0';
+ }
+
+ GROUPDETAILS *details = GetDetails(hwnd);
+ if (NULL != details && NULL == details->fontTitle)
+ {
+ HFONT dialogFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L);
+ LOGFONT lf;
+ if (0 != GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ {
+ StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Arial Bold");
+ lf.lfWidth = 0;
+ lf.lfWeight = FW_DONTCARE;
+ lf.lfHeight += (lf.lfHeight < 0) ? -2 : +2;
+ lf.lfQuality = 5/*ANTIALIASED_QUALITY*/;
+ details->fontTitle = CreateFontIndirect(&lf);
+ }
+
+ if (NULL != details->fontTitle)
+ {
+ SendMessage(hTitle, WM_SETFONT, (WPARAM)details->fontTitle, 0L);
+ }
+ }
+
+ SetWindowText(hTitle, szBuffer);
+ InvalidateRect(hTitle, NULL, TRUE);
+}
+
+static void GroupDetails_SetDescription(HWND hwnd, SetupGroup *group)
+{
+ HWND hDescription = GetDlgItem(hwnd, IDC_DESCRIPTION);
+ if (NULL == hDescription) return;
+
+ WCHAR szBuffer[4096] = {0};
+ if (NULL == group ||
+ FAILED(group->GetDescription(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ szBuffer[0] = L'\0';
+ }
+
+ SetupDetails_SetDescription(hDescription, szBuffer);
+}
+
+static BOOL GroupDetails_ShowHelp(HWND hwnd)
+{
+ INT result = (INT)(INT_PTR)ShellExecuteW(hwnd, L"open",
+ L"https://help.winamp.com/hc/articles/8112753225364-Online-Services-Security",
+ NULL, NULL, SW_SHOWNORMAL);
+ return (result > 32);
+}
+
+static void GroupDetails_UpdateLayout(HWND hwnd, BOOL fRedraw)
+{
+ RECT clientRect, rect;
+ if(FALSE == GetClientRect(hwnd, &clientRect))
+ return;
+
+
+ UINT commonFlags = SWP_NOACTIVATE | SWP_NOZORDER;
+ if (FALSE != fRedraw) commonFlags |= SWP_NOREDRAW;
+
+ LONG bottomLine = clientRect.bottom;
+
+ HWND hControl;
+ SIZE linkSize;
+ RECT linkMargins;
+
+ if (NULL != (hControl = GetDlgItem(hwnd, IDC_HELPLINK)) &&
+ CommandLink_GetIdealSize(hControl, &linkSize))
+ {
+ if (!CommandLink_GetMargins(hControl, &linkMargins))
+ SetRectEmpty(&linkMargins);
+
+ if (linkSize.cy > 0)
+ bottomLine -= linkSize.cy;
+
+ SetWindowPos(hControl, NULL, clientRect.left + 4, bottomLine, linkSize.cx, linkSize.cy, commonFlags);
+ }
+ else
+ {
+ ZeroMemory(&linkSize, sizeof(linkSize));
+ SetRectEmpty(&linkMargins);
+ }
+
+ if (NULL != (hControl = GetDlgItem(hwnd, IDC_HELPTEXT)))
+ {
+ LONG x = clientRect.left + 4 + linkSize.cx/* - linkMargins.right*/;
+ LONG y = 0;
+
+ HDC hdc = GetDCEx(hControl, NULL, DCX_CACHE | DCX_WINDOW | DCX_NORESETATTRS);
+ if (NULL != hdc)
+ {
+ HFONT font = (HFONT)SendMessage(hControl, WM_GETFONT, 0, 0L);
+ HFONT originalFont = (HFONT)SelectObject(hdc, font);
+
+ TEXTMETRICW tm;
+ if (GetTextMetricsW(hdc, &tm))
+ y = tm.tmHeight;
+
+ SelectObject(hdc, originalFont);
+ ReleaseDC(hControl, hdc);
+ }
+
+ SetWindowPos(hControl, NULL, x, clientRect.bottom - linkMargins.bottom - y, clientRect.right - x, y, commonFlags);
+ }
+
+ if (NULL != (hControl = GetDlgItem(hwnd, IDC_DESCRIPTION)) &&
+ FALSE != GetWindowRect(hControl, &rect))
+ {
+ MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2);
+ SetWindowPos(hControl, NULL, rect.left, rect.right, rect.right - rect.left, bottomLine - rect.top,
+ commonFlags | SWP_NOMOVE);
+ }
+}
+
+static INT_PTR GroupDetails_OnInitDialog(HWND hwnd, HWND hFocus, LPARAM lParam)
+{
+ GROUPDETAILSCREATEPARAM *param = (GROUPDETAILSCREATEPARAM*)lParam;
+
+ GROUPDETAILS *details = (GROUPDETAILS*)calloc(1, sizeof(GROUPDETAILS));
+ if (NULL == details) return FALSE;
+
+ if (!SetProp(hwnd, MAKEINTATOM(DETAILS_PROP), details))
+ return FALSE;
+
+ if (NULL != param)
+ {
+ if (NULL != param->group)
+ {
+ details->group = param->group;
+ details->group->AddRef();
+ }
+
+ details->name = Plugin_CopyString(param->name);
+ }
+
+ HINSTANCE winampInstance = (NULL != WASABI_API_APP) ? WASABI_API_APP->main_gethInstance() : NULL;
+ if (NULL != winampInstance)
+ {
+ WCHAR szBuffer[256] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_CLICKHERE, szBuffer, ARRAYSIZE(szBuffer));
+ HWND hLink = CreateWindowExW(WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT, NWC_COMMANDLINKW, szBuffer,
+ WS_VISIBLE | WS_CHILD | WS_TABSTOP | CLS_ALWAYSUNDERLINE | CLS_DEFAULTCOLORS /* | CLS_HOTTRACK */,
+ 0, 0, 0, 0, hwnd, (HMENU)IDC_HELPLINK, winampInstance, NULL);
+
+ if (NULL != hLink)
+ {
+ SendMessageW(hLink, WM_SETFONT, (WPARAM)SendMessageW(hwnd, WM_GETFONT, 0, 0L), 0L);
+ }
+ }
+
+ GroupDetails_UpdateLayout(hwnd, FALSE);
+
+ if (NULL != param)
+ {
+ GroupDetails_SetTitle(hwnd, param->group);
+ GroupDetails_SetDescription(hwnd, param->group);
+ }
+
+ return FALSE;
+}
+
+static void GroupDetails_OnDestroy(HWND hwnd)
+{
+ GROUPDETAILS *details = GetDetails(hwnd);
+ RemoveProp(hwnd, MAKEINTATOM(DETAILS_PROP));
+
+ if (NULL != details)
+ {
+ if (NULL != details->group)
+ details->group->Release();
+ if (NULL != details->fontTitle)
+ DeleteObject(details->fontTitle);
+
+ Plugin_FreeString(details->name);
+ }
+}
+
+
+static INT_PTR GroupDetails_OnDialogColor(HWND hwnd, HDC hdc, HWND hControl)
+{
+ HWND hParent = GetAncestor(hwnd, GA_PARENT);
+ if (NULL != hParent && hParent != hwnd)
+ return (INT_PTR)SendMessage(hParent, WM_CTLCOLORDLG, (WPARAM)hdc, (LPARAM)hControl);
+ return 0;
+}
+
+
+static INT_PTR GroupDetails_OnStaticColor(HWND hwnd, HDC hdc, HWND hControl)
+{
+ HWND hParent = GetAncestor(hwnd, GA_PARENT);
+ if (NULL != hParent && hParent != hwnd)
+ return (INT_PTR)SendMessage(hParent, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hControl);
+ return 0;
+}
+
+static LRESULT GroupDetails_OnNotify(HWND hwnd, INT controlId, NMHDR *pnmh)
+{
+ switch(controlId)
+ {
+ case IDC_HELPLINK:
+ if (NM_CLICK == pnmh->code)
+ GroupDetails_ShowHelp(hwnd);
+ return TRUE;
+ }
+ return 0;
+}
+
+static BOOL GroupDetails_OnGetUniqueName(HWND hwnd, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return FALSE;
+
+ GROUPDETAILS *details = GetDetails(hwnd);
+ return SUCCEEDED(StringCchCopy(pszBuffer, cchBufferMax,
+ (NULL != details && NULL != details->name) ? details->name : L""));
+}
+static INT_PTR WINAPI GroupDetails_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return GroupDetails_OnInitDialog(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: GroupDetails_OnDestroy(hwnd); break;
+ case WM_CTLCOLORDLG: return GroupDetails_OnDialogColor(hwnd, (HDC)wParam, (HWND)lParam);
+ case WM_CTLCOLORSTATIC: return GroupDetails_OnStaticColor(hwnd, (HDC)wParam, (HWND)lParam);
+ case WM_NOTIFY: MSGRESULT(hwnd, GroupDetails_OnNotify(hwnd, (INT)wParam, (NMHDR*)lParam));
+
+ case NSDM_GETUNIQUENAME: MSGRESULT(hwnd, GroupDetails_OnGetUniqueName(hwnd, (LPWSTR)lParam, (UINT)wParam));
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupDetailsService.cpp b/Src/Plugins/Library/ml_online/Setup/setupDetailsService.cpp
new file mode 100644
index 00000000..3e3907fa
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupDetailsService.cpp
@@ -0,0 +1,596 @@
+#include "./setupDetails.h"
+#include "../common.h"
+#include "../resource.h"
+#include "../wasabi.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omservicedetails.h>
+#include <ifc_omcachemanager.h>
+#include <ifc_omserviceevent.h>
+#include <ifc_omcachegroup.h>
+#include <ifc_omcacherecord.h>
+#include <ifc_imageloader.h>
+#include <ifc_omgraphics.h>
+#include <ifc_omserviceeventmngr.h>
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+
+
+#define GetPanel(__hwnd) ((ServicePanel*)GetPropW((__hwnd), MAKEINTATOM(DETAILS_PROP)))
+
+#define GET_IDETAILS(__service, __details)\
+ (NULL != (service) && SUCCEEDED((service)->QueryInterface(IFC_OmServiceDetails, (void**)&(__details))))
+
+static INT_PTR WINAPI ServicePanel_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+
+
+HWND OmSetupDetails_CreateServiceView(HWND hParent, ifc_omservice *service)
+{
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_SETUP_SERVICEDETAILS, hParent, ServicePanel_DialogProc, (LPARAM)service);
+}
+
+static HFONT ServicePanel_PickTitleFont(HWND hwnd, LPCWSTR pszTitle, INT cchTitle, INT maxWidth)
+{
+ HFONT dialogFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L);
+
+ LOGFONT lf;
+ if (0 == GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ return NULL;
+
+ HFONT titleFont = NULL;
+ if (cchTitle > 0)
+ {
+ LOGFONT lf;
+ if (0 != GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ {
+ StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Arial Bold");
+ lf.lfWidth = 0;
+ lf.lfWeight = FW_DONTCARE;
+ lf.lfQuality = 5/*ANTIALIASED_QUALITY*/;
+
+ HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
+ if (NULL != hdc)
+ {
+ HFONT origFont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
+ SIZE textSize;
+
+ INT heightLimit = (lf.lfHeight < 0) ? 1 : -1;
+ lf.lfHeight += (lf.lfHeight < 0) ? -2 : +2;
+ do
+ {
+ textSize.cx = 0;
+ if (NULL != titleFont) DeleteObject(titleFont);
+ titleFont = CreateFontIndirect(&lf);
+ if (NULL != titleFont)
+ {
+ SelectObject(hdc, titleFont);
+ GetTextExtentPoint32(hdc, pszTitle, cchTitle, &textSize);
+ }
+ lf.lfHeight += (lf.lfHeight < 0) ? 1 : -1;
+
+ } while(textSize.cx > maxWidth && lf.lfHeight != heightLimit);
+
+ if (0 == textSize.cx)
+ {
+ DeleteObject(titleFont);
+ titleFont = NULL;
+ }
+
+ SelectObject(hdc, origFont);
+ ReleaseDC(hwnd, hdc);
+ }
+ }
+ }
+
+ if (NULL == titleFont &&
+ 0 != GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ {
+ titleFont = CreateFontIndirect(&lf);
+ }
+ return titleFont;
+}
+
+static void ServicePanel_SetServiceName(HWND hwnd, ifc_omservice *service)
+{
+ HWND hTitle = GetDlgItem(hwnd, IDC_TITLE);
+ if (NULL == hTitle) return;
+
+ WCHAR szBuffer[128];
+ if (NULL == service ||
+ FAILED(service->GetName(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ szBuffer[0] = L'\0';
+ }
+
+
+
+ SERVICEDETAILS *details = GetDetails(hwnd);
+ if (NULL != details)
+ {
+ INT cchBuffer = lstrlen(szBuffer);
+ RECT rc;
+ GetClientRect(hTitle, &rc);
+ HFONT titleFont = ServicePanel_PickTitleFont(hwnd, szBuffer, cchBuffer, rc.right - rc.left);
+ if (NULL != titleFont)
+ {
+ if (NULL != details->fontTitle) DeleteObject(details->fontTitle);
+ details->fontTitle = titleFont;
+ SendMessage(hTitle, WM_SETFONT, (WPARAM)details->fontTitle, 0L);
+ }
+ }
+
+ SetWindowText(hTitle, szBuffer);
+ InvalidateRect(hTitle, NULL, TRUE);
+}
+
+
+static void ServicePanel_SetServiceDescription(HWND hwnd, ifc_omservice *service)
+{
+ HWND hDescription = GetDlgItem(hwnd, IDC_DESCRIPTION);
+ if (NULL == hDescription) return;
+
+ WCHAR szBuffer[4096] = {0};
+
+ ifc_omservicedetails *details = 0;
+ if (GET_IDETAILS(service, details))
+ {
+ details->GetDescription(szBuffer, ARRAYSIZE(szBuffer));
+ details->Release();
+ }
+
+ OmSetupDetails_SetDescription(hDescription, szBuffer);
+}
+
+static LPCWSTR ServicePanel_FormatDate(LPCWSTR pszDate, LPWSTR pszBuffer, INT cchBufferMax)
+{
+ SYSTEMTIME st;
+ ZeroMemory(&st, sizeof(SYSTEMTIME));
+ LPCWSTR cursor;
+
+ cursor = pszDate;
+ INT index = 0;
+
+ for(;;)
+ {
+
+ INT iVal;
+
+ if (FALSE == StrToIntEx(cursor, STIF_DEFAULT, &iVal) || iVal < 1)
+ {
+ index = 0;
+ break;
+ }
+
+ if (0 == index)
+ {
+ if (iVal < 2000 || iVal > 2100)
+ break;
+ st.wYear = iVal;
+ index++;
+ }
+ else if (1 == index)
+ {
+ if (iVal < 1 || iVal > 12)
+ break;
+ st.wMonth = iVal;
+ index++;
+ }
+ else if (2 == index)
+ {
+ if (iVal < 1 || iVal > 31)
+ break;
+ st.wDay = iVal;
+ index++;
+ }
+ else
+ {
+ index = 0;
+ break;
+ }
+
+ while(L'\0' != *cursor && L'-' != *cursor) cursor++;
+ if (L'-' == *cursor) cursor++;
+ if (L'\0' == *cursor)
+ break;
+
+ }
+
+ if (3 == index &&
+ 0 != GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, pszBuffer, cchBufferMax))
+ {
+ return pszBuffer;
+ }
+
+ return pszDate;
+}
+
+
+
+static HRESULT ServicePanel_GetFullName(ifc_omservicedetails *details, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ *pszBuffer = L'\0';
+
+ HRESULT hr = S_OK;
+ if (NULL != details)
+ {
+ hr = details->GetAuthorFirst(pszBuffer, cchBufferMax);
+ if (SUCCEEDED(hr))
+ {
+ UINT cchBuffer = lstrlen(pszBuffer);
+ LPWSTR cursor = pszBuffer + cchBuffer;
+ size_t remaining = cchBufferMax - cchBuffer;
+
+ if (cursor != pszBuffer)
+ {
+ hr = StringCchCopyEx(cursor, remaining, L" ", &cursor, &remaining, 0);
+ if (SUCCEEDED(hr))
+ {
+ hr = details->GetAuthorLast(cursor, (UINT)remaining);
+ if (FAILED(hr) || L'\0' == *cursor)
+ {
+ pszBuffer[cchBuffer] = L'\0';
+ }
+ }
+ }
+ }
+ }
+ return hr;
+}
+
+static void ServicePanel_SetServiceMeta(HWND hwnd, ifc_omservice *service)
+{
+ HWND hMeta = GetDlgItem(hwnd, IDC_SERVICEMETA);
+ if (NULL == hMeta) return;
+
+ WCHAR szBuffer[512] = {0};
+
+ ifc_omservicedetails *svcdetails = 0;
+ if (GET_IDETAILS(service, svcdetails))
+ {
+ HRESULT hr = S_OK;
+ LPWSTR cursor = szBuffer;
+ WCHAR szValue[256] = {0}, szPrefix[64] = {0};
+ size_t remaining = ARRAYSIZE(szBuffer);
+
+ if (SUCCEEDED(ServicePanel_GetFullName(svcdetails, szValue, ARRAYSIZE(szValue))) && L'\0' != szValue[0])
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_SERVICE_BYAUTHOR, szPrefix, ARRAYSIZE(szPrefix));
+ hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ L"%s%s", szPrefix, szValue);
+ }
+
+ if (SUCCEEDED(svcdetails->GetUpdated(szValue, ARRAYSIZE(szValue))) && L'\0' != szValue[0])
+ {
+ if (cursor != szBuffer)
+ hr = StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE);
+
+ if (SUCCEEDED(hr))
+ {
+ WCHAR szDate[128] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_SERVICE_LASTUPDATED, szPrefix, ARRAYSIZE(szPrefix));
+ StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ L"%s%s", szPrefix, ServicePanel_FormatDate(szValue, szDate, ARRAYSIZE(szDate)));
+ }
+ }
+
+ svcdetails->Release();
+
+ }
+
+ SERVICEDETAILS *details = GetDetails(hwnd);
+ if (NULL != details && NULL == details->fontMeta)
+ {
+ HFONT dialogFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L);
+ LOGFONT lf;
+ if (0 != GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ {
+ StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Tahoma");
+ lf.lfWidth = 0;
+ lf.lfHeight += (lf.lfHeight < 0) ? 1 : -1;
+ lf.lfQuality = ANTIALIASED_QUALITY;
+ details->fontMeta = CreateFontIndirect(&lf);
+ }
+
+ if (NULL != details->fontMeta)
+ {
+ SendMessage(hMeta, WM_SETFONT, (WPARAM)details->fontMeta, 0L);
+ }
+ }
+
+ SetWindowText(hMeta, szBuffer);
+ if (0 != ShowWindow(hMeta, (L'\0' != szBuffer[0]) ? SW_SHOWNA : SW_HIDE))
+ InvalidateRect(hMeta, NULL, TRUE);
+}
+
+static void ServicePanel_SetThumbnail(HWND hwnd, ifc_omservice *service)
+{
+ HWND hThumbnail = GetDlgItem(hwnd, IDC_THUMBNAIL);
+ if (NULL == hThumbnail) return;
+
+ SendMessage(hThumbnail, WM_SETREDRAW, FALSE, 0L);
+
+ HBITMAP hBitmap;
+
+ BITMAPINFOHEADER header;
+ void *pixelData;
+
+ WCHAR szPath[2048];
+
+ ifc_omservicedetails *details = 0;
+ if (GET_IDETAILS(service, details))
+ {
+ if (SUCCEEDED(details->GetThumbnail(szPath, ARRAYSIZE(szPath))))
+ {
+ ifc_omcachemanager *cacheManager;
+ if (SUCCEEDED(OMUTILITY->GetCacheManager(&cacheManager)))
+ {
+ ifc_omcachegroup *cacheGroup;
+ if (SUCCEEDED(cacheManager->Find(L"thumbnail", TRUE, &cacheGroup, NULL)))
+ {
+ ifc_omcacherecord *cacheRecord;
+ if (SUCCEEDED(cacheGroup->Find(szPath, TRUE, &cacheRecord, FALSE)))
+ {
+ cacheRecord->Release();
+ }
+ cacheGroup->Release();
+ }
+ cacheManager->Release();
+ }
+ }
+ details->Release();
+ }
+
+ ifc_omimageloader *imageLoader;
+ if (SUCCEEDED(OMUTILITY->QueryImageLoader(NULL, szPath, FALSE, &imageLoader)))
+ {
+ if (FAILED(imageLoader->LoadBitmapEx(&hBitmap, &header, &pixelData)))
+ hBitmap = NULL;
+ imageLoader->Release();
+ }
+
+ if (NULL == hBitmap &&
+ SUCCEEDED(OMUTILITY->QueryImageLoader(WASABI_API_ORIG_HINST, MAKEINTRESOURCE(IDR_SERVICE64X64_IMAGE), FALSE, &imageLoader)))
+ {
+ if (FAILED(imageLoader->LoadBitmapEx(&hBitmap, &header, &pixelData)))
+ hBitmap = NULL;
+ imageLoader->Release();
+ }
+
+ HBITMAP hTest = (HBITMAP)SendMessage(hThumbnail, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
+ if (NULL != hTest)
+ DeleteObject(hTest);
+
+ if (NULL != hBitmap)
+ {
+ hTest = (HBITMAP)SendMessage(hThumbnail, STM_GETIMAGE, IMAGE_BITMAP, 0L);
+ if (hTest != hBitmap)
+ { // this is XP and up image copy was created and alpha channel will be handled properly
+ DeleteObject(hBitmap);
+ }
+ else
+ { // fix alpha channel
+ if (32 == header.biBitCount)
+ {
+ HDC hdcFixed = CreateCompatibleDC(NULL);
+ if (NULL != hdcFixed)
+ {
+ BITMAPINFOHEADER headerFixed;
+ CopyMemory(&headerFixed, &header, sizeof(BITMAPINFOHEADER));
+ BYTE *pixelsFixed;
+ INT cx = header.biWidth;
+ INT cy = abs(header.biHeight);
+ HBITMAP bitmapFixed = CreateDIBSection(NULL, (LPBITMAPINFO)&headerFixed, DIB_RGB_COLORS, (void**)&pixelsFixed, NULL, 0);
+
+ if (NULL != bitmapFixed)
+ {
+ HBITMAP bitmapOrig = (HBITMAP)SelectObject(hdcFixed, bitmapFixed);
+ HBRUSH hb = (HBRUSH)SendMessage(hwnd, WM_CTLCOLORDLG, (WPARAM)hdcFixed, (LPARAM)hwnd);
+ if (NULL == hb)
+ hb = GetSysColorBrush(COLOR_3DFACE);
+ RECT rect;
+ SetRect(&rect, 0, 0, cx, cy);
+ FillRect(hdcFixed, &rect, hb);
+
+ ifc_omgraphics *graphics;
+ if (SUCCEEDED(OMUTILITY->GetGraphics(&graphics)))
+ {
+ HDC hdcSrc = CreateCompatibleDC(NULL);
+ if (NULL != hdcSrc)
+ {
+ HBITMAP bitmapSrcOrig = (HBITMAP)SelectObject(hdcSrc, hBitmap);
+ BLENDFUNCTION bf;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.BlendFlags = 0;
+ bf.SourceConstantAlpha = 0xFF;
+ bf.AlphaFormat = AC_SRC_ALPHA;
+
+ RECT blendRect;
+ SetRect(&blendRect, 0, 0, cx, cy);
+
+ graphics->Premultiply((BYTE*)pixelData, cx, cy);
+ graphics->AlphaBlend(hdcFixed, &blendRect, hdcSrc, &blendRect, bf);
+
+ SelectObject(hdcSrc, bitmapSrcOrig);
+ DeleteDC(hdcSrc);
+ }
+ graphics->Release();
+ }
+
+ SelectObject(hdcFixed, bitmapOrig);
+ SendMessage(hThumbnail, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapFixed);
+ DeleteObject(hBitmap);
+
+ }
+ DeleteDC(hdcFixed);
+ }
+ }
+ }
+ }
+
+ RECT clientRect;
+ if (GetClientRect(hThumbnail, &clientRect))
+ {
+ INT cx = clientRect.right - clientRect.left;
+ INT cy = clientRect.bottom - clientRect.top;
+ if (64 != cx || 64 != cy)
+ {
+ SetWindowPos(hThumbnail, NULL, 0, 0, 64, 64, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ }
+
+ SendMessage(hThumbnail, WM_SETREDRAW, TRUE, 0L);
+
+ if (0 != ShowWindow(hThumbnail, (NULL != hBitmap) ? SW_SHOWNA : SW_HIDE))
+ InvalidateRect(hThumbnail, NULL, TRUE);
+}
+
+
+static void CALLBACK ServicePanel_OnServiceNotify(UINT serviceUid, UINT callbackType, UINT callbackParam, ULONG_PTR user)
+{
+ HWND hwnd = (HWND)user;
+ if (NULL == hwnd) return;
+
+ SERVICEDETAILS *details = GetDetails(hwnd);
+
+ if (NULL == details ||
+ NULL == details->service ||
+ serviceUid != details->service->GetId())
+ {
+ return;
+ }
+
+ switch(callbackType)
+ {
+ case OmService::eventServiceModified:
+ if (0 != (OmService::modifiedName & callbackParam))
+ ServicePanel_SetServiceName(hwnd, details->service);
+ if (0 != (OmService::modifiedDescription & callbackParam))
+ ServicePanel_SetServiceDescription(hwnd, details->service);
+ if (0 != ((OmService::modifiedAuthor | OmService::modifiedDate) & callbackParam))
+ ServicePanel_SetServiceMeta(hwnd, details->service);
+ if (0 != (OmService::modifiedThumbnail & callbackParam))
+ ServicePanel_SetThumbnail(hwnd, details->service);
+ break;
+ }
+}
+
+static INT_PTR ServicePanel_OnInitDialog(HWND hwnd, HWND hFocus, LPARAM lParam)
+{
+ ifc_omservice *service = (ifc_omservice*)lParam;
+
+ SERVICEDETAILS *details = (SERVICEDETAILS*)malloc(sizeof(SERVICEDETAILS));
+ if (NULL == details) return FALSE;
+ ZeroMemory(details, sizeof(SERVICEDETAILS));
+
+ if (!SetProp(hwnd, MAKEINTATOM(DETAILS_PROP), details))
+ return FALSE;
+
+ details->service = service;
+ details->service->AddRef();
+
+ ServicePanel_SetServiceName(hwnd, service);
+ ServicePanel_SetServiceDescription(hwnd, service);
+ ServicePanel_SetThumbnail(hwnd, service);
+ ServicePanel_SetServiceMeta(hwnd, service);
+
+ if (NULL != service)
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(service->GetEventManager(&eventManager)))
+ {
+ if (SUCCEEDED(eventManager->RegisterHandler(eventHander)))
+ {
+ details->eventHandler = eventHandler;
+ }
+ else
+ {
+ eventHandler->Release();
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static void ServicePanel_OnDestroy(HWND hwnd)
+{
+ OMSERVICEMNGR->UnregisterCallback(ServicePanel_OnServiceNotify, (ULONG_PTR)hwnd);
+
+ SERVICEDETAILS *details = GetDetails(hwnd);
+ RemoveProp(hwnd, MAKEINTATOM(DETAILS_PROP));
+
+ if (NULL != details)
+ {
+ if (NULL != details->service)
+ details->service->Release();
+ if (NULL != details->fontTitle)
+ DeleteObject(details->fontTitle);
+ if (NULL != details->fontMeta)
+ DeleteObject(details->fontMeta);
+ }
+
+ HWND hThumbnail = GetDlgItem(hwnd, IDC_THUMBNAIL);
+ if (NULL != hThumbnail)
+ {
+ HBITMAP hBitmap = (HBITMAP)SendMessage(hThumbnail, STM_SETIMAGE, IMAGE_BITMAP, 0L);
+ if (NULL != hBitmap)
+ DeleteObject(hBitmap);
+ }
+}
+
+
+static INT_PTR ServicePanel_OnDialogColor(HWND hwnd, HDC hdc, HWND hControl)
+{
+ HWND hParent = GetAncestor(hwnd, GA_PARENT);
+ if (NULL != hParent && hParent != hwnd)
+ return (INT_PTR)SendMessage(hParent, WM_CTLCOLORDLG, (WPARAM)hdc, (LPARAM)hControl);
+ return 0;
+}
+
+
+static INT_PTR ServicePanel_OnStaticColor(HWND hwnd, HDC hdc, HWND hControl)
+{
+ INT_PTR result = 0;
+ HWND hParent = GetAncestor(hwnd, GA_PARENT);
+ if (NULL != hParent && hParent != hwnd)
+ result = (INT_PTR)SendMessage(hParent, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hControl);
+
+ INT controlId = GetDlgCtrlID(hControl);
+ switch(controlId)
+ {
+ case IDC_SERVICEMETA:
+ {
+ COLORREF rgbBk = GetBkColor(hdc);
+ COLORREF rgbFg = GetTextColor(hdc);
+
+ ifc_omgraphics *graphics;
+ if (SUCCEEDED(OMUTILITY->GetGraphics(&graphics)))
+ {
+ graphics->BlendColor(rgbFg, rgbBk, 180, &rgbFg);
+ graphics->Release();
+ }
+
+ SetTextColor(hdc, rgbFg);
+ }
+ break;
+ }
+ return result;
+}
+
+static INT_PTR WINAPI ServicePanel_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return ServicePanel_OnInitDialog(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: ServicePanel_OnDestroy(hwnd); break;
+ case WM_CTLCOLORDLG: return ServicePanel_OnDialogColor(hwnd, (HDC)wParam, (HWND)lParam);
+ case WM_CTLCOLORSTATIC: return ServicePanel_OnStaticColor(hwnd, (HDC)wParam, (HWND)lParam);
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupGroup.cpp b/Src/Plugins/Library/ml_online/Setup/setupGroup.cpp
new file mode 100644
index 00000000..bf1faf28
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupGroup.cpp
@@ -0,0 +1,929 @@
+#include "./setupGroup.h"
+#include "./setupGroupFilter.h"
+#include "./setupListboxLabel.h"
+#include "./setupDetails.h"
+#include "./setupPage.h"
+#include "../common.h"
+#include "../api__ml_online.h"
+#include "../resource.h"
+#include "../serviceHost.h"
+#include "../serviceHelper.h"
+
+#include "../../nu/menuHelpers.h"
+#include <vector>
+
+#include <ifc_omservice.h>
+#include <ifc_omstorage.h>
+#include <ifc_omstorageasync.h>
+#include <ifc_omserviceenum.h>
+#include <ifc_omfilestorage.h>
+
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <algorithm>
+
+typedef std::vector<ifc_omservice*> ServiceList;
+
+#define GROUP_MARGINCX 0
+#define GROUP_MARGINCY 1
+
+#define TEXT_OFFSET_LEFT 2
+#define TEXT_OFFSET_BOTTOM 2
+#define TEXT_ALIGN (TA_LEFT | TA_BOTTOM)
+
+SetupGroup::SetupGroup(INT groupId, LPCWSTR pszName, LPCWSTR pszAddress, const GUID *storageId, const GUID *filterId, UINT fStyle)
+ : ref(1), id(groupId), name(NULL), flags(0), emptyLabel(NULL), errorCode(S_OK), hPage(NULL),
+ longName(NULL), description(NULL), address(NULL), loadResult(NULL), style(fStyle), loadComplete(NULL)
+{
+ name = Plugin_DuplicateResString(pszName);
+ address = Plugin_DuplicateResString(pszAddress);
+ this->storageId = (NULL != storageId) ? *storageId : GUID_NULL;
+ this->filterId = (NULL != filterId) ? *filterId : GUID_NULL;
+
+ InitializeCriticalSection(&lock);
+}
+
+SetupGroup::~SetupGroup()
+{
+ Plugin_FreeResString(name);
+ Plugin_FreeResString(address);
+
+ SetLongName(NULL);
+ SetDescription(NULL);
+
+ EnterCriticalSection(&lock);
+
+ size_t index = list.size();
+ while(index--)
+ {
+ list[index]->Release();
+ }
+
+ if (NULL != emptyLabel)
+ emptyLabel->Release();
+
+ if (NULL != loadResult)
+ {
+ ifc_omstorage *storage;
+ HRESULT hr = OMSERVICEMNGR->QueryStorage(&storageId, &storage);
+ if (SUCCEEDED(hr))
+ {
+ storage->RequestAbort(loadResult, TRUE);
+ }
+
+ loadResult->Release();
+ loadResult = NULL;
+ }
+
+ if (NULL != loadComplete)
+ CloseHandle(loadComplete);
+
+ LeaveCriticalSection(&lock);
+
+ DeleteCriticalSection(&lock);
+
+}
+
+SetupGroup *SetupGroup::CreateInstance(INT groupId, LPCWSTR pszName, LPCWSTR pszAddress, const GUID *storageId, const GUID *filterId, UINT fStyle)
+{
+ return new SetupGroup(groupId, pszName, pszAddress, storageId, filterId, fStyle);
+}
+
+ULONG SetupGroup::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+ULONG SetupGroup::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+ return r;
+}
+
+HRESULT SetupGroup::GetName(LPWSTR pszBuffer, INT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ HRESULT hr;
+ if (NULL != name)
+ {
+ if (IS_INTRESOURCE(name))
+ {
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)name, pszBuffer, cchBufferMax);
+ hr = (L'\0' != *pszBuffer) ? S_OK : E_FAIL;
+ }
+ else
+ {
+ hr = StringCchCopyW(pszBuffer, cchBufferMax, name);
+ }
+ }
+ else
+ {
+ hr = StringCchCopyW(pszBuffer, cchBufferMax, L"Unknown");
+ }
+ return hr;
+}
+
+HRESULT SetupGroup::GetLongName(LPWSTR pszBuffer, INT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ HRESULT hr;
+ if (NULL == longName)
+ return GetName(pszBuffer, cchBufferMax);
+
+ if (IS_INTRESOURCE(longName))
+ {
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)longName, pszBuffer, cchBufferMax);
+ hr = (L'\0' != *pszBuffer) ? S_OK : E_FAIL;
+ }
+ else
+ {
+ hr = StringCchCopyW(pszBuffer, cchBufferMax, longName);
+ }
+ return hr;
+}
+
+HRESULT SetupGroup::GetDescription(LPWSTR pszBuffer, INT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ HRESULT hr;
+
+ if (NULL != description && IS_INTRESOURCE(description))
+ {
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)description, pszBuffer, cchBufferMax);
+ hr = (L'\0' != *pszBuffer) ? S_OK : E_FAIL;
+ }
+ else
+ {
+ hr = StringCchCopyEx(pszBuffer, cchBufferMax, description, NULL, NULL, STRSAFE_IGNORE_NULLS);
+ }
+ return hr;
+}
+size_t SetupGroup::GetRecordCount()
+{
+ return list.size();
+}
+
+size_t SetupGroup::GetListboxCount()
+{
+ if (0 != (flagCollapsed & flags)) return 0;
+ size_t listSize = list.size();
+
+ if (0 == listSize)
+ {
+ return (NULL != emptyLabel && FALSE == emptyLabel->IsNameNull()) ? 1 : 0;
+ }
+
+ return listSize;
+}
+
+SetupListboxItem *SetupGroup::GetListboxItem(size_t index)
+{
+ if (0 != (flagCollapsed & flags)) return NULL;
+ size_t listSize = list.size();
+
+ if (0 == listSize)
+ {
+ return (NULL != emptyLabel && FALSE == emptyLabel->IsNameNull()) ? emptyLabel : NULL;
+ }
+ return list[index];
+}
+
+BOOL SetupGroup::IsModified()
+{
+ size_t index = list.size();
+ while(index--)
+ {
+ if (list[index]->IsModified())
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL SetupGroup::IsExpanded()
+{
+ return (0 == (flagCollapsed & flags));
+}
+
+void SetupGroup::SetExpanded(BOOL fExpanded)
+{
+ if ((FALSE == fExpanded) == (FALSE == IsExpanded()))
+ return;
+
+ if (FALSE == fExpanded)
+ flags |= flagCollapsed;
+ else
+ flags &= ~flagCollapsed;
+}
+
+void SetupGroup::Clear(BOOL fInvalidate)
+{
+ size_t index = list.size();
+ if (0 == index) return;
+
+ EnterCriticalSection(&lock);
+
+ while(index--)
+ {
+ SetupRecord *record = list[index];
+ if (NULL != record)
+ {
+ record->Release();
+ }
+ }
+ list.clear();
+
+ LeaveCriticalSection(&lock);
+
+ SetEmptyText(MAKEINTRESOURCE(IDS_SETUP_EMPTYGROUP), FALSE);
+
+ if (FALSE != fInvalidate && NULL != hPage)
+ PostMessage(hPage, SPM_UPDATELIST, (WPARAM)id, NULL);
+}
+
+static void CALLBACK SetupGroup_LoadCallback(ifc_omstorageasync *result)
+{
+ if (NULL == result) return;
+ SetupGroup *group;
+ if (SUCCEEDED(result->GetData((void**)&group)) && NULL != group)
+ {
+ group->OnLoadCompleted();
+ }
+}
+
+
+__inline static int __cdecl SetupGroup_AlphabeticalSorter(const void *elem1, const void *elem2)
+{
+ SetupRecord *record1 = (SetupRecord*)elem1;
+ SetupRecord *record2 = (SetupRecord*)elem2;
+
+ if (NULL == record1 || NULL == record2)
+ return (INT)(INT_PTR)(record1 - record2);
+
+ ifc_omservice *svc1 = record1->GetService();
+ ifc_omservice *svc2 = record2->GetService();
+
+ if (NULL == svc1 || NULL == svc2)
+ return (INT)(INT_PTR)(svc1 - svc2);
+
+ WCHAR szBuffer1[256] = {0}, szBuffer2[256] = {0};
+ if (FAILED(svc1->GetName(szBuffer1, ARRAYSIZE(szBuffer1))))
+ szBuffer1[0] = L'\0';
+ if (FAILED(svc2->GetName(szBuffer2, ARRAYSIZE(szBuffer2))))
+ szBuffer2[0] = L'\0';
+
+ return CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, szBuffer1, -1, szBuffer2, -1) - 2;
+}
+
+__inline static bool __cdecl SetupGroup_AlphabeticalSorter_V2(const void* elem1, const void* elem2)
+{
+ return SetupGroup_AlphabeticalSorter(elem1, elem2) < 0;
+}
+void SetupGroup::OnLoadCompleted()
+{
+ ifc_omstorage *storage;
+ HRESULT hr = OMSERVICEMNGR->QueryStorage(&storageId, &storage);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omserviceenum *serviceEnum;
+ hr = storage->EndLoad(loadResult, &serviceEnum);
+ if (SUCCEEDED(hr))
+ {
+ SetupGroupFilter *filter;
+ if (FAILED(SetupGroupFilter::CreateInstance(&filterId, &filter)))
+ {
+ filter = NULL;
+ }
+ else if (FAILED(filter->Initialize()))
+ {
+ filter->Release();
+ filter = NULL;
+ }
+
+ EnterCriticalSection(&lock);
+
+ ifc_omservice *service;
+ UINT filterResult, defaultFilter;
+ defaultFilter = SetupGroupFilter::serviceInclude;
+ if (0 != (styleDefaultUnsubscribed & style))
+ defaultFilter |= SetupGroupFilter::serviceForceUnsubscribe;
+ else if (0 != (styleDefaultSubscribed & style))
+ defaultFilter |= SetupGroupFilter::serviceForceSubscribe;
+
+ while(S_OK == serviceEnum->Next(1, &service, NULL))
+ {
+
+ filterResult = defaultFilter;
+ if (NULL != filter && FAILED(filter->ProcessService(service, &filterResult)))
+ filterResult = defaultFilter;
+
+ if (0 == (SetupGroupFilter::serviceInclude & filterResult))
+ {
+ service->Release();
+ service = NULL;
+ continue;
+ }
+
+ if (0 != (SetupGroupFilter::serviceForceUnsubscribe & filterResult))
+ ServiceHelper_Subscribe(service, TRUE, 0);
+ else if (0 != (SetupGroupFilter::serviceForceSubscribe & filterResult))
+ ServiceHelper_Subscribe(service, FALSE, 0);
+
+ if (0 != (styleSaveAll & style))
+ ServiceHelper_MarkModified(service, (UINT)-1, (UINT)-1);
+
+ SetupRecord *record = SetupRecord::CreateInstance(service);
+ if (NULL != record)
+ {
+ if (0 != (SetupGroupFilter::serviceForceUnsubscribe & filterResult))
+ record->SetSelected(FALSE);
+ else if (0 != (SetupGroupFilter::serviceForceSubscribe & filterResult))
+ record->SetSelected(TRUE);
+ list.push_back(record);
+ }
+
+ service->Release();
+ }
+
+ if (0 != (styleSortAlphabetically & style))
+ {
+ //qsort(list.first(), list.size(), sizeof(SetupRecord*), SetupGroup_AlphabeticalSorter);
+ std::sort(list.begin(), list.end(), SetupGroup_AlphabeticalSorter_V2);
+ }
+
+ LeaveCriticalSection(&lock);
+
+ serviceEnum->Release();
+ if (NULL != filter)
+ filter->Release();
+ }
+
+ storage->Release();
+ }
+
+ EnterCriticalSection(&lock);
+
+ loadResult->Release();
+ loadResult = NULL;
+
+ if (NULL != loadComplete)
+ {
+ SetEvent(loadComplete);
+ CloseHandle(loadComplete);
+ loadComplete = NULL;
+ }
+
+ LeaveCriticalSection(&lock);
+
+ LPCWSTR pszText = MAKEINTRESOURCE(((FAILED(hr)) ? IDS_SETUP_GROUPLOADFAILED : IDS_SETUP_EMPTYGROUP));
+ SetEmptyText( pszText, TRUE);
+}
+
+HRESULT SetupGroup::RequestReload()
+{
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ HRESULT hr;
+
+ EnterCriticalSection(&lock);
+
+ if (NULL != loadResult)
+ hr = E_PENDING;
+ else
+ {
+ if (NULL != loadComplete)
+ {
+ CloseHandle(loadComplete);
+ loadComplete = NULL;
+ }
+
+ Clear(FALSE);
+ SetEmptyText(MAKEINTRESOURCE(IDS_SETUP_LOADINGGROUP), TRUE);
+
+ ifc_omstorage *storage;
+ hr = OMSERVICEMNGR->QueryStorage(&storageId, &storage);
+ if (SUCCEEDED(hr))
+ {
+ ServiceHost *serviceHost;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ hr = storage->BeginLoad(address, serviceHost, SetupGroup_LoadCallback, this, &loadResult);
+ storage->Release();
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+ }
+
+ if (FAILED(hr))
+ {
+ SetEmptyText(MAKEINTRESOURCE(IDS_SETUP_GROUPLOADFAILED), TRUE);
+ }
+ }
+
+ LeaveCriticalSection(&lock);
+
+ return hr;
+}
+
+void SetupGroup::SetPageWnd(HWND hPage)
+{
+ this->hPage = hPage;
+}
+
+HRESULT SetupGroup::SignalLoadCompleted(HANDLE event)
+{
+ HRESULT hr;
+ if (NULL == event) return E_INVALIDARG;
+
+ EnterCriticalSection(&lock);
+
+ if (NULL == loadResult)
+ {
+ SetEvent(event);
+ hr = S_OK;
+ }
+ else
+ {
+ if (NULL != loadComplete)
+ CloseHandle(loadComplete);
+
+ if (FALSE == DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(), &loadComplete, 0, FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ DWORD error = GetLastError();
+ hr = HRESULT_FROM_WIN32(error);
+ }
+
+ hr = S_OK;
+ }
+
+ LeaveCriticalSection(&lock);
+ return hr;
+}
+
+HRESULT SetupGroup::Save(SetupLog *log)
+{
+ HRESULT hr(S_OK);
+ size_t index = list.size();
+ while(index--)
+ {
+ if (FAILED(list[index]->Save(log)))
+ hr = E_FAIL;
+ }
+ return hr;
+}
+
+void SetupGroup::GetColors(HDC hdc, UINT state, COLORREF *rgbBkOut, COLORREF *rgbTextOut)
+{
+ COLORREF rgbBk, rgbText;
+
+
+ if (0 != (ODS_DISABLED & state))
+ {
+ rgbBk = GetBkColor(hdc);
+ rgbText = GetSysColor(COLOR_GRAYTEXT);
+ }
+ else
+ {
+ if (0 != (ODS_SELECTED & state))
+ {
+ if (0 == (ODS_INACTIVE & state))
+ {
+ rgbBk = GetSysColor(COLOR_HIGHLIGHT);
+ rgbText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ }
+ else
+ {
+ rgbBk = GetSysColor(COLOR_3DFACE);
+ rgbText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ }
+ else
+ {
+ rgbBk = GetSysColor(COLOR_WINDOW);
+ rgbText = GetSysColor(COLOR_WINDOWTEXT);
+ }
+ }
+
+ if (NULL != rgbBkOut) *rgbBkOut = rgbBk;
+ if (NULL != rgbTextOut) *rgbTextOut = rgbText;
+}
+
+HBRUSH SetupGroup::GetBrush(HDC hdc, UINT state)
+{
+ if (0 != (ODS_DISABLED & state))
+ {
+ return GetSysColorBrush(COLOR_WINDOW);
+ }
+ if (0 != (ODS_COMBOBOXEDIT & state))
+ {
+ return GetSysColorBrush(COLOR_WINDOWTEXT);
+ }
+ if (0 != (ODS_SELECTED & state))
+ {
+ return GetSysColorBrush( (0 == (ODS_INACTIVE & state)) ? COLOR_HIGHLIGHT : COLOR_3DFACE);
+ }
+
+ return GetSysColorBrush(COLOR_WINDOW);
+}
+
+BOOL SetupGroup::MeasureItem(SetupListbox *instance, UINT *cx, UINT *cy)
+{
+ HDC hdc = GetDCEx(instance->GetHwnd(), NULL, DCX_CACHE | DCX_NORESETATTRS);
+ if (NULL == hdc) return FALSE;
+
+ HFONT originalFont = (HFONT)SelectObject(hdc, instance->GetFont());
+ SIZE imageSize;
+ if (!instance->GetExpandboxMetrics(hdc, IsExpanded(), &imageSize))
+ ZeroMemory(&imageSize, sizeof(SIZE));
+
+ if (NULL != cy)
+ {
+ *cy = 0;
+ TEXTMETRIC tm;
+ if (GetTextMetrics(hdc, &tm))
+ {
+ *cy = tm.tmHeight + tm.tmExternalLeading;
+ if (imageSize.cy > (INT)*cy) *cy = imageSize.cy;
+ *cy += GROUP_MARGINCY*2;
+ }
+ }
+
+ if (NULL != cx)
+ {
+ *cx = imageSize.cx;
+ WCHAR szBuffer[128] = {0};
+ if (SUCCEEDED(GetName(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ INT cchBuffer = lstrlenW(szBuffer);
+ SIZE textSize;
+ if (0 != cchBuffer && GetTextExtentPoint32(hdc, szBuffer, cchBuffer, &textSize))
+ {
+ *cx += textSize.cx;
+ }
+ }
+ if (0 != *cx) *cx += GROUP_MARGINCX*2;
+ }
+
+ SelectObject(hdc, originalFont);
+ ReleaseDC(instance->GetHwnd(), hdc);
+ return TRUE;
+}
+
+
+static void SetupGroup_DrawFrame(HDC hdc, const RECT *prc, INT width, COLORREF rgbFrame)
+{
+ if (width > 0)
+ {
+ COLORREF rgbOld = SetBkColor(hdc, rgbFrame);
+
+ RECT rcPart;
+ SetRect(&rcPart, prc->left, prc->top, prc->right, prc->top + width);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL);
+ SetRect(&rcPart, prc->left, prc->bottom - width, prc->right, prc->bottom);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL);
+ SetRect(&rcPart, prc->left, prc->top + width, prc->left + width, prc->bottom - width);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL);
+ SetRect(&rcPart, prc->right - width, prc->top + width, prc->right, prc->bottom - width);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL);
+
+ if (rgbOld != rgbFrame)
+ SetBkColor(hdc, rgbOld);
+ }
+}
+BOOL SetupGroup::DrawItem(SetupListbox *instance, HDC hdc, const RECT *prc, UINT state)
+{
+ LONG paintLeft = prc->left + GROUP_MARGINCX;
+ RECT partRect;
+
+ SetRectEmpty(&partRect);
+
+ COLORREF rgbBk, rgbText;
+ GetColors(hdc, state, &rgbBk, &rgbText);
+
+ COLORREF origBk = SetBkColor(hdc, rgbBk);
+ COLORREF origText = SetTextColor(hdc, rgbText);
+ UINT textAlign = SetTextAlign(hdc, TEXT_ALIGN);
+
+ HRGN backRgn, rgn;
+ backRgn = CreateRectRgnIndirect(prc);
+ rgn = CreateRectRgn(0,0,0,0);
+
+ SetRectEmpty(&partRect);
+ if (instance->GetExpandboxMetrics(hdc, IsExpanded(), (((SIZE*)&partRect) + 1)))
+ {
+ INT space = (prc->bottom - prc->top) - (partRect.bottom- partRect.top);
+ INT offsetY = space / 2 + space%2;
+ if (offsetY < 0) offsetY = 0;
+ OffsetRect(&partRect, paintLeft, prc->top + offsetY);
+ if (instance->DrawExpandbox(hdc, IsExpanded(), &partRect, rgbBk, rgbText))
+ {
+ paintLeft = partRect.right;
+ if (SetRectRgn(rgn, partRect.left, partRect.top, partRect.right, partRect.bottom))
+ CombineRgn(backRgn, backRgn, rgn, RGN_DIFF);
+ }
+ }
+
+ WCHAR szBuffer[128] = {0};
+ INT cchBuffer = 0;
+ if (SUCCEEDED(GetName(szBuffer, ARRAYSIZE(szBuffer))))
+ cchBuffer = lstrlenW(szBuffer);
+
+ SetRect(&partRect, paintLeft, prc->top, prc->right, prc->bottom);
+ if (ExtTextOut(hdc, partRect.left + TEXT_OFFSET_LEFT, partRect.bottom - TEXT_OFFSET_BOTTOM,
+ ETO_OPAQUE | ETO_CLIPPED, &partRect, szBuffer, cchBuffer, NULL))
+ {
+ if (SetRectRgn(rgn, partRect.left, partRect.top, partRect.right, partRect.bottom))
+ CombineRgn(backRgn, backRgn, rgn, RGN_DIFF);
+ }
+
+
+
+ COLORREF rgbLine = ColorAdjustLuma(rgbBk, -150, TRUE);
+ if (rgbLine != rgbBk)
+ {
+ RECT lineRect;
+ SetRect(&lineRect, prc->left, prc->bottom - 1, prc->right, prc->bottom);
+
+ SetBkColor(hdc, rgbLine);
+ if (ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &lineRect, NULL, 0, NULL))
+ {
+ if (SetRectRgn(rgn, lineRect.left, lineRect.top, lineRect.right, lineRect.bottom))
+ CombineRgn(backRgn, backRgn, rgn, RGN_DIFF);
+ }
+
+ SetBkColor(hdc, rgbBk);
+ }
+
+ if (0 != (flagMenuActive & flags))
+ {
+ COLORREF rgbFrame = rgbLine; //ColorAdjustLuma(GetSysColor(COLOR_HIGHLIGHT), 100, TRUE);
+ SetupGroup_DrawFrame(hdc, prc, 1, rgbFrame);
+ if (SetRectRgn(rgn, prc->left + 1, prc->top + 1, prc->right - 1, prc->bottom - 1))
+ CombineRgn(backRgn, backRgn, rgn, RGN_AND);
+ }
+
+
+ if (NULL != backRgn)
+ {
+ FillRgn(hdc, backRgn, GetBrush(hdc, state));
+ DeleteObject(backRgn);
+ }
+ if (NULL != rgn)
+ DeleteObject(rgn);
+
+
+
+ if (ODS_FOCUS == ((ODS_FOCUS | 0x0200/*ODS_NOFOCUSRECT*/) & state))
+ DrawFocusRect(hdc, prc);
+
+ if (TEXT_ALIGN != textAlign) SetTextAlign(hdc, textAlign);
+ if (origBk != rgbBk) SetBkColor(hdc, origBk);
+ if (origText != rgbText) SetTextColor(hdc, origText);
+ return TRUE;
+}
+
+INT_PTR SetupGroup::KeyToItem(SetupListbox *instance, const RECT *prcItem, INT vKey)
+{
+ switch(vKey)
+ {
+ case VK_SPACE:
+ InvertExpanded(instance);
+ return -2;
+ }
+ return -1;
+}
+BOOL SetupGroup::MouseMove(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupGroup::MouseLeave(SetupListbox *instance, const RECT *prcItem)
+{
+ return FALSE;
+}
+BOOL SetupGroup::LButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupGroup::LButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupGroup::LButtonDblClk(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ InvertExpanded(instance);
+ return TRUE;
+}
+
+BOOL SetupGroup::RButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return TRUE;
+}
+
+BOOL SetupGroup::RButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ HMENU hMenu = instance->GetContextMenu(SetupListbox::menuGroupContext);
+ if (NULL == hMenu)
+ return FALSE;
+
+ hMenu = MenuHelper_DuplcateMenu(hMenu);
+ if (NULL == hMenu) return FALSE;
+
+ MENUITEMINFO mi;
+ mi.cbSize = sizeof(MENUITEMINFO);
+ mi.fMask = MIIM_STATE;
+ GetMenuItemInfo(hMenu, ID_GROUP_TOGGLE, FALSE, &mi);
+
+ mi.fMask = 0;
+ if (0 == (MFS_DEFAULT & mi.fState))
+ {
+ mi.fMask |= MIIM_STATE;
+ mi.fState |= MFS_DEFAULT;
+ }
+
+ WCHAR szBuffer[128] = {0};
+ WASABI_API_LNGSTRINGW_BUF(((IsExpanded()) ? IDS_COLLAPSE : IDS_EXPAND), szBuffer, ARRAYSIZE(szBuffer));
+ mi.fMask |= MIIM_STRING;
+ mi.dwTypeData = szBuffer;
+
+ if (0 != mi.fMask)
+ SetMenuItemInfo(hMenu, ID_GROUP_TOGGLE, FALSE, &mi);
+
+ if (0 == list.size())
+ {
+ EnableMenuItem(hMenu, ID_GROUP_SELECTALL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(hMenu, ID_GROUP_UNSELECTALL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ }
+
+ if (0 != (flagLoading & flags))
+ {
+ EnableMenuItem(hMenu, ID_GROUP_RELOAD, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ }
+
+ MapWindowPoints(instance->GetHwnd(), HWND_DESKTOP, &pt, 1);
+
+ flags |= flagMenuActive;
+ instance->InvalidateRect(prcItem, TRUE);
+
+ INT cmd = TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, pt.x, pt.y, instance->GetHwnd(), NULL);
+ if (0 != cmd)
+ {
+ Command(instance, cmd, 0);
+ }
+
+ DestroyMenu(hMenu);
+
+ flags &= ~flagMenuActive;
+ instance->InvalidateRect(prcItem, TRUE);
+
+ return TRUE;
+}
+
+void SetupGroup::CaptureChanged(SetupListbox *instance, const RECT *prcItem, SetupListboxItem *captured)
+{
+}
+
+void SetupGroup::InvertExpanded(SetupListbox *instance)
+{
+ if (FALSE != IsExpanded())
+ ValidateSelection(instance);
+
+ SetExpanded(!IsExpanded());
+ if (NULL != instance)
+ {
+ instance->UpdateCount();
+ UpdateWindow(instance->GetHwnd());
+ }
+}
+void SetupGroup::SelectAll(SetupListbox *instance, BOOL fSelect)
+{
+ size_t index = list.size();
+
+ INT baseIndex;
+ if (NULL == instance || FALSE == instance->GetIndex(this, &baseIndex))
+ baseIndex = -1;
+ else
+ baseIndex++;
+
+ while(index--)
+ {
+ SetupRecord *record = list[index];
+ if (NULL != record && !record->IsDisabled())
+ {
+ if ((FALSE == fSelect) != (FALSE == record->IsSelected()))
+ {
+ record->SetSelected(fSelect);
+ if (0 == (flagCollapsed & flags) && NULL != instance && -1 != baseIndex)
+ {
+ instance->InvalidateItem((INT)(baseIndex + index), TRUE);
+ }
+ }
+ }
+ }
+}
+
+
+void SetupGroup::SetEmptyText(LPCWSTR pszText, BOOL fInvalidate)
+{
+ if (NULL == emptyLabel)
+ emptyLabel = SetupListboxLabel::CreateInstance(pszText);
+ else
+ emptyLabel->SetName(pszText);
+
+ if (FALSE != fInvalidate && NULL != hPage)
+ PostMessage(hPage, SPM_UPDATELIST, (WPARAM)id, NULL);
+}
+
+HWND SetupGroup::CreateDetailsView(HWND hParent)
+{
+ WCHAR szName[64] = {0};
+ if (FALSE == GetUniqueName(szName, ARRAYSIZE(szName)))
+ szName[0] = L'\0';
+
+ return SetupDetails_CreateGroupView(hParent, szName, this);
+}
+
+BOOL SetupGroup::GetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer ||
+ FAILED(StringCchPrintf(pszBuffer, cchBufferMax, L"grp_id_%d", id)))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void SetupGroup::SetLongName(LPCWSTR pszText)
+{
+ if (NULL != longName && !IS_INTRESOURCE(longName))
+ Plugin_FreeString(longName);
+ longName = NULL;
+
+ if (NULL != pszText)
+ longName = (IS_INTRESOURCE(pszText)) ? (LPWSTR)pszText : Plugin_CopyString(pszText);
+}
+
+void SetupGroup::SetDescription(LPCWSTR pszText)
+{
+ if (NULL != description && !IS_INTRESOURCE(description))
+ Plugin_FreeString(description);
+ description = NULL;
+ if (NULL != pszText)
+ description = (IS_INTRESOURCE(pszText)) ? (LPWSTR)pszText : Plugin_CopyString(pszText);
+}
+
+void SetupGroup::ValidateSelection(SetupListbox *instance)
+{
+ if (NULL == instance)
+ return;
+
+ SetupListboxItem *selection = instance->GetSelection();
+ if (NULL != selection)
+ {
+ EnterCriticalSection(&lock);
+ size_t index = list.size();
+ while(index--)
+ {
+ if (list[index] == selection)
+ {
+ instance->SetSelection(this);
+ break;
+ }
+ }
+
+ LeaveCriticalSection(&lock);
+ }
+}
+
+void SetupGroup::Command(SetupListbox *instance, INT commandId, INT eventId)
+{
+ switch(commandId)
+ {
+ case ID_GROUP_TOGGLE:
+ InvertExpanded(instance);
+ break;
+ case ID_GROUP_SELECTALL:
+ SelectAll(instance, TRUE);
+ break;
+ case ID_GROUP_UNSELECTALL:
+ SelectAll(instance, FALSE);
+ break;
+ case ID_GROUP_RELOAD:
+ ValidateSelection(instance);
+ RequestReload();
+ break;
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupGroup.h b/Src/Plugins/Library/ml_online/Setup/setupGroup.h
new file mode 100644
index 00000000..ce3f3c5f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupGroup.h
@@ -0,0 +1,131 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUP_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUP_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "./setupRecord.h"
+#include "./setupListbox.h"
+#include <vector>
+
+class SetupListboxLabel;
+class SetupLog;
+class SetupPage;
+class ifc_omstorage;
+
+class SetupGroup : public SetupListboxItem
+{
+public:
+ typedef enum
+ {
+ styleDefaultUnsubscribed = 0x00000001,
+ styleDefaultSubscribed = 0x00000002,
+ styleSortAlphabetically = 0x00000008,
+ styleSaveAll = 0x00000010,
+ } GroupStyles;
+protected:
+ typedef enum
+ {
+ flagCollapsed = 0x0001,
+ flagMenuActive = 0x0002,
+ flagLoading = 0x0004,
+ } GroupFlags;
+
+protected:
+ SetupGroup(INT groupId, LPCWSTR pszName, LPCWSTR pszAddress, const GUID *storageId, const GUID *filterId, UINT fStyle);
+ ~SetupGroup();
+
+public:
+ static SetupGroup *CreateInstance(INT groupId, LPCWSTR pszName, LPCWSTR pszAddress, const GUID *storageId, const GUID *filterId, UINT fStyle);
+
+public:
+ ULONG AddRef();
+ ULONG Release();
+
+ INT GetId() { return id; }
+ HRESULT GetName(LPWSTR pszBuffer, INT cchBufferMax);
+ HRESULT GetLongName(LPWSTR pszBuffer, INT cchBufferMax);
+ HRESULT GetDescription(LPWSTR pszBuffer, INT cchBufferMax);
+
+ size_t GetRecordCount();
+ SetupRecord *GetRecord(size_t index) { return list[index]; }
+
+ size_t GetListboxCount();
+ SetupListboxItem *GetListboxItem(size_t index);
+
+ BOOL IsModified();
+
+ BOOL IsExpanded();
+ void SetExpanded(BOOL fExpanded);
+ void SelectAll(SetupListbox *instance, BOOL fSelect);
+
+ HRESULT RequestReload();
+ HRESULT Save(SetupLog *log);
+
+
+ void SetEmptyText(LPCWSTR pszText, BOOL fInvalidate);
+ void SetLongName(LPCWSTR pszText);
+ void SetDescription(LPCWSTR pszText);
+
+ void GetColors(HDC hdc, UINT state, COLORREF *rgbBkOut, COLORREF *rgbTextOut);
+ HBRUSH GetBrush(HDC hdc, UINT state);
+
+ HRESULT SignalLoadCompleted(HANDLE event);
+ void ValidateSelection(SetupListbox *instance);
+
+ /* SetupListboxItem */
+ BOOL MeasureItem(SetupListbox *instance, UINT *cx, UINT *cy);
+ BOOL DrawItem(SetupListbox *instance, HDC hdc, const RECT *prc, UINT state);
+ INT_PTR KeyToItem(SetupListbox *instance, const RECT *prcItem, INT vKey);
+ BOOL MouseMove(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL MouseLeave(SetupListbox *instance, const RECT *prcItem);
+ BOOL LButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL LButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL LButtonDblClk(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL RButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL RButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ void CaptureChanged(SetupListbox *instance, const RECT *prcItem, SetupListboxItem *captured);
+ BOOL IsDisabled() { return FALSE; }
+ void Command(SetupListbox *instance, INT commandId, INT eventId);
+ HWND CreateDetailsView(HWND hParent);
+ BOOL GetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax);
+
+ void SetError(HRESULT code) { errorCode = code; }
+ HRESULT GetError() { return errorCode; }
+
+ void Clear(BOOL fInvalidate);
+
+ void SetPageWnd(HWND hPage);
+
+protected:
+ void InvertExpanded(SetupListbox *instance);
+ void OnLoadCompleted();
+
+
+private:
+ friend static void CALLBACK SetupGroup_LoadCallback(ifc_omstorageasync *result);
+
+
+protected:
+ ULONG ref;
+ INT id;
+ LPWSTR name;
+ LPWSTR longName;
+ LPWSTR description;
+ UINT style;
+ UINT flags;
+ LPWSTR address;
+ GUID storageId;
+ GUID filterId;
+ HRESULT errorCode;
+ std::vector<SetupRecord*> list;
+ SetupListboxLabel *emptyLabel;
+ CRITICAL_SECTION lock;
+ ifc_omstorageasync *loadResult;
+ HWND hPage;
+ HANDLE loadComplete;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUP_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupGroupFilter.cpp b/Src/Plugins/Library/ml_online/Setup/setupGroupFilter.cpp
new file mode 100644
index 00000000..c4d4311f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupGroupFilter.cpp
@@ -0,0 +1,226 @@
+#include "./setupGroupFilter.h"
+#include "../api__ml_online.h"
+#include "../serviceHost.h"
+#include "../config.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omstorage.h>
+#include <ifc_omserviceenum.h>
+#include <ifc_omfilestorage.h>
+
+
+SetupGroupFilter::SetupGroupFilter(const GUID *filterId)
+ : ref(1)
+{
+ id = (NULL != filterId) ? *filterId : GUID_NULL;
+}
+
+SetupGroupFilter::~SetupGroupFilter()
+{
+}
+
+HRESULT SetupGroupFilter::CreateInstance(const GUID *filterId, SetupGroupFilter **instance)
+{
+ if (NULL == filterId)
+ {
+ *instance = NULL;
+ return E_INVALIDARG;
+ }
+
+ if (FALSE != IsEqualGUID(*filterId, FUID_SetupFeaturedGroupFilter))
+ return SetupFeaturedGroupFilter::CreateInstance((SetupFeaturedGroupFilter**)instance);
+ if (FALSE != IsEqualGUID(*filterId, FUID_SetupKnownGroupFilter))
+ return SetupKnownGroupFilter::CreateInstance((SetupKnownGroupFilter**)instance);
+
+ *instance = NULL;
+ return E_NOINTERFACE;
+}
+
+ULONG SetupGroupFilter::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+ULONG SetupGroupFilter::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+ return r;
+}
+
+HRESULT SetupGroupFilter::GetId(GUID *filterId)
+{
+ if (NULL == filterId)
+ return E_POINTER;
+ *filterId = id;
+ return S_OK;
+}
+
+BOOL CALLBACK SetupGroupFilter::AppendServiceIdCallback(UINT serviceId, void *data)
+{
+ ServiceIdList *list = (ServiceIdList*)data;
+ if (NULL == list) return FALSE;
+ list->push_back(serviceId);
+ return TRUE;
+}
+
+SetupFeaturedGroupFilter::SetupFeaturedGroupFilter()
+ : SetupGroupFilter(&FUID_SetupFeaturedGroupFilter)
+{
+}
+
+SetupFeaturedGroupFilter::~SetupFeaturedGroupFilter()
+{
+}
+
+HRESULT SetupFeaturedGroupFilter::CreateInstance(SetupFeaturedGroupFilter **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+ *instance = new SetupFeaturedGroupFilter();
+ if (NULL == *instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+HRESULT SetupFeaturedGroupFilter::Initialize()
+{
+ HRESULT hr;
+ ifc_omstorage *storage;
+
+ filterList.clear();
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ hr = OMSERVICEMNGR->QueryStorage(&SUID_OmStorageIni, &storage);
+ if(FAILED(hr))
+ return hr;
+
+ ServiceHost *serviceHost;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ ifc_omserviceenum *serviceEnum;
+ hr = storage->Load(L"*.ini", serviceHost, &serviceEnum);
+ if(SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ while(S_OK == serviceEnum->Next(1, &service, NULL) && NULL != service)
+ {
+ filterList.push_back(service->GetId());
+ }
+ serviceEnum->Release();
+ }
+ storage->Release();
+
+ ServiceIdList promoList;
+ Config_ReadServiceIdList("Setup", "featuredExtra", ',', AppendServiceIdCallback, &promoList);
+
+ ServiceIdList historyList;
+ Config_ReadServiceIdList("Setup", "featuredHistory", ',', AppendServiceIdCallback, &historyList);
+
+ size_t index = historyList.size();
+ size_t filterIndex, filterSize = filterList.size();
+
+ while(index--)
+ {
+ size_t promoSize = promoList.size();
+ for(filterIndex = 0; filterIndex < promoSize; filterIndex++)
+ {
+ if (promoList[filterIndex] == historyList[index])
+ {
+ promoList.erase(promoList.begin() + filterIndex);
+ break;
+ }
+ }
+ if (filterIndex == promoSize)
+ {
+ for(filterIndex = 0; filterIndex < filterSize; filterIndex++)
+ {
+ if (filterList[filterIndex] == historyList[index])
+ break;
+ }
+
+ if (filterIndex == filterSize)
+ filterList.push_back(historyList[index]);
+ }
+ }
+
+ return hr;
+}
+
+HRESULT SetupFeaturedGroupFilter::ProcessService(ifc_omservice *service, UINT *filterResult)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == filterResult)
+ return E_POINTER;
+
+ size_t index = filterList.size();
+ while(index--)
+ {
+ if (filterList[index] == service->GetId())
+ {
+ filterList.erase(filterList.begin() + index);
+ *filterResult = serviceIgnore;
+ break;
+ }
+ }
+ return S_OK;
+}
+
+SetupKnownGroupFilter::SetupKnownGroupFilter()
+ : SetupGroupFilter(&FUID_SetupKnownGroupFilter)
+{
+}
+
+SetupKnownGroupFilter::~SetupKnownGroupFilter()
+{
+}
+
+HRESULT SetupKnownGroupFilter::CreateInstance(SetupKnownGroupFilter **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+ *instance = new SetupKnownGroupFilter();
+ if (NULL == *instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+HRESULT SetupKnownGroupFilter::Initialize()
+{
+ Config_ReadServiceIdList("Setup", "featuredExtra", ',', AppendServiceIdCallback, &filterList);
+ return S_OK;
+}
+
+HRESULT SetupKnownGroupFilter::ProcessService(ifc_omservice *service, UINT *filterResult)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == filterResult)
+ return E_POINTER;
+
+ size_t index = filterList.size();
+ while(index--)
+ {
+ if (filterList[index] == service->GetId())
+ {
+ filterList.erase(filterList.begin() + index);
+ *filterResult &= ~serviceForceUnsubscribe;
+ *filterResult |= serviceForceSubscribe;
+ break;
+ }
+ }
+
+ return S_OK;
+}
diff --git a/Src/Plugins/Library/ml_online/Setup/setupGroupList.cpp b/Src/Plugins/Library/ml_online/Setup/setupGroupList.cpp
new file mode 100644
index 00000000..9ee1de1f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupGroupList.cpp
@@ -0,0 +1,190 @@
+#include "./setupGroupList.h"
+#include "../api__ml_online.h"
+#include <strsafe.h>
+
+SetupGroupList::SetupGroupList()
+ : ref(1)
+{
+}
+
+SetupGroupList::~SetupGroupList()
+{
+ size_t index = list.size();
+ while(index--)
+ {
+ list[index]->Release();
+ }
+}
+
+SetupGroupList *SetupGroupList::CreateInstance()
+{
+ return new SetupGroupList();
+}
+
+ULONG SetupGroupList::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+ULONG SetupGroupList::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+ return r;
+}
+
+BOOL SetupGroupList::AddGroup(SetupGroup *group)
+{
+ if (NULL == group) return FALSE;
+ list.push_back(group);
+ group->AddRef();
+ return TRUE;
+}
+size_t SetupGroupList::GetGroupCount()
+{
+ return list.size();
+}
+
+BOOL SetupGroupList::IsModified()
+{
+ size_t index = list.size();
+ while(index--)
+ {
+ if (list[index]->IsModified())
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL SetupGroupList::FindGroupIndex(SetupGroup *group, size_t *groupIndex)
+{
+ if (NULL == group) return FALSE;
+
+ size_t index = list.size();
+ while(index--)
+ {
+ if (list[index] == group)
+ {
+ if (NULL != groupIndex)
+ *groupIndex = index;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+}
+
+HRESULT SetupGroupList::FindGroupById(UINT groupId, SetupGroup **group)
+{
+ if (NULL == group) return E_POINTER;
+
+ size_t index = list.size();
+ while(index--)
+ {
+ if (list[index]->GetId() == groupId)
+ {
+ *group = list[index];
+ (*group)->AddRef();
+ return S_OK;
+ }
+ }
+ return S_FALSE;
+}
+
+size_t SetupGroupList::GetListboxCount()
+{
+ size_t recordCount = list.size();
+ size_t index = recordCount;
+ while(index--)
+ {
+ recordCount += list[index]->GetListboxCount();
+ }
+ return recordCount;
+}
+
+HRESULT SetupGroupList::Save(SetupLog *log)
+{
+ HRESULT hr(S_OK);
+ size_t index = list.size();
+ while(index--)
+ {
+ if (FAILED(list[index]->Save(log)))
+ hr = E_FAIL;
+ }
+ return hr;
+}
+
+HRESULT SetupGroupList::FindListboxItem(size_t listboxId, SetupListboxItem **listboxItem)
+{
+ if (NULL == listboxItem) return E_POINTER;
+
+ size_t index = 0;
+ size_t groupCount = list.size();
+
+ SetupGroup *group;
+ for (size_t i = 0; i < groupCount; i++)
+ {
+ group = list[i];
+ if (index == listboxId)
+ {
+ *listboxItem = (SetupListboxItem*)group;
+ return S_OK;
+ }
+ index++;
+
+ size_t itemCount;
+ if (0 != (itemCount = group->GetListboxCount()))
+ {
+ if (listboxId < (index + itemCount))
+ {
+ size_t itemIndex = (listboxId - index);
+ *listboxItem = group->GetListboxItem(itemIndex);
+ return S_OK;
+ }
+ index += itemCount;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+INT SetupGroupList::GetListboxItem(SetupListboxItem *item)
+{
+ if (NULL == item) return LB_ERR;
+ size_t index = 0;
+ size_t groupCount = list.size();
+ SetupGroup *group;
+ SetupListboxItem *groupItem;
+
+ for (size_t i = 0; i < groupCount; i++)
+ {
+ group = list[i];
+ if (item == group)
+ return (INT)index;
+
+ index++;
+ size_t itemCount = group->GetListboxCount();
+ for (size_t j = 0; j < itemCount; j++)
+ {
+ groupItem = group->GetListboxItem(j);
+ if (groupItem == item)
+ return (INT)index;
+ index++;
+ }
+ }
+ return LB_ERR;
+}
+
+void SetupGroupList::SetPageWnd(HWND hPage)
+{
+ size_t index = list.size();
+ while(index--)
+ {
+ list[index]->SetPageWnd(hPage);
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupGroupList.h b/Src/Plugins/Library/ml_online/Setup/setupGroupList.h
new file mode 100644
index 00000000..0b4445cb
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupGroupList.h
@@ -0,0 +1,52 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUPLIST_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUPLIST_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "./setupGroup.h"
+#include <vector>
+
+class SetupListboxItem;
+class SetupLog;
+
+class SetupGroupList
+{
+
+protected:
+ SetupGroupList();
+ ~SetupGroupList();
+
+public:
+ static SetupGroupList *CreateInstance();
+
+public:
+ ULONG AddRef();
+ ULONG Release();
+
+ BOOL AddGroup(SetupGroup *group);
+ SetupGroup *GetGroup(size_t index) { return list[index]; }
+ size_t GetGroupCount();
+ BOOL FindGroupIndex(SetupGroup *group, size_t *groupIndex);
+ HRESULT FindGroupById(UINT groupId, SetupGroup **group);
+
+
+ BOOL IsModified();
+
+ HRESULT Save(SetupLog *log);
+
+ size_t GetListboxCount();
+ INT GetListboxItem(SetupListboxItem *item);
+ HRESULT FindListboxItem(size_t listboxId, SetupListboxItem **listboxItem);
+
+ void SetPageWnd(HWND hPage);
+
+
+protected:
+ ULONG ref;
+ std::vector<SetupGroup*> list;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPGROUPLIST_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupImage.cpp b/Src/Plugins/Library/ml_online/Setup/setupImage.cpp
new file mode 100644
index 00000000..46979043
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupImage.cpp
@@ -0,0 +1,282 @@
+#include "./setupImage.h"
+#include "../api__ml_online.h"
+
+#include <shlwapi.h>
+
+static BOOL SetupImage_CopyImage(HDC hdc, HBITMAP bitmapDst, INT x, INT y, INT cx, INT cy, HBITMAP bitmapSrc, INT srcX, INT srcY)
+{
+ BOOL resultOk = FALSE;
+ HDC hdcDst = CreateCompatibleDC(hdc);
+ HDC hdcSrc = CreateCompatibleDC(hdc);
+
+ if (NULL != hdcDst && NULL != hdcSrc)
+ {
+ HBITMAP bitmapDstOrig = (HBITMAP)SelectObject(hdcDst, bitmapDst);
+ HBITMAP bitmapSrcOrig = (HBITMAP)SelectObject(hdcSrc, bitmapSrc);
+
+ resultOk = BitBlt(hdcDst, x, y, cx, cy, hdcSrc, srcX, srcY, SRCCOPY);
+
+ SelectObject(hdcDst, bitmapDstOrig);
+ SelectObject(hdcSrc, bitmapSrcOrig);
+ }
+
+ if (NULL != hdcDst) DeleteDC(hdcDst);
+ if (NULL != hdcSrc) DeleteDC(hdcSrc);
+
+ return resultOk;
+}
+
+static BOOL SetupImage_ColorizeImage(BYTE *pPixels, LONG x, LONG y, LONG cx, LONG cy, WORD bpp, LONG dstX, LONG dstY, COLORREF rgbBk, COLORREF rgbFg, BOOL removeAlpha)
+{
+ LONG pitch;
+ INT step;
+ BYTE rFg, gFg, bFg;
+ LPBYTE srcCursor, srcLine;
+ LPBYTE dstLine;
+
+ if (bpp < 24) return FALSE;
+
+ step = (bpp>>3);
+ pitch = cx*step;
+ while (pitch%4) pitch++;
+
+ rFg = GetRValue(rgbFg); gFg = GetGValue(rgbFg); bFg = GetBValue(rgbFg);
+
+ INT bK = (bFg - GetBValue(rgbBk));
+ INT gK = (gFg - GetGValue(rgbBk));
+ INT rK = (rFg - GetRValue(rgbBk));
+
+ srcLine = pPixels + pitch * y + x*step;
+ dstLine = pPixels + pitch * dstY + dstX*step;
+
+ if (24 == bpp)
+ {
+ for (; cy-- != 0; srcLine += pitch, dstLine += pitch )
+ {
+ LONG i;
+ LPBYTE dstCursor;
+ for (i = cx, srcCursor = srcLine, dstCursor = dstLine ; i-- != 0; srcCursor += 3, dstCursor +=3)
+ {
+ dstCursor[0] = bFg - (bK*(255 - srcCursor[0])>>8);
+ dstCursor[1] = gFg - (gK*(255 - srcCursor[1])>>8);
+ dstCursor[2] = rFg - (rK*(255 - srcCursor[2])>>8);
+ }
+ }
+ }
+ else
+ {
+ // nothing for now
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+SetupImage::SetupImage(HDC hdc, HBITMAP bitmapSource, INT maxColors)
+ : ref(1), bitmap(NULL), pixels(NULL),
+ table(NULL), tableSize(0), tableCount(0), insertCursor(0), readCursor(0)
+{
+ ZeroMemory(&header, sizeof(BITMAPINFOHEADER));
+
+ BITMAP bm;
+ if (sizeof(BITMAP) != GetObject(bitmapSource, sizeof(BITMAP), &bm))
+ return;
+
+ if (bm.bmHeight < 0)
+ bm.bmHeight = -bm.bmHeight;
+
+ header.biSize = sizeof(BITMAPINFOHEADER);
+ header.biCompression = BI_RGB;
+ header.biBitCount = 24;
+ header.biPlanes = 1;
+ header.biWidth = bm.bmWidth;
+ header.biHeight = -(bm.bmHeight * (maxColors + 1));
+
+ bitmap = CreateDIBSection(hdc, (LPBITMAPINFO)&header, DIB_RGB_COLORS, (void**)&pixels, NULL, 0);
+ if (NULL == bitmap)
+ return;
+
+ if (FALSE == SetupImage_CopyImage(hdc, bitmap, 0, 0, bm.bmWidth, bm.bmHeight, bitmapSource, 0, 0))
+ {
+ DeleteObject(bitmap);
+ bitmap = NULL;
+ return;
+ }
+
+ tableSize = maxColors;
+ table = (IMAGEINDEX*)calloc(tableSize, sizeof(IMAGEINDEX));
+ if (NULL == table)
+ {
+ DeleteObject(bitmap);
+ bitmap = NULL;
+ return;
+ }
+}
+
+SetupImage::~SetupImage()
+{
+ if (NULL != bitmap)
+ {
+ DeleteObject(bitmap);
+ bitmap = NULL;
+ }
+
+ if (NULL != table)
+ {
+ free(table);
+ table = NULL;
+ }
+}
+
+SetupImage *SetupImage::CreateInstance(HDC hdc, HBITMAP bitmapSource, INT maxColors)
+{
+ if (NULL == bitmapSource || maxColors < 1 || maxColors > 120)
+ return NULL;
+
+ SetupImage *instance = new SetupImage(hdc, bitmapSource, maxColors);
+ if (NULL == instance) return NULL;
+ if (NULL == instance->bitmap || NULL == instance->table)
+ {
+ instance->Release();
+ instance = NULL;
+ }
+ return instance;
+}
+
+SetupImage *SetupImage::CreateFromPluginBitmap(HDC hdc, LPCWSTR pszModuleName, LPCWSTR resourceName, INT maxColors)
+{
+ SetupImage *instance = NULL;
+ WCHAR szPath[MAX_PATH] = {0};
+
+ if (0 != GetModuleFileName(WASABI_API_ORIG_HINST, szPath, ARRAYSIZE(szPath)))
+ {
+ PathRemoveFileSpec(szPath);
+ PathAppend(szPath, pszModuleName);
+ HMODULE hModule = LoadLibraryEx(szPath, NULL, LOAD_LIBRARY_AS_DATAFILE | 0x00000020/*LOAD_LIBRARY_AS_IMAGE_RESOURCE*/);
+ if (NULL != hModule)
+ {
+ HBITMAP bitmapSource = (HBITMAP)LoadImage(hModule, resourceName, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
+ if (NULL != bitmapSource)
+ instance = CreateInstance(hdc, bitmapSource, maxColors);
+ FreeLibrary(hModule);
+ }
+ }
+ return instance;
+}
+
+ULONG SetupImage::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+ULONG SetupImage::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+BOOL SetupImage::GetSize(SIZE *pSize)
+{
+ if (NULL == pSize) return FALSE;
+
+ INT cy = header.biHeight;
+ if (cy < 0) cy = -cy;
+
+ pSize->cx = header.biWidth;
+ pSize->cy = cy / (tableSize + 1);
+ return TRUE;
+}
+
+BOOL SetupImage::DrawImage(HDC hdc, INT x, INT y, INT cx, INT cy, INT srcX, INT srcY, COLORREF rgbBk, COLORREF rgbFg)
+{
+ BYTE bitmapIndex = 0xFF;
+
+ SIZE imageSize;
+ if (!GetSize(&imageSize))
+ return FALSE;
+
+ for(BYTE i = readCursor; i < tableCount; i++)
+ {
+ if (table[i].rgbBk == rgbBk && table[i].rgbFg == rgbFg)
+ {
+ bitmapIndex = i;
+ break;
+ }
+ }
+
+ if (0xFF == bitmapIndex)
+ {
+ if (readCursor > tableCount)
+ readCursor = tableCount;
+ for(BYTE i = 0; i < readCursor; i++)
+ {
+ if (table[i].rgbBk == rgbBk && table[i].rgbFg == rgbFg)
+ {
+ bitmapIndex = i;
+ break;
+ }
+ }
+
+ if (0xFF == bitmapIndex)
+ {
+ if (tableCount < tableSize)
+ {
+ insertCursor = tableCount;
+ tableCount++;
+ }
+ else if (++insertCursor == tableCount)
+ insertCursor = 0;
+
+ INT targetY = (insertCursor + 1) * imageSize.cy;
+
+ if (!SetupImage_ColorizeImage(pixels, 0, 0, imageSize.cx, imageSize.cy,
+ header.biBitCount, 0, targetY, rgbBk, rgbFg, TRUE))
+ {
+ return FALSE;
+ }
+ table[insertCursor].rgbBk = rgbBk;
+ table[insertCursor].rgbFg = rgbFg;
+ bitmapIndex = insertCursor;
+
+ }
+ }
+
+ readCursor = bitmapIndex;
+ srcY += ((bitmapIndex + 1) * imageSize.cy);
+
+ INT dstY = y;
+ INT dstCY = cy;
+
+ INT imageHeight = header.biHeight;
+ if (imageHeight < 0)
+ {
+ header.biHeight = -imageHeight;
+ dstY += (cy - 1);
+ dstCY = -cy;
+ }
+
+ BOOL resultOk = StretchDIBits(hdc, x, dstY, cx, dstCY, srcX, srcY, cx, cy,
+ pixels, (BITMAPINFO*)&header, DIB_RGB_COLORS, SRCCOPY);
+
+ if (imageHeight < 0)
+ header.biHeight = imageHeight;
+
+ return resultOk;
+}
+
+BOOL SetupImage::ResetCache()
+{
+ if (NULL != table)
+ ZeroMemory(&table, sizeof(IMAGEINDEX) * tableSize);
+ tableCount = 0;
+ insertCursor = 0;
+ readCursor = 0;
+ return TRUE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupImage.h b/Src/Plugins/Library/ml_online/Setup/setupImage.h
new file mode 100644
index 00000000..893e2c93
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupImage.h
@@ -0,0 +1,50 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPIMAGE_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPIMAGE_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+class SetupImage
+{
+private:
+ typedef struct __IMAGEINDEX
+ {
+ COLORREF rgbBk;
+ COLORREF rgbFg;
+ } IMAGEINDEX;
+
+protected:
+ SetupImage(HDC hdc, HBITMAP bitmapSource, INT maxColors);
+ ~SetupImage();
+
+public:
+ static SetupImage *CreateInstance(HDC hdc, HBITMAP bitmapSource, INT maxColors);
+ static SetupImage *CreateFromPluginBitmap(HDC hdc, LPCWSTR pszModuleName, LPCWSTR resourceName, INT maxColors);
+
+public:
+ ULONG AddRef();
+ ULONG Release();
+
+ BOOL GetSize(SIZE *pSize);
+ BOOL DrawImage(HDC hdc, INT x, INT y, INT cx, INT cy, INT srcX, INT srcY, COLORREF rgbBk, COLORREF rgbFg);
+
+ BOOL ResetCache();
+
+private:
+ ULONG ref;
+ HBITMAP bitmap;
+ BYTE *pixels;
+ BITMAPINFOHEADER header;
+
+
+ IMAGEINDEX *table;
+ BYTE tableSize;
+ BYTE tableCount;
+ BYTE insertCursor;
+ BYTE readCursor;
+};
+
+#endif // NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPIMAGE_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupListbox.cpp b/Src/Plugins/Library/ml_online/Setup/setupListbox.cpp
new file mode 100644
index 00000000..1035c699
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupListbox.cpp
@@ -0,0 +1,878 @@
+#include "./setupPage.h"
+#include "./setupListbox.h"
+#include "./setupGroupList.h"
+#include "./setupImage.h"
+#include "../common.h"
+#include "../resource.h"
+#include "../api__ml_online.h"
+
+#include "../../nu/windowsTheme.h"
+
+#include <vssym32.h>
+
+//#include <tmschema.h>
+
+static ATOM SERVICELIST_PROP = 0;
+
+#define SLF_UNICODE 0x0001
+#define SLF_DRAGMOVE 0x0002
+
+typedef BOOL (SetupListboxItem::*ITEMMOUSEPROC)(SetupListbox*, const RECT*, UINT, POINT);
+
+class Listbox : public SetupListbox
+{
+
+protected:
+ Listbox(HWND hListbox, SetupGroupList *groupList);
+ ~Listbox();
+
+public:
+ static HRESULT AttachToWindow(HWND hwndListbox, SetupGroupList *groupList, SetupListbox **pInstance);
+
+public:
+ HWND GetHwnd() { return hwnd; }
+ HFONT GetFont() { return (HFONT)::SendMessage(hwnd, WM_GETFONT, 0, 0L); }
+ LRESULT CallDefaultProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ LRESULT CallPrevProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ BOOL MeasureItem(INT itemId, UINT *cx, UINT *cy);
+ BOOL DrawItem(HDC hdc, const RECT *itemRect, INT itemId, UINT itemState, UINT itemAction);
+ INT_PTR KeyToItem(INT vKey, INT caretPos);
+ INT_PTR CharToItem(INT vKey, INT caretPos);
+
+ BOOL DrawCheckbox(HDC hdc, BOOL checked, UINT state, const RECT *pRect, const RECT *pClipRect);
+ BOOL GetCheckboxMetrics(HDC hdc, BOOL checked, UINT state, SIZE *pSize);
+
+ INT GetCheckboxThemeState(BOOL checked, UINT state);
+ INT HitTest(POINT pt, RECT *prcItem);
+
+ void SetCapture(SetupListboxItem *item);
+ SetupListboxItem *GetCapture();
+ void ReleaseCapture();
+ BOOL InvalidateRect(const RECT *prcInvalidate, BOOL fErase);
+ BOOL InvalidateItem(INT itemId, BOOL fErase);
+ void UpdateCount();
+
+ BOOL DrawExpandbox(HDC hdc, BOOL fExpanded, const RECT *pRect, COLORREF rgbBk, COLORREF rgbFg);
+ BOOL GetExpandboxMetrics(HDC hdc, BOOL fExpanded, SIZE *pSize);
+
+ INT GetPageCount();
+ INT GetNextEnabledItem(INT iItem, SetupListboxItem **itemOut);
+ INT GetPrevEnabledItem(INT iItem, SetupListboxItem **itemOut);
+
+ SetupListboxItem *GetSelection();
+ BOOL SetSelection(SetupListboxItem *item);
+ BOOL GetIndex(SetupListboxItem *item, INT *iItem);
+
+ BOOL DoDragAndDrop(UINT mouseEvent, UINT mouseFlags, POINT pt);
+ HMENU GetContextMenu(UINT menuId);
+
+protected:
+ friend static LRESULT WINAPI Listbox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+ LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ void OnDestroy();
+ void GetItemRect(INT itemId, RECT *prcItem);
+
+ SetupImage *GetExpandboxImage(HDC hdc, BOOL fExpanded);
+
+ void OnMouseEvent(UINT mouseEvent, UINT mouseFlags, POINTS pts, BOOL fDefaultHandler, ITEMMOUSEPROC proc);
+ void OnMouseLeave();
+ void OnEraseBkGround(HDC hdc);
+ void OnCaptureChanged(HWND hwndGained);
+ void NotifyReleaseCapture(INT itemId, SetupListboxItem *itemGain);
+ void OnCommand(INT commandId, INT eventId, HWND hControl);
+
+protected:
+ HWND hwnd;
+ UINT flags;
+ WNDPROC originalProc;
+ UXTHEME buttonTheme;
+ SetupGroupList *groups;
+ INT mouseoverId;
+ INT capturedId;
+ SetupImage *expandedImage;
+ SetupImage *collapsedImage;
+};
+
+#define GetList(__hwnd) ((Listbox*)GetPropW((__hwnd), MAKEINTATOM(SERVICELIST_PROP)))
+
+
+
+
+HRESULT SetupListbox::CreateInstance(HWND hListbox, SetupGroupList *groupList, SetupListbox **pInstance)
+{
+ return Listbox::AttachToWindow(hListbox, groupList, pInstance);
+}
+
+SetupListbox *SetupListbox::GetInstance(HWND hListbox)
+{
+ return GetList(hListbox);
+}
+
+Listbox::Listbox(HWND hListbox, SetupGroupList *groupList)
+ : hwnd(hListbox), flags(0), originalProc(NULL), buttonTheme(NULL), groups(groupList),
+ mouseoverId(LB_ERR), capturedId(LB_ERR), expandedImage(NULL), collapsedImage(NULL)
+{
+ if (IsWindowUnicode(hwnd))
+ flags |= SLF_UNICODE;
+
+ buttonTheme = (UxIsAppThemed()) ? UxOpenThemeData(hwnd, L"Button") : NULL;
+
+ groups->AddRef();
+ UpdateCount();
+
+}
+
+Listbox::~Listbox()
+{
+ if (NULL != hwnd)
+ {
+ RemoveProp(hwnd, MAKEINTATOM(SERVICELIST_PROP));
+ if (NULL != originalProc)
+ {
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)originalProc);
+ CallPrevProc(WM_DESTROY, 0, 0L);
+ }
+ }
+
+ if (NULL != groups)
+ groups->Release();
+
+ if (NULL != buttonTheme)
+ UxCloseThemeData(buttonTheme);
+
+ if (NULL != expandedImage)
+ expandedImage->Release();
+
+ if (NULL != collapsedImage)
+ collapsedImage->Release();
+}
+
+HRESULT Listbox::AttachToWindow(HWND hListbox, SetupGroupList *groupList, SetupListbox **pInstance)
+{
+ if (0 == SERVICELIST_PROP)
+ {
+ SERVICELIST_PROP = GlobalAddAtom(TEXT("omSetupListbox"));
+ if (0 == SERVICELIST_PROP) return E_UNEXPECTED;
+ }
+
+
+ if(NULL == hListbox || !IsWindow(hListbox) || NULL == groupList)
+ return E_INVALIDARG;
+
+ Listbox *list = new Listbox(hListbox, groupList);
+ if (NULL == list)
+ return E_OUTOFMEMORY;
+
+ list->originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtr(hListbox, GWLP_WNDPROC,
+ (LONGX86)(LONG_PTR)Listbox_WindowProc);
+
+ if (NULL == list->originalProc || !SetProp(hListbox, MAKEINTATOM(SERVICELIST_PROP), list))
+ {
+ if (NULL != list->originalProc)
+ SetWindowLongPtr(hListbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)list->originalProc);
+ delete(list);
+ return E_FAIL;
+ }
+
+
+ if (NULL != pInstance)
+ *pInstance = list;
+
+ return S_OK;
+}
+
+LRESULT Listbox::CallDefaultProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ return (0 != (SLF_UNICODE & flags)) ?
+ DefWindowProcW(hwnd, uMsg, wParam, lParam) :
+ DefWindowProcA(hwnd, uMsg, wParam, lParam);
+}
+
+LRESULT Listbox::CallPrevProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ return (0 != (SLF_UNICODE & flags)) ?
+ CallWindowProcW(originalProc, hwnd, uMsg, wParam, lParam) :
+ CallWindowProcA(originalProc, hwnd, uMsg, wParam, lParam);
+}
+
+
+void Listbox::UpdateCount()
+{
+ SendMessage(hwnd, WM_SETREDRAW, FALSE, 0L);
+
+ INT iSelected = (INT)SendMessage(hwnd, LB_GETCURSEL, 0, 0L);
+
+ size_t recordCount = (NULL != groups) ? groups->GetListboxCount() : 0;
+ SendMessage(hwnd, LB_SETCOUNT, (WPARAM)recordCount, 0L);
+
+ SetupListboxItem *item;
+ UINT cy, maxCY = 0;
+ for (size_t i = 0; i < recordCount; i++)
+ {
+ if (SUCCEEDED(groups->FindListboxItem(i, &item)) &&
+ item->MeasureItem(this, NULL, &cy) && cy > maxCY)
+ {
+ maxCY = cy;
+ }
+ }
+
+ SendMessage(hwnd, LB_SETITEMHEIGHT, (WPARAM)0, (LPARAM)maxCY);
+
+ if (recordCount > 0)
+ {
+ if (iSelected < 0)
+ iSelected = 0;
+
+ if ((size_t)iSelected >= recordCount)
+ iSelected = (INT)(recordCount - 1);
+
+ SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)iSelected, TRUE);
+ SendMessage(hwnd, LB_SETANCHORINDEX, (WPARAM)iSelected, 0L);
+ SendMessage(hwnd, LB_SETCURSEL, (WPARAM)iSelected, 0L);
+ }
+
+ SendMessage(hwnd, WM_SETREDRAW, TRUE, 0L);
+}
+
+BOOL Listbox::MeasureItem(INT itemId, UINT *cx, UINT *cy)
+{
+ SetupListboxItem *item;
+ HRESULT hr = groups->FindListboxItem(itemId, &item);
+ return (SUCCEEDED(hr)) ? item->MeasureItem(this, cx, cy) : FALSE;
+}
+
+BOOL Listbox::DrawItem(HDC hdc, const RECT *prcItem, INT itemId, UINT itemState, UINT itemAction)
+{
+ SetupListboxItem *item;
+ HRESULT hr = groups->FindListboxItem(itemId, &item);
+ if (FAILED(hr)) return FALSE;
+
+ if (0 != (ODS_SELECTED & itemState) && GetFocus() != hwnd)
+ itemState |= ODS_INACTIVE;
+
+ if (item->IsDisabled())
+ itemState |= ODS_DISABLED;
+
+ return item->DrawItem(this, hdc, prcItem, itemState);
+}
+
+INT Listbox::GetPageCount()
+{
+ RECT clientRect;
+ if (NULL == hwnd || !GetClientRect(hwnd, &clientRect))
+ return 0;
+
+ INT itemHeight = (INT)SendMessage(hwnd, LB_GETITEMHEIGHT, 0, 0L);
+ return (clientRect.bottom - clientRect.top) / itemHeight;
+}
+
+INT Listbox::GetNextEnabledItem(INT iItem, SetupListboxItem **itemOut)
+{
+ INT iLast = (INT)SendMessage(hwnd, LB_GETCOUNT, 0, 0L);
+ if (iLast >= 0) iLast--;
+
+ SetupListboxItem *testItem;
+ while(iItem++ < iLast)
+ {
+ if (SUCCEEDED(groups->FindListboxItem(iItem, &testItem)))
+ {
+ if (FALSE == testItem->IsDisabled()) break;
+ }
+ }
+ if (NULL != itemOut)
+ {
+ *itemOut = (iItem <= iLast) ? testItem : NULL;
+ }
+ return (iItem <= iLast) ? iItem : LB_ERR;
+}
+
+INT Listbox::GetPrevEnabledItem(INT iItem, SetupListboxItem **itemOut)
+{
+ SetupListboxItem *testItem;
+ while(iItem-- > 0)
+ {
+ if (SUCCEEDED(groups->FindListboxItem(iItem, &testItem)))
+ {
+ if (FALSE == testItem->IsDisabled()) break;
+ }
+ }
+
+ if (NULL != itemOut)
+ {
+ *itemOut = (iItem >= 0) ? testItem : NULL;
+ }
+ return (iItem >= 0) ? iItem : LB_ERR;
+}
+
+SetupListboxItem *Listbox::GetSelection()
+{
+ INT iSelected = (INT)SendMessage(hwnd, LB_GETCURSEL, 0, 0L);
+ if (LB_ERR == iSelected)
+ return NULL;
+
+ SetupListboxItem *item;
+ return (SUCCEEDED(groups->FindListboxItem(iSelected, &item))) ? item : NULL;
+}
+
+BOOL Listbox::SetSelection(SetupListboxItem *item)
+{
+ INT iItem = LB_ERR;
+ if (NULL != item)
+ {
+ iItem = groups->GetListboxItem(item);
+ if (LB_ERR == iItem)
+ return FALSE;
+ }
+
+ BOOL resultOk = (LB_ERR != SendMessage(hwnd, LB_SETCURSEL, (WPARAM)iItem, 0L));
+ if (LB_ERR == iItem) resultOk = TRUE;
+
+ if (LB_ERR != iItem)
+ {
+ HWND hParent = GetParent(hwnd);
+ if (NULL != hParent)
+ SendMessage(hParent, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), LBN_SELCHANGE), (LPARAM)hwnd);
+ }
+ return resultOk;
+}
+
+BOOL Listbox::GetIndex(SetupListboxItem *item, INT *iItem)
+{
+ if (NULL == iItem)
+ return FALSE;
+
+ *iItem = (NULL != item && NULL != groups) ? groups->GetListboxItem(item) : LB_ERR;
+ return (LB_ERR != *iItem);
+}
+
+INT_PTR Listbox::KeyToItem(INT vKey, INT iCaret)
+{
+ SetupListboxItem *item;
+ HRESULT hr = groups->FindListboxItem(iCaret, &item);
+ if (FAILED(hr)) return -1;
+
+ RECT itemRect;
+ GetItemRect(iCaret, &itemRect);
+ INT_PTR result = item->KeyToItem(this, &itemRect, vKey);
+ if (-1 != result) return result;
+
+ INT iTarget, iCount;
+
+ switch(vKey)
+ {
+ case VK_UP:
+ case VK_LEFT:
+ iTarget = GetPrevEnabledItem(iCaret, NULL);
+ if (LB_ERR != iTarget) return iTarget;
+
+ SendMessage(hwnd, LB_SETTOPINDEX, (WPARAM)0, 0L);
+ SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)iCaret, FALSE);
+ return -2;
+
+ case VK_DOWN:
+ case VK_RIGHT:
+ iTarget = GetNextEnabledItem(iCaret, NULL);
+ if (LB_ERR != iTarget) return iTarget;
+
+ SendMessage(hwnd, LB_SETTOPINDEX, (WPARAM)iCaret, 0L);
+ return -2;
+
+ case VK_HOME:
+ if (iCaret > 0)
+ {
+ SendMessage(hwnd, LB_SETTOPINDEX, (WPARAM)0, 0L);
+ iTarget = GetNextEnabledItem(-1, NULL);
+ if (iTarget >= iCaret) iTarget = LB_ERR;
+ if (LB_ERR != iTarget) return iTarget;
+ SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)iCaret, FALSE);
+ }
+ return -2;
+
+ case VK_PRIOR:
+ if (iCaret > 0)
+ {
+ INT iTop = (INT)SendMessage(hwnd, LB_GETTOPINDEX, 0, 0L);
+ if (iTop == iCaret)
+ {
+ INT iPage = iCaret - GetPageCount() + 1;
+ iTop = (iPage <= 0) ? 0 : (iPage - 1);
+ }
+
+ iTarget = GetPrevEnabledItem(iTop + 1, NULL);
+
+ if (LB_ERR == iTarget)
+ {
+ SendMessage(hwnd, LB_SETTOPINDEX, (WPARAM)0, 0L);
+ iTarget = GetNextEnabledItem(iTop, NULL);
+ if (iTarget > iCaret) iTarget = LB_ERR;
+ }
+
+ if (LB_ERR != iTarget) return iTarget;
+ SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)iCaret, FALSE);
+ }
+ return -2;
+
+ case VK_END:
+ iCount = (INT)SendMessage(hwnd, LB_GETCOUNT, 0, 0L);
+ if (iCount > 0 && iCaret != (iCount - 1))
+ {
+ SendMessage(hwnd, LB_SETTOPINDEX, (WPARAM)(iCount - 1), 0L);
+ iTarget = GetPrevEnabledItem(iCount, NULL);
+ if (iTarget <= iCaret) iTarget = LB_ERR;
+ if (LB_ERR != iTarget) return iTarget;
+ SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)iCaret, FALSE);
+ }
+ return -2;
+
+ case VK_NEXT:
+ iCount = (INT)SendMessage(hwnd, LB_GETCOUNT, 0, 0L);
+ if (iCount > 0 && iCaret != (iCount - 1))
+ {
+ INT iPage = GetPageCount();
+ INT iBottom = (INT)SendMessage(hwnd, LB_GETTOPINDEX, 0, 0L) + iPage - 1;
+ if (iBottom == iCaret)
+ {
+ iBottom += iPage;
+ if (iBottom >= iCount) iBottom = iCount -1;
+ }
+ iTarget = GetNextEnabledItem(iBottom - 1, NULL);
+ if (LB_ERR == iTarget)
+ {
+ SendMessage(hwnd, LB_SETTOPINDEX, (WPARAM)(iCount - 1), 0L);
+ iTarget = GetPrevEnabledItem(iBottom, NULL);
+ if (iTarget < iCaret) iTarget = LB_ERR;
+ }
+
+ if (LB_ERR != iTarget) return iTarget;
+ SendMessage(hwnd, LB_SETCARETINDEX, (WPARAM)iCaret, FALSE);
+ }
+ return -2;
+ }
+
+ return result;
+}
+
+INT_PTR Listbox::CharToItem(INT vKey, INT caretPos)
+{
+ return -2;
+ //SetupListboxItem *item;
+ //HRESULT hr = groups->FindListboxItem(caretPos, &item);
+ //return (SUCCEEDED(hr)) ? item->CharToItem(this, vKey) : -1;
+}
+
+INT Listbox::GetCheckboxThemeState(BOOL checked, UINT state)
+{
+ if (FALSE != checked)
+ {
+ if (0 != (ODS_DISABLED & state)) return CBS_CHECKEDDISABLED;
+ if (0 != (ODS_HOTLIGHT & state)) return CBS_CHECKEDHOT;
+ if (0 != (ODS_SELECTED & state)) return CBS_CHECKEDPRESSED;
+ return CBS_CHECKEDNORMAL;
+ }
+
+ if (0 != (ODS_DISABLED & state)) return CBS_UNCHECKEDDISABLED;
+ if (0 != (ODS_HOTLIGHT & state)) return CBS_UNCHECKEDHOT;
+ if (0 != (ODS_SELECTED & state)) return CBS_UNCHECKEDPRESSED;
+ return CBS_UNCHECKEDNORMAL;
+}
+
+BOOL Listbox::DrawCheckbox(HDC hdc, BOOL checked, UINT state, const RECT *pRect, const RECT *pClipRect)
+{
+ if (NULL != buttonTheme)
+ {
+ INT stateId = GetCheckboxThemeState(checked, state);
+ if (SUCCEEDED(UxDrawThemeBackground(buttonTheme, hdc, BP_CHECKBOX, stateId, pRect, pClipRect)))
+ return TRUE;
+ }
+
+ UINT stateId = DFCS_BUTTONCHECK;
+ if (FALSE != checked) stateId |= DFCS_CHECKED;
+ if (0 != (ODS_DISABLED & state)) stateId |= DFCS_INACTIVE;
+ if (0 != (ODS_HOTLIGHT & state)) stateId |= DFCS_HOT;
+ if (0 != (ODS_SELECTED & state)) stateId |= DFCS_PUSHED;
+
+ return DrawFrameControl(hdc, (LPRECT)pRect,DFC_BUTTON, stateId);
+}
+
+BOOL Listbox::GetCheckboxMetrics(HDC hdc, BOOL checked, UINT state, SIZE *pSize)
+{
+ if (NULL != buttonTheme)
+ {
+ HDC hdcMine = NULL;
+
+ if (NULL == hdc)
+ {
+ hdcMine = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
+ hdc = hdcMine;
+ }
+
+ INT stateId = GetCheckboxThemeState(checked, state);
+ HRESULT hr = UxGetThemePartSize(buttonTheme, hdc, BP_CHECKBOX,
+ stateId, NULL, TS_DRAW, pSize);
+
+ if (NULL != hdcMine)
+ ReleaseDC(hwnd, hdcMine);
+ if (SUCCEEDED(hr)) return TRUE;
+ }
+ pSize->cx = 13;
+ pSize->cy = 13;
+ return TRUE;
+}
+
+void Listbox::OnDestroy()
+{
+ delete(this);
+}
+
+
+INT Listbox::HitTest(POINT pt, RECT *prcItem)
+{
+ RECT itemRect;
+ INT itemId = (INT)SendMessage(hwnd, LB_ITEMFROMPOINT, 0, MAKELONG(pt.x, pt.y));
+
+ if (LB_ERR == itemId ||
+ LB_ERR == SendMessage(hwnd, LB_GETITEMRECT, (WPARAM)itemId, (LPARAM)&itemRect) ||
+ FALSE == PtInRect(&itemRect, pt))
+ {
+ return LB_ERR;
+ }
+
+ if (NULL != prcItem)
+ CopyRect(prcItem, &itemRect);
+
+ return itemId;
+}
+
+void Listbox::GetItemRect(INT itemId, RECT *prcItem)
+{
+ if (LB_ERR == ::SendMessage(hwnd, LB_GETITEMRECT, (WPARAM)itemId, (LPARAM)prcItem))
+ SetRectEmpty(prcItem);
+}
+
+BOOL Listbox::DoDragAndDrop(UINT mouseEvent, UINT mouseFlags, POINT pt)
+{
+ if (WM_MOUSEMOVE == mouseEvent &&
+ 0 != ((MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) & mouseFlags))
+ {
+ flags |= SLF_DRAGMOVE;
+ }
+ else
+ {
+ flags &= ~SLF_DRAGMOVE;
+ }
+ return (0 != (SLF_DRAGMOVE & flags));
+}
+
+void Listbox::OnMouseEvent(UINT mouseEvent, UINT mouseFlags, POINTS pts, BOOL fDefaultHandler, ITEMMOUSEPROC proc)
+{
+ POINT pt;
+ POINTSTOPOINT(pt, pts);
+ SetupListboxItem *item;
+ RECT itemRect;
+
+ if (LB_ERR != capturedId)
+ {
+ if (SUCCEEDED(groups->FindListboxItem(capturedId, &item)))
+ {
+ GetItemRect(capturedId, &itemRect);
+ if (FALSE == ((item->*proc)(this, &itemRect, mouseFlags, pt)))
+ {
+ CallPrevProc(mouseEvent, (WPARAM)mouseFlags, *((LPARAM*)&pts));
+ }
+ return;
+ }
+ capturedId = LB_ERR;
+ }
+
+ if (DoDragAndDrop(mouseEvent, mouseFlags, pt))
+ return;
+
+ INT itemId = HitTest(pt, &itemRect);
+ if (mouseoverId != itemId)
+ {
+ if (SUCCEEDED(groups->FindListboxItem(mouseoverId, &item)))
+ {
+ RECT leaveRect;
+ GetItemRect(mouseoverId, &leaveRect);
+ item->MouseLeave(this, &leaveRect);
+ }
+ mouseoverId = itemId;
+
+ TRACKMOUSEEVENT tm;
+ tm.cbSize = sizeof(TRACKMOUSEEVENT);
+ tm.hwndTrack = hwnd;
+ tm.dwFlags = TME_LEAVE;
+ if (LB_ERR == mouseoverId)
+ tm.dwFlags |= TME_CANCEL;
+ TrackMouseEvent(&tm);
+ }
+
+ if (LB_ERR != mouseoverId)
+ {
+ if (SUCCEEDED(groups->FindListboxItem(mouseoverId, &item)))
+ {
+ BOOL callListbox = FALSE;
+ if (FALSE != item->IsDisabled())
+ {
+ switch(mouseEvent)
+ {
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case 0x020C /*WM_XBUTTONUP*/:
+ callListbox = TRUE;
+ break;
+ }
+ }
+ else
+ {
+ if (FALSE == ((item->*proc)(this, &itemRect, mouseFlags, pt)))
+ {
+ callListbox = TRUE;
+ }
+ }
+
+ if (FALSE != callListbox)
+ {
+ CallPrevProc(mouseEvent, (WPARAM)mouseFlags, *((LPARAM*)&pts));
+ }
+ return;
+ }
+ }
+
+ if (FALSE != fDefaultHandler)
+ {
+ CallPrevProc(mouseEvent, (WPARAM)mouseFlags, *((LPARAM*)&pts));
+ }
+}
+
+void Listbox::OnMouseLeave()
+{
+ if (LB_ERR != mouseoverId)
+ {
+ SetupListboxItem *item;
+ INT itemId = mouseoverId;
+ mouseoverId = LB_ERR;
+ if (SUCCEEDED(groups->FindListboxItem(itemId, &item)))
+ {
+ RECT itemRect;
+ GetItemRect(itemId, &itemRect);
+ if (item->MouseLeave(this, &itemRect))
+ return;
+ }
+ }
+ CallPrevProc(WM_MOUSELEAVE, 0, 0L);
+}
+
+void Listbox::SetCapture(SetupListboxItem *item)
+{
+ INT prevCapturedId = capturedId;
+ capturedId = (NULL != item) ? groups->GetListboxItem(item) : LB_ERR;
+
+ NotifyReleaseCapture(prevCapturedId, item);
+
+ if (LB_ERR != capturedId && ::GetCapture() != hwnd)
+ ::SetCapture(hwnd);
+}
+
+SetupListboxItem *Listbox::GetCapture()
+{
+ SetupListboxItem *capturedItem;
+ if (LB_ERR == capturedId || FAILED(groups->FindListboxItem(capturedId, &capturedItem)))
+ capturedItem = NULL;
+
+ return capturedItem;
+}
+
+void Listbox::ReleaseCapture()
+{
+ if (LB_ERR != capturedId)
+ {
+ INT prevCapturedId = capturedId;
+ capturedId = LB_ERR;
+
+ NotifyReleaseCapture(prevCapturedId, NULL);
+ if (::GetCapture() == hwnd)
+ ::ReleaseCapture();
+ }
+}
+
+void Listbox::NotifyReleaseCapture(INT itemId, SetupListboxItem *itemGain)
+{
+ if (LB_ERR == itemId)
+ return;
+
+ SetupListboxItem *item;
+ if (SUCCEEDED(groups->FindListboxItem(itemId, &item)))
+ {
+ RECT itemRect;
+ GetItemRect(itemId, &itemRect);
+ item->CaptureChanged(this, &itemRect, itemGain);
+ }
+}
+
+void Listbox::OnCaptureChanged(HWND hwndGained)
+{
+ if (hwnd != hwndGained && LB_ERR != capturedId)
+ {
+ INT prevCapturedId = capturedId;
+ capturedId = LB_ERR;
+ NotifyReleaseCapture(prevCapturedId, NULL);
+ }
+}
+
+BOOL Listbox::InvalidateRect(const RECT *prcInvalidate, BOOL fErase)
+{
+ return ::InvalidateRect(hwnd, prcInvalidate, fErase);
+}
+
+BOOL Listbox::InvalidateItem(INT itemId, BOOL fErase)
+{
+ if (itemId < 0) return FALSE;
+
+ RECT itemRect;
+ if (LB_ERR == SendMessage(hwnd, LB_GETITEMRECT, (WPARAM)itemId, (LPARAM)&itemRect))
+ return FALSE;
+
+ return ::InvalidateRect(hwnd, &itemRect, fErase);
+}
+
+void Listbox::OnEraseBkGround(HDC hdc)
+{
+ INT iCount = (INT)SendMessage(hwnd, LB_GETCOUNT, 0, 0L);
+ RECT clientRect, itemRect;
+
+ GetClientRect(hwnd, &clientRect);
+ if (iCount > 0 &&
+ LB_ERR != SendMessage(hwnd, LB_GETITEMRECT, (WPARAM)(iCount - 1), (LPARAM)&itemRect))
+ {
+ clientRect.top = itemRect.top;
+ }
+
+ if (clientRect.top < clientRect.bottom)
+ {
+ HBRUSH hb = NULL;
+ HWND hParent = GetParent(hwnd);
+ if (NULL != hParent)
+ {
+ hb = (HBRUSH)SendMessage(hParent, WM_CTLCOLORLISTBOX, (WPARAM)hdc, (LPARAM)hwnd);
+ }
+ if (NULL == hb)
+ {
+ hb = GetSysColorBrush(COLOR_WINDOW);
+ }
+ FillRect(hdc, &clientRect, hb);
+ }
+}
+
+void Listbox::OnCommand(INT commandId, INT eventId, HWND hControl)
+{
+ if (NULL == hControl)
+ {
+ SetupListboxItem *item = GetSelection();
+ if (NULL != item)
+ {
+ item->Command(this, commandId, eventId);
+ }
+
+ }
+}
+SetupImage *Listbox::GetExpandboxImage(HDC hdc, BOOL fExpanded)
+{
+ if (fExpanded)
+ {
+ if (NULL == expandedImage)
+ expandedImage = SetupImage::CreateFromPluginBitmap(hdc, L"gen_ml.dll", MAKEINTRESOURCE(137), 3);
+ return expandedImage;
+ }
+
+ if (NULL == collapsedImage)
+ collapsedImage = SetupImage::CreateFromPluginBitmap(hdc, L"gen_ml.dll", MAKEINTRESOURCE(135), 3);
+ return collapsedImage;
+}
+
+BOOL Listbox::DrawExpandbox(HDC hdc, BOOL fExpanded, const RECT *pRect, COLORREF rgbBk, COLORREF rgbFg)
+{
+ SetupImage *image = GetExpandboxImage(hdc, fExpanded);
+ return (NULL != image) ?
+ image->DrawImage(hdc, pRect->left, pRect->top,
+ pRect->right - pRect->left, pRect->bottom- pRect->top,
+ 0, 0, rgbBk, rgbFg)
+ : FALSE;
+}
+
+BOOL Listbox::GetExpandboxMetrics(HDC hdc, BOOL fExpanded, SIZE *pSize)
+{
+ SetupImage *image = GetExpandboxImage(hdc, fExpanded);
+ return (NULL != image) ? image->GetSize(pSize) : FALSE;
+}
+
+HMENU Listbox::GetContextMenu(UINT menuId)
+{
+ HMENU baseMenu = WASABI_API_LOADMENUW(IDR_SETUPMENU);
+ if (NULL == baseMenu)
+ return NULL;
+
+ switch(menuId)
+ {
+ case menuGroupContext:
+ return GetSubMenu(baseMenu, 0);
+ }
+ return NULL;
+}
+
+LRESULT Listbox::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_DESTROY:
+ OnDestroy();
+ return 0;
+ case WM_MOUSEMOVE:
+ OnMouseEvent(uMsg, (UINT)wParam, MAKEPOINTS(lParam), TRUE, &SetupListboxItem::MouseMove);
+ return 0;
+ case WM_LBUTTONDOWN:
+ OnMouseEvent(uMsg, (UINT)wParam, MAKEPOINTS(lParam), FALSE, &SetupListboxItem::LButtonDown);
+ return 0;
+ case WM_LBUTTONUP:
+ OnMouseEvent(uMsg, (UINT)wParam, MAKEPOINTS(lParam), TRUE, &SetupListboxItem::LButtonUp);
+ return 0;
+ case WM_LBUTTONDBLCLK:
+ OnMouseEvent(uMsg, (UINT)wParam, MAKEPOINTS(lParam), FALSE, &SetupListboxItem::LButtonDblClk);
+ return 0;
+ case WM_RBUTTONDOWN:
+ OnMouseEvent(uMsg, (UINT)wParam, MAKEPOINTS(lParam), FALSE, &SetupListboxItem::RButtonDown);
+ return 0;
+ case WM_RBUTTONUP:
+ OnMouseEvent(uMsg, (UINT)wParam, MAKEPOINTS(lParam), TRUE, &SetupListboxItem::RButtonUp);
+ return 0;
+ case WM_MOUSELEAVE:
+ OnMouseLeave();
+ return 0;
+ case WM_CAPTURECHANGED:
+ OnCaptureChanged((HWND)lParam);
+ break;
+ case WM_ERASEBKGND:
+ OnEraseBkGround((HDC)wParam);
+ return TRUE;
+ case WM_COMMAND:
+ OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
+ return 0;
+ }
+
+ return CallPrevProc(uMsg, wParam, lParam);
+}
+
+static LRESULT WINAPI Listbox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ Listbox *list = GetList(hwnd);
+ if (NULL != list)
+ return list->WindowProc(uMsg, wParam, lParam);
+
+ return (IsWindowUnicode(hwnd)) ?
+ DefWindowProcW(hwnd, uMsg, wParam, lParam) :
+ DefWindowProcA(hwnd, uMsg, wParam, lParam);
+
+}
diff --git a/Src/Plugins/Library/ml_online/Setup/setupListbox.h b/Src/Plugins/Library/ml_online/Setup/setupListbox.h
new file mode 100644
index 00000000..0fceffc3
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupListbox.h
@@ -0,0 +1,84 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLISTBOX_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLISTBOX_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <windows.h>
+
+class SetupGroupList;
+class SetupListboxItem;
+
+class __declspec(novtable) SetupListbox
+{
+public:
+ typedef enum
+ {
+ menuGroupContext = 0,
+ } contextMenu;
+
+public:
+ static HRESULT CreateInstance(HWND hListbox, SetupGroupList *groupList, SetupListbox **pInstance);
+ static SetupListbox *GetInstance(HWND hListbox);
+
+public:
+ virtual HWND GetHwnd() = 0;
+ virtual HFONT GetFont() = 0;
+ virtual LRESULT CallDefaultProc(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
+ virtual LRESULT CallPrevProc(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
+
+ virtual BOOL MeasureItem(INT itemId, UINT *cx, UINT *cy) = 0;
+ virtual BOOL DrawItem(HDC hdc, const RECT *prcItem, INT itemId, UINT itemState, UINT itemAction) = 0;
+ virtual INT_PTR KeyToItem(INT vKey, INT caretPos) = 0;
+ virtual INT_PTR CharToItem(INT vKey, INT caretPos) = 0;
+
+ virtual BOOL DrawCheckbox(HDC hdc, BOOL checked, UINT state, const RECT *pRect, const RECT *pClipRect) = 0;
+ virtual BOOL GetCheckboxMetrics(HDC hdc, BOOL checked, UINT state, SIZE *pSize) = 0;
+ virtual void SetCapture(SetupListboxItem *item) = 0;
+ virtual SetupListboxItem *GetCapture() = 0;
+ virtual void ReleaseCapture() = 0;
+
+ virtual BOOL InvalidateRect(const RECT *prcInvalidate, BOOL fErase) = 0;
+ virtual BOOL InvalidateItem(INT iItem, BOOL fErase) = 0;
+ virtual void UpdateCount() = 0;
+
+
+ virtual BOOL DrawExpandbox(HDC hdc, BOOL fExpanded, const RECT *pRect, COLORREF rgbBk, COLORREF rgbFg) = 0;
+ virtual BOOL GetExpandboxMetrics(HDC hdc, BOOL fExpanded, SIZE *pSize) = 0;
+
+ virtual INT GetPageCount() = 0;
+ virtual INT GetNextEnabledItem(INT iItem, SetupListboxItem **itemOut) = 0;
+ virtual INT GetPrevEnabledItem(INT iItem, SetupListboxItem **itemOut) = 0;
+ virtual SetupListboxItem *GetSelection() = 0;
+ virtual BOOL SetSelection(SetupListboxItem *item) = 0;
+ virtual BOOL GetIndex(SetupListboxItem *item, INT *iItem) = 0;
+
+ virtual HMENU GetContextMenu(UINT menuId) = 0;
+
+
+};
+
+class __declspec(novtable) SetupListboxItem
+{
+
+public:
+ virtual BOOL MeasureItem(SetupListbox *instance, UINT *cx, UINT *cy) = 0;
+ virtual BOOL DrawItem(SetupListbox *instance, HDC hdc, const RECT *prc, UINT state) = 0;
+ virtual INT_PTR KeyToItem(SetupListbox *instance, const RECT *prcItem, INT vKey) = 0;
+ virtual BOOL MouseMove(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt) = 0;
+ virtual BOOL MouseLeave(SetupListbox *instance, const RECT *prcItem) = 0;
+ virtual BOOL LButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt) = 0;
+ virtual BOOL LButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt) = 0;
+ virtual BOOL LButtonDblClk(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt) = 0;
+ virtual BOOL RButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt) = 0;
+ virtual BOOL RButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt) = 0;
+ virtual void CaptureChanged(SetupListbox *instance, const RECT *prcItem, SetupListboxItem *captured) = 0;
+ virtual BOOL IsDisabled() = 0;
+ virtual void Command(SetupListbox *instance, INT commandId, INT eventId) = 0;
+
+ virtual HWND CreateDetailsView(HWND hParent) = 0;
+ virtual BOOL GetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax) = 0;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLISTBOX_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupListboxLabel.cpp b/Src/Plugins/Library/ml_online/Setup/setupListboxLabel.cpp
new file mode 100644
index 00000000..0a24b61e
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupListboxLabel.cpp
@@ -0,0 +1,228 @@
+#include "./setupListboxLabel.h"
+#include "../api__ml_online.h"
+#include "../common.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define LABEL_MARGINCX 0
+#define LABEL_MARGINCY 1
+
+#define TEXT_OFFSET_LEFT 2
+#define TEXT_OFFSET_BOTTOM 2
+#define TEXT_ALIGN (TA_CENTER | TA_BOTTOM)
+
+SetupListboxLabel::SetupListboxLabel(LPCWSTR pszName)
+ : ref(1), name(NULL)
+{
+ SetName(pszName);
+}
+
+SetupListboxLabel::~SetupListboxLabel()
+{
+ SetName(NULL);
+}
+
+SetupListboxLabel *SetupListboxLabel::CreateInstance(LPCWSTR pszName)
+{
+ return new SetupListboxLabel(pszName);
+}
+
+ULONG SetupListboxLabel::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+ULONG SetupListboxLabel::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+ return r;
+}
+
+HRESULT SetupListboxLabel::GetName(LPWSTR pszBuffer, INT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ if (NULL != name && IS_INTRESOURCE(name))
+ {
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)name, pszBuffer, cchBufferMax);
+ return (L'\0' != *pszBuffer) ? S_OK : E_FAIL;
+ }
+ return StringCchCopyExW(pszBuffer, cchBufferMax, name, NULL, NULL, STRSAFE_IGNORE_NULLS);
+}
+HRESULT SetupListboxLabel::SetName(LPCWSTR pszName)
+{
+ if (NULL != name && !IS_INTRESOURCE(name))
+ Plugin_FreeString(name);
+
+ if (IS_INTRESOURCE(pszName))
+ {
+ name = (LPWSTR)pszName;
+ }
+ else
+ {
+ name = Plugin_CopyString(pszName);
+ }
+
+ return S_OK;
+}
+
+BOOL SetupListboxLabel::IsNameNull()
+{
+ return (NULL == name);
+}
+
+void SetupListboxLabel::GetColors(HDC hdc, UINT state, COLORREF *rgbBkOut, COLORREF *rgbTextOut)
+{
+ COLORREF rgbBk, rgbText;
+
+ rgbBk = GetSysColor(COLOR_WINDOW);
+ rgbText = GetSysColor( (0 == (ODS_DISABLED & state)) ? COLOR_GRAYTEXT : COLOR_GRAYTEXT);
+
+ if (NULL != rgbBkOut) *rgbBkOut = rgbBk;
+ if (NULL != rgbTextOut) *rgbTextOut = rgbText;
+}
+
+HBRUSH SetupListboxLabel::GetBrush(HDC hdc, UINT state)
+{
+ return GetSysColorBrush(COLOR_WINDOW);
+}
+
+BOOL SetupListboxLabel::MeasureItem(SetupListbox *instance, UINT *cx, UINT *cy)
+{
+ HDC hdc = GetDCEx(instance->GetHwnd(), NULL, DCX_CACHE | DCX_NORESETATTRS);
+ if (NULL == hdc) return FALSE;
+
+ HFONT originalFont = (HFONT)SelectObject(hdc, instance->GetFont());
+
+ if (NULL != cy)
+ {
+ *cy = 0;
+ TEXTMETRIC tm;
+ if (GetTextMetrics(hdc, &tm))
+ *cy = tm.tmHeight + tm.tmExternalLeading + LABEL_MARGINCY*2;
+ }
+
+ if (NULL != cx)
+ {
+ *cx = 0;
+ WCHAR szBuffer[128] = {0};
+ if (SUCCEEDED(GetName(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ INT cchBuffer = lstrlenW(szBuffer);
+ SIZE textSize;
+ if (0 != cchBuffer && GetTextExtentPoint32(hdc, szBuffer, cchBuffer, &textSize))
+ {
+ *cx = textSize.cx + LABEL_MARGINCX*2;
+ }
+ }
+ }
+
+ SelectObject(hdc, originalFont);
+ ReleaseDC(instance->GetHwnd(), hdc);
+ return TRUE;
+}
+
+BOOL SetupListboxLabel::DrawItem(SetupListbox *instance, HDC hdc, const RECT *prc, UINT state)
+{
+ LONG paintLeft = prc->left + LABEL_MARGINCX;
+
+ COLORREF rgbBk, rgbText;
+ GetColors(hdc, state, &rgbBk, &rgbText);
+
+ COLORREF origBk = SetBkColor(hdc, rgbBk);
+ COLORREF origText = SetTextColor(hdc, rgbText);
+ UINT textAlign = SetTextAlign(hdc, TEXT_ALIGN);
+
+ HRGN backRgn, rgn;
+ backRgn = CreateRectRgnIndirect(prc);
+ rgn = CreateRectRgn(0,0,0,0);
+
+ RECT partRect;
+ WCHAR szBuffer[128] = {0};
+ INT cchBuffer = 0;
+
+ if (SUCCEEDED(GetName(szBuffer, ARRAYSIZE(szBuffer))))
+ cchBuffer = lstrlenW(szBuffer);
+
+ SetRect(&partRect, paintLeft, prc->top, prc->right - LABEL_MARGINCX, prc->bottom);
+ if (ExtTextOut(hdc, partRect.left + (partRect.right - partRect.left)/2, partRect.bottom - TEXT_OFFSET_BOTTOM,
+ ETO_OPAQUE | ETO_CLIPPED, &partRect, szBuffer, cchBuffer, NULL))
+ {
+ if (SetRectRgn(rgn, partRect.left, partRect.top, partRect.right, partRect.bottom))
+ CombineRgn(backRgn, backRgn, rgn, RGN_DIFF);
+ }
+
+ if (NULL != backRgn)
+ {
+ FillRgn(hdc, backRgn, GetBrush(hdc, state));
+ DeleteObject(backRgn);
+ }
+ if (NULL != rgn)
+ DeleteObject(rgn);
+
+
+ if (ODS_FOCUS == ((ODS_FOCUS | 0x0200/*ODS_NOFOCUSRECT*/) & state))
+ DrawFocusRect(hdc, prc);
+
+ if (TEXT_ALIGN != textAlign) SetTextAlign(hdc, textAlign);
+ if (origBk != rgbBk) SetBkColor(hdc, origBk);
+ if (origText != rgbText) SetTextColor(hdc, origText);
+ return TRUE;
+}
+
+INT_PTR SetupListboxLabel::KeyToItem(SetupListbox *instance, const RECT *prcItem, INT vKey)
+{
+ return -1;
+}
+BOOL SetupListboxLabel::MouseMove(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupListboxLabel::MouseLeave(SetupListbox *instance, const RECT *prcItem)
+{
+ return FALSE;
+}
+BOOL SetupListboxLabel::LButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupListboxLabel::LButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupListboxLabel::LButtonDblClk(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupListboxLabel::RButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+BOOL SetupListboxLabel::RButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+void SetupListboxLabel::CaptureChanged(SetupListbox *instance, const RECT *prcItem, SetupListboxItem *captured)
+{
+}
+
+BOOL SetupListboxLabel::GetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ WCHAR szName[128] = {0};
+ if (FAILED(GetName(szName, ARRAYSIZE(szName))))
+ return FALSE;
+
+ if (NULL == pszBuffer ||
+ FAILED(StringCchPrintf(pszBuffer, cchBufferMax, L"lbl_empty_%s", szName)))
+ {
+ return FALSE;
+ }
+ return TRUE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupListboxLabel.h b/Src/Plugins/Library/ml_online/Setup/setupListboxLabel.h
new file mode 100644
index 00000000..b1b474e4
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupListboxLabel.h
@@ -0,0 +1,57 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLISTBOX_LABEL_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLISTBOX_LABEL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "./setupListbox.h"
+
+
+class SetupListboxLabel: public SetupListboxItem
+{
+
+protected:
+ SetupListboxLabel(LPCWSTR pszName);
+ ~SetupListboxLabel();
+
+public:
+ static SetupListboxLabel *CreateInstance(LPCWSTR pszNamee);
+
+public:
+ ULONG AddRef();
+ ULONG Release();
+
+ HRESULT GetName(LPWSTR pszBuffer, INT cchBufferMax);
+ HRESULT SetName(LPCWSTR pszName);
+ BOOL IsNameNull();
+
+ void GetColors(HDC hdc, UINT state, COLORREF *rgbBkOut, COLORREF *rgbTextOut);
+ HBRUSH GetBrush(HDC hdc, UINT state);
+
+
+ /* SetupListboxItem */
+ BOOL MeasureItem(SetupListbox *instance, UINT *cx, UINT *cy);
+ BOOL DrawItem(SetupListbox *instance, HDC hdc, const RECT *prc, UINT state);
+ INT_PTR KeyToItem(SetupListbox *instance, const RECT *prcItem, INT vKey);
+ BOOL MouseMove(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL MouseLeave(SetupListbox *instance, const RECT *prcItem);
+ BOOL LButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL LButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL LButtonDblClk(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL RButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL RButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ void CaptureChanged(SetupListbox *instance, const RECT *prcItem, SetupListboxItem *captured);
+ BOOL IsDisabled() { return TRUE; }
+ void Command(SetupListbox *instance, INT commandId, INT eventId) {}
+ HWND CreateDetailsView(HWND hParent) { return NULL; }
+ BOOL GetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax);
+
+
+protected:
+ ULONG ref;
+ LPWSTR name;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLISTBOX_LABEL_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupLog.cpp b/Src/Plugins/Library/ml_online/Setup/setupLog.cpp
new file mode 100644
index 00000000..a53c469f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupLog.cpp
@@ -0,0 +1,395 @@
+#include "./setupLog.h"
+#include "../common.h"
+#include "../api__ml_online.h"
+#include "../config.h"
+
+#include "../../nu/trace.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omwebstorage.h>
+#include <ifc_omstorageasync.h>
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define SETUPLOG_SEPARATOR ','
+#define SETUPLOG_SECTION "Setup"
+#define SETUPLOG_KEY_SUBSCRIBED "subscribed"
+#define SETUPLOG_KEY_UNSUBSCRIBED "unsubscribed"
+
+struct LOGPARSERPARAM
+{
+ LOGPARSERPARAM() : instance(NULL), operation(0) {}
+
+ SetupLog *instance;
+ UINT operation;
+};
+
+static size_t SetupLog_GetMaxServiceIdCount(SetupLog::ServiceMap *serviceMap)
+{
+ SetupLog::ServiceMap::iterator it;
+
+ size_t c1 = 0, c2 = 0;
+ for (it = serviceMap->begin(); it != serviceMap->end(); it++)
+ {
+ switch(it->second)
+ {
+ case SetupLog::opServiceAdded:
+ c1++;
+ break;
+ case SetupLog::opServiceRemoved:
+ c2++;
+ break;
+ }
+ }
+ return (c1 > c2) ? c1 : c2;
+}
+
+static HRESULT SetupLog_FormatServiceId(SetupLog::ServiceMap *serviceMap, INT operation, LPSTR pszBuffer, size_t cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_INVALIDARG;
+
+ *pszBuffer = '\0';
+
+ if (NULL == serviceMap)
+ return S_OK;
+
+ HRESULT hr = S_OK;
+ size_t remaining = cchBufferMax;
+ LPSTR cursor = pszBuffer;
+ SetupLog::ServiceMap::iterator it;
+
+ const char format[] = { SETUPLOG_SEPARATOR, '%', 'u', '\0'};
+
+ for (it = serviceMap->begin(); it != serviceMap->end() && SUCCEEDED(hr); it++)
+ {
+ if (it->second == operation)
+ {
+ hr = StringCchPrintfExA(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ ((cursor == pszBuffer) ? (format + 1) : format), it->first);
+ }
+ }
+ return hr;
+}
+
+static LPCSTR SetupLog_GetOperationKey(UINT operation)
+{
+ switch(operation)
+ {
+ case SetupLog::opServiceAdded: return SETUPLOG_KEY_SUBSCRIBED;
+ case SetupLog::opServiceRemoved: return SETUPLOG_KEY_UNSUBSCRIBED;
+ }
+ return NULL;
+}
+
+static LPCWSTR SetupLog_GetOperationAction(UINT operation)
+{
+ switch(operation)
+ {
+ case SetupLog::opServiceAdded: return L"add";
+ case SetupLog::opServiceRemoved: return L"remove";
+ }
+ return NULL;
+}
+
+static BOOL SetupLog_WriteOperationLog(UINT operation, LPCSTR pszValue)
+{
+ LPCSTR pszKey = SetupLog_GetOperationKey(operation);
+ if (NULL == pszKey) return FALSE;
+ return Config_WriteStr(SETUPLOG_SECTION, pszKey, pszValue);
+}
+
+static HRESULT SetupLog_ReadOperationLog(UINT operation, LPSTR pszBuffer, UINT cchBufferMax, UINT *cchReaded)
+{
+ LPCSTR pszKey = SetupLog_GetOperationKey(operation);
+ if (NULL == pszKey) return E_INVALIDARG;
+
+ DWORD readed = Config_ReadStr(SETUPLOG_SECTION, pszKey, NULL, pszBuffer, cchBufferMax);
+
+ if (NULL != cchReaded)
+ {
+ *cchReaded = readed;
+ }
+
+ return S_OK;
+}
+
+SetupLog::SetupLog() : ref(1)
+{
+}
+
+SetupLog::~SetupLog()
+{
+
+}
+
+SetupLog *SetupLog::Open()
+{
+ SetupLog *instance = new SetupLog();
+ if (NULL == instance) return NULL;
+
+ INT cchBuffer = 32000;
+ LPSTR buffer = Plugin_MallocAnsiString(cchBuffer);
+ if (NULL == buffer)
+ {
+ instance->Release();
+ return NULL;
+ }
+
+ UINT cchReaded = 0;
+ const UINT szOperations[] = { opServiceAdded,
+ opServiceRemoved, };
+
+ UINT serviceId;
+ for (INT i = 0; i < ARRAYSIZE(szOperations); i++)
+ {
+ if (SUCCEEDED(SetupLog_ReadOperationLog(szOperations[i], buffer, cchBuffer, &cchReaded)) && cchReaded > 0)
+ {
+ LPSTR cursor = buffer;
+ LPSTR block = cursor;
+
+ for(;;)
+ {
+ if (SETUPLOG_SEPARATOR == *cursor || '\0' == *cursor)
+ {
+ while (' ' == *block && block < cursor) block++;
+
+ if (block < cursor &&
+ FALSE != StrToIntExA(block, STIF_SUPPORT_HEX, (INT*)&serviceId) &&
+ 0 != serviceId)
+ {
+ instance->LogServiceById(serviceId, szOperations[i]);
+ }
+
+ if ('\0' == *cursor)
+ break;
+
+ cursor++;
+ block = cursor;
+ }
+ else
+ {
+ cursor++;
+ }
+ }
+ }
+ }
+
+ Plugin_FreeAnsiString(buffer);
+ return instance;
+}
+
+ULONG SetupLog::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+ULONG SetupLog::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+BOOL SetupLog::IsOperationSupported(UINT operation)
+{
+ switch(operation)
+ {
+ case SetupLog::opServiceAdded:
+ case SetupLog::opServiceRemoved:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+HRESULT SetupLog::LogServiceById(UINT serviceUid, UINT operation)
+{
+ if (0 == serviceUid || FALSE == IsOperationSupported(operation))
+ return E_INVALIDARG;
+
+ serviceMap[serviceUid] = operation;
+ return S_OK;
+}
+
+HRESULT SetupLog::LogService(ifc_omservice *service, UINT operation)
+{
+ if (NULL == service || !IsOperationSupported(operation))
+ return E_INVALIDARG;
+
+ return LogServiceById(service->GetId(), operation);
+}
+
+HRESULT SetupLog::Save()
+{
+ LPSTR buffer = NULL;
+ size_t cchBuffer = SetupLog_GetMaxServiceIdCount(&serviceMap) * 11;
+
+ if (0 != cchBuffer)
+ {
+ cchBuffer += 1;
+ buffer = Plugin_MallocAnsiString(cchBuffer);
+ if (NULL == buffer)
+ return E_OUTOFMEMORY;
+ }
+
+ const UINT szOperations[] = { opServiceAdded,
+ opServiceRemoved, };
+
+ for (INT i = 0; i < ARRAYSIZE(szOperations); i++)
+ {
+ LPCSTR value = (NULL != buffer &&
+ SUCCEEDED(SetupLog_FormatServiceId(&serviceMap, szOperations[i], buffer, cchBuffer)) &&
+ '\0' != *buffer) ? buffer : NULL;
+ SetupLog_WriteOperationLog(szOperations[i], value);
+ }
+
+ if (NULL != buffer)
+ Plugin_FreeAnsiString(buffer);
+
+ return S_OK;
+}
+
+HRESULT SetupLog::Erase()
+{
+ HRESULT hr = S_OK;
+ if (FALSE == Config_WriteStr(SETUPLOG_SECTION, SETUPLOG_KEY_SUBSCRIBED, NULL))
+ hr = E_FAIL;
+ if (FALSE == Config_WriteStr(SETUPLOG_SECTION, SETUPLOG_KEY_UNSUBSCRIBED, NULL))
+ hr = E_FAIL;
+
+ return hr;
+}
+
+struct LOGSENDJOBPARAM
+{
+ LOGSENDJOBPARAM() : totalJobs(0), storage(NULL), completeEvent(NULL) {}
+
+ ULONG totalJobs;
+ CRITICAL_SECTION lock;
+ ifc_omstorage *storage;
+ HANDLE completeEvent;
+};
+
+static void CALLBACK SetupLog_SendCompleted(ifc_omstorageasync *async)
+{
+ if (NULL != async)
+ {
+ LOGSENDJOBPARAM *param = NULL;
+ if (SUCCEEDED(async->GetData((void**)&param)) && NULL != param)
+ {
+ EnterCriticalSection(&param->lock);
+
+ if (NULL != param->storage)
+ {
+ param->storage->EndLoad(async, NULL);
+ param->storage->Release();
+ }
+
+ LONG r = InterlockedDecrement((LONG*)&param->totalJobs);
+ if (0 == r)
+ {
+ if (NULL != param->completeEvent)
+ SetEvent(param->completeEvent);
+
+ LeaveCriticalSection(&param->lock);
+ DeleteCriticalSection(&param->lock);
+ free(param);
+ param = NULL;
+ }
+ else
+ {
+ LeaveCriticalSection(&param->lock);
+ }
+ }
+ }
+}
+
+HRESULT SetupLog::Send(HANDLE completeEvent)
+{
+ size_t cchAlloc = serviceMap.size();
+ if (0 == cchAlloc)
+ {
+ if (NULL != completeEvent) SetEvent(completeEvent);
+ return S_OK;
+ }
+
+ UINT *buffer = (UINT*)calloc(cchAlloc, sizeof(UINT));
+ LOGSENDJOBPARAM *param = (LOGSENDJOBPARAM*)calloc(1, sizeof(LOGSENDJOBPARAM));
+
+ if (NULL == buffer || NULL == param)
+ {
+ if (NULL != buffer) { free(buffer); buffer = NULL; }
+ if (NULL != param) { free(param); param = NULL; }
+ if (NULL != completeEvent) { SetEvent(completeEvent); completeEvent = NULL; }
+ return E_OUTOFMEMORY;
+ }
+
+ ifc_omstorage *storage = NULL;
+ HRESULT hr = OMSERVICEMNGR->QueryStorage(&SUID_OmStorageUrl, &storage);
+ if (SUCCEEDED(hr) && storage != NULL)
+ {
+ const UINT szOperations[] = { opServiceAdded,
+ opServiceRemoved, };
+ param->totalJobs = 0;
+ param->completeEvent = completeEvent;
+ param->storage = storage;
+
+ InitializeCriticalSection(&param->lock);
+ EnterCriticalSection(&param->lock);
+
+ for (INT i = 0; i < ARRAYSIZE(szOperations); i++)
+ {
+ size_t count = 0;
+ hr = S_OK;
+ for (SetupLog::ServiceMap::iterator it = serviceMap.begin(); it != serviceMap.end() && SUCCEEDED(hr); it++)
+ {
+ if (it->second == szOperations[i])
+ {
+ buffer[count] = it->first;
+ count++;
+ }
+ }
+
+ if (0 != count)
+ {
+ LPWSTR url = NULL;
+ LPCWSTR action = SetupLog_GetOperationAction(szOperations[i]);
+ if (NULL != action &&
+ SUCCEEDED(Plugin_BuildActionUrl(&url, action, buffer, count)))
+ {
+ ifc_omstorageasync *async = NULL;;
+ if (SUCCEEDED(storage->BeginLoad(url, NULL, SetupLog_SendCompleted, param, &async)))
+ {
+ InterlockedIncrement((LONG*)&param->totalJobs);
+ storage->AddRef();
+ async->Release();
+ }
+ Plugin_FreeString(url);
+ }
+ }
+ }
+
+ if (0 == param->totalJobs)
+ {
+ LeaveCriticalSection(&param->lock);
+ DeleteCriticalSection(&param->lock);
+ hr = E_FAIL;
+ if (param) { free(param); param = NULL; }
+ }
+ else
+ {
+ LeaveCriticalSection(&param->lock);
+ }
+
+ storage->Release();
+ }
+
+ if (buffer) { free(buffer); buffer = NULL; }
+ return hr;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupLog.h b/Src/Plugins/Library/ml_online/Setup/setupLog.h
new file mode 100644
index 00000000..e701046a
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupLog.h
@@ -0,0 +1,54 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLOG_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLOG_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include <map>
+
+class ifc_omservice;
+
+class SetupLog
+{
+public:
+ typedef enum
+ {
+ opUnknown = 0,
+ opServiceAdded = 1,
+ opServiceRemoved = 2,
+ };
+
+protected:
+ SetupLog();
+ ~SetupLog();
+
+public:
+ static SetupLog *Open();
+ static HRESULT Erase();
+
+public:
+ ULONG AddRef();
+ ULONG Release();
+
+ HRESULT LogServiceById(UINT serviceUid, UINT operation);
+ HRESULT LogService(ifc_omservice *service, UINT operation);
+ HRESULT Save();
+ HRESULT Send(HANDLE completeEvent);
+
+ BOOL IsOperationSupported(UINT operation);
+
+
+
+protected:
+ typedef std::map<UINT, UINT> ServiceMap;
+ friend static size_t SetupLog_GetMaxServiceIdCount(SetupLog::ServiceMap *serviceMap);
+ friend static HRESULT SetupLog_FormatServiceId(SetupLog::ServiceMap *serviceMap, INT operation, LPSTR pszBuffer, size_t cchBufferMax);
+
+protected:
+ ULONG ref;
+ ServiceMap serviceMap;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPLOG_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupPage.cpp b/Src/Plugins/Library/ml_online/Setup/setupPage.cpp
new file mode 100644
index 00000000..8b2db86d
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupPage.cpp
@@ -0,0 +1,648 @@
+#include "./setupPage.h"
+#include "./setupListbox.h"
+#include "./setupGroupList.h"
+#include "./setupGroupFilter.h"
+#include "./setupListboxLabel.h"
+#include "../common.h"
+#include "../config.h"
+#include "../resource.h"
+#include "../api__ml_online.h"
+
+#include "./setupDetails.h"
+#include "./setupLog.h"
+
+#include "../../winamp/setup/svc_setup.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omfilestorage.h>
+#include <ifc_omwebstorage.h>
+
+#include <windows.h>
+#include <strsafe.h>
+#include <vector>
+
+#define FEATURED_SERVICES_URL L"http://services.winamp.com/svc/default"
+
+#define IDC_DETAILS 10000
+
+typedef std::vector<ifc_omservice*> ServiceList;
+typedef std::vector<UINT> ServiceIdList;
+
+HWND SetupPage_CreateWindow(HWND hParent, SetupPage *page);
+static INT_PTR SetupPage_ModalLoop(HWND hwnd, HACCEL hAccel, HANDLE hCancel);
+
+struct AppendServiceToStringData
+{
+ AppendServiceToStringData() : formatFirst(NULL), formatNext(NULL), cursor(NULL),
+ remaining(0), inserted(0), result(S_OK) {}
+
+ LPCWSTR formatFirst;
+ LPCWSTR formatNext;
+ LPWSTR cursor;
+ size_t remaining;
+ size_t inserted;
+ HRESULT result;
+};
+
+static BOOL CALLBACK SetupPage_AppendServiceToStringCallback(UINT serviceId, void *data)
+{
+ AppendServiceToStringData *param = (AppendServiceToStringData*)data;
+ if (NULL == param) return FALSE;
+
+ param->result = StringCchPrintfEx(param->cursor, param->remaining, &param->cursor, &param->remaining,
+ STRSAFE_NULL_ON_FAILURE,
+ ((0 == param->inserted) ? param->formatFirst : param->formatNext), serviceId);
+
+ if (FAILED(param->result))
+ return FALSE;
+
+ param->inserted++;
+ return TRUE;
+}
+
+static BOOL CALLBACK SetupPage_AppendServiceIdCallback(UINT serviceId, void *data)
+{
+ ServiceIdList *list = (ServiceIdList*)data;
+ if (NULL == list) return FALSE;
+ list->push_back(serviceId);
+ return TRUE;
+}
+
+static HRESULT SetupPage_GetFeaturedServicesUrl(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ LPWSTR cursor = pszBuffer;
+ size_t remaining = cchBufferMax;
+
+ HRESULT hr = StringCchCopyEx(cursor, remaining, FEATURED_SERVICES_URL, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE);
+ if (FAILED(hr))
+ return hr;
+
+ AppendServiceToStringData param;
+ param.cursor = cursor;
+ param.remaining = remaining;
+ param.formatFirst = L"?svc_ids=%u";
+ param.formatNext = L",%u";
+ param.inserted = 0;
+ param.result = S_OK;
+
+ hr = Config_ReadServiceIdList("Setup", "featuredExtra", ',', SetupPage_AppendServiceToStringCallback, &param);
+ if (SUCCEEDED(hr))
+ hr = param.result;
+
+ return hr;
+}
+
+SetupPage* SetupPage::CreateInstance()
+{
+ SetupPage *instance = new SetupPage();
+ return instance;
+}
+
+SetupPage::SetupPage()
+ : ref(1), hwnd(NULL), name(NULL), title(NULL),
+ groupList(NULL), completeEvent(NULL),
+ servicesInitialized(FALSE)
+{
+ WasabiApi_AddRef();
+ SetupDetails_Initialize();
+}
+
+SetupPage::~SetupPage()
+{
+ if (NULL != name)
+ {
+ Plugin_FreeString(name);
+ name = NULL;
+ }
+
+ if (NULL != title)
+ {
+ Plugin_FreeString(title);
+ title = NULL;
+ }
+
+ if (NULL != groupList)
+ {
+ groupList->Release();
+ groupList = NULL;
+ }
+
+ if (NULL != completeEvent)
+ {
+ CloseHandle(completeEvent);
+ completeEvent = NULL;
+ }
+
+ SetupDetails_Uninitialize();
+
+ if (FALSE != servicesInitialized)
+ {
+ if (NULL != OMBROWSERMNGR)
+ OMBROWSERMNGR->Finish();
+ }
+
+ WasabiApi_Release();
+}
+
+size_t SetupPage::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t SetupPage::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int SetupPage::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+ return E_NOTIMPL;
+}
+
+HRESULT SetupPage::GetName(bool bShort, const wchar_t **pszName)
+{
+ InitializeServices();
+
+ if (false == bShort)
+ {
+ if (NULL == title)
+ {
+ WCHAR szBuffer[128] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_SETUPPAGE_TITLE, szBuffer, ARRAYSIZE(szBuffer));
+ title = Plugin_CopyString(szBuffer);
+ }
+ *pszName = title;
+ }
+ else
+ {
+ if (NULL == name)
+ {
+ WCHAR szBuffer[128] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_ONLINE_SERVICES, szBuffer, ARRAYSIZE(szBuffer));
+ name = Plugin_CopyString(szBuffer);
+ }
+ *pszName = name;
+ }
+ return S_OK;
+}
+
+HRESULT SetupPage::Save(HWND hwndText)
+{
+ if (NULL == groupList)
+ return S_OK;
+
+ SetupLog *log = SetupLog::Open();
+ HRESULT hr = groupList->Save(log);
+
+ if (NULL != log)
+ {
+ log->Save();
+ log->Release();
+ }
+
+ return hr;
+}
+
+HRESULT SetupPage::Revert(void)
+{
+ HRESULT hr(S_OK);
+
+ if (NULL != groupList)
+ {
+ groupList->Release();
+ groupList = NULL;
+ }
+ return hr;
+}
+
+HRESULT SetupPage::IsDirty(void)
+{
+ return (NULL != groupList && groupList->IsModified()) ? S_OK : S_FALSE;
+}
+
+HRESULT SetupPage::Validate(void)
+{
+ return S_OK;
+}
+
+HRESULT SetupPage::InitializeServices()
+{
+ if (FALSE != servicesInitialized)
+ return S_FALSE;
+
+ HWND hWinamp = NULL;
+ svc_setup *setupSvc = QueryWasabiInterface(svc_setup, UID_SVC_SETUP);
+ if (NULL == setupSvc) return E_UNEXPECTED;
+ HRESULT hr = setupSvc->GetWinampWnd(&hWinamp);
+ ReleaseWasabiInterface(UID_SVC_SETUP, setupSvc);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = WasabiApi_LoadDefaults();
+ if (SUCCEEDED(hr))
+ {
+ if (NULL != OMBROWSERMNGR &&
+ NULL != OMSERVICEMNGR &&
+ NULL != OMUTILITY)
+ {
+ hr = OMBROWSERMNGR->Initialize(NULL, hWinamp);
+ }
+ else
+ hr = E_UNEXPECTED;
+
+ if (SUCCEEDED(hr))
+ servicesInitialized = TRUE;
+ }
+ }
+
+ return hr;
+}
+
+HRESULT SetupPage::CreateView(HWND hParent, HWND *phwnd)
+{
+ if (NULL == phwnd)
+ return E_INVALIDARG;
+
+ if (FAILED(InitializeServices()))
+ {
+ *phwnd = NULL;
+ return E_FAIL;
+ }
+
+ *phwnd = SetupPage_CreateWindow(hParent, this);
+ return (NULL == phwnd) ? E_FAIL : S_OK;
+}
+
+
+BOOL SetupPage::UpdateListAsync(INT groupId)
+{
+ if (NULL == hwnd)
+ return FALSE;
+
+ return PostMessage(hwnd, SPM_UPDATELIST, (WPARAM)groupId, NULL);
+}
+
+BOOL SetupPage::AttachWindow(HWND hAttach)
+{
+ hwnd = hAttach;
+
+ if (NULL == completeEvent)
+ completeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+
+ if (NULL == groupList)
+ {
+ groupList = SetupGroupList::CreateInstance();
+ if (NULL != groupList)
+ {
+ WCHAR szBuffer[4096] = {0};
+ SetupPage_GetFeaturedServicesUrl(szBuffer, ARRAYSIZE(szBuffer));
+
+ SetupGroup *group = SetupGroup::CreateInstance(ID_FEATUREDGROUP, MAKEINTRESOURCE(IDS_SERVICEGROUP_FEATURED),
+ szBuffer, &SUID_OmStorageUrl, &FUID_SetupFeaturedGroupFilter,
+ SetupGroup::styleSortAlphabetically | SetupGroup::styleDefaultSubscribed | SetupGroup::styleSaveAll);
+ if (NULL != group)
+ {
+ group->SetLongName(MAKEINTRESOURCE(IDS_SERVICEGROUP_FEATUREDLONG));
+ group->SetDescription(MAKEINTRESOURCE(IDS_SERVICEGROUP_FEATURED_DESC));
+ groupList->AddGroup(group);
+ group->RequestReload();
+ group->Release();
+ }
+
+ group = SetupGroup::CreateInstance(ID_KNOWNGROUP, MAKEINTRESOURCE(IDS_SERVICEGROUP_KNOWN),
+ L"*.ini", &SUID_OmStorageIni, &FUID_SetupKnownGroupFilter,
+ SetupGroup::styleSortAlphabetically);
+ if (NULL != group)
+ {
+ group->SetLongName(MAKEINTRESOURCE(IDS_SERVICEGROUP_KNOWNLONG));
+ group->SetDescription(MAKEINTRESOURCE(IDS_SERVICEGROUP_KNOWN_DESC));
+ group->RequestReload();
+ groupList->AddGroup(group);
+ group->Release();
+ }
+ }
+ }
+
+ if (NULL != groupList)
+ groupList->SetPageWnd(hwnd);
+
+ HWND hList = GetDlgItem(hwnd, IDC_SERVICELIST);
+ if (NULL != hList)
+ {
+ SetupListbox *listbox;
+ if (SUCCEEDED(SetupListbox::CreateInstance(hList, groupList, &listbox)))
+ {
+ }
+ ListboxSelectionChanged();
+ }
+
+ return TRUE;
+}
+
+void SetupPage::DetachWindow()
+{
+ hwnd = NULL;
+ if (NULL != groupList)
+ {
+ groupList->SetPageWnd(NULL);
+ }
+}
+
+
+HRESULT SetupPage_AppendUnselectedServices(LPCSTR pszSection, LPCSTR pszKey, SetupGroup *group)
+{
+ size_t index = group->GetRecordCount();
+ size_t filterIndex, filterSize;
+
+ ServiceIdList list;
+ Config_ReadServiceIdList(pszSection, pszKey, ',', SetupPage_AppendServiceIdCallback, &list);
+ filterSize = list.size();
+
+ while(index--)
+ {
+ SetupRecord *record = group->GetRecord(index);
+ if (NULL == record || FALSE != record->IsSelected()) continue;
+
+ ifc_omservice *service = record->GetService();
+ if (NULL == service) continue;
+
+ UINT serviceId = service->GetId();
+ for(filterIndex = 0; filterIndex < filterSize; filterIndex++)
+ {
+ if (list[filterIndex] == serviceId)
+ break;
+ }
+
+ if (filterIndex == filterSize)
+ list.push_back(serviceId);
+ }
+
+ if (0 != list.size())
+ {
+ size_t bufferAlloc = (list.size() * 11) * sizeof(CHAR);
+ LPSTR buffer = Plugin_MallocAnsiString(bufferAlloc);
+ if (NULL != buffer)
+ {
+ filterSize = list.size();
+ LPSTR cursor = buffer;
+ size_t remaining = bufferAlloc;
+ for(index = 0; index < filterSize; index++)
+ {
+ if (FAILED(StringCchPrintfExA(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ ((0 == index) ? "%u" : ",%u"), list[index])))
+ {
+ break;
+ }
+ }
+ Config_WriteStr(pszSection, pszKey, buffer);
+ Plugin_FreeAnsiString(buffer);
+ }
+ }
+ else
+ Config_WriteStr(pszSection, pszKey, NULL);
+
+ return S_OK;
+}
+
+HRESULT SetupPage::Execute(HWND hwndText)
+{
+ SetupGroup *group = NULL;
+ SetupLog *log = SetupLog::Open();
+ WCHAR szBuffer[128] = {0};
+
+ if (FAILED(InitializeServices()))
+ return E_FAIL;
+
+ if (NULL == groupList)
+ {
+ groupList = SetupGroupList::CreateInstance();
+ if (NULL == groupList) return E_UNEXPECTED;
+ }
+
+ if (S_OK != groupList->FindGroupById(ID_FEATUREDGROUP, &group) && group != NULL)
+ {
+ WCHAR szBuffer[4096] = {0};
+ SetupPage_GetFeaturedServicesUrl(szBuffer, ARRAYSIZE(szBuffer));
+
+ group = SetupGroup::CreateInstance(ID_FEATUREDGROUP, MAKEINTRESOURCE(IDS_SERVICEGROUP_FEATURED),
+ szBuffer, &SUID_OmStorageUrl, &FUID_SetupFeaturedGroupFilter,
+ SetupGroup::styleDefaultSubscribed | SetupGroup::styleSaveAll);
+
+ if (NULL != group)
+ {
+ group->SetLongName(MAKEINTRESOURCE(IDS_SERVICEGROUP_FEATUREDLONG));
+ group->SetDescription(MAKEINTRESOURCE(IDS_SERVICEGROUP_FEATURED_DESC));
+ groupList->AddGroup(group);
+ group->RequestReload();
+ }
+ }
+
+ if (NULL == completeEvent)
+ completeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (NULL != group)
+ {
+ if (SUCCEEDED(group->SignalLoadCompleted(completeEvent)))
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_DOWNLOADSERVICE_JOB, szBuffer, ARRAYSIZE(szBuffer));
+ SetWindowText(hwndText, szBuffer);
+ SetupPage_ModalLoop(hwndText, NULL, completeEvent);
+ }
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_SAVESERVICE_JOB, szBuffer, ARRAYSIZE(szBuffer));
+ SetWindowText(hwndText, szBuffer);
+ group->Save(log);
+ SetupPage_AppendUnselectedServices("Setup", "featuredHistory", group);
+ group->Release();
+ group = NULL;
+ }
+
+ // ensure that promotions are subscribed
+ if (S_OK != groupList->FindGroupById(ID_KNOWNGROUP, &group) && group != NULL)
+ {
+ group = SetupGroup::CreateInstance(ID_KNOWNGROUP, MAKEINTRESOURCE(IDS_SERVICEGROUP_KNOWN),
+ L"*.ini", &SUID_OmStorageIni, &FUID_SetupKnownGroupFilter,
+ SetupGroup::styleSortAlphabetically);
+ if (NULL != group)
+ {
+ group->SetLongName(MAKEINTRESOURCE(IDS_SERVICEGROUP_KNOWNLONG));
+ group->SetDescription(MAKEINTRESOURCE(IDS_SERVICEGROUP_KNOWN_DESC));
+ group->RequestReload();
+ groupList->AddGroup(group);
+ }
+ }
+
+ if (NULL != group)
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_SAVESERVICE_JOB, szBuffer, ARRAYSIZE(szBuffer));
+ SetWindowText(hwndText, szBuffer);
+
+ ResetEvent(completeEvent);
+ if (SUCCEEDED(group->SignalLoadCompleted(completeEvent)))
+ SetupPage_ModalLoop(hwndText, NULL, completeEvent);
+
+ group->Save(log);
+ group->Release();
+ group = NULL;
+ }
+
+ if (NULL != log)
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_REGISTERINGSERVICE_JOB, szBuffer, ARRAYSIZE(szBuffer));
+ SetWindowText(hwndText, szBuffer);
+ ResetEvent(completeEvent);
+ log->Send(completeEvent);
+ SetupPage_ModalLoop(hwndText, NULL, completeEvent);
+ log->Release();
+ }
+
+ SetupLog::Erase();
+ Config_WriteStr("Setup", "featuredExtra", NULL); // delete promo offer
+ return S_OK;
+}
+
+HRESULT SetupPage::Cancel(HWND hwndText)
+{
+ if (NULL != completeEvent)
+ SetEvent(completeEvent);
+ return S_OK;
+}
+
+HRESULT SetupPage::IsCancelSupported(void)
+{
+ return S_OK;
+}
+void SetupPage::ListboxSelectionChanged()
+{
+ if (NULL == hwnd) return;
+ HWND hList = GetDlgItem(hwnd, IDC_SERVICELIST);
+ if (NULL == hList) return;
+
+ SetupListboxItem *item = NULL;
+ INT iSelection = (INT)SendMessage(hList, LB_GETCURSEL, 0, 0L);
+ if (LB_ERR == iSelection ||
+ FAILED(groupList->FindListboxItem(iSelection, &item)))
+ {
+ item = NULL;
+ }
+
+ HWND hDiscard = GetDlgItem(hwnd, IDC_DETAILS);
+ if (NULL != hDiscard)
+ {
+ WCHAR szPanel[64] = {0}, szItem[64] = {0};
+ if (FALSE != SetupDetails_GetUniqueName(hDiscard, szPanel, ARRAYSIZE(szPanel)) &&
+ FALSE != item->GetUniqueName(szItem, ARRAYSIZE(szItem)) &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, 0, szPanel, - 1, szItem, -1))
+ {
+ return;
+ }
+ }
+
+ HWND hDetails = (NULL != item) ? item->CreateDetailsView(hwnd) : NULL;
+ if (NULL != hDiscard)
+ {
+ SetWindowLongPtr(hDiscard, GWL_STYLE, GetWindowLongPtr(hDiscard, GWL_STYLE) & ~WS_VISIBLE);
+ }
+
+ if (NULL != hDetails)
+ {
+ HWND hPlaceholder = GetDlgItem(hwnd, IDC_PLACEHOLDER);
+ if (NULL != hPlaceholder)
+ {
+ RECT windowRect;
+ if (GetWindowRect(hPlaceholder, &windowRect))
+ {
+ MapWindowPoints(HWND_DESKTOP,hwnd, (POINT*)&windowRect,2);
+ SetWindowPos(hDetails, NULL, windowRect.left, windowRect.top,
+ windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
+ SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ SetWindowLongPtr(hDetails, GWLP_ID, IDC_DETAILS);
+ ShowWindow(hDetails, SW_SHOWNA);
+ RedrawWindow(hDetails, NULL, NULL, RDW_ERASENOW |RDW_UPDATENOW | RDW_ALLCHILDREN);
+ }
+ }
+
+ if (NULL != hDiscard)
+ {
+ DestroyWindow(hDiscard);
+ }
+}
+
+static INT_PTR SetupPage_ModalLoop(HWND hwnd, HACCEL hAccel, HANDLE hCancel)
+{
+ MSG msg = {0};
+ HWND hParent = NULL;
+ while (NULL != (hParent = GetAncestor(hwnd, GA_PARENT)))
+ {
+ hwnd = hParent;
+ }
+
+ if (NULL == hwnd)
+ return 0;
+
+ for (;;)
+ {
+ DWORD status = MsgWaitForMultipleObjectsEx(1, &hCancel, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
+ if (WAIT_OBJECT_0 == status)
+ {
+ return 0;
+ }
+ else if ((WAIT_OBJECT_0 + 1)== status)
+ {
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ //if (!CallMsgFilter(&msg, MSGF_DIALOGBOX))
+ {
+ if (msg.message == WM_QUIT)
+ {
+ PostQuitMessage((INT)msg.wParam);
+ return msg.wParam;
+ }
+
+ if (!TranslateAcceleratorW(hwnd, hAccel, &msg) &&
+ !IsDialogMessageW(hwnd, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+#define CBCLASS SetupPage
+START_MULTIPATCH;
+ START_PATCH(MPIID_SETUPPAGE)
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, ADDREF, AddRef);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, RELEASE, Release);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, QUERYINTERFACE, QueryInterface);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, API_SETUPPAGE_GET_NAME, GetName);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, API_SETUPPAGE_CREATEVIEW, CreateView);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, API_SETUPPAGE_SAVE, Save);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, API_SETUPPAGE_REVERT, Revert);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, API_SETUPPAGE_ISDIRTY, IsDirty);
+ M_CB(MPIID_SETUPPAGE, ifc_setuppage, API_SETUPPAGE_VALIDATE, Validate);
+
+ NEXT_PATCH(MPIID_SETUPJOB)
+ M_CB(MPIID_SETUPJOB, ifc_setupjob, ADDREF, AddRef);
+ M_CB(MPIID_SETUPJOB, ifc_setupjob, RELEASE, Release);
+ M_CB(MPIID_SETUPJOB, ifc_setupjob, QUERYINTERFACE, QueryInterface);
+ M_CB(MPIID_SETUPJOB, ifc_setupjob, API_SETUPJOB_EXECUTE, Execute);
+ M_CB(MPIID_SETUPJOB, ifc_setupjob, API_SETUPJOB_CANCEL, Cancel);
+ M_CB(MPIID_SETUPJOB, ifc_setupjob, API_SETUPJOB_ISCANCELSUPPORTED, IsCancelSupported);
+
+ END_PATCH
+END_MULTIPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupPage.h b/Src/Plugins/Library/ml_online/Setup/setupPage.h
new file mode 100644
index 00000000..ea45357f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupPage.h
@@ -0,0 +1,85 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPPAGE_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPPAGE_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include <bfc/multipatch.h>
+
+#include "../../winamp/setup/ifc_setuppage.h"
+#include "../../winamp/setup/ifc_setupjob.h"
+
+#include "./setupGroupList.h"
+
+class SetupListboxLabel;
+
+#define ID_KNOWNGROUP 0
+#define ID_FEATUREDGROUP 1
+
+#define MPIID_SETUPPAGE 10
+#define MPIID_SETUPJOB 20
+
+#define SPM_FIRST (WM_APP + 2)
+#define SPM_UPDATELIST (SPM_FIRST + 0)
+
+class SetupPage : public MultiPatch<MPIID_SETUPPAGE, ifc_setuppage>,
+ public MultiPatch<MPIID_SETUPJOB, ifc_setupjob>
+{
+protected:
+ typedef enum
+ {
+ flagInitWasabi = 0x00000001,
+ };
+
+protected:
+ SetupPage();
+ virtual ~SetupPage();
+
+public:
+ static SetupPage* CreateInstance();
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_setuppage */
+ HRESULT GetName(bool bShort, const wchar_t **pszName);
+ HRESULT Save(HWND hText);
+ HRESULT CreateView(HWND hParent, HWND *phwnd);
+ HRESULT Revert(void);
+ HRESULT IsDirty(void);
+ HRESULT Validate(void);
+
+ /* ifc_setupjob */
+ HRESULT Execute(HWND hwndText);
+ HRESULT Cancel(HWND hwndText);
+ HRESULT IsCancelSupported(void);
+
+public:
+ BOOL AttachWindow(HWND hAttach);
+ void DetachWindow();
+
+ void ListboxSelectionChanged();
+ BOOL UpdateListAsync(INT groupId);
+
+protected:
+ HRESULT InitializeServices();
+
+private:
+ size_t ref;
+ HWND hwnd;
+ LPWSTR name;
+ LPWSTR title;
+ SetupGroupList *groupList;
+ HANDLE completeEvent;
+ BOOL servicesInitialized;
+
+protected:
+ RECVS_MULTIPATCH;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPPAGE_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupPageWnd.cpp b/Src/Plugins/Library/ml_online/Setup/setupPageWnd.cpp
new file mode 100644
index 00000000..338df688
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupPageWnd.cpp
@@ -0,0 +1,145 @@
+#include "./setupPage.h"
+#include "./setupListbox.h"
+#include "../common.h"
+#include "../resource.h"
+#include "../api__ml_online.h"
+
+static ATOM SETUPPAGE_PROP = 0;
+
+#define GetPage(__hwnd) ((SetupPage*)GetPropW((__hwnd), MAKEINTATOM(SETUPPAGE_PROP)))
+
+static INT_PTR WINAPI SetupPage_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+
+
+HWND SetupPage_CreateWindow(HWND hParent, SetupPage *page)
+{
+ return WASABI_API_CREATEDIALOGPARAMW(IDD_SETUPPAGE, hParent, SetupPage_DialogProc, (LPARAM)page);
+}
+
+static INT_PTR SetupPage_OnInitDialog(HWND hwnd, HWND hFocus, LPARAM lParam)
+{
+ if (0 == SETUPPAGE_PROP)
+ {
+ SETUPPAGE_PROP = GlobalAddAtom(L"omSetupPageProp");
+ if (0 == SETUPPAGE_PROP) return FALSE;
+ }
+
+ SetupPage *page = (SetupPage*)lParam;
+ if (NULL != page && page->AttachWindow(hwnd))
+ {
+ SetProp(hwnd, MAKEINTATOM(SETUPPAGE_PROP), page);
+ }
+
+ return FALSE;
+}
+
+static void SetupPage_OnDestroy(HWND hwnd)
+{
+ SetupPage *page = GetPage(hwnd);
+ if (NULL != page)
+ {
+ page->DetachWindow();
+ }
+ RemoveProp(hwnd, MAKEINTATOM(SETUPPAGE_PROP));
+}
+
+static void SetupPage_OnCommand(HWND hwnd, INT controlId, INT eventId, HWND hControl)
+{
+ SetupPage *page;
+ switch(controlId)
+ {
+ case IDC_SERVICELIST:
+ switch(eventId)
+ {
+ case LBN_SELCHANGE:
+ page = GetPage(hwnd);
+ if (NULL != page)
+ page->ListboxSelectionChanged();
+ break;
+ }
+ break;
+ }
+}
+
+static BOOL SetupPage_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT *pmis)
+{
+ SetupListbox *instance;
+ switch(pmis->CtlID)
+ {
+ case IDC_SERVICELIST:
+ instance = SetupListbox::GetInstance(GetDlgItem(hwnd, pmis->CtlID));
+ if (NULL != instance)
+ {
+ return instance->MeasureItem(pmis->itemID, &pmis->itemWidth, &pmis->itemHeight);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static BOOL SetupPage_OnDrawItem(HWND hwnd, DRAWITEMSTRUCT *pdis)
+{
+ SetupListbox *instance;
+ switch(pdis->CtlID)
+ {
+ case IDC_SERVICELIST:
+ instance = SetupListbox::GetInstance(pdis->hwndItem);
+ if (NULL != instance)
+ {
+ return instance->DrawItem(pdis->hDC, &pdis->rcItem, pdis->itemID, pdis->itemState, pdis->itemAction);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static INT_PTR SetupPage_OnCharToItem(HWND hwnd, INT vKey, INT caretPos, HWND hList)
+{
+
+ if (IDC_SERVICELIST == GetDlgCtrlID(hList))
+ {
+ SetupListbox *instance = SetupListbox::GetInstance(hList);
+ if (NULL != instance)
+ return instance->CharToItem(vKey, caretPos);
+ }
+ return -1;
+}
+
+static INT_PTR SetupPage_OnKeyToItem(HWND hwnd, INT vKey, INT caretPos, HWND hList)
+{
+ if (IDC_SERVICELIST == GetDlgCtrlID(hList))
+ {
+ SetupListbox *instance = SetupListbox::GetInstance(hList);
+ if (NULL != instance)
+ return instance->KeyToItem(vKey, caretPos);
+ }
+ return -1;
+}
+static void SetupPage_OnUpdateList(HWND hwnd, INT groupId)
+{
+ HWND hList = GetDlgItem(hwnd, IDC_SERVICELIST);
+ if (NULL != hList)
+ {
+ SetupListbox *listbox = SetupListbox::GetInstance(hList);
+ if (NULL != listbox)
+ {
+ listbox->UpdateCount();
+ }
+ }
+}
+static INT_PTR WINAPI SetupPage_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return SetupPage_OnInitDialog(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: SetupPage_OnDestroy(hwnd); break;
+ case WM_COMMAND: SetupPage_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
+ case WM_MEASUREITEM: return SetupPage_OnMeasureItem(hwnd, (MEASUREITEMSTRUCT*)lParam);
+ case WM_DRAWITEM: return SetupPage_OnDrawItem(hwnd, (DRAWITEMSTRUCT*)lParam);
+ case WM_CHARTOITEM: return SetupPage_OnCharToItem(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
+ case WM_VKEYTOITEM: return SetupPage_OnKeyToItem(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
+ case SPM_UPDATELIST: SetupPage_OnUpdateList(hwnd, (INT)wParam); return TRUE;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupRecord.cpp b/Src/Plugins/Library/ml_online/Setup/setupRecord.cpp
new file mode 100644
index 00000000..995ed222
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupRecord.cpp
@@ -0,0 +1,567 @@
+#include "./main.h"
+#include "../api__ml_online.h"
+#include "./setupRecord.h"
+#include "./setupDetails.h"
+#include "./setupLog.h"
+#include "../resource.h"
+
+#include "./serviceHelper.h"
+#include "./serviceHost.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omwebstorage.h>
+#include <ifc_omserviceenum.h>
+#include <ifc_omservicecopier.h>
+
+#include <wininet.h>
+#include <strsafe.h>
+
+#define RECORD_MARGINCX 6
+#define RECORD_MARGINCY 2
+#define CHECKBOX_MARGIN_RIGHT 2
+
+#define TEXT_OFFSET_LEFT 3
+#define TEXT_OFFSET_BOTTOM RECORD_MARGINCY
+#define TEXT_ALIGN (TA_LEFT | TA_BOTTOM)
+
+SetupRecord::SetupRecord(ifc_omservice *serviceToUse)
+ : ref(1), service(serviceToUse), flags(0), async(NULL)
+{
+ if (NULL != service)
+ {
+ if (S_OK == ServiceHelper_IsSubscribed(service))
+ flags |= recordSelected;
+ service->AddRef();
+ }
+
+ InitializeCriticalSection(&lock);
+}
+
+SetupRecord::~SetupRecord()
+{
+ EnterCriticalSection(&lock);
+
+ if (NULL != async)
+ {
+ ifc_omstorage *storage = NULL;
+ HRESULT hr = OMSERVICEMNGR->QueryStorage(&SUID_OmStorageUrl, &storage);
+ if (SUCCEEDED(hr) && storage != NULL)
+ {
+ storage->RequestAbort(async, TRUE);
+ }
+
+ async->Release();
+ async = NULL;
+ }
+
+ if (NULL != service)
+ service->Release();
+
+ LeaveCriticalSection(&lock);
+ DeleteCriticalSection(&lock);
+}
+
+SetupRecord *SetupRecord::CreateInstance(ifc_omservice *serviceToUse)
+{
+ if (NULL == serviceToUse) return NULL;
+ return new SetupRecord(serviceToUse);
+}
+
+ULONG SetupRecord::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+ULONG SetupRecord::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+HRESULT SetupRecord::GetServiceName(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == service) return E_UNEXPECTED;
+ return service->GetName(pszBuffer, cchBufferMax);
+}
+
+HRESULT SetupRecord::GetDisplayName(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ HRESULT hr = GetServiceName(pszBuffer, cchBufferMax);
+ if (SUCCEEDED(hr) && L'\0' == *pszBuffer)
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_DEFAULT_SERVICENAME, pszBuffer, cchBufferMax);
+ DownloadDetails();
+ }
+ return hr;
+}
+
+HRESULT SetupRecord::DownloadDetails()
+{
+ HRESULT hr;
+ EnterCriticalSection(&lock);
+ if (NULL == async && 0 == (recordDownloaded & flags))
+ {
+ WCHAR szUrl[INTERNET_MAX_URL_LENGTH] = {0};
+ hr = ServiceHelper_GetDetailsUrl(szUrl, ARRAYSIZE(szUrl), service, FALSE);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *storage = NULL;
+ hr = OMSERVICEMNGR->QueryStorage(&SUID_OmStorageUrl, &storage);
+ if (SUCCEEDED(hr) && storage != NULL)
+ {
+ ServiceHost *serviceHost = NULL;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ hr = storage->BeginLoad(szUrl, serviceHost, SetupRecord_ServiceDownloadedCallback, this, &async);
+ storage->Release();
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+ }
+ }
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+ LeaveCriticalSection(&lock);
+ return hr;
+}
+
+HRESULT SetupRecord::Save(SetupLog *log)
+{
+ if (NULL == service) return E_POINTER;
+
+ HRESULT hr = ServiceHelper_Subscribe(service, IsSelected(), SHF_SAVE);
+ if (S_OK == hr)
+ {
+ if (NULL != log)
+ {
+ INT operation = (IsSelected()) ? SetupLog::opServiceAdded : SetupLog::opServiceRemoved;
+ log->LogService(service, operation);
+ }
+
+ }
+
+ return hr;
+}
+
+BOOL SetupRecord::IsModified()
+{
+ if (NULL == service)
+ return FALSE;
+
+ if (S_OK == ServiceHelper_IsSubscribed(service) != (FALSE != IsSelected()))
+ return TRUE;
+ if (S_OK == ServiceHelper_IsModified(service))
+ return TRUE;
+
+ return FALSE;
+}
+
+BOOL SetupRecord::IsSelected()
+{
+ return (0 != (recordSelected & flags));
+}
+
+void SetupRecord::SetSelected(BOOL fSelected)
+{
+ if ((FALSE == fSelected) == !IsSelected())
+ return;
+
+ if (FALSE == fSelected)
+ flags &= ~recordSelected;
+ else
+ flags |= recordSelected;
+}
+
+BOOL SetupRecord::AdjustCheckboxRect(SetupListbox *instance, RECT *prcItem)
+{
+ SIZE checkSize;
+ if (!instance->GetCheckboxMetrics(NULL, IsSelected(), 0, &checkSize))
+ return FALSE;
+
+ prcItem->left += RECORD_MARGINCX;
+ prcItem->right = prcItem->left + checkSize.cx;
+
+ if (checkSize.cy > (prcItem->bottom - prcItem->top))
+ checkSize.cy = (prcItem->bottom - prcItem->top);
+ prcItem->top += ((prcItem->bottom - prcItem->top) - checkSize.cy) / 2;
+ prcItem->bottom = prcItem->top + checkSize.cy;
+ return TRUE;
+}
+
+BOOL SetupRecord::MeasureItem(SetupListbox *instance, UINT *cx, UINT *cy)
+{
+ HDC hdc = GetDCEx(instance->GetHwnd(), NULL, DCX_CACHE | DCX_NORESETATTRS);
+ if (NULL == hdc) return FALSE;
+
+ HFONT originalFont = (HFONT)SelectObject(hdc, instance->GetFont());
+
+ SIZE checkSize;
+ instance->GetCheckboxMetrics(hdc, IsSelected(), 0, &checkSize);
+
+ if (NULL != cy)
+ {
+ *cy = 0;
+ TEXTMETRIC tm = {0};
+ if (GetTextMetrics(hdc, &tm))
+ {
+ *cy = tm.tmHeight + tm.tmExternalLeading;
+ if (checkSize.cy > (INT)*cy) *cy = checkSize.cy;
+ *cy += RECORD_MARGINCY*2;
+ }
+ }
+
+ if (NULL != cx)
+ {
+ *cx = checkSize.cx;
+ WCHAR szBuffer[128] = {0};
+ if (SUCCEEDED(GetDisplayName(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ INT cchBuffer = lstrlenW(szBuffer);
+ SIZE textSize;
+ if (0 != cchBuffer && GetTextExtentPoint32(hdc, szBuffer, cchBuffer, &textSize))
+ {
+ *cx += textSize.cx;
+ if (0 != checkSize.cx)
+ *cx += CHECKBOX_MARGIN_RIGHT + RECORD_MARGINCX;
+ }
+ }
+ if (0 != *cx) *cx += RECORD_MARGINCX*2;
+ }
+
+ SelectObject(hdc, originalFont);
+ ReleaseDC(instance->GetHwnd(), hdc);
+ return TRUE;
+}
+
+void SetupRecord::GetColors(HDC hdc, UINT state, COLORREF *rgbBkOut, COLORREF *rgbTextOut)
+{
+ COLORREF rgbBk, rgbText;
+
+ if (0 != (ODS_DISABLED & state))
+ {
+ rgbBk = GetBkColor(hdc);
+ rgbText = GetSysColor(COLOR_GRAYTEXT);
+ }
+ else if (0 != (ODS_SELECTED & state))
+ {
+ if (0 == (ODS_INACTIVE & state))
+ {
+ rgbBk = GetSysColor(COLOR_HIGHLIGHT);
+ rgbText = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ }
+ else
+ {
+ rgbBk = GetSysColor(COLOR_3DFACE);
+ rgbText = GetTextColor(hdc);
+ }
+ }
+ else
+ {
+ rgbBk = GetBkColor(hdc);
+ rgbText = GetTextColor(hdc);
+ }
+
+ if (NULL != rgbBkOut) *rgbBkOut = rgbBk;
+ if (NULL != rgbTextOut) *rgbTextOut = rgbText;
+}
+
+BOOL SetupRecord::DrawItem(SetupListbox *instance, HDC hdc, const RECT *prc, UINT state)
+{
+ LONG paintLeft = prc->left + RECORD_MARGINCX;
+ RECT partRect;
+
+ UINT checkState = state & ~ODS_SELECTED;
+ if (0 == (checkboxPressed & flags))
+ {
+ if (0 != (checkboxHighlighted & flags))
+ checkState |= ODS_HOTLIGHT;
+ }
+ else
+ {
+ checkState |= ((0 != (checkboxHighlighted & flags)) ? ODS_SELECTED : ODS_HOTLIGHT);
+ }
+
+ HRGN backRgn, rgn;
+ backRgn = CreateRectRgnIndirect(prc);
+ rgn = CreateRectRgn(0,0,0,0);
+
+ SetRectEmpty(&partRect);
+ instance->GetCheckboxMetrics(hdc, IsSelected(), checkState, (((SIZE*)&partRect) + 1));
+
+ INT space = (prc->bottom - prc->top) - (partRect.bottom- partRect.top);
+ INT offsetY = space / 2 + space%2;
+ if (offsetY < 0) offsetY = 0;
+
+ OffsetRect(&partRect, paintLeft, prc->top + offsetY);
+ if (instance->DrawCheckbox(hdc, IsSelected(), checkState, &partRect, prc))
+ {
+ paintLeft = partRect.right + CHECKBOX_MARGIN_RIGHT;
+ if (SetRectRgn(rgn, partRect.left, partRect.top, partRect.right, partRect.bottom))
+ CombineRgn(backRgn, backRgn, rgn, RGN_DIFF);
+
+ }
+
+ COLORREF rgbBk, rgbText;
+ GetColors(hdc, state, &rgbBk, &rgbText);
+
+ COLORREF origBk = SetBkColor(hdc, rgbBk);
+ COLORREF origText = SetTextColor(hdc, rgbText);
+ UINT textAlign = SetTextAlign(hdc, TEXT_ALIGN);
+
+ WCHAR szBuffer[128] = {0};
+ INT cchBuffer = 0;
+ if (SUCCEEDED(GetDisplayName(szBuffer, ARRAYSIZE(szBuffer))))
+ cchBuffer = lstrlenW(szBuffer);
+
+ SetRect(&partRect, paintLeft, prc->top, prc->right, prc->bottom);
+ if (ExtTextOut(hdc, partRect.left + TEXT_OFFSET_LEFT, partRect.bottom - TEXT_OFFSET_BOTTOM,
+ ETO_OPAQUE | ETO_CLIPPED, &partRect, szBuffer, cchBuffer, NULL))
+ {
+ if (SetRectRgn(rgn, partRect.left, partRect.top, partRect.right, partRect.bottom))
+ CombineRgn(backRgn, backRgn, rgn, RGN_DIFF);
+ }
+
+ if (ODS_FOCUS == ((ODS_FOCUS | 0x0200/*ODS_NOFOCUSRECT*/) & state))
+ DrawFocusRect(hdc, &partRect);
+
+ if (NULL != backRgn)
+ {
+ PaintRgn(hdc, backRgn);
+ DeleteObject(backRgn);
+ }
+ if (NULL != rgn)
+ DeleteObject(rgn);
+
+ if (TEXT_ALIGN != textAlign) SetTextAlign(hdc, textAlign);
+ if (origBk != rgbBk) SetBkColor(hdc, origBk);
+ if (origText != rgbText) SetTextColor(hdc, origText);
+
+ return TRUE;
+}
+
+INT_PTR SetupRecord::KeyToItem(SetupListbox *instance, const RECT *prcItem, INT vKey)
+{
+ switch(vKey)
+ {
+ case VK_SPACE:
+ InvertCheckbox(instance, prcItem);
+ return -2;
+ }
+ return -1;
+}
+
+BOOL SetupRecord::MouseMove(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ RECT checkboxRect;
+ BOOL fInvalidate = FALSE;
+ CopyRect(&checkboxRect, prcItem);
+ AdjustCheckboxRect(instance, &checkboxRect);
+
+ if (prcItem->top <= pt.y && pt.y < prcItem->bottom &&
+ checkboxRect.left <= pt.x && pt.x < checkboxRect.right)
+ {
+ if (0 == (checkboxHighlighted & flags))
+ {
+ flags |= checkboxHighlighted;
+ fInvalidate = TRUE;
+ }
+ }
+ else
+ {
+ if (0 != (checkboxHighlighted & flags))
+ {
+ flags &= ~checkboxHighlighted;
+ fInvalidate = TRUE;
+ }
+ }
+
+ if (FALSE != fInvalidate)
+ instance->InvalidateRect(&checkboxRect, FALSE);
+
+ return FALSE;
+}
+
+BOOL SetupRecord::MouseLeave(SetupListbox *instance, const RECT *prcItem)
+{
+ if (0 != (checkboxHighlighted & flags))
+ {
+ flags &= ~checkboxHighlighted;
+ RECT checkboxRect;
+ CopyRect(&checkboxRect, prcItem);
+ AdjustCheckboxRect(instance, &checkboxRect);
+ instance->InvalidateRect(&checkboxRect, FALSE);
+ }
+ return FALSE;
+}
+
+BOOL SetupRecord::LButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ RECT checkboxRect;
+ BOOL handled = FALSE;
+ CopyRect(&checkboxRect, prcItem);
+ AdjustCheckboxRect(instance, &checkboxRect);
+
+ if (prcItem->top <= pt.y && pt.y < prcItem->bottom &&
+ prcItem->left <= pt.x && pt.x < (checkboxRect.right + CHECKBOX_MARGIN_RIGHT))
+ {
+ handled = TRUE;
+ if (checkboxRect.left <= pt.x && pt.x < checkboxRect.right)
+ {
+ flags |= (checkboxHighlighted | checkboxPressed);
+ instance->SetCapture(this);
+ instance->InvalidateRect(&checkboxRect, FALSE);
+ }
+ }
+ return handled;
+}
+
+BOOL SetupRecord::LButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ RECT checkboxRect;
+ BOOL handled = FALSE;
+ CopyRect(&checkboxRect, prcItem);
+ AdjustCheckboxRect(instance, &checkboxRect);
+
+ if (0 != (checkboxPressed & flags))
+ {
+ flags &= ~checkboxPressed;
+ if (this == instance->GetCapture())
+ instance->ReleaseCapture();
+
+ if (prcItem->top <= pt.y && pt.y < prcItem->bottom &&
+ checkboxRect.left <= pt.x && pt.x < checkboxRect.right)
+ {
+ SetSelected(!IsSelected());
+ handled = TRUE;
+ }
+ instance->InvalidateRect(&checkboxRect, FALSE);
+ }
+ return handled;
+}
+
+BOOL SetupRecord::LButtonDblClk(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ InvertCheckbox(instance, prcItem);
+ return TRUE;
+}
+
+BOOL SetupRecord::RButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+
+BOOL SetupRecord::RButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt)
+{
+ return FALSE;
+}
+
+void SetupRecord::CaptureChanged(SetupListbox *instance, const RECT *prcItem, SetupListboxItem *captured)
+{
+}
+
+void SetupRecord::InvertCheckbox(SetupListbox *instance, const RECT *prcItem)
+{
+ SetSelected(!IsSelected());
+
+ if (NULL != instance && NULL != prcItem)
+ {
+ RECT checkboxRect;
+ CopyRect(&checkboxRect, prcItem);
+ AdjustCheckboxRect(instance, &checkboxRect);
+ instance->InvalidateRect(&checkboxRect, FALSE);
+ }
+}
+
+HWND SetupRecord::CreateDetailsView(HWND hParent)
+{
+ DownloadDetails();
+
+ WCHAR szName[64] = {0};
+ if (FALSE == GetUniqueName(szName, ARRAYSIZE(szName)))
+ szName[0] = L'\0';
+
+ return SetupDetails_CreateServiceView(hParent, szName, service);
+}
+
+BOOL SetupRecord::GetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer ||
+ FAILED(StringCchPrintf(pszBuffer, cchBufferMax, L"record_svc_%u", service->GetId())))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void SetupRecord::OnDownloadCompleted()
+{
+ ifc_omstorage *storage = NULL;
+ HRESULT hr = OMSERVICEMNGR->QueryStorage(&SUID_OmStorageUrl, &storage);
+ if (SUCCEEDED(hr) && service != NULL)
+ {
+ ifc_omserviceenum *serviceEnum = NULL;
+ hr = storage->EndLoad(async, &serviceEnum);
+ if (SUCCEEDED(hr) && serviceEnum != NULL)
+ {
+ EnterCriticalSection(&lock);
+
+ ifc_omservice *result = NULL;
+ while(S_OK == serviceEnum->Next(1, &result, NULL))
+ {
+ if (result)
+ {
+ if (result->GetId() == service->GetId())
+ {
+ ifc_omservicecopier *copier;
+ if (SUCCEEDED(result->QueryInterface(IFC_OmServiceCopier, (void**)&copier)))
+ {
+ copier->CopyTo(service, NULL);
+ copier->Release();
+ }
+ result->Release();
+ flags |= recordDownloaded;
+ break;
+ }
+ else
+ {
+ result->Release();
+ }
+ }
+ result = NULL;
+ }
+
+ LeaveCriticalSection(&lock);
+
+ serviceEnum->Release();
+ }
+
+ storage->Release();
+ }
+
+ EnterCriticalSection(&lock);
+
+ async->Release();
+ async = NULL;
+
+ LeaveCriticalSection(&lock);
+}
+
+void CALLBACK SetupRecord_ServiceDownloadedCallback(ifc_omstorageasync *result)
+{
+ if (NULL == result) return;
+ SetupRecord *record = NULL;
+ if (SUCCEEDED(result->GetData((void**)&record)) && NULL != record)
+ {
+ record->OnDownloadCompleted();
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupRecord.h b/Src/Plugins/Library/ml_online/Setup/setupRecord.h
new file mode 100644
index 00000000..b6573e1a
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupRecord.h
@@ -0,0 +1,84 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPRECORD_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPRECORD_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "./setupListbox.h"
+
+class ifc_omservice;
+class ifc_omstorageasync;
+class SetupLog;
+
+class SetupRecord : public SetupListboxItem
+
+{
+protected:
+ typedef enum
+ {
+ recordSelected = 0x0001,
+ recordDownloaded = 0x0002,
+ checkboxHighlighted = 0x0100,
+ checkboxPressed = 0x0200,
+ } RecordFlags;
+
+protected:
+ SetupRecord(ifc_omservice *serviceToUse);
+ ~SetupRecord();
+
+public:
+ static SetupRecord *CreateInstance(ifc_omservice *serviceToUse);
+
+public:
+ ULONG AddRef();
+ ULONG Release();
+
+ ifc_omservice *GetService() { return service; }
+
+ BOOL IsModified();
+ BOOL IsSelected();
+ void SetSelected(BOOL fSelected);
+
+ HRESULT GetServiceName(LPWSTR pszBuffer, UINT cchBufferMax);
+ HRESULT GetDisplayName(LPWSTR pszBuffer, UINT cchBufferMax);
+ HRESULT Save(SetupLog *log);
+ HRESULT DownloadDetails();
+
+ /* SetupListboxItem */
+ BOOL MeasureItem(SetupListbox *instance, UINT *cx, UINT *cy);
+ BOOL DrawItem(SetupListbox *instance, HDC hdc, const RECT *prc, UINT state);
+ INT_PTR KeyToItem(SetupListbox *instance, const RECT *prcItem, INT vKey);
+ BOOL MouseMove(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL MouseLeave(SetupListbox *instance, const RECT *prcItem);
+ BOOL LButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL LButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL LButtonDblClk(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL RButtonDown(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ BOOL RButtonUp(SetupListbox *instance, const RECT *prcItem, UINT mouseFlags, POINT pt);
+ void CaptureChanged(SetupListbox *instance, const RECT *prcItem, SetupListboxItem *captured);
+ BOOL IsDisabled() { return FALSE; }
+ void Command(SetupListbox *instance, INT commandId, INT eventId) {}
+ HWND CreateDetailsView(HWND hParent);
+ BOOL GetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax);
+
+
+protected:
+ BOOL AdjustCheckboxRect(SetupListbox *instance, RECT *prcItem);
+ void GetColors(HDC hdc, UINT state, COLORREF *rgbBkOut, COLORREF *rgbTextOut);
+ void InvertCheckbox(SetupListbox *instance, const RECT *prcItem);
+ void OnDownloadCompleted();
+
+private:
+ friend static void CALLBACK SetupRecord_ServiceDownloadedCallback(ifc_omstorageasync *result);
+
+protected:
+ ULONG ref;
+ ifc_omservice *service;
+ ifc_omstorageasync *async;
+ CRITICAL_SECTION lock;
+ UINT flags;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUPRECORD_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Setup/setupServicePanel.cpp b/Src/Plugins/Library/ml_online/Setup/setupServicePanel.cpp
new file mode 100644
index 00000000..eb7c2737
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupServicePanel.cpp
@@ -0,0 +1,797 @@
+#include "../common.h"
+#include "./setupServicePanel.h"
+#include "./setupDetails.h"
+#include "./setupPage.h"
+#include "../resource.h"
+#include "../api__ml_online.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omservicedetails.h>
+#include <ifc_omcachemanager.h>
+#include <ifc_omcachegroup.h>
+#include <ifc_omcacherecord.h>
+#include <ifc_imageloader.h>
+#include <ifc_omgraphics.h>
+#include <ifc_omserviceeventmngr.h>
+#include <ifc_omserviceeditor.h>
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define GetPanel(__hwnd) ((ServicePanel*)GetPropW((__hwnd), MAKEINTATOM(DETAILS_PROP)))
+
+#define GET_IDETAILS(__service, __details)\
+ (NULL != (service) && SUCCEEDED((service)->QueryInterface(IFC_OmServiceDetails, (void**)&(__details))))
+
+
+ServicePanel::ServicePanel(LPCWSTR pszName, ifc_omservice *service)
+ : ref(1), name(NULL), service(NULL), hwnd(NULL), fontTitle(NULL), fontMeta(NULL), thumbnailCache(NULL)
+{
+ name = Plugin_CopyString(pszName);
+ this->service = service;
+ if (NULL != service)
+ service->AddRef();
+}
+
+ServicePanel::~ServicePanel()
+{
+ Plugin_FreeString(name);
+
+ if (NULL != service)
+ service->Release();
+
+ if (NULL != fontTitle)
+ DeleteObject(fontTitle);
+
+ if (NULL != fontMeta)
+ DeleteObject(fontMeta);
+
+ if (NULL != thumbnailCache)
+ thumbnailCache->Release();
+}
+
+HWND ServicePanel::CreateInstance(HWND hParent, LPCWSTR pszName, ifc_omservice *service, ServicePanel **instance)
+{
+ ServicePanel *panel = new ServicePanel(pszName, service);
+ if (NULL == panel)
+ {
+ if (NULL != instance) *instance = NULL;
+ return NULL;
+ }
+
+ HWND hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_SETUP_SERVICEDETAILS, hParent, ServicePanel_DialogProc, (LPARAM)panel);
+
+ if (NULL != instance)
+ {
+ if (NULL != hwnd)
+ {
+ *instance = panel;
+ panel->AddRef();
+ }
+ else
+ *instance = NULL;
+ }
+
+ panel->Release();
+ return hwnd;
+}
+
+size_t ServicePanel::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t ServicePanel::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int ServicePanel::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_OmServiceEvent))
+ *object = static_cast<ifc_omserviceevent*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+static void CALLBACK ThreadCallback_ServiceChange(Dispatchable *instance, ULONG_PTR param1, ULONG_PTR param2)
+{
+ ifc_omserviceevent *panel = (ifc_omserviceevent*)instance;
+ ifc_omservice *service = (ifc_omservice*)param1;
+ if (NULL != service)
+ {
+ if (NULL != panel)
+ panel->ServiceChange(service, (UINT)param2);
+ service->Release();
+ }
+}
+void ServicePanel::ServiceChange(ifc_omservice *service, unsigned int modifiedFlags)
+{
+
+ DWORD currentTID = GetCurrentThreadId();
+ DWORD windowTID = GetWindowThreadProcessId(hwnd, NULL);
+ if (NULL != windowTID && currentTID != windowTID)
+ {
+ if(NULL != OMUTILITY)
+ {
+ service->AddRef();
+ if (FAILED(OMUTILITY->PostMainThreadCallback2(ThreadCallback_ServiceChange, (ifc_omserviceevent*)this, (ULONG_PTR)service, (ULONG_PTR)modifiedFlags)))
+ service->Release();
+ }
+ return;
+ }
+
+
+ if ( 0 != (ifc_omserviceeditor::modifiedName & modifiedFlags))
+ {
+ UpdateName();
+ HWND hPage = GetParent(hwnd);
+ if (NULL != hPage)
+ PostMessage(hPage, SPM_UPDATELIST, (WPARAM)service->GetId(), NULL);
+ }
+
+ if ( 0 != (ifc_omserviceeditor::modifiedDescription & modifiedFlags))
+ UpdateDescription();
+
+ if ( 0 != (ifc_omserviceeditor::modifiedThumbnail& modifiedFlags))
+ UpdateThumbnail();
+
+ if ( 0 != ((ifc_omserviceeditor::modifiedAuthorFirst |
+ ifc_omserviceeditor::modifiedAuthorLast |
+ ifc_omserviceeditor::modifiedUpdated |
+ ifc_omserviceeditor::modifiedPublished) & modifiedFlags))
+ {
+ UpdateMeta();
+ }
+
+}
+
+HRESULT ServicePanel::LoadLocalThumbnail(LPCWSTR pszPath)
+{
+ HWND hThumbnail = GetDlgItem(hwnd, IDC_THUMBNAIL);
+ if (NULL == hThumbnail) return E_UNEXPECTED;
+
+ SendMessage(hThumbnail, WM_SETREDRAW, FALSE, 0L);
+
+ HBITMAP hBitmap = NULL;
+
+ BITMAPINFOHEADER header;
+ void *pixelData;
+
+ ifc_omimageloader *imageLoader;
+ if (SUCCEEDED(OMUTILITY->QueryImageLoader(NULL, pszPath, FALSE, &imageLoader)))
+ {
+ imageLoader->LoadBitmapEx(&hBitmap, &header, &pixelData);
+ imageLoader->Release();
+ }
+
+ if (NULL == hBitmap &&
+ SUCCEEDED(OMUTILITY->QueryImageLoader(WASABI_API_ORIG_HINST, MAKEINTRESOURCE(IDR_SERVICE64X64_IMAGE), FALSE, &imageLoader)))
+ {
+ imageLoader->LoadBitmapEx(&hBitmap, &header, &pixelData);
+ imageLoader->Release();
+ }
+
+ HBITMAP hTest = (HBITMAP)SendMessage(hThumbnail, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
+ if (NULL != hTest)
+ DeleteObject(hTest);
+
+ if (NULL != hBitmap)
+ {
+ hTest = (HBITMAP)SendMessage(hThumbnail, STM_GETIMAGE, IMAGE_BITMAP, 0L);
+ if (hTest != hBitmap)
+ { // this is XP and up image copy was created and alpha channel will be handled properly
+ DeleteObject(hBitmap);
+ }
+ else
+ { // fix alpha channel
+ if (32 == header.biBitCount)
+ {
+ HDC hdcFixed = CreateCompatibleDC(NULL);
+ if (NULL != hdcFixed)
+ {
+ BITMAPINFOHEADER headerFixed;
+ CopyMemory(&headerFixed, &header, sizeof(BITMAPINFOHEADER));
+ BYTE *pixelsFixed;
+ INT cx = header.biWidth;
+ INT cy = abs(header.biHeight);
+ HBITMAP bitmapFixed = CreateDIBSection(NULL, (LPBITMAPINFO)&headerFixed, DIB_RGB_COLORS, (void**)&pixelsFixed, NULL, 0);
+
+ if (NULL != bitmapFixed)
+ {
+ HBITMAP bitmapOrig = (HBITMAP)SelectObject(hdcFixed, bitmapFixed);
+ HBRUSH hb = (HBRUSH)SendMessage(hwnd, WM_CTLCOLORDLG, (WPARAM)hdcFixed, (LPARAM)hwnd);
+ if (NULL == hb)
+ hb = GetSysColorBrush(COLOR_3DFACE);
+ RECT rect;
+ SetRect(&rect, 0, 0, cx, cy);
+ FillRect(hdcFixed, &rect, hb);
+
+ ifc_omgraphics *graphics;
+ if (SUCCEEDED(OMUTILITY->GetGraphics(&graphics)))
+ {
+ HDC hdcSrc = CreateCompatibleDC(NULL);
+ if (NULL != hdcSrc)
+ {
+ HBITMAP bitmapSrcOrig = (HBITMAP)SelectObject(hdcSrc, hBitmap);
+ BLENDFUNCTION bf;
+ bf.BlendOp = AC_SRC_OVER;
+ bf.BlendFlags = 0;
+ bf.SourceConstantAlpha = 0xFF;
+ bf.AlphaFormat = AC_SRC_ALPHA;
+
+ RECT blendRect;
+ SetRect(&blendRect, 0, 0, cx, cy);
+
+ graphics->Premultiply((BYTE*)pixelData, cx, cy);
+ graphics->AlphaBlend(hdcFixed, &blendRect, hdcSrc, &blendRect, bf);
+
+ SelectObject(hdcSrc, bitmapSrcOrig);
+ DeleteDC(hdcSrc);
+ }
+ graphics->Release();
+ }
+
+ SelectObject(hdcFixed, bitmapOrig);
+ SendMessage(hThumbnail, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bitmapFixed);
+ DeleteObject(hBitmap);
+
+ }
+ DeleteDC(hdcFixed);
+ }
+ }
+ }
+ }
+
+ RECT clientRect;
+ if (GetClientRect(hThumbnail, &clientRect))
+ {
+ INT cx = clientRect.right - clientRect.left;
+ INT cy = clientRect.bottom - clientRect.top;
+ if (64 != cx || 64 != cy)
+ {
+ SetWindowPos(hThumbnail, NULL, 0, 0, 64, 64, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ }
+
+ SendMessage(hThumbnail, WM_SETREDRAW, TRUE, 0L);
+
+ if (0 != ShowWindow(hThumbnail, (NULL != hBitmap) ? SW_SHOWNA : SW_HIDE))
+ InvalidateRect(hThumbnail, NULL, TRUE);
+
+ return S_OK;
+}
+
+static void CALLBACK ThreadCallback_PathChanged(Dispatchable *instance, ULONG_PTR param1, ULONG_PTR param2)
+{
+ ifc_omcachecallback *panel = (ifc_omcachecallback*)instance;
+ ifc_omcacherecord *record = (ifc_omcacherecord*)param1;
+ if (NULL != record)
+ {
+ if (NULL != panel)
+ panel->PathChanged(record);
+ record->Release();
+ }
+}
+
+void ServicePanel::PathChanged(ifc_omcacherecord *record)
+{
+ if (NULL == hwnd || FALSE == IsWindow(hwnd))
+ return;
+
+ DWORD currentTID = GetCurrentThreadId();
+ DWORD windowTID = GetWindowThreadProcessId(hwnd, NULL);
+ if (NULL != windowTID && currentTID != windowTID)
+ {
+ if(NULL != OMUTILITY)
+ {
+ record->AddRef();
+ if (FAILED(OMUTILITY->PostMainThreadCallback2(ThreadCallback_PathChanged, (ifc_omcachecallback*)this, (ULONG_PTR)record, 0L)))
+ record->Release();
+ }
+ return;
+ }
+
+ WCHAR szPath[2048] = {0};
+ if (FAILED(record->GetPath(szPath, ARRAYSIZE(szPath))))
+ szPath[0] = L'\0';
+
+ LoadLocalThumbnail(szPath);
+}
+
+void ServicePanel::Attach(HWND hwnd)
+{
+ this->hwnd = hwnd;
+ if (NULL != hwnd &&
+ FALSE != SetProp(hwnd, MAKEINTATOM(DETAILS_PROP), this))
+ {
+ AddRef();
+ }
+}
+
+void ServicePanel::Detach()
+{
+ RemoveProp(hwnd, MAKEINTATOM(DETAILS_PROP));
+
+ if (NULL != thumbnailCache)
+ {
+ thumbnailCache->UnregisterCallback(this);
+ thumbnailCache->Release();
+ thumbnailCache = NULL;
+ }
+
+ if (NULL != service)
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEventMngr, (void**)&eventManager)))
+ {
+ eventManager->UnregisterHandler(this);
+ eventManager->Release();
+ }
+ }
+
+ Release();
+}
+
+HFONT ServicePanel::PickTitleFont(LPCWSTR pszTitle, INT cchTitle, INT maxWidth)
+{
+ HFONT dialogFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L);
+
+ LOGFONT lf;
+ if (0 == GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ return NULL;
+
+ HFONT titleFont = NULL;
+ if (cchTitle > 0)
+ {
+ LOGFONT lf;
+ if (0 != GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ {
+ StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Arial Bold");
+ lf.lfWidth = 0;
+ lf.lfWeight = FW_DONTCARE;
+ lf.lfQuality = 5/*ANTIALIASED_QUALITY*/;
+
+ HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
+ if (NULL != hdc)
+ {
+ HFONT origFont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
+ SIZE textSize;
+
+ INT heightLimit = (lf.lfHeight < 0) ? 1 : -1;
+ lf.lfHeight += (lf.lfHeight < 0) ? -2 : +2;
+ do
+ {
+ textSize.cx = 0;
+ if (NULL != titleFont) DeleteObject(titleFont);
+ titleFont = CreateFontIndirect(&lf);
+ if (NULL != titleFont)
+ {
+ SelectObject(hdc, titleFont);
+ GetTextExtentPoint32(hdc, pszTitle, cchTitle, &textSize);
+ }
+ lf.lfHeight += (lf.lfHeight < 0) ? 1 : -1;
+
+ } while(textSize.cx > maxWidth && lf.lfHeight != heightLimit);
+
+ if (0 == textSize.cx)
+ {
+ DeleteObject(titleFont);
+ titleFont = NULL;
+ }
+
+ SelectObject(hdc, origFont);
+ ReleaseDC(hwnd, hdc);
+ }
+ }
+ }
+
+ if (NULL == titleFont &&
+ 0 != GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ {
+ titleFont = CreateFontIndirect(&lf);
+ }
+ return titleFont;
+}
+
+LPCWSTR ServicePanel::FormatDate(LPCWSTR pszDate, LPWSTR pszBuffer, INT cchBufferMax)
+{
+ SYSTEMTIME st;
+ ZeroMemory(&st, sizeof(SYSTEMTIME));
+ LPCWSTR cursor;
+
+ cursor = pszDate;
+ INT index = 0;
+
+ for(;;)
+ {
+
+ INT iVal;
+
+ if (FALSE == StrToIntEx(cursor, STIF_DEFAULT, &iVal) || iVal < 1)
+ {
+ index = 0;
+ break;
+ }
+
+ if (0 == index)
+ {
+ if (iVal < 2000 || iVal > 2100)
+ break;
+ st.wYear = iVal;
+ index++;
+ }
+ else if (1 == index)
+ {
+ if (iVal < 1 || iVal > 12)
+ break;
+ st.wMonth = iVal;
+ index++;
+ }
+ else if (2 == index)
+ {
+ if (iVal < 1 || iVal > 31)
+ break;
+ st.wDay = iVal;
+ index++;
+ }
+ else
+ {
+ index = 0;
+ break;
+ }
+
+ while(L'\0' != *cursor && L'-' != *cursor) cursor++;
+ if (L'-' == *cursor) cursor++;
+ if (L'\0' == *cursor)
+ break;
+
+ }
+
+ if (3 == index &&
+ 0 != GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, pszBuffer, cchBufferMax))
+ {
+ return pszBuffer;
+ }
+
+ return pszDate;
+}
+
+
+
+HRESULT ServicePanel::GetFullName(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+ *pszBuffer = L'\0';
+
+ if (NULL == service) return E_UNEXPECTED;
+
+ ifc_omservicedetails *details;
+ HRESULT hr = service->QueryInterface(IFC_OmServiceDetails, (void**)&details);
+ if (SUCCEEDED(hr))
+ {
+ hr = details->GetAuthorFirst(pszBuffer, cchBufferMax);
+ if (SUCCEEDED(hr))
+ {
+ UINT cchBuffer = lstrlen(pszBuffer);
+ LPWSTR cursor = pszBuffer + cchBuffer;
+ size_t remaining = cchBufferMax - cchBuffer;
+
+ if (cursor != pszBuffer)
+ {
+ hr = StringCchCopyEx(cursor, remaining, L" ", &cursor, &remaining, 0);
+ if (SUCCEEDED(hr))
+ {
+ hr = details->GetAuthorLast(cursor, (UINT)remaining);
+ if (FAILED(hr) || L'\0' == *cursor)
+ {
+ pszBuffer[cchBuffer] = L'\0';
+ }
+ }
+ }
+ }
+ }
+ return hr;
+}
+void ServicePanel::UpdateName()
+{
+ HWND hTitle = GetDlgItem(hwnd, IDC_TITLE);
+ if (NULL == hTitle) return;
+
+ WCHAR szBuffer[128] = {0};
+ if (NULL == service ||
+ FAILED(service->GetName(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ szBuffer[0] = L'\0';
+ }
+
+ INT cchBuffer = lstrlen(szBuffer);
+ RECT rc;
+ GetClientRect(hTitle, &rc);
+ HFONT font = PickTitleFont(szBuffer, cchBuffer, rc.right - rc.left);
+ if (NULL != font)
+ {
+ if (NULL != fontTitle)
+ DeleteObject(fontTitle);
+
+ fontTitle = font;
+ SendMessage(hTitle, WM_SETFONT, (WPARAM)fontTitle, 0L);
+ }
+
+
+ SetWindowText(hTitle, szBuffer);
+ InvalidateRect(hTitle, NULL, TRUE);
+}
+
+
+void ServicePanel::UpdateDescription()
+{
+ HWND hDescription = GetDlgItem(hwnd, IDC_DESCRIPTION);
+ if (NULL == hDescription) return;
+
+ WCHAR szBuffer[4096] = {0};
+ ifc_omservicedetails *details = 0;
+ if (GET_IDETAILS(service, details))
+ {
+ details->GetDescription(szBuffer, ARRAYSIZE(szBuffer));
+ details->Release();
+ }
+
+ SetupDetails_SetDescription(hDescription, szBuffer);
+}
+
+void ServicePanel::UpdateMeta()
+{
+ HWND hMeta = GetDlgItem(hwnd, IDC_SERVICEMETA);
+ if (NULL == hMeta) return;
+
+ WCHAR szBuffer[512] = {0};
+ ifc_omservicedetails *svcdetails = 0;
+ if (GET_IDETAILS(service, svcdetails))
+ {
+ WCHAR szValue[256] = {0}, szPrefix[64] = {0};
+ HRESULT hr = S_OK;
+ LPWSTR cursor = szBuffer;
+ size_t remaining = ARRAYSIZE(szBuffer);
+
+ if (SUCCEEDED(GetFullName(szValue, ARRAYSIZE(szValue))) && L'\0' != szValue[0])
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_SERVICE_BYAUTHOR, szPrefix, ARRAYSIZE(szPrefix));
+ hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ L"%s%s", szPrefix, szValue);
+ }
+
+ if (SUCCEEDED(svcdetails->GetUpdated(szValue, ARRAYSIZE(szValue))) && L'\0' != szValue[0])
+ {
+ if (cursor != szBuffer)
+ hr = StringCchCopyEx(cursor, remaining, L"\r\n", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE);
+
+ if (SUCCEEDED(hr))
+ {
+ WCHAR szDate[128] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_SERVICE_LASTUPDATED, szPrefix, ARRAYSIZE(szPrefix));
+ StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ L"%s%s", szPrefix, FormatDate(szValue, szDate, ARRAYSIZE(szDate)));
+ }
+ }
+
+ svcdetails->Release();
+ }
+
+ if (NULL == fontMeta)
+ {
+ HFONT dialogFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L);
+ LOGFONT lf;
+ if (0 != GetObject(dialogFont, sizeof(LOGFONT), &lf))
+ {
+ StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), L"Tahoma");
+ lf.lfWidth = 0;
+ lf.lfHeight += (lf.lfHeight < 0) ? 1 : -1;
+ lf.lfQuality = ANTIALIASED_QUALITY;
+ fontMeta = CreateFontIndirect(&lf);
+ }
+
+ if (NULL != fontMeta)
+ {
+ SendMessage(hMeta, WM_SETFONT, (WPARAM)fontMeta, 0L);
+ }
+ }
+
+ SetWindowText(hMeta, szBuffer);
+ if (0 != ShowWindow(hMeta, (L'\0' != szBuffer[0]) ? SW_SHOWNA : SW_HIDE))
+ InvalidateRect(hMeta, NULL, TRUE);
+}
+
+void ServicePanel::UpdateThumbnail()
+{
+ if (NULL != thumbnailCache)
+ {
+ thumbnailCache->UnregisterCallback(this);
+ thumbnailCache->Release();
+ thumbnailCache = NULL;
+ }
+ LoadLocalThumbnail(NULL);
+
+ ifc_omservicedetails *details = 0;
+ if (GET_IDETAILS(service, details))
+ {
+ WCHAR szPath[2048] = {0};
+ if (SUCCEEDED(details->GetThumbnail(szPath, ARRAYSIZE(szPath))) && L'\0' != szPath[0])
+ {
+ ifc_omcachemanager *cacheManager;
+ if (SUCCEEDED(OMUTILITY->GetCacheManager(&cacheManager)))
+ {
+ ifc_omcachegroup *cacheGroup;
+ if (SUCCEEDED(cacheManager->Find(L"thumbnails", TRUE, &cacheGroup, NULL)))
+ {
+
+ if (SUCCEEDED(cacheGroup->Find(szPath, TRUE, &thumbnailCache, FALSE)))
+ {
+ thumbnailCache->RegisterCallback(this);
+ if (SUCCEEDED(thumbnailCache->GetPath(szPath, ARRAYSIZE(szPath))))
+ LoadLocalThumbnail(szPath);
+ }
+ cacheGroup->Release();
+ }
+ cacheManager->Release();
+ }
+ }
+ details->Release();
+ }
+}
+
+
+
+
+INT_PTR ServicePanel::OnInitDialog(HWND hFocus, LPARAM lParam)
+{
+ UpdateName();
+ UpdateDescription();
+ UpdateThumbnail();
+ UpdateMeta();
+
+ if (NULL != service)
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEventMngr, (void**)&eventManager)))
+ {
+ eventManager->RegisterHandler(this);
+ eventManager->Release();
+ }
+ }
+
+ return FALSE;
+}
+
+void ServicePanel::OnDestroy()
+{
+
+ HWND hThumbnail = GetDlgItem(hwnd, IDC_THUMBNAIL);
+ if (NULL != hThumbnail)
+ {
+ HBITMAP hBitmap = (HBITMAP)SendMessage(hThumbnail, STM_SETIMAGE, IMAGE_BITMAP, 0L);
+ if (NULL != hBitmap)
+ DeleteObject(hBitmap);
+ }
+
+ Detach();
+}
+
+
+INT_PTR ServicePanel::OnDialogColor(HDC hdc, HWND hControl)
+{
+ HWND hParent = GetAncestor(hwnd, GA_PARENT);
+ if (NULL != hParent && hParent != hwnd)
+ return (INT_PTR)SendMessage(hParent, WM_CTLCOLORDLG, (WPARAM)hdc, (LPARAM)hControl);
+
+ return 0;
+}
+
+
+INT_PTR ServicePanel::OnStaticColor(HDC hdc, HWND hControl)
+{
+ INT_PTR result = 0;
+ HWND hParent = GetAncestor(hwnd, GA_PARENT);
+ if (NULL != hParent && hParent != hwnd)
+ result = (INT_PTR)SendMessage(hParent, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hControl);
+
+ INT controlId = GetDlgCtrlID(hControl);
+ switch(controlId)
+ {
+ case IDC_SERVICEMETA:
+ {
+ COLORREF rgbBk = GetBkColor(hdc);
+ COLORREF rgbFg = GetTextColor(hdc);
+
+ ifc_omgraphics *graphics;
+ if (SUCCEEDED(OMUTILITY->GetGraphics(&graphics)))
+ {
+ graphics->BlendColor(rgbFg, rgbBk, 180, &rgbFg);
+ graphics->Release();
+ }
+
+ SetTextColor(hdc, rgbFg);
+ }
+ break;
+ }
+ return result;
+}
+
+
+INT_PTR ServicePanel::OnGetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer) return FALSE;
+ return SUCCEEDED(StringCchCopy(pszBuffer, cchBufferMax, (NULL != name) ? name : L""));
+}
+
+INT_PTR ServicePanel::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return OnInitDialog((HWND)wParam, lParam);
+ case WM_DESTROY: OnDestroy(); break;
+ case WM_CTLCOLORDLG: return OnDialogColor((HDC)wParam, (HWND)lParam);
+ case WM_CTLCOLORSTATIC: return OnStaticColor((HDC)wParam, (HWND)lParam);
+
+ case NSDM_GETUNIQUENAME: MSGRESULT(hwnd, OnGetUniqueName((LPWSTR)lParam, (UINT)wParam));
+ }
+ return 0;
+}
+
+static INT_PTR WINAPI ServicePanel_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ ServicePanel *panel = GetPanel(hwnd);
+ if (NULL == panel)
+ {
+ if (WM_INITDIALOG == uMsg)
+ {
+ panel = (ServicePanel*)lParam;
+ if (NULL != panel)
+ panel->Attach(hwnd);
+ }
+
+ if (NULL == panel) return 0;
+ }
+
+ return panel->DialogProc(uMsg, wParam, lParam);
+}
+
+
+
+#define CBCLASS ServicePanel
+START_MULTIPATCH;
+ START_PATCH(MPIID_SERVICEEVENT)
+ M_CB(MPIID_SERVICEEVENT, ifc_omserviceevent, ADDREF, AddRef);
+ M_CB(MPIID_SERVICEEVENT, ifc_omserviceevent, RELEASE, Release);
+ M_CB(MPIID_SERVICEEVENT, ifc_omserviceevent, QUERYINTERFACE, QueryInterface);
+ M_VCB(MPIID_SERVICEEVENT, ifc_omserviceevent, API_SERVICECHANGE, ServiceChange);
+
+
+ NEXT_PATCH(MPIID_CACHECALLBACK)
+ M_CB(MPIID_CACHECALLBACK, ifc_omcachecallback, ADDREF, AddRef);
+ M_CB(MPIID_CACHECALLBACK, ifc_omcachecallback, RELEASE, Release);
+ M_CB(MPIID_CACHECALLBACK, ifc_omcachecallback, QUERYINTERFACE, QueryInterface);
+ M_VCB(MPIID_CACHECALLBACK, ifc_omcachecallback, API_PATHCHANGED, PathChanged);
+
+ END_PATCH
+END_MULTIPATCH;
+#undef CBCLASS
diff --git a/Src/Plugins/Library/ml_online/Setup/setupServicePanel.h b/Src/Plugins/Library/ml_online/Setup/setupServicePanel.h
new file mode 100644
index 00000000..f60adac1
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Setup/setupServicePanel.h
@@ -0,0 +1,79 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_SETUP_SERVICE_PANEL_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_SETUP_SERVICE_PANEL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include <bfc/multipatch.h>
+#include <ifc_omserviceevent.h>
+#include <ifc_omcachecallback.h>
+
+class ifc_omservice;
+
+#define MPIID_SERVICEEVENT 10
+#define MPIID_CACHECALLBACK 20
+
+
+class ServicePanel : public MultiPatch<MPIID_SERVICEEVENT, ifc_omserviceevent>,
+ public MultiPatch<MPIID_CACHECALLBACK, ifc_omcachecallback>
+{
+protected:
+ ServicePanel(LPCWSTR pszName, ifc_omservice *service);
+ ~ServicePanel();
+
+public:
+ static HWND CreateInstance(HWND hParent, LPCWSTR pszName, ifc_omservice *service, ServicePanel **instance);
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_omserviceevent */
+ void ServiceChange(ifc_omservice *service, unsigned int modifiedFlags);
+
+ /* ifc_omcachecallback */
+ void PathChanged(ifc_omcacherecord *record);
+
+protected:
+ void Attach(HWND hwnd);
+ void Detach();
+
+ void UpdateName();
+ void UpdateDescription();
+ void UpdateMeta();
+ void UpdateThumbnail();
+
+ HFONT PickTitleFont(LPCWSTR pszTitle, INT cchTitle, INT maxWidth);
+ LPCWSTR FormatDate(LPCWSTR pszDate, LPWSTR pszBuffer, INT cchBufferMax);
+ HRESULT GetFullName(LPWSTR pszBuffer, UINT cchBufferMax);
+
+ INT_PTR OnInitDialog(HWND hFocus, LPARAM lParam);
+ void OnDestroy();
+ INT_PTR OnDialogColor(HDC hdc, HWND hControl);
+ INT_PTR OnStaticColor(HDC hdc, HWND hControl);
+ INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
+ INT_PTR OnGetUniqueName(LPWSTR pszBuffer, UINT cchBufferMax);
+ HRESULT LoadLocalThumbnail(LPCWSTR pszPath);
+
+private:
+ friend static INT_PTR WINAPI ServicePanel_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+protected:
+ size_t ref;
+ HWND hwnd;
+ LPWSTR name;
+ ifc_omservice *service;
+ ifc_omcacherecord *thumbnailCache;
+ HFONT fontTitle;
+ HFONT fontMeta;
+
+private:
+ RECVS_MULTIPATCH;
+};
+
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_SETUP_SERVICE_PANEL_HEADER \ No newline at end of file