aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Library/ml_online
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Library/ml_online')
-rw-r--r--Src/Plugins/Library/ml_online/BufferCache.h13
-rw-r--r--Src/Plugins/Library/ml_online/JSAPI2_Creator.cpp93
-rw-r--r--Src/Plugins/Library/ml_online/JSAPI2_Creator.h31
-rw-r--r--Src/Plugins/Library/ml_online/JnetCOM.cpp656
-rw-r--r--Src/Plugins/Library/ml_online/JnetCOM.h91
-rw-r--r--Src/Plugins/Library/ml_online/Main.cpp599
-rw-r--r--Src/Plugins/Library/ml_online/Main.h79
-rw-r--r--Src/Plugins/Library/ml_online/OMCOM.cpp821
-rw-r--r--Src/Plugins/Library/ml_online/OMCOM.h43
-rw-r--r--Src/Plugins/Library/ml_online/Preferences.cpp110
-rw-r--r--Src/Plugins/Library/ml_online/Preferences.h16
-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
-rw-r--r--Src/Plugins/Library/ml_online/api__ml_online.h62
-rw-r--r--Src/Plugins/Library/ml_online/browserEvent.cpp100
-rw-r--r--Src/Plugins/Library/ml_online/browserEvent.h41
-rw-r--r--Src/Plugins/Library/ml_online/commands.cpp418
-rw-r--r--Src/Plugins/Library/ml_online/commands.h29
-rw-r--r--Src/Plugins/Library/ml_online/common.cpp374
-rw-r--r--Src/Plugins/Library/ml_online/common.h81
-rw-r--r--Src/Plugins/Library/ml_online/config.cpp304
-rw-r--r--Src/Plugins/Library/ml_online/config.h54
-rw-r--r--Src/Plugins/Library/ml_online/external.cpp273
-rw-r--r--Src/Plugins/Library/ml_online/external.h49
-rw-r--r--Src/Plugins/Library/ml_online/forceUrl.cpp51
-rw-r--r--Src/Plugins/Library/ml_online/forceUrl.h27
-rw-r--r--Src/Plugins/Library/ml_online/handler.cpp168
-rw-r--r--Src/Plugins/Library/ml_online/handler.h20
-rw-r--r--Src/Plugins/Library/ml_online/import.h18
-rw-r--r--Src/Plugins/Library/ml_online/importFile.cpp265
-rw-r--r--Src/Plugins/Library/ml_online/importUrl.cpp321
-rw-r--r--Src/Plugins/Library/ml_online/jsapi2_omcom.cpp136
-rw-r--r--Src/Plugins/Library/ml_online/jsapi2_omcom.h27
-rw-r--r--Src/Plugins/Library/ml_online/local_menu.cpp379
-rw-r--r--Src/Plugins/Library/ml_online/local_menu.h40
-rw-r--r--Src/Plugins/Library/ml_online/messageBox.cpp193
-rw-r--r--Src/Plugins/Library/ml_online/messageBox.h13
-rw-r--r--Src/Plugins/Library/ml_online/ml_online.rc453
-rw-r--r--Src/Plugins/Library/ml_online/ml_online.sln30
-rw-r--r--Src/Plugins/Library/ml_online/ml_online.vcxproj395
-rw-r--r--Src/Plugins/Library/ml_online/ml_online.vcxproj.filters301
-rw-r--r--Src/Plugins/Library/ml_online/navigation.cpp1472
-rw-r--r--Src/Plugins/Library/ml_online/navigation.h96
-rw-r--r--Src/Plugins/Library/ml_online/png.rc27
-rw-r--r--Src/Plugins/Library/ml_online/resource.h206
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconAol.pngbin0 -> 253 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconAolGames.pngbin0 -> 206 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconDefault.pngbin0 -> 245 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconIn2Tv.pngbin0 -> 247 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconMusicNow.pngbin0 -> 294 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconShoutcastRadio.pngbin0 -> 277 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconShoutcastTv.pngbin0 -> 197 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconSingingfish.pngbin0 -> 249 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconWaMusic.pngbin0 -> 300 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/iconWaRemote.pngbin0 -> 264 bytes
-rw-r--r--Src/Plugins/Library/ml_online/resources/pages/serviceEditor.htm30
-rw-r--r--Src/Plugins/Library/ml_online/resources/pages/webdev.js95
-rw-r--r--Src/Plugins/Library/ml_online/resources/service64x64.pngbin0 -> 1667 bytes
-rw-r--r--Src/Plugins/Library/ml_online/serviceHelper.cpp1232
-rw-r--r--Src/Plugins/Library/ml_online/serviceHelper.h57
-rw-r--r--Src/Plugins/Library/ml_online/serviceHost.cpp394
-rw-r--r--Src/Plugins/Library/ml_online/serviceHost.h75
-rw-r--r--Src/Plugins/Library/ml_online/testPages.rc7
-rw-r--r--Src/Plugins/Library/ml_online/version.rc239
-rw-r--r--Src/Plugins/Library/ml_online/wasabi.cpp144
89 files changed, 18161 insertions, 0 deletions
diff --git a/Src/Plugins/Library/ml_online/BufferCache.h b/Src/Plugins/Library/ml_online/BufferCache.h
new file mode 100644
index 00000000..fdc3b1e4
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/BufferCache.h
@@ -0,0 +1,13 @@
+#ifndef NULLSOFT_BUFFERCACHEH
+#define NULLSOFT_BUFFERCACHEH
+#include <time.h>
+#include "../nu/GrowBuf.h"
+
+class Buffer_GrowBuf : public GrowBuf
+{
+public:
+ Buffer_GrowBuf() : expire_time(0) {}
+ time_t expire_time;
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/JSAPI2_Creator.cpp b/Src/Plugins/Library/ml_online/JSAPI2_Creator.cpp
new file mode 100644
index 00000000..d6d99442
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/JSAPI2_Creator.cpp
@@ -0,0 +1,93 @@
+#include "api__ml_online.h"
+#include "JSAPI2_Creator.h"
+#include "jsapi2_omcom.h"
+#include "resource.h"
+
+
+IDispatch *JSAPI2_Creator::CreateAPI(const wchar_t *name, const wchar_t *key, JSAPI::ifc_info *info)
+{
+ if (!wcscmp(name, L"OnlineServices"))
+ return new JSAPI2::OnlineServicesAPI(key, info);
+ else
+ return 0;
+}
+
+int JSAPI2_Creator::PromptForAuthorization(HWND parent, const wchar_t *group, const wchar_t *action, const wchar_t *authorization_key, JSAPI2::api_security::AuthorizationData *data)
+{
+ if (group && !wcscmp(group, L"onlineservices"))
+ {
+ return JSAPI2::svc_apicreator::AUTHORIZATION_DENY;
+ }
+ else
+ return JSAPI2::svc_apicreator::AUTHORIZATION_UNDEFINED;
+}
+
+#define CBCLASS JSAPI2_Creator
+START_DISPATCH;
+CB(JSAPI2_SVC_APICREATOR_CREATEAPI, CreateAPI);
+CB(JSAPI2_SVC_APICREATOR_PROMPTFORAUTHORIZATION, PromptForAuthorization);
+END_DISPATCH;
+#undef CBCLASS
+
+static JSAPI2_Creator jsapi2_svc;
+static const char serviceName[] = "Online Services Javascript Objects";
+
+// {4B0FA456-2B3B-4584-A96C-54765EA46448}
+static const GUID jsapi2_factory_guid =
+{ 0x4b0fa456, 0x2b3b, 0x4584, { 0xa9, 0x6c, 0x54, 0x76, 0x5e, 0xa4, 0x64, 0x48 } };
+
+FOURCC JSAPI2Factory::GetServiceType()
+{
+ return jsapi2_svc.getServiceType();
+}
+
+const char *JSAPI2Factory::GetServiceName()
+{
+ return serviceName;
+}
+
+GUID JSAPI2Factory::GetGUID()
+{
+ return jsapi2_factory_guid;
+}
+
+void *JSAPI2Factory::GetInterface(int global_lock)
+{
+// if (global_lock)
+// WASABI_API_SVC->service_lock(this, (void *)ifc);
+ return &jsapi2_svc;
+}
+
+int JSAPI2Factory::SupportNonLockingInterface()
+{
+ return 1;
+}
+
+int JSAPI2Factory::ReleaseInterface(void *ifc)
+{
+ //WASABI_API_SVC->service_unlock(ifc);
+ return 1;
+}
+
+const char *JSAPI2Factory::GetTestString()
+{
+ return 0;
+}
+
+int JSAPI2Factory::ServiceNotify(int msg, int param1, int param2)
+{
+ return 1;
+}
+
+#define CBCLASS JSAPI2Factory
+START_DISPATCH;
+CB(WASERVICEFACTORY_GETSERVICETYPE, GetServiceType)
+CB(WASERVICEFACTORY_GETSERVICENAME, GetServiceName)
+CB(WASERVICEFACTORY_GETGUID, GetGUID)
+CB(WASERVICEFACTORY_GETINTERFACE, GetInterface)
+CB(WASERVICEFACTORY_SUPPORTNONLOCKINGGETINTERFACE, SupportNonLockingInterface)
+CB(WASERVICEFACTORY_RELEASEINTERFACE, ReleaseInterface)
+CB(WASERVICEFACTORY_GETTESTSTRING, GetTestString)
+CB(WASERVICEFACTORY_SERVICENOTIFY, ServiceNotify)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/JSAPI2_Creator.h b/Src/Plugins/Library/ml_online/JSAPI2_Creator.h
new file mode 100644
index 00000000..4190b391
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/JSAPI2_Creator.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "../Winamp/JSAPI2_svc_apicreator.h"
+
+#include <api/service/waservicefactory.h>
+#include <api/service/services.h>
+
+class JSAPI2Factory : public waServiceFactory
+{
+ public:
+ FOURCC GetServiceType();
+ const char *GetServiceName();
+ GUID GetGUID();
+ void *GetInterface( int global_lock );
+ int SupportNonLockingInterface();
+ int ReleaseInterface( void *ifc );
+ const char *GetTestString();
+ int ServiceNotify( int msg, int param1, int param2 );
+
+ protected:
+ RECVS_DISPATCH;
+};
+
+
+class JSAPI2_Creator : public JSAPI2::svc_apicreator
+{
+ IDispatch *CreateAPI(const wchar_t *name, const wchar_t *key, JSAPI::ifc_info *info);
+ int PromptForAuthorization(HWND parent, const wchar_t *group, const wchar_t *action, const wchar_t *authorization_key, JSAPI2::api_security::AuthorizationData *data);
+protected:
+ RECVS_DISPATCH;
+};
diff --git a/Src/Plugins/Library/ml_online/JnetCOM.cpp b/Src/Plugins/Library/ml_online/JnetCOM.cpp
new file mode 100644
index 00000000..e93506f3
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/JnetCOM.cpp
@@ -0,0 +1,656 @@
+#include "JnetCOM.h"
+#include "main.h"
+#include "../nu/AutoChar.h"
+#include "../nu/AutoWide.h"
+#include <algorithm>
+#include <map>
+#include "config.h"
+#include <time.h>
+#include "../nu/MediaLibraryInterface.h"
+#include "api__ml_online.h"
+#include <api/service/waServiceFactory.h>
+#include <strsafe.h>
+
+extern C_Config *g_config;
+
+BufferMap buffer_map;
+
+enum
+{
+ DISP_JNET_INIT = 9323,
+ DISP_JNET_DOWNLOAD,
+ DISP_JNET_STATUS,
+ DISP_JNET_SIZE,
+ DISP_JNET_BUFFER,
+ DISP_JNET_BUFFER_RAW,
+ DISP_JNET_POST,
+};
+
+HANDLE DuplicateCurrentThread()
+{
+ HANDLE fakeHandle = GetCurrentThread();
+ HANDLE copiedHandle = 0;
+ HANDLE processHandle = GetCurrentProcess();
+ DuplicateHandle(processHandle, fakeHandle, processHandle, &copiedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ return copiedHandle;
+}
+
+#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; }
+HRESULT JnetCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ bool unknowns = false;
+ for (unsigned int i = 0;i != cNames;i++)
+ {
+ CHECK_ID("Init", DISP_JNET_INIT)
+ CHECK_ID("Download", DISP_JNET_DOWNLOAD)
+ CHECK_ID("Status", DISP_JNET_STATUS)
+ CHECK_ID("Buffer", DISP_JNET_BUFFER)
+ CHECK_ID("BufferRaw", DISP_JNET_BUFFER_RAW)
+ CHECK_ID("Size", DISP_JNET_SIZE)
+ CHECK_ID("Post", DISP_JNET_POST)
+
+ rgdispid[i] = DISPID_UNKNOWN;
+ unknowns = true;
+
+ }
+ if (unknowns)
+ return DISP_E_UNKNOWNNAME;
+ else
+ return S_OK;
+}
+
+HRESULT JnetCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT JnetCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ return E_NOTIMPL;
+}
+
+
+HRESULT JnetCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ switch (dispid)
+ {
+ case DISP_JNET_INIT:
+ {
+ if (!active && NULL == callback)
+ {
+ finalSize = 0;
+ isBlocking = true;
+ if ( pdispparams->cArgs == 2 )
+ {
+ isBlocking = false;
+ htmldiv = SysAllocString(pdispparams->rgvarg[0].bstrVal); // HTML DIV
+ callback = pdispparams->rgvarg[1].pdispVal; // callback object;
+ if (NULL != callback)
+ callback->AddRef();
+ }
+ }
+ else
+ {
+ if (pvarResult)
+ {
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_I4;
+ V_I4(pvarResult) = -1;
+ }
+ }
+ return S_OK;
+ }
+ break;
+ case DISP_JNET_DOWNLOAD:
+ if (pdispparams->cArgs == 1 || pdispparams->cArgs == 2)
+ {
+ int result = -1;
+ if ( !active )
+ {
+ time_t ret = 0;
+ active = 1;
+
+ DownloadURL(pdispparams);
+ result = 1;
+ }
+ else result = -1;
+
+ if (pvarResult)
+ {
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_I4;
+ V_I4(pvarResult) = result;
+ }
+
+ return S_OK;
+ }
+ break;
+ case DISP_JNET_POST:
+ if (pdispparams->cArgs == 2)
+ {
+ int result = -1;
+ if ( !active )
+ {
+ time_t ret = 0;
+ active = 1;
+
+ PostURL(pdispparams);
+ result = 1;
+ }
+ else result = -1;
+
+ if (pvarResult)
+ {
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_I4;
+ V_I4(pvarResult) = result;
+ }
+
+ return S_OK;
+ }
+ break;
+ case DISP_JNET_STATUS:
+ {
+ if (pvarResult && errorstr && active )
+ {
+ AutoWide result(errorstr);
+ BSTR tag = SysAllocString(result);
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_BSTR;
+ V_BSTR(pvarResult) = tag;
+ }
+ return S_OK;
+ }
+ break;
+ case DISP_JNET_SIZE:
+ {
+ if (pvarResult && active )
+ {
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_I4;
+ V_I4(pvarResult) = (INT)finalSize;
+ }
+ return S_OK;
+ }
+ break;
+ case DISP_JNET_BUFFER_RAW:
+ {
+ if (pvarResult && jnetbuf->get() && active)
+ {
+ char dummy[1]={0};
+ size_t len = jnetbuf->getlen();
+ char *source = (char *)jnetbuf->get();
+ if (!len || !source)
+ {
+ source=dummy;
+ len=1;
+ }
+ else
+ {
+ while (*(source+len-1) == 0 && len)
+ len--;
+ }
+ SAFEARRAY *bufferArray=SafeArrayCreateVector(VT_UI1, 0, (ULONG)len);
+ void *data;
+ SafeArrayAccessData(bufferArray, &data);
+ memcpy(data, source, len);
+ SafeArrayUnaccessData(bufferArray);
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_ARRAY|VT_UI1;
+ V_ARRAY(pvarResult) = bufferArray;
+ }
+ return S_OK;
+ }
+ case DISP_JNET_BUFFER:
+ {
+ if (pvarResult && jnetbuf->get() && active )
+ {
+ AutoWide result((char *)jnetbuf->get());
+ BSTR tag = SysAllocString(result);
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_BSTR;
+ V_BSTR(pvarResult) = tag;
+ }
+ return S_OK;
+ }
+ break;
+ }
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+STDMETHODIMP JnetCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch *)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG JnetCOM::AddRef(void)
+{
+ return ++refCount;
+}
+
+ULONG JnetCOM::Release(void)
+{
+ if (--refCount)
+ return refCount;
+
+ threadexit = 1; // exit the thread if active
+ if (!isBlocking)
+ { // Only if a callback was associated would the thread be running
+ while (getter) // If there no getter, the thread is gone
+ Sleep(10);
+ }
+
+ if (!finalSize)
+ {
+ BufferMap::iterator buffer_it = buffer_map.find(url);
+ if (buffer_it != buffer_map.end())
+ {
+ jnetbuf = buffer_it->second;
+ buffer_map.erase(buffer_it->first);
+ delete jnetbuf;
+ jnetbuf = NULL;
+ }
+ }
+ Plugin_FreeString(url);
+ url=0;
+ if (postData)
+ {
+ free(postData);
+ postData=0;
+ }
+
+ if (NULL != callback)
+ {
+ callback->Release();
+ callback = NULL;
+ }
+
+ delete this;
+ return 0;
+}
+
+static VOID CALLBACK CallbackAPC(ULONG_PTR param)
+{
+ VARIANT arguments[2];
+ DISPPARAMS params;
+ unsigned int ret;
+
+ JnetCOM *jnet = (JnetCOM *)param;
+ if (NULL == jnet || NULL == jnet->callback)
+ return;
+
+ VariantInit(&arguments[0]);
+ VariantInit(&arguments[1]);
+
+ V_VT(&arguments[0]) = VT_DISPATCH;
+ V_DISPATCH(&arguments[0]) = jnet;
+
+ V_VT(&arguments[1]) = VT_BSTR;
+ V_BSTR(&arguments[1]) = jnet->htmldiv;
+
+ params.cArgs = ARRAYSIZE(arguments);
+ params.cNamedArgs = 0;
+ params.rgdispidNamedArgs = NULL;
+ params.rgvarg = arguments;
+
+ jnet->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, &params, NULL, NULL, &ret);
+
+
+ V_DISPATCH(&arguments[0]) = NULL;
+ V_BSTR(&arguments[1]) = NULL;
+
+ VariantClear(&arguments[0]);
+ VariantClear(&arguments[1]);
+
+ jnet->Release();
+
+
+}
+
+void JnetCOM::callBack()
+{
+ AddRef();
+ if (GetCurrentThreadId() == callingThreadId)
+ CallbackAPC((ULONG_PTR)this);
+ else
+ {
+ if (NULL == callingThreadHandle ||
+ 0 == QueueUserAPC(CallbackAPC, callingThreadHandle, (ULONG_PTR)this))
+ {
+ Release();
+ }
+ }
+}
+
+#define USER_AGENT_SIZE (10 /*User-Agent*/ + 2 /*: */ + 6 /*Winamp*/ + 1 /*/*/ + 1 /*5*/ + 3/*.21*/ + 1 /*Null*/)
+void SetUserAgent(api_httpreceiver *http)
+{
+ char user_agent[USER_AGENT_SIZE] = {0};
+ int bigVer = ((winampVersion & 0x0000FF00) >> 12);
+ int smallVer = ((winampVersion & 0x000000FF));
+ StringCchPrintfA(user_agent, USER_AGENT_SIZE, "User-Agent: Winamp/%01x.%02x", bigVer, smallVer);
+ http->addheader(user_agent);
+}
+
+int JnetCOM::JthreadProc()
+{
+ int ret;
+ char temp[WORKSIZE] = {0};
+
+ char *proxy = mediaLibrary.GetProxy();
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) getter = (api_httpreceiver *)sf->getInterface();
+
+ // we're keying off of 'getter' to know if the thread is running or not, so we can release now
+ Release();
+
+ if (!getter)
+ return -1;
+
+ getter->AllowCompression();
+ getter->open(API_DNS_AUTODNS, WORKSIZE, proxy);
+
+ SetUserAgent(getter);
+
+ getter->connect(AutoChar(url));
+
+ BOOL bDone = FALSE;
+ while (!bDone && !threadexit)
+ {
+ Sleep(10);
+ ret = getter->run();
+ if (ret == -1 || ret == 1)
+ bDone = TRUE;
+
+ int bytesReceived = getter->get_bytes(temp, WORKSIZE);
+ if (bytesReceived)
+ jnetbuf->add(temp, bytesReceived);
+ }
+
+ if (!threadexit)
+ {
+
+ if (ret == -1) StringCchCopyA(errorstr, 2048, getter->geterrorstr());
+ else
+ {
+ int bytesReceived;
+ do // flush out the socket
+ {
+ bytesReceived = getter->get_bytes(temp, WORKSIZE);
+ if (bytesReceived)
+ jnetbuf->add(temp, bytesReceived);
+ }
+ while (bytesReceived);
+
+ temp[0] = 0;
+ jnetbuf->add(temp, 1);
+ }
+
+ finalSize = jnetbuf->getlen();
+
+ callBack();
+ }
+
+ sf->releaseInterface(getter);
+ threadexit = 0;
+ if (callingThreadHandle)
+ CloseHandle(callingThreadHandle);
+ getter = NULL;
+ return ret;
+}
+
+int JnetCOM::PostProcedure()
+{
+ int ret;
+ char temp[WORKSIZE] = {0};
+
+ char *proxy = mediaLibrary.GetProxy();
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(httpreceiverGUID);
+ if (sf) getter = (api_httpreceiver *)sf->getInterface();
+
+ // we're keying off of 'getter' to know if the thread is running or not, so we can release now
+ Release();
+ if (!getter)
+ return -1;
+
+ getter->AllowCompression();
+ getter->open(API_DNS_AUTODNS, WORKSIZE, proxy);
+
+ SetUserAgent(getter);
+ getter->addheader("Content-Type: application/x-www-form-urlencoded");
+
+ size_t contentLength = strlen(postData);
+ char contentLengthHeader[256] = {0};
+ StringCchPrintfA(contentLengthHeader, 256, "Content-Length: %u", contentLength);
+ getter->addheader(contentLengthHeader);
+
+ char *dataIndex = postData;
+ bool done=false;
+
+ getter->connect(AutoChar(url), 0, "POST");
+
+ // time to post data!
+ api_connection *connection = getter->GetConnection();
+
+ while (contentLength && !threadexit)
+ {
+ Sleep(1);
+ getter->run();
+
+ size_t lengthToSend = min(contentLength, connection->GetSendBytesAvailable());
+ if (lengthToSend)
+ {
+ connection->send(dataIndex, (int)lengthToSend);
+ dataIndex+=lengthToSend;
+ contentLength-=lengthToSend;
+ }
+ }
+
+ while (!threadexit && !done)
+ {
+ Sleep(10);
+ ret = getter->run();
+ if (ret == -1)
+ break;
+
+ // ---- check our reply code ----
+ int replycode = getter->getreplycode();
+ switch (replycode)
+ {
+ case 0:
+ break;
+ case 100:
+ break;
+ case 200:
+ {
+ int bytesReceived = getter->get_bytes(temp, WORKSIZE);
+ if (bytesReceived)
+ jnetbuf->add(temp, bytesReceived);
+ do
+ {
+ Sleep(10);
+ ret = getter->run();
+ bytesReceived = getter->get_bytes(temp, WORKSIZE);
+ if (bytesReceived)
+ jnetbuf->add(temp, bytesReceived);
+ }
+ while (ret == HTTPRECEIVER_RUN_OK);
+ done=true; // just in case
+ }
+ break;
+ default:
+ done=true;
+ break;
+ }
+ if (ret != HTTPRECEIVER_RUN_OK)
+ break;
+ }
+ if (!threadexit)
+ {
+ if (ret == -1)
+ StringCchCopyA(errorstr, 2048, getter->geterrorstr());
+ else
+ {
+ int bytesReceived;
+ do // flush out the socket
+ {
+ bytesReceived = getter->get_bytes(temp, WORKSIZE);
+ if (bytesReceived)
+ jnetbuf->add(temp, bytesReceived);
+ }
+ while (bytesReceived && !threadexit);
+
+ temp[0] = 0;
+ jnetbuf->add(temp, 1);
+ }
+
+ if ( !threadexit )
+ {
+ finalSize = jnetbuf->getlen();
+ callBack();
+ }
+
+ }
+ sf->releaseInterface(getter);
+
+ threadexit = 0;
+ if (callingThreadHandle)
+ CloseHandle(callingThreadHandle);
+ getter = NULL;
+ return ret;
+}
+
+void JnetCOM::DownloadURL(DISPPARAMS FAR *pdispparams)
+{
+ Plugin_FreeString(url);
+ url = Plugin_CopyString(pdispparams->rgvarg[pdispparams->cArgs - 1].bstrVal);
+
+ callingThreadId = GetCurrentThreadId();
+ callingThreadHandle = DuplicateCurrentThread();
+
+ BufferMap::iterator buffer_it = buffer_map.find(url);
+ if (buffer_it != buffer_map.end())
+ {
+ time_t check = time(NULL);
+ jnetbuf = buffer_it->second;
+ if ( check >= jnetbuf->expire_time)
+ {
+ buffer_map.erase(buffer_it->first);
+ delete jnetbuf;
+ jnetbuf = NULL;
+ }
+ else
+ {
+ finalSize = jnetbuf->getlen();
+ callBack();
+ }
+
+ }
+ if (!jnetbuf)
+ {
+ time_t now = 0;
+
+ jnetbuf = buffer_map[url] = new Buffer_GrowBuf;
+
+ if ( pdispparams->cArgs == 2 ) // passed in a time from Javascript, or 0 to not cache
+ {
+ if ( pdispparams->rgvarg[0].iVal )
+ {
+ now = time(NULL);
+ jnetbuf->expire_time = now + pdispparams->rgvarg[0].iVal;
+ }
+ }
+ else // Use winamp config cache time
+ {
+ int when = 0, x = 0;
+
+ x = g_config->ReadInt("radio_upd_freq", 0);
+ switch ( x )
+ {
+ case 0:
+ {
+ when = 3600; // One Hour
+ }
+ break;
+ case 1:
+ {
+ when = 3600 * 24; // One Day aka 24 hours
+ }
+ break;
+ case 2:
+ {
+ when = 3600 * 24 * 7; // One week (weak)
+ }
+ break;
+ default:
+ break;
+ }
+ if (when)
+ now = time(NULL);
+
+ jnetbuf->expire_time = now + when;
+
+ }
+ if (isBlocking)
+ {
+ AddRef(); // need to call this because JthreadProc does a Release
+ JthreadProc(); // Call it directly, block until done.
+ }
+ else
+ {
+ // Launch the thread
+ DWORD threadId;
+ AddRef(); // make sure jnetcom object doesn't die while the thread is launching.
+ jnetThread = CreateThread(NULL, NULL, JThreadProc, (void *)this, NULL, &threadId);
+ }
+
+ }
+}
+
+void JnetCOM::PostURL(DISPPARAMS FAR *pdispparams)
+{
+ postData = AutoCharDup(pdispparams->rgvarg[0].bstrVal);
+ Plugin_FreeString(url);
+ url = Plugin_CopyString(pdispparams->rgvarg[1].bstrVal);
+
+ callingThreadId = GetCurrentThreadId();
+ callingThreadHandle = DuplicateCurrentThread();
+
+ if (!jnetbuf)
+ {
+ time_t now = 0;
+
+ jnetbuf = buffer_map[url] = new Buffer_GrowBuf;
+
+ if (isBlocking)
+ {
+ AddRef(); // need to do this beacuse PostProcedure calls Release
+ PostProcedure(); // Call it directly, block until done.
+ }
+ else
+ {
+ // Launch the thread
+ DWORD threadId;
+ AddRef(); // make sure the jnetcom doesn't get deleted before the thread launches
+ jnetThread = CreateThread(NULL, NULL, jnetPostProcedure, (void *)this, NULL, &threadId);
+ }
+
+ }
+}
+
diff --git a/Src/Plugins/Library/ml_online/JnetCOM.h b/Src/Plugins/Library/ml_online/JnetCOM.h
new file mode 100644
index 00000000..c3a5e023
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/JnetCOM.h
@@ -0,0 +1,91 @@
+#ifndef NULLSOFT_JNETCOMH
+#define NULLSOFT_JNETCOMH
+
+#include <process.h>
+#include <ocidl.h>
+
+#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
+#include <objbase.h>
+#include "BufferCache.h"
+#include <map>
+#include <string>
+
+
+typedef std::map<std::wstring, Buffer_GrowBuf *> BufferMap;
+extern BufferMap buffer_map;
+#define WORKSIZE 32768
+
+class JnetCOM : public IDispatch
+{
+public:
+ JnetCOM() :
+ refCount(1),
+ threadexit(0),
+ active(0),
+ jnetThread(0),
+ jnetbuf(NULL),
+ isBlocking(0),
+ finalSize(0),
+ getter(NULL),
+ callback(NULL),
+ postData(0),
+ url(0),
+ callingThreadId(0),
+ callingThreadHandle(0)
+ {
+ memset(errorstr, 0, sizeof(errorstr));
+ }
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+ int refCount;
+ // *** IDispatch Methods ***
+ STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
+ STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
+ STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
+ STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
+
+ void DownloadURL(DISPPARAMS FAR *pdispparams );
+ void PostURL(DISPPARAMS FAR *pdispparams);
+ void callBack();
+
+ int threadexit;
+ int isBlocking;
+
+ api_httpreceiver *getter;
+ Buffer_GrowBuf *jnetbuf;
+ IDispatch *callback;
+ BSTR htmldiv;
+ DWORD callingThreadId;
+ HANDLE callingThreadHandle;
+
+ static DWORD WINAPI JThreadProc(void *param)
+ {
+ CoInitialize(NULL);
+ JnetCOM *th = static_cast<JnetCOM*>(param);
+ int ret = th->JthreadProc();
+ _endthread();
+ return ret;
+ }
+
+ static DWORD WINAPI jnetPostProcedure(void *param)
+ {
+ CoInitialize(NULL);
+ JnetCOM *th = static_cast<JnetCOM*>(param);
+ int ret = th->PostProcedure();
+ _endthread();
+ return ret;
+ }
+
+ size_t finalSize;
+ wchar_t *url;
+ char *postData;
+ int active;
+ char errorstr[2048];
+ HANDLE jnetThread;
+private:
+ int JthreadProc();
+ int PostProcedure();
+};
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Main.cpp b/Src/Plugins/Library/ml_online/Main.cpp
new file mode 100644
index 00000000..4f4f3c86
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Main.cpp
@@ -0,0 +1,599 @@
+#include "main.h"
+#include "./api__ml_online.h"
+#include "./config.h"
+#include "./navigation.h"
+#include "./resource.h"
+#include "./preferences.h"
+#include "./serviceHelper.h"
+
+#include "../../General/gen_ml/ml.h"
+#include "../../General/gen_ml/ml_ipc_0313.h"
+
+#include "../nu/MediaLibraryInterface.h"
+#include "../nu/AutoChar.h"
+#include "../nu/ns_wc.h"
+#include "../nu/AutoWide.h"
+#include <vector>
+#include "../nu/nonewthrow.c"
+#include "../nu/ConfigCOM.h"
+
+#include "BufferCache.h"
+#include "OMCOM.h"
+#include "JNetCom.h" // for buffer_map
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+
+static int Plugin_Init();
+static void Plugin_Quit();
+static INT_PTR Plugin_MessageProc(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3);
+
+static Navigation *navigation = NULL;
+static std::vector<PLUGINUNLOADCALLBACK> *unloadCallbacks = NULL;
+
+C_Config *g_config=NULL;
+
+extern "C" winampMediaLibraryPlugin plugin =
+{
+ MLHDR_VER,
+ "nullsoft(ml_online.dll)",
+ Plugin_Init,
+ Plugin_Quit,
+ Plugin_MessageProc,
+ 0,
+ 0,
+ 0,
+};
+
+
+HINSTANCE Plugin_GetInstance(void)
+{
+ return plugin.hDllInstance;
+}
+
+HWND Plugin_GetWinamp(void)
+{
+ return plugin.hwndWinampParent;
+}
+
+HWND Plugin_GetLibrary(void)
+{
+ return plugin.hwndLibraryParent;
+}
+
+HRESULT Plugin_GetNavigation(Navigation **instance)
+{
+ if(NULL == instance) return E_POINTER;
+
+ if (NULL == navigation)
+ {
+ *instance = NULL;
+ return E_UNEXPECTED;
+ }
+
+ *instance = navigation;
+ navigation->AddRef();
+
+ return S_OK;
+}
+
+typedef struct __PLUGINTIMERREC
+{
+ UINT_PTR id;
+ PLUGINTIMERPROC callback;
+ ULONG_PTR data;
+} PLUGINTIMERREC;
+
+typedef std::vector<PLUGINTIMERREC> PluginTimerList;
+
+static void CALLBACK Plugin_TimerProcDispath(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elapsedMs)
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL != hLibrary && FALSE != IsWindow(hLibrary))
+ {
+ PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
+ if (NULL != list)
+ {
+ size_t index = list->size();
+ while(index--)
+ {
+ PLUGINTIMERREC *rec = &list->at(index);
+ if (rec->id == eventId)
+ {
+ rec->callback(eventId, elapsedMs, rec->data);
+ return;
+ }
+ }
+ }
+ }
+
+ KillTimer(hwnd, eventId);
+}
+
+UINT_PTR Plugin_SetTimer(UINT elapseMs, PLUGINTIMERPROC callback, ULONG_PTR data)
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
+ return 0;
+
+ if (GetCurrentThreadId() != GetWindowThreadProcessId(hLibrary, NULL))
+ return 0;
+
+ if (NULL == callback)
+ return 0;
+
+ PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
+ if (NULL == list)
+ {
+ list = new PluginTimerList();
+ if (NULL == list) return 0;
+ if (0 == SetProp(hLibrary, L"OnlineMediaTimerData", list))
+ {
+ delete(list);
+ return 0;
+ }
+ }
+
+ PLUGINTIMERREC rec;
+ rec.data = data;
+ rec.callback = callback;
+ rec.id = SetTimer(NULL, NULL, elapseMs, Plugin_TimerProcDispath);
+ if (0 == rec.id)
+ {
+ if (0 == list->size())
+ {
+ RemoveProp(hLibrary, L"OnlineMediaTimerData");
+ delete(list);
+ }
+ return 0;
+ }
+
+ list->push_back(rec);
+ return rec.id;
+}
+
+void Plugin_KillTimer(UINT_PTR eventId)
+{
+ KillTimer(NULL, eventId);
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
+ return;
+
+ PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
+ if (NULL == list) return;
+
+ size_t index = list->size();
+ while(index--)
+ {
+ if (list->at(index).id == eventId)
+ {
+ list->erase(list->begin() + index);
+ break;
+ }
+ }
+
+ if (0 == list->size())
+ {
+ RemoveProp(hLibrary, L"OnlineMediaTimerData");
+ delete(list);
+ }
+}
+
+static void Plugin_UninitializeTimer()
+{
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary || FALSE == IsWindow(hLibrary))
+ return;
+
+ PluginTimerList *list = (PluginTimerList*)GetProp(hLibrary, L"OnlineMediaTimerData");
+ RemoveProp(hLibrary, L"OnlineMediaTimerData");
+ if (NULL == list) return;
+
+ size_t index = list->size();
+ while(index--)
+ {
+ KillTimer(NULL, list->at(index).id);
+ }
+
+ delete(list);
+}
+
+
+wchar_t g_w_cachedir[2048] = {0};
+int winampVersion=0;
+
+OMCOM omCOM;
+
+URLMap urlMap;
+MetadataMap metadataMap;
+Nullsoft::Utility::LockGuard urlMapGuard;
+
+void LoadCacheItem( wchar_t *path )
+{
+ FILECACHETYPE cachefile = {0};
+ unsigned long size = 0;
+ HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) return;
+ ReadFile(hFile, &cachefile, sizeof(FILECACHETYPE),&size, NULL);
+ if ( size == sizeof(FILECACHETYPE ))
+ {
+ size = 0;
+ time_t now = time(NULL);
+ //read the header, validate
+ if ( cachefile.version == FILECACHEVERSION )
+ {
+ if ( now < cachefile.expires )
+ {
+ char *url = (char *)calloc((size_t)cachefile.urllen, sizeof(char));
+ if (url)
+ {
+ size = 0;
+ ReadFile(hFile, url, (DWORD)cachefile.urllen, &size, NULL);
+ if ( cachefile.urllen == size ) // we read it ok!
+ {
+ char tempbuf[16384] = {0};
+ Buffer_GrowBuf *newbuffer = new Buffer_GrowBuf;
+ INT64 readin=0;
+ newbuffer->expire_time = (time_t)cachefile.expires;
+ while ( readin != cachefile.datalen )
+ {
+ DWORD toread=(DWORD)cachefile.datalen - (DWORD)readin;
+ if ( toread > 16384 ) toread=16384;
+ size = 0;
+ int success = ReadFile(hFile, &tempbuf, toread , &size, NULL);
+ if ( success )
+ {
+ if ( size )
+ {
+ newbuffer->add(tempbuf,(int)size);
+ readin += size;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ if ( readin != cachefile.datalen )
+ {
+ delete newbuffer;
+ }
+ else
+ {
+ buffer_map[(wchar_t *)AutoWide(url)]=newbuffer;
+ }
+ }
+ else
+ {
+ free(url);
+ }
+ }
+ }
+ }
+ }
+ CloseHandle(hFile);
+ DeleteFile(path);
+}
+
+void LoadCache()
+{
+ WIN32_FIND_DATA FindFileData = {0};
+ HANDLE hFind;
+ wchar_t searchs[2048] = {0};
+
+ buffer_map.clear();
+
+ StringCchPrintf(searchs, 2048, L"%s\\*.w5x",g_w_cachedir);
+ hFind = FindFirstFile(searchs, &FindFileData);
+ if ( hFind != INVALID_HANDLE_VALUE )
+ {
+ do
+ {
+ wchar_t activefile[2048] = {0};
+ StringCchPrintf(activefile, 2048, L"%s\\%s",g_w_cachedir,FindFileData.cFileName);
+ LoadCacheItem(activefile);
+ } while ( FindNextFile(hFind, &FindFileData) );
+ FindClose(hFind);
+ }
+}
+
+void SaveCache()
+{
+ BufferMap::iterator buffer_it;
+ DWORD start=0xABBACAFE;
+ for(buffer_it = buffer_map.begin();buffer_it != buffer_map.end(); buffer_it++)
+ {
+ time_t now = time(NULL);
+ if ( buffer_it->second->expire_time > now )
+ {
+ wchar_t filename[2048] = {0};
+ FILECACHETYPE cachefile;
+ HANDLE hFile;
+ INT64 size=0;
+ memset((void *)&cachefile,0,sizeof(FILECACHETYPE));
+ cachefile.version = FILECACHEVERSION;
+ cachefile.expires = buffer_it->second->expire_time;
+ AutoChar charUrl(buffer_it->first.c_str());
+ cachefile.urllen = strlen(charUrl)+1;
+ cachefile.datalen = buffer_it->second->getlen()+1;
+
+ StringCchPrintf(filename, 2048, L"%s\\%08X.w5x",g_w_cachedir,start++);
+ hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ WriteFile(hFile, &cachefile, sizeof(FILECACHETYPE),(LPDWORD)&size,NULL);
+ if ( size == sizeof(FILECACHETYPE) )
+ {
+ char blank[2]="\0";
+ size = 0; WriteFile(hFile, (char *)charUrl ,(DWORD)cachefile.urllen, (LPDWORD)&size, NULL);
+ size = 0; WriteFile(hFile, buffer_it->second->get() , (DWORD)buffer_it->second->getlen(), (LPDWORD)&size, NULL);
+ size = 0; WriteFile(hFile, blank , 1, (LPDWORD)&size, NULL);
+ }
+ else
+ {
+ CloseHandle(hFile);
+ hFile=NULL;
+ DeleteFile(filename);
+ }
+ }
+ if (hFile)
+ {
+ CloseHandle(hFile);
+ hFile=NULL;
+ }
+ }
+ }
+}
+
+void initConfigCache()
+{
+ wchar_t iniFileName[2048] = {0};
+ mediaLibrary.BuildPath(L"Plugins\\ml", iniFileName, 2048);
+ CreateDirectory(iniFileName, NULL);
+ mediaLibrary.BuildPath(L"Plugins\\ml\\cache", g_w_cachedir, 2048);
+ CreateDirectory(g_w_cachedir, NULL);
+ mediaLibrary.BuildPath(L"Plugins\\ml\\ml_online.ini", iniFileName, 2048);
+ AutoChar charFn(iniFileName);
+ g_config = new C_Config(AutoChar(iniFileName));
+
+ int x = g_config->ReadInt("maxbandwidth", MAXBANDWIDTH );
+ g_config->WriteInt("maxbandwidth",x);
+
+ x = g_config->ReadInt("minbandwidth",1);
+ g_config->WriteInt("minbandwidth",x);
+
+ LoadCache();
+}
+
+static void Plugin_ExecuteOpenOnce()
+{
+ CHAR szBuffer[128] = {0};
+ INT cchLen = Config_ReadStr("Navigation", "openOnce", NULL, szBuffer, ARRAYSIZE(szBuffer));
+ if (0 != cchLen)
+ {
+ UINT serviceId;
+ if (FALSE != StrToIntExA(szBuffer, STIF_SUPPORT_HEX, (INT*)&serviceId))
+ {
+
+ cchLen = Config_ReadStr("Navigation", "openOnceMode", NULL, szBuffer, ARRAYSIZE(szBuffer));
+ UINT showMode;
+ if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "popup", -1, szBuffer, cchLen))
+ showMode = SHOWMODE_POPUP;
+ else if (CSTR_EQUAL == CompareStringA(CSTR_INVARIANT, NORM_IGNORECASE, "ensureVisible", -1, szBuffer, cchLen))
+ showMode = SHOWMODE_ENSUREVISIBLE;
+ else
+ showMode = SHOWMODE_NORMAL;
+
+ ServiceHelper_ShowService(serviceId, showMode);
+ }
+
+ Config_WriteStr("Navigation", "openOnce", NULL);
+ Config_WriteStr("Navigation", "openOnceMode", NULL);
+ }
+}
+
+static int Plugin_Init()
+{
+ if (FAILED(WasabiApi_Initialize(plugin.hDllInstance, plugin.service)))
+ return 1;
+
+ if (FAILED(WasabiApi_LoadDefaults()) ||
+ NULL == OMBROWSERMNGR ||
+ NULL == OMSERVICEMNGR ||
+ NULL == OMUTILITY)
+ {
+ WasabiApi_Release();
+ return 2;
+ }
+
+ ServiceHelper_Initialize();
+
+ if (NULL != WASABI_API_LNG)
+ {
+ static wchar_t szDescription[256];
+ StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME),
+ PLUGIN_VERSION_MAJOR, PLUGIN_VERSION_MINOR);
+ plugin.description = (char*)szDescription;
+ }
+
+ mediaLibrary.library = plugin.hwndLibraryParent;
+ mediaLibrary.winamp = plugin.hwndWinampParent;
+ mediaLibrary.instance = plugin.hDllInstance;
+
+ winampVersion = mediaLibrary.GetWinampVersion();
+
+ omCOM.Publish();
+
+ Preferences_Register();
+
+ if (NULL == navigation)
+ {
+ if (FAILED(Navigation::CreateInstance(&navigation)))
+ {
+ navigation = NULL;
+
+ if (NULL != unloadCallbacks)
+ {
+ size_t index = unloadCallbacks->size();
+ while(index--)
+ unloadCallbacks->at(index)();
+ delete(unloadCallbacks);
+ }
+
+ Preferences_Unregister();
+ WasabiApi_Release();
+ return 3;
+ }
+ }
+
+ initConfigCache();
+
+ Plugin_ExecuteOpenOnce();
+ return ML_INIT_SUCCESS;
+}
+
+static void Plugin_Quit()
+{
+ SaveCache();
+ buffer_map.clear();
+
+ Plugin_UninitializeTimer();
+
+ if (NULL != navigation)
+ {
+ navigation->Finish();
+ navigation->Release();
+ navigation = NULL;
+ }
+
+ if (NULL != unloadCallbacks)
+ {
+ size_t index = unloadCallbacks->size();
+ while(index--)
+ unloadCallbacks->at(index)();
+ delete(unloadCallbacks);
+ unloadCallbacks = NULL;
+ }
+
+ Preferences_Unregister();
+
+ WasabiApi_Release();
+}
+
+static INT_PTR TitleHook(waHookTitleStructW *hookTitle)
+{
+ if (NULL == hookTitle ||
+ NULL == hookTitle->filename)
+ {
+ return 0;
+ }
+
+ Nullsoft::Utility::AutoLock lock(urlMapGuard);
+ // this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much
+ URLMap::iterator itr;
+ for (itr=urlMap.begin();itr!=urlMap.end();itr++)
+ {
+ if (!_wcsnicmp(hookTitle->filename, itr->url.c_str(), itr->url_wcslen))
+ {
+ if (NULL != hookTitle->title)
+ StringCchCopy(hookTitle->title, 2048, itr->title.c_str());
+
+ hookTitle->length = itr->length;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static INT_PTR MetadataHook(extendedFileInfoStructW *hookMetadata)
+{
+ if (NULL == hookMetadata ||
+ NULL == hookMetadata->filename ||
+ NULL == hookMetadata->metadata)
+ {
+ return 0;
+ }
+
+ Nullsoft::Utility::AutoLock lock(urlMapGuard);
+ // this is kinda slow but AOL Videos is so underused anyway that this map won't fill up much
+ MetadataMap::iterator itr;
+
+ for (itr=metadataMap.begin();itr!=metadataMap.end();itr++)
+ {
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->filename, -1, itr->url.c_str(), - 1) &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, hookMetadata->metadata, -1, itr->tag.c_str(), - 1))
+ {
+ StringCchCopy(hookMetadata->ret, hookMetadata->retlen, itr->metadata.c_str());
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static INT_PTR Plugin_MessageProc(int msg, INT_PTR param1, INT_PTR param2, INT_PTR param3)
+{
+ INT_PTR result = 0;
+ if (NULL != navigation &&
+ FALSE != navigation->ProcessMessage(msg, param1, param2, param3, &result))
+ {
+ return result;
+ }
+
+ switch (msg)
+ {
+ case ML_IPC_HOOKTITLEW: return TitleHook((waHookTitleStructW *)param1);
+ case ML_IPC_HOOKEXTINFOW: return MetadataHook((extendedFileInfoStructW *)param1);
+ case ML_MSG_CONFIG: Preferences_Show(); return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void Plugin_RegisterUnloadCallback(PLUGINUNLOADCALLBACK callback)
+{
+ if (NULL == unloadCallbacks)
+ {
+ unloadCallbacks = new std::vector<PLUGINUNLOADCALLBACK>();
+ if (NULL == unloadCallbacks)
+ return;
+ }
+ unloadCallbacks->push_back(callback);
+}
+
+
+extern "C" __declspec(dllexport) winampMediaLibraryPlugin *winampGetMediaLibraryPlugin()
+{
+ return &plugin;
+}
+
+#if 0
+extern "C" __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
+
+ // prompt to remove our settings with default as no (just incase)
+ /*if(MessageBoxA(hwndDlg,"Do you also want to remove the saved settings for this plugin?",
+ plugin.description,MB_YESNO|MB_DEFBUTTON2) == IDYES)
+ {
+ WritePrivateProfileString("ml_rg", 0, 0, iniFile);
+ }*/
+
+ // also attempt to remove the ReplayGainAnalysis.dll so everything is kept cleaner
+ /*char path[MAX_PATH] = {0};
+ GetModuleFileName(hDllInst, path, MAX_PATH);
+ PathRemoveFileSpec(path);
+ PathAppend(path, "ReplayGainAnalysis.dll");
+ // if we get a handle then try to lower the handle count so we can delete
+ HINSTANCE rgLib = GetModuleHandle(path);
+ if(rgLib)
+ FreeLibrary(rgLib);
+ DeleteFile(path);*/
+
+ // allow an on-the-fly removal (since we've got to be with a compatible client build)
+ return ML_PLUGIN_UNINSTALL_NOW;
+ }
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Main.h b/Src/Plugins/Library/ml_online/Main.h
new file mode 100644
index 00000000..5b6c887d
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Main.h
@@ -0,0 +1,79 @@
+#ifndef NULLSOFT_MAINH
+#define NULLSOFT_MAINH
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../../General/gen_ml/ml.h"
+#include "./common.h"
+
+#include <string>
+#include <vector>
+#include "../nu/AutoLock.h"
+
+#define PLUGIN_VERSION_MAJOR 2
+#define PLUGIN_VERSION_MINOR 03
+
+HINSTANCE Plugin_GetInstance(void);
+HWND Plugin_GetWinamp(void);
+HWND Plugin_GetLibrary(void);
+
+class Navigation;
+HRESULT Plugin_GetNavigation(Navigation **instance);
+
+typedef void (CALLBACK *PLUGINUNLOADCALLBACK)(void);
+void Plugin_RegisterUnloadCallback(PLUGINUNLOADCALLBACK callback);
+
+
+extern int winampVersion;
+
+#define MUTEX_T CRITICAL_SECTION
+#define MUTEX_ENTER(n) EnterCriticalSection(&(n))
+#define MUTEX_LEAVE(n) LeaveCriticalSection(&(n))
+#define MUTEX_INIT(n) InitializeCriticalSection(&(n))
+#define MUTEX_DEL(n) DeleteCriticalSection(&(n))
+
+#define FILECACHEVERSION 0x00000001
+typedef struct FileCacheType {
+ INT64 version;
+ INT64 expires;
+ INT64 urllen;
+ INT64 datalen;
+ INT64 resv1; // Future use, older versions MUST ignore
+ INT64 resv2; // Future use, older versions MUST ignore
+ INT64 resv3; // Future use, older versions MUST ignore
+ INT64 resv4; // Future use, older versions MUST ignore
+} FileCacheType;
+#define FILECACHETYPE FileCacheType
+
+struct url_info
+{
+ std::wstring url;
+ size_t url_wcslen;
+ std::wstring title;
+ int length;
+} ;
+
+struct metadata_info
+{
+ std::wstring url;
+ std::wstring tag;
+ std::wstring metadata;
+} ;
+
+typedef std::vector<url_info> URLMap; // just to save some typing & template code ugliness
+typedef std::vector<metadata_info> MetadataMap;
+
+
+extern URLMap urlMap;
+extern MetadataMap metadataMap;
+
+extern Nullsoft::Utility::LockGuard urlMapGuard;
+
+typedef void (CALLBACK *PLUGINTIMERPROC)(UINT_PTR /*eventId*/, DWORD /*elapsedMs*/, ULONG_PTR /*data*/);
+UINT_PTR Plugin_SetTimer(UINT elapseMs, PLUGINTIMERPROC callback, ULONG_PTR data);
+void Plugin_KillTimer(UINT_PTR eventId);
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/OMCOM.cpp b/Src/Plugins/Library/ml_online/OMCOM.cpp
new file mode 100644
index 00000000..8a00b456
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/OMCOM.cpp
@@ -0,0 +1,821 @@
+#include "main.h"
+#include "./omcom.h"
+#include "./resource.h"
+#include "./api__ml_online.h"
+
+#include "JnetCOM.h"
+
+#include "./navigation.h"
+#include "./preferences.h"
+#include "./serviceHelper.h"
+#include "./serviceHost.h"
+#include "./config.h"
+
+#include "../nu/ConfigCOM.h"
+#include "../nu/MediaLibraryInterface.h"
+#include "../nu/AutoChar.h"
+#include "../nu/AutoWide.h"
+#include "../nu/AutoUrl.h"
+
+
+#include <ifc_omservice.h>
+#include <ifc_omserviceeditor.h>
+#include <ifc_omfilestorage.h>
+
+
+#include "../Winamp/JSAPI.h" // IDispatch helper macros
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+extern MediaLibraryInterface mediaLibrary;
+extern C_Config *g_config;
+
+#define CONFIG_SERIALNUMBER "serialNumber"
+#define SERIALNUMBER_INVALID ((INT)-1)
+#define SERIALNUMBER_DEFAULT ((INT)0)
+
+enum
+{
+ DISPATCH_ISSUBSCRIBED = 12312,
+ DISPATCH_ADDSUBSCRIBED,
+ DISPATCH_CLEARSUBSCRIBED,
+ DISPATCH_SERIALNUMBER,
+ DISPATCH_JNETCREATE,
+ DISPATCH_GETWID,
+ DISPATCH_GETSID,
+ DISPATCH_PLAY,
+ DISPATCH_CONFIG,
+ DISPATCH_ENQUEUE,
+ DISPATCH_PREF,
+ DISPATCH_SETSIZE,
+ DISPATCH_GETX,
+ DISPATCH_GETY,
+ DISPATCH_NAVDISPLAY,
+ DISPATCH_FOCUSURL,
+ DISPATCH_SETCURRENTGUID,
+ DISPATCH_ADDTITLEHOOK,
+ DISPATCH_REMOVETITLEHOOK,
+ DISPATCH_ADDMETADATAHOOK,
+ DISPATCH_REMOVEMETADATAHOOK,
+ DISPATCH_SUBSCRIBE,
+};
+
+
+
+OMCOM::OMCOM()
+ : config(NULL), serialNumber(SERIALNUMBER_INVALID), publishCookie(0)
+{
+
+}
+
+OMCOM::~OMCOM()
+{
+ if (NULL != config)
+ config->Release();
+
+ if (0 != publishCookie)
+ {
+ SENDWAIPC(Plugin_GetWinamp(), IPC_REMOVE_DISPATCH_OBJECT, publishCookie);
+ publishCookie = NULL;
+ }
+}
+
+HRESULT OMCOM::Publish()
+{
+ if (0 != publishCookie)
+ return E_FAIL;
+
+ DispatchInfo dispatchInfo;
+ ZeroMemory(&dispatchInfo, sizeof(dispatchInfo));
+
+ dispatchInfo.name = L"OnMedia";
+ dispatchInfo.dispatch = this;
+
+ if (0 != SENDWAIPC(Plugin_GetWinamp(), IPC_ADD_DISPATCH_OBJECT, (WPARAM)&dispatchInfo))
+ return E_FAIL;
+
+ publishCookie = dispatchInfo.id;
+ return S_OK;
+}
+
+HRESULT OMCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ bool unknowns = false;
+ for (unsigned int i = 0;i != cNames;i++)
+ {
+ if (wcscmp(rgszNames[i], L"IsOmSubscribed") == 0)
+ rgdispid[i] = DISPATCH_ISSUBSCRIBED;
+ else if (wcscmp(rgszNames[i], L"AddOmSubscribed") == 0)
+ rgdispid[i] = DISPATCH_ADDSUBSCRIBED;
+ else if (wcscmp(rgszNames[i], L"ClearOmSubscribed") == 0)
+ rgdispid[i] = DISPATCH_CLEARSUBSCRIBED;
+ else if (wcscmp(rgszNames[i], L"OmSerialNumber") == 0)
+ rgdispid[i] = DISPATCH_SERIALNUMBER;
+ else if (wcscmp(rgszNames[i], L"JnetCreate") == 0)
+ rgdispid[i] = DISPATCH_JNETCREATE;
+ else if (wcscmp(rgszNames[i], L"GetUniqueID") == 0)
+ rgdispid[i] = DISPATCH_GETWID;
+ else if (wcscmp(rgszNames[i], L"GetSessionID") == 0)
+ rgdispid[i] = DISPATCH_GETSID;
+ else if (wcscmp(rgszNames[i], L"PlayUrl") == 0)
+ rgdispid[i] = DISPATCH_PLAY;
+ else if (wcscmp(rgszNames[i], L"Config") == 0)
+ rgdispid[i] = DISPATCH_CONFIG;
+ else if (wcscmp(rgszNames[i], L"EnqueueUrl") == 0)
+ rgdispid[i] = DISPATCH_ENQUEUE;
+ else if (wcscmp(rgszNames[i], L"ShowPreferences") == 0)
+ rgdispid[i] = DISPATCH_PREF;
+ else if (wcscmp(rgszNames[i], L"SetSize") == 0)
+ rgdispid[i] = DISPATCH_SETSIZE;
+ else if (wcscmp(rgszNames[i], L"GetX") == 0)
+ rgdispid[i] = DISPATCH_GETX;
+ else if (wcscmp(rgszNames[i], L"GetY") == 0)
+ rgdispid[i] = DISPATCH_GETY;
+ else if (wcscmp(rgszNames[i], L"DisplayNav") == 0)
+ rgdispid[i] = DISPATCH_NAVDISPLAY;
+ else if (wcscmp(rgszNames[i], L"FocusUrl") == 0)
+ rgdispid[i] = DISPATCH_FOCUSURL;
+ else if (wcscmp(rgszNames[i], L"SetCurrentGUID") == 0)
+ rgdispid[i] = DISPATCH_SETCURRENTGUID;
+ else if (wcscmp(rgszNames[i], L"AddTitleHook") == 0)
+ rgdispid[i] = DISPATCH_ADDTITLEHOOK;
+ else if (wcscmp(rgszNames[i], L"RemoveTitleHook") == 0)
+ rgdispid[i] = DISPATCH_REMOVETITLEHOOK;
+ else if (wcscmp(rgszNames[i], L"AddMetadataHook") == 0)
+ rgdispid[i] = DISPATCH_ADDMETADATAHOOK;
+ else if (wcscmp(rgszNames[i], L"RemoveMetadataHook") == 0)
+ rgdispid[i] = DISPATCH_REMOVEMETADATAHOOK;
+ else if (wcscmp(rgszNames[i], L"Subscribe") == 0)
+ rgdispid[i] = DISPATCH_SUBSCRIBE;
+ else
+ {
+ rgdispid[i] = DISPID_UNKNOWN;
+ unknowns = true;
+ }
+ }
+ if (unknowns)
+ return DISP_E_UNKNOWNNAME;
+ else
+ return S_OK;
+}
+HRESULT OMCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT OMCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ return E_NOTIMPL;
+}
+
+#define VIDEO_GENFF_SIZEREQUEST (WM_USER+2048)
+static void RemoveTitleHook(const wchar_t *url)
+{
+ Nullsoft::Utility::AutoLock lock(urlMapGuard);
+DISPATCH_REMOVETITLEHOOK_again:
+ URLMap::iterator itr;
+ for (itr=urlMap.begin();itr!=urlMap.end();itr++)
+ {
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, url, -1, itr->url.c_str(), - 1))
+ {
+ urlMap.erase(itr);
+ goto DISPATCH_REMOVETITLEHOOK_again;
+ }
+ }
+}
+
+static void RemoveMetadataHook(const wchar_t *url)
+{
+ Nullsoft::Utility::AutoLock lock(urlMapGuard);
+DISPATCH_REMOVEMETADATAHOOK_again:
+ MetadataMap::iterator itr;
+ for (itr=metadataMap.begin();itr!=metadataMap.end();itr++)
+ {
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, url, -1, itr->url.c_str(), - 1))
+ {
+ metadataMap.erase(itr);
+ goto DISPATCH_REMOVEMETADATAHOOK_again;
+ }
+ }
+}
+
+static void RemoveMetadataHook(const wchar_t *url, const wchar_t *tag)
+{
+ Nullsoft::Utility::AutoLock lock(urlMapGuard);
+DISPATCH_REMOVEMETADATAHOOK_again2:
+ MetadataMap::iterator itr;
+ for (itr=metadataMap.begin();itr!=metadataMap.end();itr++)
+ {
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, url, -1, itr->url.c_str(), - 1) &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, tag, -1, itr->tag.c_str(), - 1))
+ {
+ metadataMap.erase(itr);
+ goto DISPATCH_REMOVEMETADATAHOOK_again2;
+ }
+ }
+}
+
+HRESULT OMCOM::FindService(VARIANTARG *pArg, ifc_omservice **service)
+{
+ if (NULL == service)
+ return E_POINTER;
+
+ *service = NULL;
+ if (NULL == pArg)
+ return E_INVALIDARG;
+
+ HRESULT hr = E_INVALIDARG;
+ UINT serviceId;
+
+ if (VT_BSTR == pArg->vt)
+ {
+ if (FALSE != StrToIntEx(pArg->bstrVal, STIF_SUPPORT_HEX, (INT*)&serviceId))
+ hr = S_OK;
+ }
+ else if (VT_I4 == pArg->vt)
+ {
+ serviceId = pArg->lVal;
+ hr = S_OK;
+ }
+
+ if (SUCCEEDED(hr))
+ hr = ServiceHelper_Find(serviceId, service);
+
+ return hr;
+}
+static HRESULT OmCom_AddServiceToNavigation(ifc_omservice *service)
+{
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ if (NULL != navigation->CreateItem(service))
+ {
+ hr = S_OK;
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+ navigation->Release();
+ }
+ return hr;
+}
+
+static HRESULT OmCom_SubscribeToService(UINT serviceId, LPCWSTR pszName, LPCWSTR pszUrl, INT iconId)
+{
+ ifc_omservice *service = NULL;
+ HRESULT hr = ServiceHelper_Find(serviceId, &service);
+ if (FAILED(hr)) return hr;
+
+ if (S_FALSE == hr)
+ {
+ ServiceHost *serviceHost;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ if (NULL != OMSERVICEMNGR)
+ {
+ hr = OMSERVICEMNGR->CreateService(serviceId, serviceHost, &service);
+ }
+ else
+ hr = E_FAIL;
+ }
+ else if (S_OK == ServiceHelper_IsSubscribed(service))
+ {
+ hr = S_FALSE;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (S_OK == hr)
+ {
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ if (NULL != pszName && L'\0' != *pszName)
+ editor->SetName(pszName, FALSE);
+
+ if (NULL != pszUrl && L'\0' != *pszUrl)
+ editor->SetUrl(pszUrl, FALSE);
+
+ WCHAR szIcon[256] = {0};
+ if (SUCCEEDED(StringCchPrintf(szIcon, ARRAYSIZE(szIcon), L"%u", iconId)))
+ {
+ editor->SetIcon(szIcon, FALSE);
+ }
+
+ hr = editor->SetFlags(SVCF_SUBSCRIBED, SVCF_SUBSCRIBED);
+ if (SUCCEEDED(hr))
+ ServiceHelper_Save(service);
+ editor->Release();
+ }
+
+ if (SUCCEEDED(hr))
+ OmCom_AddServiceToNavigation(service);
+ }
+ service->Release();
+ }
+
+ return hr;
+}
+
+HRESULT OMCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ if (dispid == DISPATCH_ADDTITLEHOOK)
+ {
+ if (pdispparams->cArgs == 3)
+ {
+ Nullsoft::Utility::AutoLock lock(urlMapGuard);
+ RemoveTitleHook(pdispparams->rgvarg[2].bstrVal); // ensure no duplicates
+ url_info info;
+ info.url = pdispparams->rgvarg[2].bstrVal;
+ info.url_wcslen = wcslen(info.url.c_str());
+ info.title=pdispparams->rgvarg[1].bstrVal;
+ info.length= pdispparams->rgvarg[0].lVal;
+ urlMap.push_back(info);
+ return S_OK;
+ }
+ }
+ if (dispid == DISPATCH_REMOVETITLEHOOK)
+ {
+ if (pdispparams->cArgs == 1)
+ {
+ RemoveTitleHook(pdispparams->rgvarg[0].bstrVal);
+ return S_OK;
+ }
+ else
+ return DISP_E_BADPARAMCOUNT;
+ }
+
+ if (dispid == DISPATCH_REMOVEMETADATAHOOK)
+ {
+ if (pdispparams->cArgs == 1)
+ {
+ RemoveMetadataHook(pdispparams->rgvarg[0].bstrVal);
+ return S_OK;
+ }
+ else if (pdispparams->cArgs == 2)
+ {
+ RemoveMetadataHook(pdispparams->rgvarg[1].bstrVal, pdispparams->rgvarg[0].bstrVal);
+ return S_OK;
+ }
+ else
+ return DISP_E_BADPARAMCOUNT;
+ }
+
+ if (dispid == DISPATCH_ADDMETADATAHOOK)
+ {
+ if (pdispparams->cArgs == 3)
+ {
+ Nullsoft::Utility::AutoLock lock(urlMapGuard);
+ RemoveMetadataHook(pdispparams->rgvarg[2].bstrVal, pdispparams->rgvarg[1].bstrVal); // ensure no duplicates
+ metadata_info info;
+ info.url = pdispparams->rgvarg[2].bstrVal;
+ info.tag = pdispparams->rgvarg[1].bstrVal;
+ info.metadata= pdispparams->rgvarg[0].bstrVal;
+ metadataMap.push_back(info);
+ return S_OK;
+ }
+ }
+
+ if (dispid == DISPATCH_SERIALNUMBER)
+ {
+ int serial = GetSerialNumber(FALSE);
+ if (pdispparams->cArgs == 1)
+ {
+ SetSerialNumber(pdispparams->rgvarg[0].lVal);
+ serial = GetSerialNumber(FALSE);
+ }
+
+ JSAPI_INIT_RESULT(pvarResult, VT_I4);
+ JSAPI_SET_RESULT(pvarResult, lVal, serial);
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_ADDSUBSCRIBED)
+ {
+ return AddOmSubscribed(wFlags, pdispparams, pvarResult, puArgErr);
+ }
+
+ if (dispid == DISPATCH_SUBSCRIBE)
+ {
+ return Subscribe(wFlags, pdispparams, pvarResult, puArgErr);
+ }
+
+ if (dispid == DISPATCH_CLEARSUBSCRIBED && pdispparams->cArgs == 1)
+ {
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
+ JSAPI_INIT_RESULT(pvarResult, VT_I4);
+
+ BOOL result = FALSE;
+ ifc_omservice *service;
+ if (S_OK == FindService(&JSAPI_PARAM(pdispparams, 1), &service))
+ {
+ if (SUCCEEDED(ServiceHelper_Subscribe(service, FALSE, SHF_SAVE /* | SHF_NOTIFY*/)))
+ result = TRUE;
+
+ service->Release();
+ }
+
+ JSAPI_SET_RESULT(pvarResult, lVal, result);
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_ISSUBSCRIBED)
+ {
+ return IsOmSubscribed(wFlags, pdispparams, pvarResult, puArgErr);
+ }
+
+ if (dispid == DISPATCH_JNETCREATE)
+ {
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_DISPATCH;
+ V_DISPATCH(pvarResult) = new JnetCOM();
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_GETWID)
+ {
+ WCHAR szBuffer[512] = {0};
+ if (NULL == OMBROWSERMNGR ||
+ FAILED(OMBROWSERMNGR->GetClientId(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ szBuffer[0] = L'\0';
+ }
+
+ BSTR tag = SysAllocString(szBuffer);
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_BSTR;
+ V_BSTR(pvarResult) = tag;
+
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_GETSID)
+ {
+ WCHAR szBuffer[512] = {0};
+ if (NULL == OMBROWSERMNGR ||
+ FAILED(OMBROWSERMNGR->GetSessionId(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ szBuffer[0] = L'\0';
+ }
+
+ BSTR tag = SysAllocString(szBuffer);
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_BSTR;
+ V_BSTR(pvarResult) = tag;
+
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_PLAY && pdispparams->cArgs == 1)
+ {
+ if (pdispparams->rgvarg[0].bstrVal)
+ mediaLibrary.PlayStream(pdispparams->rgvarg[0].bstrVal);
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_PREF)
+ {
+ Preferences_Show();
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_CONFIG)
+ {
+ VariantInit(pvarResult);
+ if(NULL == config && FAILED(ConfigCOM::CreateInstanceA("ml_online_config", g_config->GetPath(), &config)))
+ {
+ V_VT(pvarResult) = VT_NULL;
+ }
+ else
+ {
+ V_VT(pvarResult) = VT_DISPATCH;
+ V_DISPATCH(pvarResult) = config;
+ config->AddRef();
+ }
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_ENQUEUE && pdispparams->cArgs == 1)
+ {
+ if (pdispparams->rgvarg[0].bstrVal)
+ mediaLibrary.EnqueueStream(pdispparams->rgvarg[0].bstrVal);
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_SETSIZE && pdispparams->cArgs == 2)
+ {
+ HWND hView = NULL;
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ hView = navigation->GetActiveView(NULL);
+ navigation->Release();
+ }
+
+ if (NULL != hView && mediaLibrary.library &&
+ GetParent(mediaLibrary.library) &&
+ g_config->ReadInt("AutoSize",1))
+ {
+ HWND hWnd;
+ bool GenFF = false;
+ if (GetParent(GetParent(mediaLibrary.library)))
+ {
+ hWnd = GetParent(GetParent(mediaLibrary.library));
+ GenFF = true;
+ }
+ else
+ hWnd = GetParent(mediaLibrary.library);
+
+ int width = pdispparams->rgvarg[1].lVal;
+ int height = pdispparams->rgvarg[0].lVal;
+
+ RECT rc;
+ GetWindowRect(hView, &rc); // Our Html page
+ int WWidth = rc.right - rc.left;
+ int WHeight = rc.bottom - rc.top;
+
+ GetWindowRect(hWnd, &rc); // Gen ML Size
+ int PWidth = rc.right - rc.left;
+ int PHeight = rc.bottom - rc.top;
+
+ // Subtract the original window size from the parent(base) size
+ PWidth -= WWidth;
+ PHeight -= WHeight;
+
+ // Add the target size to the parent(base) size
+ PWidth += width;
+ PHeight += height;
+
+ if (GenFF)
+ {
+ SendMessage(hWnd, VIDEO_GENFF_SIZEREQUEST, PWidth, PHeight);
+ }
+ else
+ {
+ SetWindowPos(hWnd, 0, 0, 0, PWidth, PHeight, SWP_NOMOVE|SWP_ASYNCWINDOWPOS);
+ // weird? sometimes height isnt set if called once...
+ SetWindowPos(hWnd, 0, 0, 0, PWidth, PHeight, SWP_NOMOVE|SWP_ASYNCWINDOWPOS);
+ }
+ }
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_GETX)
+ {
+ RECT rc;
+ HWND hView = NULL;
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ hView = navigation->GetActiveView(NULL);
+ navigation->Release();
+ }
+
+ if (NULL != hView)
+ {
+ GetWindowRect(hView, &rc); // Our Html page
+ int WWidth = rc.right - rc.left;
+
+ if (pvarResult)
+ {
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_I4;
+ V_I4(pvarResult) = WWidth;
+ }
+ }
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_GETY)
+ {
+ RECT rc;
+ HWND hView = NULL;
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ hView = navigation->GetActiveView(NULL);
+ navigation->Release();
+ }
+
+ if (NULL != hView)
+ {
+ GetWindowRect(hView, &rc); // Our Html page
+ int WHeight = rc.bottom - rc.top;
+ if (pvarResult)
+ {
+ VariantInit(pvarResult);
+ V_VT(pvarResult) = VT_I4;
+ V_I4(pvarResult) = WHeight;
+ }
+ }
+ return S_OK;
+ }
+
+ if (dispid == DISPATCH_NAVDISPLAY && pdispparams->cArgs == 1)
+ {
+ //int visible = pdispparams->rgvarg[0].lVal;
+ return E_NOTIMPL;
+ }
+
+ if (dispid == DISPATCH_FOCUSURL && pdispparams->cArgs == 2)
+ {
+ ifc_omservice *service;
+ if (S_OK == FindService(&pdispparams->rgvarg[1], &service))
+ {
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ navigation->ShowService(service->GetId(), pdispparams->rgvarg[0].bstrVal);
+ navigation->Release();
+ }
+
+ service->Release();
+ }
+ return S_OK;
+ }
+
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+STDMETHODIMP OMCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch *)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG OMCOM::AddRef(void)
+{
+ return 0;
+}
+
+ULONG OMCOM::Release(void)
+{
+ return 0;
+}
+
+HRESULT OMCOM::IsOmSubscribed(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
+ JSAPI_INIT_RESULT(pvarResult, VT_I4);
+ // 0 No knowledge , 1 Knowledge and Disabled, 2 Knowledge and Enabled
+
+ ifc_omservice *service;
+ if (S_OK == FindService(&JSAPI_PARAM(pdispparams, 1), &service))
+ {
+ JSAPI_SET_RESULT(pvarResult, lVal, (S_OK == ServiceHelper_IsSubscribed(service)) ? 2 : 1);
+ service->Release();
+ }
+ else
+ {
+ JSAPI_SET_RESULT(pvarResult, lVal, 0);
+ }
+
+ return S_OK;
+}
+
+HRESULT OMCOM::AddOmSubscribed(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 3, 4);
+
+ JSAPI_INIT_RESULT(pvarResult, VT_I4);
+
+ LPCWSTR pszUrl, pszName;
+ UINT baseArg, serviceId, iconId;
+
+ baseArg = 0;
+
+ if (pdispparams->cArgs >= 4)
+ {
+ JSAPI_GETUNSIGNED_AS_NUMBER(iconId, pdispparams, (baseArg + 1), puArgErr);
+ baseArg++;
+ }
+ else
+ iconId = 0;
+
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, (baseArg + 1), puArgErr);
+ JSAPI_GETSTRING(pszName, pdispparams, (baseArg + 2), puArgErr);
+ JSAPI_GETSTRING(pszUrl, pdispparams, (baseArg + 3), puArgErr);
+
+
+
+
+ INT result = OmCom_SubscribeToService(serviceId, pszName, pszUrl, iconId);
+ JSAPI_SET_RESULT(pvarResult, lVal, result);
+
+ return S_OK;
+}
+
+typedef int (*HTTPRETRIEVEFILEW)(HWND hwnd, char *url, wchar_t *file, wchar_t *dlgtitle);
+HRESULT OMCOM::Subscribe(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ // window.external.OnMedia.Subscribe(String name, String url, String id, String icon, String version);
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 5);
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+
+ WCHAR szBuffer[4096] = {0};
+ LPCWSTR pszName, pszUrl, pszIcon;
+ UINT serviceId, version;
+
+ JSAPI_GETSTRING(pszName, pdispparams, 1, puArgErr);
+ JSAPI_GETSTRING(pszUrl, pdispparams, 2, puArgErr);
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, 3, puArgErr);
+ JSAPI_GETNUMBER_AS_STRING(pszIcon, szBuffer, pdispparams, 4, puArgErr);
+ JSAPI_GETUNSIGNED_AS_NUMBER(version, pdispparams, 5, puArgErr);
+
+ HRESULT hr;
+ ifc_omservice *service;
+ hr = ServiceHelper_Find(serviceId, &service);
+ if (S_OK != hr)
+ {
+ hr = ServiceHelper_Create(serviceId, pszName, pszIcon, pszUrl, SVCF_SUBSCRIBED | SVCF_PREAUTHORIZED, 2, TRUE, &service);
+ if (SUCCEEDED(hr))
+ {
+ OmCom_AddServiceToNavigation(service);
+ service->Release();
+ }
+ }
+ else
+ {
+ hr = ServiceHelper_Subscribe(service, TRUE, SHF_SAVE /*| SHF_NOTIFY*/); // do not call SHF_NOTIFY - or it will adjust stats
+ if (S_OK == hr)
+ OmCom_AddServiceToNavigation(service);
+
+ service->Release();
+ }
+
+ JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE));
+ return S_OK;
+}
+
+HRESULT OMCOM::SetSerialNumber(INT sn)
+{
+ if (SERIALNUMBER_INVALID == sn)
+ return E_INVALIDARG;
+
+ if (serialNumber == sn)
+ return S_FALSE;
+
+ serialNumber = sn;
+
+ CHAR szBuffer[64] = {0};
+ HRESULT hr = StringCchPrintfA(szBuffer, ARRAYSIZE(szBuffer), "%d", serialNumber);
+ if (SUCCEEDED(hr))
+ {
+ hr = Config_WriteStr(NULL, CONFIG_SERIALNUMBER, szBuffer);
+ }
+
+ return hr;
+}
+
+INT OMCOM::GetSerialNumber(BOOL fForceRead)
+{
+ if (FALSE != fForceRead || SERIALNUMBER_INVALID == serialNumber)
+ {
+ serialNumber = Config_ReadInt(NULL, CONFIG_SERIALNUMBER, SERIALNUMBER_DEFAULT);
+ }
+ return serialNumber;
+}
+
+/*
+HRESULT OMCOM::Login(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ // window.external.OnMedia.Login(String url);
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+ JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_FALSE);
+ const wchar_t *url = JSAPI_PARAM(pdispparams, 1).bstrVal;
+ HWND active_view = OmView_GetActive();
+ if (active_view)
+ {
+ OmService *service = OmView_GetService(active_view);
+ if (service)
+ {
+ OMNAVIGATION->SelectService(service, url);
+ JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE);
+ }
+ }
+ return S_OK;
+}
+*/ \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/OMCOM.h b/Src/Plugins/Library/ml_online/OMCOM.h
new file mode 100644
index 00000000..07fbd5f3
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/OMCOM.h
@@ -0,0 +1,43 @@
+#ifndef NULLSOFT_OMCOMH
+#define NULLSOFT_OMCOMH
+
+#include <ocidl.h>
+
+class ConfigCOM;
+class ifc_omservice;
+
+class OMCOM : public IDispatch
+{
+public:
+ OMCOM();
+ ~OMCOM();
+
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+ // *** IDispatch Methods ***
+ STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
+ STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
+ STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
+ STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
+
+private:
+ STDMETHOD (IsOmSubscribed)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr);
+ STDMETHOD (AddOmSubscribed)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr);
+ STDMETHOD (Subscribe)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr);
+
+ HRESULT FindService(VARIANTARG *pArg, ifc_omservice **service);
+
+public:
+ INT GetSerialNumber(BOOL fForceRead);
+ HRESULT SetSerialNumber(INT sn);
+ HRESULT Publish();
+
+protected:
+ ConfigCOM *config;
+ INT serialNumber;
+ UINT publishCookie;
+};
+
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Preferences.cpp b/Src/Plugins/Library/ml_online/Preferences.cpp
new file mode 100644
index 00000000..5bac3daf
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Preferences.cpp
@@ -0,0 +1,110 @@
+#include "main.h"
+#include "./preferences.h"
+
+#include "../winamp/wa_ipc.h"
+#include "./resource.h"
+#include "./api__ml_online.h"
+#include "./config.h"
+
+#include <windows.h>
+#include <shlobj.h>
+
+static prefsDlgRecW preferences;
+extern C_Config *g_config;
+
+static INT_PTR CALLBACK Preferences_DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL Preferences_Register()
+{
+ WCHAR szBuffer[256] = {0};
+ WASABI_API_LNGSTRINGW_BUF(IDS_ONLINE_SERVICES, szBuffer, ARRAYSIZE(szBuffer));
+
+ preferences.hInst = WASABI_API_LNG_HINST;
+ preferences.dlgID = IDD_OMPREF;
+ preferences.proc = (void *)Preferences_DialogProc;
+ preferences.name = Plugin_CopyString(szBuffer);
+ preferences.where = 6; // Media Library
+
+ return (BOOL)SENDWAIPC(Plugin_GetWinamp(), IPC_ADD_PREFS_DLGW, &preferences);
+
+}
+
+void Preferences_Unregister()
+{
+ SENDWAIPC(Plugin_GetWinamp(), IPC_REMOVE_PREFS_DLG, &preferences);
+
+}
+
+BOOL Preferences_Show()
+{
+ return (BOOL)SENDWAIPC(Plugin_GetWinamp(), IPC_OPENPREFSTOPAGE, &preferences);
+}
+
+static INT_PTR CALLBACK Preferences_DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ CheckDlgButton(hwndDlg,IDC_AUTOSIZE,g_config->ReadInt("AutoSize",1));
+
+ char tmp[64] = {0};
+ wsprintfA(tmp,"%i",g_config->ReadInt("maxbandwidth", MAXBANDWIDTH ));
+ SetDlgItemTextA(hwndDlg,IDC_RADIO_MAXBW,tmp);
+ wsprintfA(tmp,"%i",g_config->ReadInt("minbandwidth",1));
+ SetDlgItemTextA(hwndDlg,IDC_RADIO_MINBW,tmp);
+ int radiofreq=g_config->ReadInt("radio_upd_freq",0);
+ CheckDlgButton(hwndDlg,radiofreq==0?IDC_RADIO_HOURLY:radiofreq==1?IDC_RADIO_DAILY:radiofreq==2?IDC_RADIO_WEEKLY:IDC_RADIO_NEVER,BST_CHECKED);
+ SetDlgItemTextA(hwndDlg, IDC_NOWPLAYINGURL, g_config->ReadString("nowplayingurl",""));
+ }
+ break;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_AUTOSIZE:
+ g_config->WriteInt("AutoSize",IsDlgButtonChecked(hwndDlg,IDC_AUTOSIZE));
+ break;
+
+ case IDC_RADIO_NEVER:
+ case IDC_RADIO_DAILY:
+ case IDC_RADIO_WEEKLY:
+ case IDC_RADIO_HOURLY:
+ {
+ int radiofreq=0;
+ if(IsDlgButtonChecked(hwndDlg,IDC_RADIO_NEVER)) radiofreq=3;
+ if(IsDlgButtonChecked(hwndDlg,IDC_RADIO_DAILY)) radiofreq=1;
+ if(IsDlgButtonChecked(hwndDlg,IDC_RADIO_WEEKLY)) radiofreq=2;
+ if(IsDlgButtonChecked(hwndDlg,IDC_RADIO_HOURLY)) radiofreq=0;
+ g_config->WriteInt("radio_upd_freq",radiofreq);
+ }
+ break;
+
+ case IDC_NOWPLAYINGURL:
+ if (HIWORD(wParam) == EN_CHANGE)
+ {
+ char nowplayingurl[1024] = {0};
+ GetDlgItemTextA(hwndDlg, IDC_NOWPLAYINGURL, nowplayingurl, ARRAYSIZE(nowplayingurl));
+ g_config->WriteString("nowplayingurl",nowplayingurl);
+ }
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ {
+ char tmp[64]={0,};
+ GetDlgItemTextA(hwndDlg,IDC_RADIO_MAXBW,tmp,sizeof(tmp)-1);
+ int x = atoi(tmp);
+ if ( x < 2 ) x = 2;
+ g_config->WriteInt("maxbandwidth",x);
+ GetDlgItemTextA(hwndDlg,IDC_RADIO_MINBW,tmp,sizeof(tmp)-1);
+ int y = atoi(tmp);
+ if ( y < 1 ) y = 1;
+ if ( y > x ) y = x-1;
+ g_config->WriteInt("minbandwidth",y);
+ }
+ break;
+ }
+ return 0;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/Preferences.h b/Src/Plugins/Library/ml_online/Preferences.h
new file mode 100644
index 00000000..0fee936b
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/Preferences.h
@@ -0,0 +1,16 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_PREFERENCES_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_PREFERENCES_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+#define MAXBANDWIDTH 3500
+
+BOOL Preferences_Register();
+void Preferences_Unregister();
+BOOL Preferences_Show();
+
+#endif // NULLOSFT_ONLINEMEDIA_PLUGIN_PREFERENCES_HEADER
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
diff --git a/Src/Plugins/Library/ml_online/api__ml_online.h b/Src/Plugins/Library/ml_online/api__ml_online.h
new file mode 100644
index 00000000..e40852bf
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/api__ml_online.h
@@ -0,0 +1,62 @@
+#ifndef NULLOSFT_ONLINEMEDIA_WASABI_API_HEADER
+#define NULLOSFT_ONLINEMEDIA_WASABI_API_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+#include <api/service/api_service.h>
+extern api_service *wasabiManager;
+#define WASABI_API_SVC wasabiManager
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+#include "../Agave/Config/api_config.h"
+#include "../Agave/Language/api_language.h"
+
+#include <api/memmgr/api_memmgr.h>
+extern api_memmgr *memManagerApi;
+#define WASABI_API_MEMMNGR memManagerApi
+
+#include "../Agave/ExplorerFindFile/api_explorerfindfile.h"
+
+#include "../Winamp/JSAPI2_api_security.h"
+extern JSAPI2::api_security *jsapi2_securityApi;
+#define AGAVE_API_JSAPI2_SECURITY jsapi2_securityApi
+
+#include <api/service/svcs/svc_imgload.h>
+extern svc_imageLoader *pngLoaderApi;
+#define WASABI_API_PNGLOADER pngLoaderApi
+EXTERN_C const GUID pngLoaderGUID;
+
+#include "../auth/api_auth.h"
+extern api_auth *authApi;
+#define AGAVE_API_AUTH authApi
+
+#include <obj_ombrowser.h>
+extern obj_ombrowser *browserManager;
+#define OMBROWSERMNGR browserManager
+
+#include <ifc_omservicemanager.h>
+extern ifc_omservicemanager *serviceManager;
+#define OMSERVICEMNGR serviceManager
+
+#include <ifc_omutility.h>
+extern ifc_omutility *omUtility;
+#define OMUTILITY omUtility
+
+HRESULT WasabiApi_Initialize(HINSTANCE hInstance, api_service *serviceApi);
+HRESULT WasabiApi_LoadDefaults();
+ULONG WasabiApi_AddRef(void);
+ULONG WasabiApi_Release(void);
+
+void *Wasabi_QueryInterface(REFGUID interfaceGuid);
+void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance);
+
+#define QueryWasabiInterface(__interfaceType, __interfaceGuid) ((##__interfaceType*)Wasabi_QueryInterface(__interfaceGuid))
+#define ReleaseWasabiInterface(__interfaceGuid, __interfaceInstance) (Wasabi_ReleaseInterface((__interfaceGuid), (__interfaceInstance)))
+
+#endif // NULLOSFT_ONLINEMEDIA_WASABI_API_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/browserEvent.cpp b/Src/Plugins/Library/ml_online/browserEvent.cpp
new file mode 100644
index 00000000..d24442a7
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/browserEvent.cpp
@@ -0,0 +1,100 @@
+#include "main.h"
+#include "./browserEvent.h"
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omserviceeventmngr.h>
+#include <ifc_omservicecommand.h>
+
+#include <browserView.h>
+#include <browserPopup.h>
+
+BrowserEvent::BrowserEvent()
+ : ref(1)
+{
+}
+
+BrowserEvent::~BrowserEvent()
+{
+}
+
+HRESULT BrowserEvent::CreateInstance(BrowserEvent **instance)
+{
+ if (NULL == instance) return E_POINTER;
+ *instance = new BrowserEvent();
+ if (NULL == *instance) return E_OUTOFMEMORY;
+ return S_OK;
+}
+
+size_t BrowserEvent::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t BrowserEvent::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int BrowserEvent::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_OmBrowserEvent))
+ *object = static_cast<ifc_ombrowserevent*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+void BrowserEvent::WindowCreate(HWND hwnd, const GUID *windowType)
+{
+ if (NULL != windowType)
+ {
+ if (IsEqualGUID(*windowType, WTID_BrowserView) ||
+ IsEqualGUID(*windowType, WTID_BrowserPopup))
+ {
+ ifc_omservice *service;
+ if (FALSE != BrowserControl_GetService(hwnd, &service))
+ {
+ UINT flags;
+ if (SUCCEEDED(service->GetFlags(&flags)) &&
+ 0 == ((SVCF_SPECIAL | SVCF_VALIDATED | SVCF_VERSIONCHECK) & flags))
+ {
+ ServiceHelper_BeginVersionCheck(service);
+ }
+ service->Release();
+ }
+ }
+ }
+}
+
+void BrowserEvent::WindowClose(HWND hwnd, const GUID *windowType)
+{
+}
+
+#define CBCLASS BrowserEvent
+START_DISPATCH;
+CB(ADDREF, AddRef)
+CB(RELEASE, Release)
+CB(QUERYINTERFACE, QueryInterface)
+VCB(API_WINDOWCREATE, WindowCreate)
+VCB(API_WINDOWCLOSE, WindowClose)
+END_DISPATCH;
+#undef CBCLASS
diff --git a/Src/Plugins/Library/ml_online/browserEvent.h b/Src/Plugins/Library/ml_online/browserEvent.h
new file mode 100644
index 00000000..07d10f52
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/browserEvent.h
@@ -0,0 +1,41 @@
+#ifndef NULLSOFT_ONLINEMEDIA_PLUGIN_BROWSER_EVENT_HANDLER_HEADER
+#define NULLSOFT_ONLINEMEDIA_PLUGIN_BROWSER_EVENT_HANDLER_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <ifc_ombrowserevent.h>
+
+class BrowserEvent : public ifc_ombrowserevent
+{
+
+protected:
+ BrowserEvent();
+ ~BrowserEvent();
+
+public:
+ static HRESULT CreateInstance(BrowserEvent **instance);
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_ombrowserevent */
+ void WindowCreate(HWND hwnd, const GUID *windowType);
+ void WindowClose(HWND hwnd, const GUID *windowType);
+
+
+protected:
+ ULONG ref;
+
+protected:
+ RECVS_DISPATCH;
+};
+
+
+
+
+#endif //NULLSOFT_ONLINEMEDIA_PLUGIN_BROWSER_EVENT_HANDLER_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/commands.cpp b/Src/Plugins/Library/ml_online/commands.cpp
new file mode 100644
index 00000000..0762991f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/commands.cpp
@@ -0,0 +1,418 @@
+#include "main.h"
+#include "./commands.h"
+#include "./api__ml_online.h"
+#include "./resource.h"
+#include "./navigation.h"
+#include "./preferences.h"
+#include "./messagebox.h"
+#include "./serviceHelper.h"
+#include "../winamp/wa_ipc.h"
+#include "./import.h"
+#include <ifc_omservice.h>
+#include <browserView.h>
+#include <wininet.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define BEGIN_COMMAND_SELECT(__commandId) switch(commandId) {
+#define END_COMMAND_SELECT }
+
+#define OMCOMMAND(__commandId, __commandCode, __resultOut) case (__commandId):\
+ { BOOL result = ##__commandCode; \
+ if (NULL != (__resultOut)) { *(__resultOut) = result;}\
+ return TRUE;}
+
+BOOL Command_SetServiceRating(ifc_omservice *service, INT rating)
+{
+ return SUCCEEDED(ServiceHelper_SetRating(service, rating, SHF_NOTIFY | SHF_VERBAL | SHF_SAVE));
+}
+
+BOOL Command_OpenServiceView(ifc_omservice *service)
+{
+ BOOL resultOk = FALSE;;
+ Navigation *navigation;
+ if (NULL != service && SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ HNAVITEM hItem = navigation->FindService(service->GetId(), NULL);
+ if (NULL != hItem)
+ {
+ HRESULT hr = navigation->SelectItem(hItem, NULL);
+
+ if (SUCCEEDED(hr))
+ resultOk = TRUE;
+ }
+ navigation->Release();
+ }
+
+ return resultOk;
+}
+
+HRESULT Command_NavigateService(ifc_omservice *service, LPCWSTR pszUrl, BOOL fActiveOnly)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_UNEXPECTED;
+
+ ifc_omservice *activeService;
+ HWND hView = navigation->GetActiveView(&activeService);
+ if (NULL == hView || activeService->GetId() != service->GetId())
+ hView = NULL;
+
+ if (NULL != activeService)
+ activeService->Release();
+
+ HRESULT hr = S_OK;
+
+ if (NULL != hView)
+ {
+ if (FALSE == BrowserView_Navigate(hView, pszUrl, TRUE))
+ hr = E_FAIL;
+ }
+ else
+ {
+ hr = (FALSE == fActiveOnly) ?
+ navigation->ShowService(service->GetId(), pszUrl) : E_NOTIMPL;
+ }
+
+ navigation->Release();
+
+ return hr;
+}
+
+HRESULT Command_EditService( ifc_omservice *service )
+{
+ if ( NULL == service )
+ return E_INVALIDARG;
+
+ WCHAR szBuffer[ 2048 ] = { 0 };
+
+ HRESULT hr = Plugin_MakeResourcePath( szBuffer, ARRAYSIZE( szBuffer ), RT_HTML, MAKEINTRESOURCE( IDR_HTML_EDITOR ), RESPATH_TARGETIE | RESPATH_COMPACT );
+ if ( FAILED( hr ) )
+ return hr;
+
+ INT cchUrl = lstrlen( szBuffer );
+ LPWSTR pszParam = szBuffer + cchUrl;
+ INT cchParamMax = ARRAYSIZE( szBuffer ) - cchUrl;
+
+ hr = StringCchPrintf( pszParam, cchParamMax, L"?serviceId=%u", service->GetId() );
+ if ( FAILED( hr ) )
+ return hr;
+
+ return Command_NavigateService( service, szBuffer, FALSE );
+}
+
+BOOL Command_OpenServicePopup(ifc_omservice *service)
+{
+ BOOL resultOk = FALSE;;
+ Navigation *navigation;
+ if (NULL != service && SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ HNAVITEM hItem = navigation->FindService(service->GetId(), NULL);
+ if (NULL != hItem)
+ {
+ HWND hPopup;
+ HRESULT hr = navigation->CreatePopup(hItem, &hPopup);
+ if (SUCCEEDED(hr))
+ {
+ ShowWindow(hPopup, SW_SHOWNORMAL);
+ resultOk = TRUE;
+ }
+ }
+ navigation->Release();
+ }
+
+ return resultOk;
+}
+
+BOOL Command_ReportService(ifc_omservice *service)
+{
+ HWND hWinamp = Plugin_GetWinamp();
+ if (NULL == hWinamp || !IsWindow(hWinamp))
+ return FALSE;
+
+ if (NULL == service)
+ return FALSE;
+
+ WCHAR szUrl[256] = {0};
+ WCHAR szClient[128] = {0};
+
+ OMBROWSERMNGR->GetClientId(szClient, ARRAYSIZE(szClient));
+
+ StringCchPrintf(szUrl, ARRAYSIZE(szUrl), L"http://www.winamp.com/legal/abuse?svc_id=%u&unique=%s",
+ service->GetId(), szClient);
+
+ SENDWAIPC(hWinamp, IPC_OPEN_URL, szUrl);
+ return TRUE;
+}
+
+BOOL Command_UnsubscribeService(ifc_omservice *service)
+{
+ return (SUCCEEDED(ServiceHelper_Subscribe(service, FALSE, SHF_NOTIFY | SHF_VERBAL | SHF_SAVE)));
+}
+
+BOOL Command_ShowServiceInfo(ifc_omservice *service)
+{
+ if (NULL == service)
+ return FALSE;
+
+ BOOL resultOk = FALSE;
+
+ HRESULT hr;
+ WCHAR szUrl[INTERNET_MAX_URL_LENGTH] = {0}, szName[INTERNET_MAX_URL_LENGTH] = {0};
+
+ DWORD cchName = ARRAYSIZE(szName);
+ if (FAILED(service->GetName(szUrl, ARRAYSIZE(szUrl))) ||
+ FAILED(UrlEscape(szUrl, szName, &cchName, URL_ESCAPE_SEGMENT_ONLY | URL_ESCAPE_PERCENT)))
+ {
+ StringCchCopy(szName, ARRAYSIZE(szName), L"Info");
+ }
+
+ hr = StringCchPrintf(szUrl, ARRAYSIZE(szUrl), L"http://client.winamp.com/service/detail/%s/%d#", szName, service->GetId());
+ if (FAILED(hr)) return hr;
+
+ Navigation *navigation;
+ hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ HNAVITEM hRoot = navigation->FindService(ROOTSERVICE_ID, NULL);
+ if (NULL != hRoot)
+ {
+ HNAVITEM hActive = navigation->GetActive(NULL);
+ if (hActive == hRoot)
+ {
+ HWND hView = navigation->GetActiveView(NULL);
+ if (NULL != hView && FALSE != BrowserView_Navigate(hView, szUrl, TRUE))
+ resultOk = TRUE;
+ }
+
+ if (FALSE == resultOk && SUCCEEDED(navigation->SelectItem(hRoot, szUrl)))
+ resultOk = TRUE;
+ }
+ navigation->Release();
+ }
+ return resultOk;
+}
+
+BOOL Command_ResetServicePolicy(ifc_omservice *service)
+{
+ return (SUCCEEDED(ServiceHelper_ResetPermissions(service, SHF_NOTIFY | SHF_VERBAL)));
+}
+
+BOOL Command_ResetSubscription()
+{
+ HRESULT hr = ServiceHelper_ResetSubscription(SHF_VERBAL);
+ return SUCCEEDED(hr);
+}
+
+static BOOL Command_OpenPreferences()
+{
+ return Preferences_Show();
+}
+
+static BOOL Command_OpenHelp()
+{
+ return (BOOL)SENDWAIPC(Plugin_GetWinamp(), IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8112533645844-Online-Services");
+}
+
+static BOOL Command_NavigateView(HWND hView, LPCWSTR navigateUrl)
+{
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_UNEXPECTED;
+
+ HWND hActive = navigation->GetActiveView(NULL);
+ if (hActive != hView) hView = NULL;
+
+ BOOL resultOk = ( NULL != hView && FALSE != BrowserView_Navigate( hView, navigateUrl, TRUE ) );
+
+ navigation->Release();
+
+ return resultOk;
+}
+
+HRESULT Command_ImportFiles()
+{
+ HWND hOwner = Plugin_GetDialogOwner();
+ return ImportService_FromFile(hOwner);
+}
+
+HRESULT Command_ImportUrl()
+{
+ HWND hOwner = Plugin_GetDialogOwner();
+ return ImportService_FromUrl(hOwner);
+}
+
+HRESULT Command_CreateService()
+{
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (FAILED(hr)) return hr;
+
+ HNAVITEM hItem;
+ hr = navigation->CreateUserService(&hItem);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ hr= navigation->GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ Command_EditService(service);
+ service->Release();
+ }
+ }
+ navigation->Release();
+ return hr;
+}
+
+HRESULT Command_LocateService(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ WCHAR szPath[512];
+
+ HRESULT hr = service->GetAddress(szPath, ARRAYSIZE(szPath));
+ if (FAILED(hr)) return hr;
+
+ if (L'\0' == szPath[0])
+ return E_FAIL;
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_UNEXPECTED;
+
+ navigation->Release();
+
+ if (WASABI_API_EXPLORERFINDFILE)
+ {
+ WASABI_API_EXPLORERFINDFILE->AddFile(szPath);
+ WASABI_API_EXPLORERFINDFILE->ShowFiles();
+ }
+ return E_UNEXPECTED;
+}
+
+HRESULT Command_EditServiceExternal(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ WCHAR szPath[512];
+
+ HRESULT hr = service->GetAddress(szPath, ARRAYSIZE(szPath));
+ if (FAILED(hr)) return hr;
+
+ if (L'\0' == szPath[0])
+ return E_FAIL;
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_UNEXPECTED;
+
+ HWND hOwner = navigation->GetActiveView(NULL);
+ navigation->Release();
+
+ if (NULL == hOwner)
+ hOwner = Plugin_GetLibrary();
+
+ HINSTANCE hInst = ShellExecute(hOwner, L"open", szPath, NULL, NULL, SW_SHOWNORMAL);
+ hr = ((INT_PTR)hInst > 32) ? S_OK : E_FAIL;
+ return hr;
+}
+
+BOOL Command_ProcessService(HWND hView, ifc_omservice *service, INT commandId, BOOL *fSuccess)
+{
+ BEGIN_COMMAND_SELECT(commandId)
+ OMCOMMAND(ID_RATING_VALUE_5, Command_SetServiceRating(service, 5), fSuccess);
+ OMCOMMAND(ID_RATING_VALUE_4, Command_SetServiceRating(service, 4), fSuccess);
+ OMCOMMAND(ID_RATING_VALUE_3, Command_SetServiceRating(service, 3), fSuccess);
+ OMCOMMAND(ID_RATING_VALUE_2, Command_SetServiceRating(service, 2), fSuccess);
+ OMCOMMAND(ID_RATING_VALUE_1, Command_SetServiceRating(service, 1), fSuccess);
+ OMCOMMAND(ID_VIEW_OPEN, Command_OpenServiceView(service), fSuccess);
+ OMCOMMAND(ID_VIEW_OPENPOPUP, Command_OpenServicePopup(service), fSuccess);
+ //OMCOMMAND(ID_SERVICE_REPORT, Command_ReportService(service), fSuccess);
+ OMCOMMAND(ID_SERVICE_UNSUBSCRIBE, Command_UnsubscribeService(service), fSuccess);
+ //OMCOMMAND(ID_SERVICE_GETINFO, Command_ShowServiceInfo(service), fSuccess);
+ OMCOMMAND(ID_SERVICE_RESETPOLICY, Command_ResetServicePolicy(service), fSuccess);
+ OMCOMMAND(ID_SERVICE_IMPORT_FILE, Command_ImportFiles(), fSuccess);
+ OMCOMMAND(ID_SERVICE_IMPORT_URL, Command_ImportUrl(), fSuccess);
+ OMCOMMAND(ID_NAVIGATION_REFRESH, Command_NavigateView(hView, NAVIGATE_REFRESH), fSuccess);
+
+ OMCOMMAND(ID_SERVICE_NEW, Command_CreateService(), fSuccess);
+ OMCOMMAND(ID_SERVICE_EDIT, Command_EditService(service), fSuccess);
+ OMCOMMAND(ID_SERVICE_LOCATE, Command_LocateService(service), fSuccess);
+ OMCOMMAND(ID_SERVICE_EDITEXTERNAL, Command_EditServiceExternal(service), fSuccess);
+
+ END_COMMAND_SELECT
+
+ return FALSE;
+}
+
+BOOL Command_ProcessGeneral(INT commandId, BOOL *fSuccess)
+{
+ BEGIN_COMMAND_SELECT(commandId)
+ //OMCOMMAND(ID_SERVICEMANAGER_RESET, Command_ResetSubscription(), fSuccess);
+ OMCOMMAND(ID_PLUGIN_PREFERENCES, Command_OpenPreferences(), fSuccess);
+ OMCOMMAND(ID_PLUGIN_HELP, Command_OpenHelp(), fSuccess);
+ END_COMMAND_SELECT
+
+ return FALSE;
+}
+
+static void CALLBACK BrowserOptions_Callback(HWND hOptions, UINT type, ULONG_PTR user)
+{
+ HWND hLibrary = (HWND)user;
+ switch(type)
+ {
+ case BOCALLBACK_INIT:
+ {
+ HWND hView = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
+ if (NULL != hView)
+ {
+ RECT viewRect, optionsRect;
+ if (GetWindowRect(hView, &viewRect) && GetWindowRect(hOptions, &optionsRect))
+ {
+ INT x = viewRect.left + ((viewRect.right - viewRect.left) - (optionsRect.right - optionsRect.left))/2;
+ INT y = viewRect.top + ((viewRect.bottom - viewRect.top) - (optionsRect.bottom - optionsRect.top))/2;
+ SetWindowPos(hOptions, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
+ SendMessage(hOptions, DM_REPOSITION, 0, 0L);
+ }
+ }
+ }
+ break;
+ }
+}
+
+HRESULT Command_ShowBrowserOptions()
+{
+ HWND hWinamp = Plugin_GetWinamp();
+ if (NULL == hWinamp || NULL == OMBROWSERMNGR)
+ return E_UNEXPECTED;
+
+ HRESULT hr = OMBROWSERMNGR->Initialize(NULL, hWinamp);
+ if (SUCCEEDED(hr))
+ {
+ HWND hOwner = Plugin_GetDialogOwner();
+ hr = OMBROWSERMNGR->ShowOptions(hOwner, BOSTYLE_NORMAL | BOSTYLE_SHOWDEBUG,
+ BrowserOptions_Callback, (ULONG_PTR)hOwner);
+ }
+ return hr;
+}
+
+BOOL Command_ProcessView(HWND hView, INT commandId, BOOL *fSuccess)
+{
+ BEGIN_COMMAND_SELECT(commandId)
+ OMCOMMAND(ID_NAVIGATION_HOME, Command_NavigateView(hView, NAVIGATE_HOME), fSuccess);
+ OMCOMMAND(ID_NAVIGATION_BACK, Command_NavigateView(hView, NAVIGATE_BACK), fSuccess);
+ OMCOMMAND(ID_NAVIGATION_FORWARD, Command_NavigateView(hView, NAVIGATE_FORWARD), fSuccess);
+ OMCOMMAND(ID_NAVIGATION_REFRESH, Command_NavigateView(hView, NAVIGATE_REFRESH), fSuccess);
+ OMCOMMAND(ID_NAVIGATION_STOP, Command_NavigateView(hView, NAVIGATE_STOP), fSuccess);
+ OMCOMMAND(ID_OMBROWSER_OPTIONS, Command_ShowBrowserOptions(), fSuccess);
+ OMCOMMAND(ID_SERVICE_IMPORT_FILE, Command_ImportFiles(), fSuccess);
+ OMCOMMAND(ID_SERVICE_IMPORT_URL, Command_ImportUrl(), fSuccess);
+ OMCOMMAND(ID_SERVICE_NEW, Command_CreateService(), fSuccess);
+ END_COMMAND_SELECT
+ return FALSE;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/commands.h b/Src/Plugins/Library/ml_online/commands.h
new file mode 100644
index 00000000..79aec555
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/commands.h
@@ -0,0 +1,29 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_COMMANDS_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_COMMANDS_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+class ifc_omservice;
+
+// returns TRUE if command was handled (fSuccess will have result code if not NULL).
+
+HRESULT Command_NavigateService(ifc_omservice *service, LPCWSTR pszUrl, BOOL fActiveOnly);
+BOOL Command_ProcessService(HWND hView, ifc_omservice *service, INT commandId, BOOL *fSuccess);
+BOOL Command_ProcessView(HWND hView, INT commandId, BOOL *fSuccess);
+BOOL Command_ProcessGeneral(INT commandId, BOOL *fSuccess);
+
+BOOL Command_ReportService(ifc_omservice *service);
+BOOL Command_UnsubscribeService(ifc_omservice *service);
+BOOL Command_ShowServiceInfo(ifc_omservice *service);
+BOOL Command_ResetServicePolicy(ifc_omservice *service);
+BOOL Command_ResetSubscription();
+BOOL Command_SetServiceRating(ifc_omservice *service, INT rating);
+HRESULT Command_CreateService(void);
+BOOL Command_OpenServiceView(ifc_omservice *service);
+BOOL Command_OpenServicePopup(ifc_omservice *service);
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_COMMANDS_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/common.cpp b/Src/Plugins/Library/ml_online/common.cpp
new file mode 100644
index 00000000..f3d88809
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/common.cpp
@@ -0,0 +1,374 @@
+#include "./common.h"
+#include "./api__ml_online.h"
+#include "./main.h"
+
+#include "../winamp/wa_ipc.h"
+
+#include <strsafe.h>
+
+LPWSTR Plugin_MallocString(size_t cchLen)
+{
+ return (LPWSTR)calloc(cchLen, sizeof(WCHAR));
+}
+
+void Plugin_FreeString(LPWSTR pszString)
+{
+ if (NULL != pszString)
+ {
+ free(pszString);
+ pszString = NULL;
+ }
+}
+
+LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen)
+{
+ return (LPWSTR)realloc(pszString, sizeof(WCHAR) * cchLen);
+}
+
+LPWSTR Plugin_CopyString(LPCWSTR pszSource)
+{
+ if (NULL == pszSource)
+ return NULL;
+
+ INT cchSource = lstrlenW(pszSource) + 1;
+
+ LPWSTR copy = Plugin_MallocString(cchSource);
+ if (NULL != copy)
+ {
+ CopyMemory(copy, pszSource, sizeof(WCHAR) * cchSource);
+ }
+ return copy;
+}
+
+LPSTR Plugin_MallocAnsiString(size_t cchLen)
+{
+ return (LPSTR)calloc(cchLen, sizeof(CHAR));
+}
+
+LPSTR Plugin_CopyAnsiString(LPCSTR pszSource)
+{
+ if (NULL == pszSource)
+ return NULL;
+
+ INT cchSource = lstrlenA(pszSource) + 1;
+
+ LPSTR copy = Plugin_MallocAnsiString(cchSource);
+ if (NULL != copy)
+ {
+ CopyMemory(copy, pszSource, sizeof(CHAR) * cchSource);
+ }
+ return copy;
+
+}
+void Plugin_FreeAnsiString(LPSTR pszString)
+{
+ Plugin_FreeString((LPWSTR)pszString);
+}
+
+LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar)
+{
+ INT cchBuffer = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar);
+ if (0 == cchBuffer) return NULL;
+
+ LPSTR buffer = Plugin_MallocAnsiString(cchBuffer);
+ if (NULL == buffer) return NULL;
+
+ if (0 == WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, buffer, cchBuffer, lpDefaultChar, lpUsedDefaultChar))
+ {
+ Plugin_FreeAnsiString(buffer);
+ return NULL;
+ }
+ return buffer;
+}
+
+LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte)
+{
+ if (NULL == lpMultiByteStr) return NULL;
+ INT cchBuffer = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0);
+ if (NULL == cchBuffer) return NULL;
+
+ if (cbMultiByte > 0) cchBuffer++;
+
+ LPWSTR buffer = Plugin_MallocString(cchBuffer);
+ if (NULL == buffer) return NULL;
+
+ if (0 == MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, buffer, cchBuffer))
+ {
+ Plugin_FreeString(buffer);
+ return NULL;
+ }
+
+ if (cbMultiByte > 0)
+ {
+ buffer[cchBuffer - 1] = L'\0';
+ }
+ return buffer;
+}
+
+LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource)
+{
+ return (IS_INTRESOURCE(pszResource)) ?
+ (LPWSTR)pszResource :
+ Plugin_CopyString(pszResource);
+}
+
+void Plugin_FreeResString(LPWSTR pszResource)
+{
+ if (!IS_INTRESOURCE(pszResource))
+ Plugin_FreeString(pszResource);
+}
+
+HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString)
+{
+ if (NULL == pszBuffer)
+ return E_INVALIDARG;
+
+ HRESULT hr = S_OK;
+
+ if (NULL == pszString)
+ {
+ pszBuffer[0] = L'\0';
+ }
+ else if (IS_INTRESOURCE(pszString))
+ {
+ if (NULL == WASABI_API_LNG)
+ hr = E_FAIL;
+ else
+ WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszString, pszBuffer, cchBufferMax);
+ }
+ else
+ {
+ hr = StringCchCopy(pszBuffer, cchBufferMax, pszString);
+ }
+ return hr;
+}
+
+void Plugin_SafeRelease(IUnknown *pUnk)
+{
+ if (NULL != pUnk)
+ pUnk->Release();
+}
+
+HRESULT Plugin_MakeResourcePath(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszType, LPCWSTR pszName, UINT flags)
+{
+ HINSTANCE hInstance = WASABI_API_LNG_HINST;
+ if (NULL == hInstance || NULL == FindResource(hInstance, pszName, pszType))
+ hInstance = Plugin_GetInstance();
+
+ if (NULL == OMUTILITY)
+ return E_UNEXPECTED;
+
+ return OMUTILITY->MakeResourcePath(pszBuffer, cchBufferMax, hInstance, pszType, pszName, flags);
+}
+
+HWND Plugin_GetDialogOwner(void)
+{
+ HWND hOwner= Plugin_GetLibrary();
+ if (NULL == hOwner || FALSE == IsWindowVisible(hOwner) ||
+ FALSE == IsWindowEnabled(hOwner))
+ {
+ hOwner = Plugin_GetWinamp();
+ if (NULL != hOwner)
+ {
+ HWND hDlgParent = (HWND)SENDWAIPC(hOwner, IPC_GETDIALOGBOXPARENT, 0L);
+ if (NULL != hDlgParent)
+ hOwner = hDlgParent;
+ }
+ }
+ return hOwner;
+}
+
+HRESULT Plugin_AppendFileFilter(LPTSTR pszBuffer, size_t cchBufferMax, LPCTSTR pName, LPCTSTR pFilter, LPTSTR *ppBufferOut, size_t *pRemaining, BOOL bShowFilter)
+{
+ HRESULT hr;
+
+ LPTSTR pCursor = pszBuffer;
+
+ if (NULL != ppBufferOut)
+ *ppBufferOut = pszBuffer;
+
+ if (NULL != pRemaining)
+ *pRemaining = cchBufferMax;
+
+ if (NULL == pszBuffer || NULL == pName || NULL == pFilter)
+ return E_INVALIDARG;
+
+ pszBuffer[0] = TEXT('\0');
+
+ hr = StringCchCopyEx(pCursor, cchBufferMax, pName, &pCursor, &cchBufferMax,
+ STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE);
+ if (bShowFilter && SUCCEEDED(hr))
+ {
+ LPTSTR p = pCursor;
+ hr = StringCchPrintfEx(pCursor, cchBufferMax, &pCursor, &cchBufferMax,
+ STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE, TEXT(" (%s)"), pFilter);
+ if (SUCCEEDED(hr) && p != pCursor)
+ CharLowerBuff(p, (INT)(INT_PTR)(pCursor - p));
+ }
+ if (SUCCEEDED(hr))
+ {
+ pCursor++;
+ cchBufferMax--;
+ hr = StringCchCopyEx(pCursor, cchBufferMax, pFilter, &pCursor, &cchBufferMax,
+ STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE);
+ }
+
+ if (cchBufferMax < 1)
+ hr = STRSAFE_E_INSUFFICIENT_BUFFER;
+
+ pCursor++;
+ cchBufferMax--;
+
+ if (SUCCEEDED(hr))
+ {
+ pCursor[0] = TEXT('\0');
+ if (NULL != ppBufferOut)
+ *ppBufferOut = pCursor;
+ if (NULL != pRemaining)
+ *pRemaining = cchBufferMax;
+ }
+ else
+ {
+ pszBuffer[0] = TEXT('\0');
+ pszBuffer[1] = TEXT('\0');
+ }
+
+ return hr;
+}
+
+HRESULT Plugin_BuildActionUrl(LPWSTR *ppStringOut, LPCWSTR pszAction, UINT *pServiceUid, size_t cchServiceUid)
+{
+ if (NULL == ppStringOut)
+ return E_POINTER;
+
+ *ppStringOut = NULL;
+
+ if (NULL == pszAction || L'\0' == *pszAction ||
+ NULL == pServiceUid || 0 == cchServiceUid)
+ {
+ return E_INVALIDARG;
+ }
+
+ const WCHAR szPrefix[] = L"http://services.winamp.com/svc/action?action=%s\0";
+ const WCHAR szService[] = L"&svc_id=%u\0";
+ const WCHAR szClient[] = L"&unique_id=%s\0";
+
+ size_t cchBuffer = ARRAYSIZE(szPrefix) + ARRAYSIZE(szService) + ARRAYSIZE(szClient);
+ cchBuffer += lstrlen(pszAction);
+ cchBuffer += (cchServiceUid * 11);
+ cchBuffer += 32; // unique id
+
+ LPWSTR buffer = Plugin_MallocString(cchBuffer);
+ if (NULL == buffer)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr;
+ LPWSTR cursor = buffer;
+ size_t remaining = cchBuffer;
+
+
+ hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ szPrefix, pszAction);
+
+ for (size_t i = 0; i < cchServiceUid && SUCCEEDED(hr); i++)
+ {
+ hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ ((0 == i) ? szService : L",%u"), pServiceUid[i]);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ WCHAR szTemp[128] = {0};
+ hr = OMBROWSERMNGR->GetClientId(szTemp, ARRAYSIZE(szTemp));
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, szClient, szTemp);
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ Plugin_FreeString(buffer);
+ hr = E_FAIL;
+ }
+ else
+ {
+ *ppStringOut = buffer;
+ }
+ return hr;
+}
+
+INT Plugin_ParseKeywords(LPCWSTR input, INT cchInput, WCHAR separator, BOOL eatSpace, KWPARSERPROC callback, ULONG_PTR user)
+{
+ if (NULL == input)
+ return 0;
+
+ if (cchInput < 0)
+ cchInput = lstrlen(input);
+
+ if (cchInput <= 0)
+ return 0;
+
+ LPCWSTR end = (input + cchInput);
+
+ if(eatSpace)
+ while(input < end && L' ' == *input) input++;
+
+ if (L'\0' == *input)
+ return 0;
+
+ INT found = 0;
+
+ for (;;)
+ {
+ LPCWSTR pBlock = input;
+ while(input <= end && separator != *input) input++;
+ LPCWSTR last = (input - 1);
+ if (eatSpace)
+ while(last >= pBlock && L' ' == *last) last--;
+
+ if (last >= pBlock)
+ {
+ UINT code = callback(pBlock, (INT)(INT_PTR)(last - pBlock) + 1, user);
+ if (KWPARSER_FOUND & code) found++;
+ if (KWPARSER_ABORT == (0x01 & code))
+ return found;
+ }
+
+ if (input >= end || L'\0' == *input)
+ return found;
+
+ input++;
+ if(eatSpace)
+ while(input < end && L' ' == *input) input++;
+ }
+
+ return found;
+}
+
+INT Plugin_MessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
+{
+ HWND hHost, hLibrary = Plugin_GetLibrary();
+ if (NULL != hLibrary && FALSE != IsWindowVisible(hLibrary))
+ {
+ hHost = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
+ if (NULL == hHost || FALSE == IsWindowVisible(hHost))
+ hHost = hLibrary;
+ }
+ else
+ hHost = Plugin_GetDialogOwner();
+
+ if(IS_INTRESOURCE(lpText) && NULL != lpText)
+ {
+ WCHAR szText[2048] = {0};
+ lpText = WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)lpText, szText, ARRAYSIZE(szText));
+ }
+
+ if(IS_INTRESOURCE(lpCaption) && NULL != lpCaption)
+ {
+ WCHAR szCaption[128] = {0};
+ lpCaption = WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)lpCaption, szCaption, ARRAYSIZE(szCaption));
+ }
+
+ return MessageBox(hHost, lpText, lpCaption, uType);
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/common.h b/Src/Plugins/Library/ml_online/common.h
new file mode 100644
index 00000000..7fe1f7a3
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/common.h
@@ -0,0 +1,81 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_COMMON_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_COMMON_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../nu/trace.h"
+
+#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(blah) (sizeof(blah)/sizeof(*blah))
+#endif
+
+#ifndef LONGX86
+#ifdef _WIN64
+ #define LONGX86 LONG_PTR
+#else /*_WIN64*/
+ #define LONGX86 LONG
+#endif /*_WIN64*/
+#endif // LONGX86
+
+#ifdef __cplusplus
+ #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam))
+#else
+ #define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam))
+#endif // __cplusplus
+
+#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId))
+#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId))
+
+#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWLP_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; }
+
+#define SENDCMD(__hwnd, __ctrlId, __eventId, __hctrl) (SENDMSG((__hwnd), WM_COMMAND, MAKEWPARAM(__ctrlId, __eventId), (LPARAM)(__hctrl)))
+
+#ifndef GetWindowStyle
+#define GetWindowStyle(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_STYLE))
+#endif //GetWindowStyle
+
+#ifndef GetWindowStyleEx
+#define GetWindowStyleEx(__hwnd) ((UINT)GetWindowLongPtr((__hwnd), GWL_EXSTYLE))
+#endif // GetWindowStyleEx
+
+LPWSTR Plugin_MallocString(size_t cchLen);
+LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen);
+void Plugin_FreeString(LPWSTR pszString);
+LPWSTR Plugin_CopyString(LPCWSTR pszSource);
+
+LPSTR Plugin_MallocAnsiString(size_t cchLen);
+LPSTR Plugin_CopyAnsiString(LPCSTR pszSource);
+void Plugin_FreeAnsiString(LPSTR pszString);
+
+LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource);
+void Plugin_FreeResString(LPWSTR pszResource);
+HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString);
+
+LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar);
+LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte);
+
+void Plugin_SafeRelease(IUnknown *pUnk);
+HRESULT Plugin_MakeResourcePath(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszType, LPCWSTR pszName, UINT uFlags);
+
+HWND Plugin_GetDialogOwner(void);
+HRESULT Plugin_AppendFileFilter(LPTSTR pszBuffer, size_t cchBufferMax, LPCTSTR pName, LPCTSTR pFilter, LPTSTR *ppBufferOut, size_t *pRemaining, BOOL bShowFilter);
+
+
+HRESULT Plugin_BuildActionUrl(LPWSTR *ppStringOut, LPCWSTR pszAction, UINT *pServiceUid, size_t cchServiceUid);
+
+#define KWPARSER_ABORT ((UINT)0x00000000)
+#define KWPARSER_CONTINUE ((UINT)0x00000001)
+#define KWPARSER_FOUND ((UINT)0x80000000) // this is additional modifier
+
+typedef UINT (CALLBACK *KWPARSERPROC)(LPCWSTR /*pszKeyword*/, INT /*cchKeyword*/, ULONG_PTR /*user*/);
+
+INT Plugin_ParseKeywords(LPCWSTR input, INT cchInput, WCHAR separator, BOOL eatSpace, KWPARSERPROC callback, ULONG_PTR user);
+
+INT Plugin_MessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_COMMON_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/config.cpp b/Src/Plugins/Library/ml_online/config.cpp
new file mode 100644
index 00000000..c6e87cb2
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/config.cpp
@@ -0,0 +1,304 @@
+#include "main.h"
+#include "./config.h"
+#include "./api__ml_online.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#define CONFIG_SUFFIX L"Plugins\\ml"
+#define CONFIG_SECTION "ml_online_config"
+
+void C_Config::Flush(void)
+{
+#ifndef C_CONFIG_WIN32NATIVE
+ if (!m_dirty) return;
+ FILE *fp=fopen(m_inifile,"wt");
+ if (!fp) return;
+
+ fprintf(fp,"[ml_online_config]\n");
+ int x;
+ if (m_strs)
+ {
+ for (x = 0; x < m_num_strs; x ++)
+ {
+ char name[17] = {0};
+ memcpy(name,m_strs[x].name,16);
+ name[16]=0;
+ if (m_strs[x].value) fprintf(fp,"%s=%s\n",name,m_strs[x].value);
+ }
+ }
+ fclose(fp);
+ m_dirty=0;
+#endif
+}
+
+C_Config::~C_Config()
+{
+#ifndef C_CONFIG_WIN32NATIVE
+ int x;
+ Flush();
+ if (m_strs) for (x = 0; x < m_num_strs; x ++) free(m_strs[x].value);
+ free(m_strs);
+#endif
+
+ Plugin_FreeAnsiString(m_inifile);
+}
+
+C_Config::C_Config(char *ini)
+{
+ memset(m_strbuf,0,sizeof(m_strbuf));
+ m_inifile= Plugin_CopyAnsiString(ini);
+
+#ifndef C_CONFIG_WIN32NATIVE
+ m_dirty=0;
+ m_strs=NULL;
+ m_num_strs=m_num_strs_alloc=0;
+
+ // read config
+ FILE *fp=fopen(m_inifile,"rt");
+ if (!fp) return;
+
+ for (;;)
+ {
+ char buf[4096] = {0};
+ fgets(buf,sizeof(buf),fp);
+ if (!buf[0] || feof(fp)) break;
+ for (;;)
+ {
+ int l=strlen(buf);
+ if (l > 0 && (buf[l-1] == '\n' || buf[l-1] == '\r')) buf[l-1]=0;
+ else break;
+ }
+ if (buf[0] != '[')
+ {
+ char *p=strstr(buf,"=");
+ if (p)
+ {
+ *p++=0;
+ WriteString(buf,p);
+ }
+ }
+ }
+ m_dirty=0;
+ fclose(fp);
+#endif
+}
+
+void C_Config::WriteInt(char *name, int value)
+{
+ char buf[32] = {0};
+ StringCchPrintfA(buf, ARRAYSIZE(buf), "%d",value);
+ WriteString(name,buf);
+}
+
+int C_Config::ReadInt(char *name, int defvalue)
+{
+#ifndef C_CONFIG_WIN32NATIVE
+ char *t=ReadString(name,"");
+ if (*t) return atoi(t);
+ return defvalue;
+#else
+ return GetPrivateProfileIntA("ml_online_config",name,defvalue,m_inifile);
+#endif
+}
+
+char *C_Config::WriteString(char *name, char *string)
+{
+#ifndef C_CONFIG_WIN32NATIVE
+ m_dirty=1;
+ for (int x = 0; x < m_num_strs; x ++)
+ {
+ if (m_strs[x].value && !strncmp(name,m_strs[x].name,16))
+ {
+ unsigned int l=(strlen(m_strs[x].value)+16)&~15;
+ if (strlen(string)<l)
+ {
+ strcpy(m_strs[x].value,string);
+ }
+ else
+ {
+ free(m_strs[x].value);
+ m_strs[x].value = (char *)malloc((strlen(string)+16)&~15);
+ strcpy(m_strs[x].value,string);
+ }
+ return m_strs[x].value;
+ }
+ }
+
+ // not already in there
+ if (m_num_strs >= m_num_strs_alloc || !m_strs)
+ {
+ m_old_num_strs_alloc = m_num_strs_alloc;
+ m_num_strs_alloc=m_num_strs*3/2+8;
+ strType *data = (strType*)::realloc(m_strs, sizeof(strType) * m_num_strs_alloc);
+ if (data)
+ {
+ m_strs = data;
+ }
+ else
+ {
+ data = (strType*)::malloc(sizeof(strType) * m_num_strs_alloc);
+ if (data)
+ {
+ memcpy(data, m_strs, sizeof(strType) * m_old_num_strs_alloc);
+ free(m_strs);
+ m_strs = data;
+ }
+ else m_num_strs_alloc = m_old_num_strs_alloc;
+ }
+ }
+ strncpy(m_strs[m_num_strs].name,name,16);
+ m_strs[m_num_strs].value = (char *)malloc((strlen(string)+16)&~15);
+ if (m_strs[m_num_strs].value)
+ {
+ strcpy(m_strs[m_num_strs].value,string);
+ return m_strs[m_num_strs++].value;
+ }
+ return "";
+#else
+ WritePrivateProfileStringA("ml_online_config",name,string,m_inifile);
+ return name;
+#endif
+}
+
+char *C_Config::ReadString( char *name, char *defstr )
+{
+#ifndef C_CONFIG_WIN32NATIVE
+ int x;
+ for ( x = 0; x < m_num_strs; x++ )
+ {
+ if ( m_strs[ x ].value && !::strncmp( name, m_strs[ x ].name, 16 ) )
+ {
+ return m_strs[ x ].value;
+ }
+ }
+ return defstr;
+#else
+ static char foobuf[] = "___________ml_online_lameness___________";
+ m_strbuf[ 0 ] = 0;
+ GetPrivateProfileStringA( "ml_online_config", name, foobuf, m_strbuf, sizeof( m_strbuf ), m_inifile );
+ if ( !strcmp( foobuf, m_strbuf ) || !strcmp( m_strbuf, "" ) )
+ {
+ return defstr;
+ }
+
+ m_strbuf[ sizeof( m_strbuf ) - 1 ] = 0;
+ return m_strbuf;
+#endif
+}
+
+static LPCSTR Config_GetPath()
+{
+ static LPSTR configPath = NULL;
+ if (NULL == configPath)
+ {
+ LPCWSTR p = (NULL != WASABI_API_APP) ? WASABI_API_APP->path_getUserSettingsPath() : NULL;
+ if (NULL != p)
+ {
+ WCHAR szBuffer[MAX_PATH * 2] = {0};
+ if (0 != PathCombine(szBuffer, p, CONFIG_SUFFIX))
+ {
+ OMUTILITY->EnsurePathExist(szBuffer);
+ PathAppend(szBuffer, L"ml_online.ini");
+ configPath = Plugin_WideCharToMultiByte(CP_UTF8, 0, szBuffer, -1, NULL, NULL);
+ }
+ }
+ }
+
+ return configPath;
+}
+
+DWORD Config_ReadStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize)
+{
+ if (NULL == lpSectionName) lpSectionName = CONFIG_SECTION;
+ return GetPrivateProfileStringA(lpSectionName, lpKeyName, lpDefault, lpReturnedString, nSize, Config_GetPath());
+}
+
+UINT Config_ReadInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nDefault)
+{
+ if (NULL == lpSectionName) lpSectionName = CONFIG_SECTION;
+ return GetPrivateProfileIntA(lpSectionName, lpKeyName, nDefault, Config_GetPath());
+}
+
+HRESULT Config_WriteStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpString)
+{
+ LPCSTR configPath = Config_GetPath();
+ if (NULL == configPath || '\0' == *configPath)
+ return E_UNEXPECTED;
+
+ if (NULL == lpSectionName) lpSectionName = CONFIG_SECTION;
+ if (0 != WritePrivateProfileStringA(lpSectionName, lpKeyName, lpString, configPath))
+ return S_OK;
+
+ DWORD errorCode = GetLastError();
+ return HRESULT_FROM_WIN32(errorCode);
+}
+
+HRESULT Config_WriteInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nValue)
+{
+ char szBuffer[32] = {0};
+ HRESULT hr = StringCchPrintfA(szBuffer, ARRAYSIZE(szBuffer), "%d", nValue);
+ if (FAILED(hr)) return hr;
+
+ if (NULL == lpSectionName) lpSectionName = CONFIG_SECTION;
+ return Config_WriteStr(lpSectionName, lpKeyName, szBuffer);
+}
+
+HRESULT Config_WriteSection(LPCSTR lpSectionName, LPCSTR lpData)
+{
+ LPCSTR configPath = Config_GetPath();
+ if (NULL == configPath || '\0' == *configPath)
+ return E_UNEXPECTED;
+
+ if (NULL == lpSectionName) lpSectionName = CONFIG_SECTION;
+ if (0 == WritePrivateProfileSectionA(lpSectionName, lpData, configPath))
+ return S_OK;
+
+ DWORD errorCode = GetLastError();
+ return HRESULT_FROM_WIN32(errorCode);
+}
+
+HRESULT Config_ReadServiceIdList(LPCSTR lpSectionName, LPCSTR lpKeyName, CHAR separator, ReadServiceIdCallback callback, void *data)
+{
+ if (NULL == callback)
+ return E_INVALIDARG;
+
+ DWORD bufferSize = 16384;
+ LPSTR buffer = Plugin_MallocAnsiString(bufferSize);
+ if (NULL == buffer) return E_OUTOFMEMORY;
+
+ DWORD bufferLen = Config_ReadStr(lpSectionName, lpKeyName, NULL, buffer, bufferSize);
+ if (0 != bufferLen)
+ {
+ LPSTR cursor = buffer;
+ LPSTR block = cursor;
+ UINT serviceId;
+ for(;;)
+ {
+ if (separator == *cursor || '\0' == *cursor)
+ {
+ while (' ' == *block && block < cursor) block++;
+
+ if (block < cursor &&
+ FALSE != StrToIntExA(block, STIF_SUPPORT_HEX, (INT*)&serviceId) &&
+ 0 != serviceId)
+ {
+
+ if (FALSE == callback(serviceId, data))
+ break;
+ }
+
+ if ('\0' == *cursor)
+ break;
+
+ cursor = CharNextA(cursor);
+ block = cursor;
+ }
+ else
+ cursor = CharNextA(cursor);
+ }
+ }
+
+ Plugin_FreeAnsiString(buffer);
+ return S_OK;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/config.h b/Src/Plugins/Library/ml_online/config.h
new file mode 100644
index 00000000..a4e3f034
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/config.h
@@ -0,0 +1,54 @@
+#ifndef NULLSOFT_ONLINEMEDIA_PLUGIN_CONFIG_HEADER
+#define NULLSOFT_ONLINEMEDIA_PLUGIN_CONFIG_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+
+#define C_CONFIG_WIN32NATIVE
+class C_Config
+{
+ public:
+ C_Config(char *ini);
+ ~C_Config();
+ void Flush(void);
+ void WriteInt(char *name, int value);
+ char *WriteString(char *name, char *string);
+ int ReadInt(char *name, int defvalue);
+ char *ReadString(char *name, char *defvalue);
+
+ const char* GetPath() { return m_inifile; }
+
+ private:
+#ifndef C_CONFIG_WIN32NATIVE
+ typedef struct
+ {
+ char name[16];
+ char *value;
+ } strType;
+
+ strType *m_strs;
+ int m_dirty;
+ int m_num_strs, m_num_strs_alloc;
+#else
+ char m_strbuf[8192];
+#endif
+
+ char *m_inifile;
+};
+
+// set lpSectionName = NULL to write to default section;
+DWORD Config_ReadStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpDefault, LPSTR lpReturnedString, DWORD nSize);
+UINT Config_ReadInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nDefault);
+HRESULT Config_WriteStr(LPCSTR lpSectionName, LPCSTR lpKeyName, LPCSTR lpString);
+HRESULT Config_WriteInt(LPCSTR lpSectionName, LPCSTR lpKeyName, INT nValue);
+HRESULT Config_WriteSection(LPCSTR lpSectionName, LPCSTR lpData);
+
+typedef BOOL (CALLBACK *ReadServiceIdCallback)(UINT /*serviceId*/, void* /*data*/);
+HRESULT Config_ReadServiceIdList(LPCSTR lpSectionName, LPCSTR lpKeyName, CHAR separator, ReadServiceIdCallback callback, void *data);
+
+
+#endif //NULLSOFT_ONLINEMEDIA_PLUGIN_CONFIG_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/external.cpp b/Src/Plugins/Library/ml_online/external.cpp
new file mode 100644
index 00000000..4fcbfb4d
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/external.cpp
@@ -0,0 +1,273 @@
+#include "main.h"
+#include "./resource.h"
+#include "./external.h"
+#include "./navigation.h"
+#include "./commands.h"
+
+#include "../winamp/jsapi.h"
+#include "../winamp/jsapi_CallbackParameters.h"
+
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+
+#include <browserView.h>
+#include <ifc_omservice.h>
+#include <ifc_omserviceeditor.h>
+
+#define DISPTABLE_CLASS ExternalDispatch
+
+DISPTABLE_BEGIN()
+ DISPENTRY_ADD(DISPATCH_SERVICE_OPEN, L"serviceOpen", OnServiceOpen)
+ DISPENTRY_ADD(DISPATCH_SERVICE_CREATE, L"serviceCreate", OnServiceCreate)
+ DISPENTRY_ADD(DISPATCH_SERVICE_GETINFO, L"serviceGetInfo", OnServiceGetInfo)
+ DISPENTRY_ADD(DISPATCH_SERVICE_SETINFO, L"serviceSetInfo", OnServiceSetInfo)
+DISPTABLE_END
+
+#undef DISPTABLE_CLASS
+
+
+static BOOL DispParam_GetStringOpt(LPCWSTR *str, DISPPARAMS *paramInfo, UINT paramNumber, UINT *argErr)
+{
+ if (paramInfo->cArgs < paramNumber ||
+ VT_NULL == paramInfo->rgvarg[paramInfo->cArgs - paramNumber].vt)
+ {
+ *str = NULL;
+ return FALSE;
+ }
+
+ JSAPI_GETSTRING((*str), paramInfo, paramNumber, argErr);
+ return TRUE;
+}
+
+ExternalDispatch::ExternalDispatch()
+ : ref(1)
+{
+}
+
+ExternalDispatch::~ExternalDispatch()
+{
+}
+
+HRESULT ExternalDispatch::CreateInstance(ExternalDispatch **instance)
+{
+ if (NULL == instance) return E_POINTER;
+
+ *instance = new ExternalDispatch();
+ if (NULL == *instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+LPCWSTR ExternalDispatch::GetName()
+{
+ return L"WebDev";
+}
+
+ULONG ExternalDispatch::AddRef(void)
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+
+ULONG ExternalDispatch::Release(void)
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+STDMETHODIMP ExternalDispatch::QueryInterface(REFIID riid, void **ppvObject)
+{
+ if (NULL == ppvObject) return E_POINTER;
+
+ if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = static_cast<IDispatch*>(this);
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = static_cast<IUnknown*>(this);
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceOpen(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 1, 2);
+
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+
+ UINT serviceId;
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, 1, puArgErr);
+
+ LPCWSTR forceUrl = NULL;
+ if (pdispparams->cArgs > 1)
+ {
+ switch(pdispparams->rgvarg[0].vt)
+ {
+ case VT_BSTR: forceUrl = pdispparams->rgvarg[0].bstrVal; break;
+ case VT_I4: forceUrl = MAKEINTRESOURCE(pdispparams->rgvarg[0].lVal); break;
+ }
+ }
+
+ if (FALSE == DispParam_GetStringOpt(&forceUrl, pdispparams,2, puArgErr))
+ forceUrl = NULL;
+
+ HRESULT hr;
+
+ Navigation *navigation;
+ hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ if (NULL != navigation->FindService(serviceId, &service))
+ {
+ hr = Command_NavigateService(service, forceUrl, FALSE);
+ service->Release();
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+ navigation->Release();
+ }
+
+ JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE));
+
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceCreate(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 0);
+
+ HRESULT hr = Command_CreateService();
+
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+ JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE));
+
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceGetInfo(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
+
+ JSAPI_INIT_RESULT(pvarResult, VT_DISPATCH);
+
+ UINT serviceId;
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, 1, puArgErr);
+
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ if (NULL != navigation->FindService(serviceId, &service))
+ {
+ WCHAR szBuffer[2048];
+ JSAPI::CallbackParameters *params = new JSAPI::CallbackParameters;
+ params->AddLong(L"id", service->GetId());
+
+ if (FAILED(service->GetName(szBuffer, ARRAYSIZE(szBuffer)))) szBuffer[0] = L'\0';
+ params->AddString(L"name", szBuffer);
+
+ if (FAILED(service->GetUrl(szBuffer, ARRAYSIZE(szBuffer)))) szBuffer[0] = L'\0';
+ params->AddString(L"url", szBuffer);
+
+ if (FAILED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer)))) szBuffer[0] = L'\0';
+ params->AddString(L"icon", szBuffer);
+
+ params->AddBoolean(L"preauthorized", (S_OK == ServiceHelper_IsPreAuthorized(service)));
+
+ service->Release();
+ V_DISPATCH(pvarResult) = params;
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+ navigation->Release();
+ }
+
+ if (FAILED(hr))
+ {
+ V_DISPATCH(pvarResult) = 0;
+ }
+
+ return S_OK;
+}
+
+HRESULT ExternalDispatch::OnServiceSetInfo(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT_OPTIONAL(pdispparams, 2, 5);
+
+ UINT serviceId;
+ JSAPI_GETUNSIGNED_AS_NUMBER(serviceId, pdispparams, 1, puArgErr);
+
+ Navigation *navigation;
+ HRESULT hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ HNAVITEM hItem = navigation->FindService(serviceId, &service);
+ if (NULL != hItem)
+ {
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ LPCWSTR value = NULL;
+
+ editor->BeginUpdate();
+
+ if (FALSE != DispParam_GetStringOpt(&value, pdispparams, 2, puArgErr) && FAILED(editor->SetName(value, FALSE)))
+ hr = E_FAIL;
+
+ if (FALSE != DispParam_GetStringOpt(&value, pdispparams, 3, puArgErr) && FAILED(ServiceHelper_UpdateIcon(editor, value)))
+ hr = E_FAIL;
+
+ if (FALSE != DispParam_GetStringOpt(&value, pdispparams, 4, puArgErr) && FAILED(editor->SetUrl(value, FALSE)))
+ hr = E_FAIL;
+
+ VARIANT_BOOL authorized = JSAPI_PARAM_OPTIONAL(pdispparams, 5, boolVal, VARIANT_FALSE);
+ if (S_OK == editor->SetFlags((VARIANT_TRUE == authorized) ? SVCF_PREAUTHORIZED : 0, SVCF_PREAUTHORIZED))
+ {
+ if (VARIANT_TRUE == authorized)
+ ServiceHelper_ResetPermissions(service, 0);
+ }
+
+ editor->EndUpdate();
+ editor->Release();
+ }
+
+ if(SUCCEEDED(hr))
+ hr = ServiceHelper_Save(service);
+
+ service->Release();
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+
+ navigation->Release();
+ }
+
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+ JSAPI_SET_RESULT(pvarResult, boolVal, (SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE));
+
+ return S_OK;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/external.h b/Src/Plugins/Library/ml_online/external.h
new file mode 100644
index 00000000..e3dfe6ca
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/external.h
@@ -0,0 +1,49 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_EXTERNAL_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_EXTERNAL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../nu/dispatchTable.h"
+
+class ExternalDispatch : public IDispatch
+{
+
+public:
+ typedef enum
+ {
+ DISPATCH_SERVICE_OPEN = 700,
+ DISPATCH_SERVICE_CREATE = 701,
+ DISPATCH_SERVICE_GETINFO = 702,
+ DISPATCH_SERVICE_SETINFO = 703,
+ } DispatchCodes;
+
+protected:
+ ExternalDispatch();
+ ~ExternalDispatch();
+
+public:
+ static HRESULT CreateInstance(ExternalDispatch **instance);
+ static LPCWSTR GetName();
+
+public:
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+
+protected:
+ DISPTABLE_INCLUDE();
+ DISPHANDLER_REGISTER(OnServiceOpen);
+ DISPHANDLER_REGISTER(OnServiceCreate);
+ DISPHANDLER_REGISTER(OnServiceGetInfo);
+ DISPHANDLER_REGISTER(OnServiceSetInfo);
+
+protected:
+ ULONG ref;
+
+};
+
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_EXTERNAL_HEADER
diff --git a/Src/Plugins/Library/ml_online/forceUrl.cpp b/Src/Plugins/Library/ml_online/forceUrl.cpp
new file mode 100644
index 00000000..3ad4a1e9
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/forceUrl.cpp
@@ -0,0 +1,51 @@
+#include "main.h"
+#include "./forceUrl.h"
+
+ForceUrl::ForceUrl() : id((UINT)-1), url(NULL)
+{
+}
+ForceUrl::~ForceUrl()
+{
+ Plugin_FreeString(url);
+}
+
+HRESULT ForceUrl::Set(UINT serviceId, LPCWSTR pszUrl)
+{
+ Plugin_FreeString(url);
+
+ id = serviceId;
+ url = Plugin_CopyString(pszUrl);
+
+ return S_OK;
+}
+
+HRESULT ForceUrl::Peek(UINT serviceId, LPWSTR *pszUrl)
+{
+ if (NULL == pszUrl) return E_POINTER;
+ if (serviceId == id && NULL != url)
+ {
+ *pszUrl = url;
+ url = NULL;
+ id = ((UINT)-1);
+ return S_OK;
+ }
+
+ return S_FALSE;
+}
+
+HRESULT ForceUrl::Remove(UINT serviceId)
+{
+ if (id == serviceId)
+ {
+ Plugin_FreeString(url);
+ url = NULL;
+ id = ((UINT)-1);
+ return S_OK;
+ }
+ return S_FALSE;
+}
+
+void ForceUrl::FreeString(LPWSTR pszValue)
+{
+ Plugin_FreeString(pszValue);
+}
diff --git a/Src/Plugins/Library/ml_online/forceUrl.h b/Src/Plugins/Library/ml_online/forceUrl.h
new file mode 100644
index 00000000..28a3f863
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/forceUrl.h
@@ -0,0 +1,27 @@
+#ifndef NULLOSFT_ONLINEMEDIA_FORCE_URL_HEADER
+#define NULLOSFT_ONLINEMEDIA_FORCE_URL_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+class ForceUrl
+{
+public:
+ ForceUrl();
+ ~ForceUrl();
+
+public:
+ HRESULT Set(UINT serviceId, LPCWSTR pszUrl);
+ HRESULT Peek(UINT serviceId, LPWSTR *pszUrl);
+ HRESULT Remove(UINT serviceId);
+
+ void FreeString(LPWSTR pszValue);
+private:
+ UINT id;
+ LPWSTR url;
+};
+
+#endif //NULLOSFT_ONLINEMEDIA_FORCE_URL_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/handler.cpp b/Src/Plugins/Library/ml_online/handler.cpp
new file mode 100644
index 00000000..2191725e
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/handler.cpp
@@ -0,0 +1,168 @@
+#include "main.h"
+#include "navigation.h"
+#include "servicehelper.h"
+#include "handler.h"
+#include "ifc_omservice.h"
+#include "../Agave/URIHandler/svc_urihandler.h"
+#include <api/service/waservicefactory.h>
+#include "api.h"
+
+#include <shlwapi.h>
+
+static uint8_t quickhex(wchar_t c)
+{
+ int hexvalue = c;
+ if (hexvalue & 0x10)
+ hexvalue &= ~0x30;
+ else
+ {
+ hexvalue &= 0xF;
+ hexvalue += 9;
+ }
+ return hexvalue;
+}
+
+static uint8_t DecodeEscape(const wchar_t *&str)
+{
+ uint8_t a = quickhex(*++str);
+ uint8_t b = quickhex(*++str);
+ str++;
+ return a * 16 + b;
+}
+
+static void DecodeEscapedUTF8(wchar_t *&output, const wchar_t *&input)
+{
+ uint8_t utf8_data[1024] = {0}; // hopefully big enough!!
+ int num_utf8_words=0;
+ bool error=false;
+
+ while (input && *input == '%' && num_utf8_words < sizeof(utf8_data))
+ {
+ if (iswxdigit(input[1]) && iswxdigit(input[2]))
+ {
+ utf8_data[num_utf8_words++]=DecodeEscape(input);
+ }
+ else if (input[1] == '%')
+ {
+ input+=2;
+ utf8_data[num_utf8_words++]='%';
+ }
+ else
+ {
+ error = true;
+ break;
+ }
+ }
+
+ int len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8_data, num_utf8_words, 0, 0);
+ MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8_data, num_utf8_words, output, len);
+ output += len;
+
+ if (error)
+ {
+ *output++ = *input++;
+ }
+}
+
+static void UrlDecode(const wchar_t *input, wchar_t *output, size_t len)
+{
+ const wchar_t *stop = output+len-4; // give ourself a cushion large enough to hold a full UTF-16 sequence
+ const wchar_t *itr = input;
+ while (itr && *itr)
+ {
+ if (output >= stop)
+ {
+ *output=0;
+ return;
+ }
+
+ switch (*itr)
+ {
+ case '%':
+ DecodeEscapedUTF8(output, itr);
+ break;
+ case '&':
+ *output = 0;
+ return;
+ default:
+ *output++ = *itr++;
+ break;
+ }
+ }
+ *output = 0;
+}
+// first parameter has param name either null or = terminated, second is null terminated
+static bool ParamCompare(const wchar_t *url_param, const wchar_t *param_name)
+{
+ while (url_param && *url_param && *param_name && *url_param!=L'=')
+ {
+ if (*url_param++ != *param_name++)
+ return false;
+ }
+ return true;
+}
+
+static bool get_request_parm(const wchar_t *params, const wchar_t *param_name, wchar_t *value, size_t value_len)
+{
+ const wchar_t *t=params;
+ while (t && *t && *t != L'?') // find start of parameters
+ t++;
+
+ while (t && *t)
+ {
+ t++; // skip ? or &
+ if (ParamCompare(t, param_name))
+ {
+ while (t && *t && *t != L'=' && *t != '&') // find start of value
+ t++;
+ switch(*t)
+ {
+ case L'=':
+ UrlDecode(++t, value, value_len);
+ return true;
+ case 0:
+ case L'&': // no value
+ *value=0;
+ return true;
+ default: // shouldn't get here
+ return false;
+ }
+ }
+ while (t && *t && *t != L'&') // find next parameter
+ t++;
+ }
+ return false;
+}
+
+int OnlineServicesURIHandler::ProcessFilename(const wchar_t *filename)
+{
+ if (HANDLED != IsMine(filename))
+ return NOT_HANDLED;
+
+ UINT serviceId = 0;
+ wchar_t szBuffer[512]=L"";
+ if (get_request_parm(filename, L"id", szBuffer, ARRAYSIZE(szBuffer)) &&
+ L'\0' != szBuffer[0])
+ {
+ if (FALSE == StrToIntEx(szBuffer, STIF_SUPPORT_HEX, (INT*)&serviceId))
+ serviceId = 0;
+ }
+
+ ServiceHelper_ShowService(serviceId, SHOWMODE_ENSUREVISIBLE);
+ return HANDLED_EXCLUSIVE;
+}
+
+int OnlineServicesURIHandler::IsMine(const wchar_t *filename)
+{
+ if (!_wcsnicmp(filename, L"winamp://Online Services", 24) || !_wcsnicmp(filename, L"winamp://Online%20Services", 26))
+ return HANDLED;
+ else
+ return NOT_HANDLED;
+}
+
+#define CBCLASS OnlineServicesURIHandler
+START_DISPATCH;
+CB(PROCESSFILENAME, ProcessFilename);
+CB(ISMINE, IsMine);
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/handler.h b/Src/Plugins/Library/ml_online/handler.h
new file mode 100644
index 00000000..a228a396
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/handler.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "../Agave/URIHandler/svc_urihandler.h"
+
+// {1E8830FA-0BDA-45fa-B106-BDF56C93BADD}
+static const GUID ml_online_uri_handler =
+{ 0x1e8830fa, 0xbda, 0x45fa, { 0xb1, 0x6, 0xbd, 0xf5, 0x6c, 0x93, 0xba, 0xdd } };
+
+
+class OnlineServicesURIHandler : public svc_urihandler
+{
+public:
+ static const char *getServiceName() { return "Online Services URI Handler"; }
+ static GUID getServiceGuid() { return ml_online_uri_handler; }
+ int ProcessFilename(const wchar_t *filename);
+ int IsMine(const wchar_t *filename); // just like ProcessFilename but don't actually process
+
+protected:
+ RECVS_DISPATCH;
+}; \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/import.h b/Src/Plugins/Library/ml_online/import.h
new file mode 100644
index 00000000..b1c079c7
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/import.h
@@ -0,0 +1,18 @@
+#ifndef NULLSOFT_WEBDEV_PLUGIN_IMPORT_HEADER
+#define NULLSOFT_WEBDEV_PLUGIN_IMPORT_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+HRESULT ImportService_GetFileSupported();
+HRESULT ImportService_GetUrlSupported();
+
+HRESULT ImportService_FromFile(HWND hOwner);
+HRESULT ImportService_FromUrl(HWND hOwner);
+
+void ImportService_SaveRecentUrl();
+
+#endif //NULLSOFT_WEBDEV_PLUGIN_IMPORT_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/importFile.cpp b/Src/Plugins/Library/ml_online/importFile.cpp
new file mode 100644
index 00000000..d186306d
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/importFile.cpp
@@ -0,0 +1,265 @@
+#include "main.h"
+#include "./import.h"
+#include "./api__ml_online.h"
+#include "./resource.h"
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+#include "./navigation.h"
+
+#include <ifc_omstorage.h>
+#include <ifc_omfilestorage.h>
+#include <ifc_omstorageenum.h>
+#include <ifc_omservice.h>
+#include <ifc_omserviceenum.h>
+
+#include <strsafe.h>
+
+static HRESULT ImportFile_GetEnumerator(ifc_omstorageenumerator **enumerator)
+{
+ if (NULL == OMSERVICEMNGR) return E_UNEXPECTED;
+ return OMSERVICEMNGR->EnumStorage(&STID_OmFileStorage, ifc_omstorage::capPublic | ifc_omstorage::capLoad, enumerator);
+}
+
+HRESULT ImportService_GetFileSupported()
+{
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ ifc_omstorageenumerator *enumerator;
+ HRESULT hr = ImportFile_GetEnumerator(&enumerator);
+
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *storage;
+ if(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ storage->Release();
+ hr = S_OK;
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+
+ enumerator->Release();
+ }
+ return hr;
+}
+
+static HRESULT ImportFile_GetFilter(LPWSTR pszBuffer, UINT cchBufferMax, DWORD *defaultIndex)
+{
+ if (NULL != defaultIndex)
+ *defaultIndex = 0;
+
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ HRESULT hr;
+ WCHAR szName[128] = {0}, szList[512] = {0};
+
+ LPWSTR cursor = pszBuffer;
+ size_t remaining = cchBufferMax;
+
+ LPWSTR listC = szList;
+ size_t listR = ARRAYSIZE(szList);
+
+ DWORD counter = 0;
+
+ szList[0] = L'\0';
+ pszBuffer[0] = L'\0';
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_FILEFILTER_ALL, szName, ARRAYSIZE(szName));
+ hr = Plugin_AppendFileFilter(cursor, remaining, szName, L"*.*", &cursor, &remaining, TRUE);
+ if (FAILED(hr)) return hr;
+ counter++;
+
+ ifc_omstorageenumerator *enumerator;
+ hr = ImportFile_GetEnumerator(&enumerator);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *storage;
+ ifc_omfilestorage *fileStorage;
+ while(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ if (SUCCEEDED(storage->QueryInterface(IFC_OmFileStorage, (void**)&fileStorage)))
+ {
+ WCHAR szFilter[64] = {0};
+ if (SUCCEEDED(fileStorage->GetFilter(szFilter, ARRAYSIZE(szFilter))) &&
+ L'\0' != szFilter[0] &&
+ SUCCEEDED(storage->GetDescription(szName, ARRAYSIZE(szName))))
+ {
+ hr = Plugin_AppendFileFilter(cursor, remaining, szName, szFilter, &cursor, &remaining, TRUE);
+ if (FAILED(hr)) break;
+
+ counter++;
+
+ if (listC == szList || SUCCEEDED(StringCchCopyEx(listC, listR, L";", &listC, &listR, 0)))
+ StringCchCopyEx(listC, listR, szFilter, &listC, &listR, 0);
+ }
+ fileStorage->Release();
+ }
+ storage->Release();
+ }
+ enumerator->Release();
+ }
+
+ if (SUCCEEDED(hr) && L'\0' != szList[0])
+ {
+ WASABI_API_LNGSTRINGW_BUF(IDS_FILEFILTER_ALLKNOWN, szName, ARRAYSIZE(szName));
+ hr = Plugin_AppendFileFilter(cursor, remaining, szName, szList, &cursor, &remaining, TRUE);
+ if (FAILED(hr)) return hr;
+
+ counter++;
+
+ if (NULL != defaultIndex)
+ *defaultIndex = counter;
+ }
+
+ return hr;
+}
+
+static HRESULT ImportFile_ProcessFile(HWND hOwner, ifc_omstorageenumerator *enumerator,
+ ifc_omservicehost *serviceHost, ifc_omstorage *serviceStorage,
+ LPCWSTR pszFile, ULONG *converted)
+{
+ ifc_omstorage *storage;
+ enumerator->Reset();
+
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ return E_FAIL;
+
+ ULONG loaded(0), saved(0);
+ while(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ ifc_omserviceenum *serviceEnum;
+ HRESULT hr = storage->Load(pszFile, serviceHost, &serviceEnum);
+ if(SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ while(S_OK == serviceEnum->Next(1, &service, NULL))
+ {
+ loaded++;
+ if (SUCCEEDED(service->SetAddress(NULL)))
+ {
+ service->UpdateFlags(SVCF_SUBSCRIBED | SVCF_PREAUTHORIZED);
+
+ ULONG savedOk;
+ if (SUCCEEDED(serviceStorage->Save(&service, 1, ifc_omstorage::saveClearModified, &savedOk)))
+ {
+ navigation->CreateItem(service, 1);
+ saved += savedOk;
+ }
+ }
+ service->Release();
+ }
+ serviceEnum->Release();
+ break;
+ }
+ else if (OMSTORAGE_E_UNKNOWN_FORMAT != hr)
+ {
+ break;
+ }
+
+ storage->Release();
+ }
+
+ if (NULL != converted)
+ *converted = saved;
+
+ navigation->Release();
+
+ return S_OK;
+}
+
+static HRESULT ImportFile_ProcessList(HWND hOwner, LPCWSTR pszList)
+{
+ if (NULL == pszList)
+ return E_INVALIDARG;
+
+ LPCWSTR base, block, c;
+ base = pszList;
+ c = base;
+ block = NULL;
+ ULONG converted;
+
+ ifc_omstorageenumerator *enumerator;
+ HRESULT hr = ImportFile_GetEnumerator(&enumerator);
+ if (FAILED(hr)) return hr;
+
+ ServiceHost *serviceHost;
+ hr = ServiceHost::GetCachedInstance(&serviceHost);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *serviceStorage;
+ hr = ServiceHelper_QueryStorage(&serviceStorage);
+ if (SUCCEEDED(hr))
+ {
+ ULONG scanned(0);
+ while(L'\0' != *c)
+ {
+ block = c;
+ while (L'\0' != *c) c++;
+ if (c != block && block != base)
+ {
+ WCHAR szBuffer[MAX_PATH * 2] = {0};
+ scanned++;
+ if (SUCCEEDED(StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%s\\%s", base, block)) &&
+ SUCCEEDED(ImportFile_ProcessFile(hOwner, enumerator, serviceHost, serviceStorage, szBuffer, &converted)))
+ {
+ }
+ }
+ c++;
+ }
+
+ if (pszList == block && c != pszList &&
+ SUCCEEDED(ImportFile_ProcessFile(hOwner, enumerator, serviceHost, serviceStorage, pszList, &converted)))
+ {
+ }
+ serviceStorage->Release();
+ }
+ serviceHost->Release();
+ }
+
+ enumerator->Release();
+ return hr;
+}
+
+HRESULT ImportService_FromFile(HWND hOwner)
+{
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ OPENFILENAME of = {0};
+ of.lStructSize = sizeof(of);
+
+ WCHAR szFilter[1024] = {0};
+ HRESULT hr = ImportFile_GetFilter(szFilter, ARRAYSIZE(szFilter), &of.nFilterIndex);
+ if (FAILED(hr)) return hr;
+
+ UINT cchResultMax = 16384;
+ LPWSTR pszResult = Plugin_MallocString(cchResultMax);
+ if (NULL == pszResult) return E_OUTOFMEMORY;
+ *pszResult = L'\0';
+
+ of.hwndOwner = hOwner;
+ of.lpstrFilter = szFilter;
+ of.lpstrFile = pszResult;
+ of.nMaxFile = cchResultMax;
+ of.lpstrInitialDir = WASABI_API_APP->path_getUserSettingsPath();
+ of.lpstrTitle = WASABI_API_LNGSTRINGW(IDS_IMPORT_FILES);
+ of.Flags = OFN_ENABLESIZING | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_ALLOWMULTISELECT;
+
+ if (0 == GetOpenFileName(&of))
+ {
+ INT err = CommDlgExtendedError();
+ hr = (0 == err) ? S_FALSE : E_FAIL;
+ }
+ else
+ {
+ hr = ImportFile_ProcessList(hOwner, pszResult);
+ }
+
+ Plugin_FreeString(pszResult);
+ return hr;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/importUrl.cpp b/Src/Plugins/Library/ml_online/importUrl.cpp
new file mode 100644
index 00000000..201bea1c
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/importUrl.cpp
@@ -0,0 +1,321 @@
+#include "main.h"
+#include "./import.h"
+#include "./api__ml_online.h"
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+#include "./navigation.h"
+#include "./resource.h"
+#include "./config.h"
+
+#include <ifc_omstorage.h>
+#include <ifc_omwebstorage.h>
+#include <ifc_omstorageenum.h>
+#include <ifc_omservice.h>
+#include <ifc_omserviceenum.h>
+
+#include <vector>
+
+#include <strsafe.h>
+
+
+typedef std::vector<WCHAR*> StringList;
+
+static StringList recentList;
+static BOOL recentListModified = FALSE;
+
+typedef struct __OPENURLDLG
+{
+ HWND hOwner;
+ LPCWSTR pszAddress;
+ LPWSTR pszBuffer;
+ UINT cchBufferMax;
+} OPENURLDLG;
+
+static INT_PTR ImportUrlDlg_Show(OPENURLDLG *poud);
+
+static HRESULT ImportUrl_GetEnumerator(ifc_omstorageenumerator **enumerator)
+{
+ if (NULL == OMSERVICEMNGR) return E_UNEXPECTED;
+ return OMSERVICEMNGR->EnumStorage(&STID_OmWebStorage, ifc_omstorage::capPublic | ifc_omstorage::capLoad, enumerator);
+}
+
+HRESULT ImportService_GetUrlSupported()
+{
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ ifc_omstorageenumerator *enumerator;
+ HRESULT hr = ImportUrl_GetEnumerator(&enumerator);
+
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *storage;
+ if(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ storage->Release();
+ hr = S_OK;
+ }
+ else
+ {
+ hr = S_FALSE;
+ }
+
+ enumerator->Release();
+ }
+ return hr;
+}
+
+HRESULT ImportUrl_LoadAddress(HWND hOwner, LPCWSTR pszAddress, ifc_omstorageenumerator *enumerator,
+ ServiceHost *serviceHost, ifc_omstorage *serviceStorage, Navigation *navigation)
+{
+ HRESULT hr = S_OK;
+ ULONG loaded(0);
+
+ ifc_omstorage *storage;
+ enumerator->Reset();
+
+ while(S_OK == enumerator->Next(1, &storage, NULL))
+ {
+ ifc_omstorageasync *async;
+ hr = storage->BeginLoad(pszAddress, serviceHost, NULL, NULL, &async);
+ if(SUCCEEDED(hr))
+ {
+ ifc_omserviceenum *serviceEnum;
+ hr = storage->EndLoad(async, &serviceEnum);
+ async->Release();
+
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ while(S_OK == serviceEnum->Next(1, &service, NULL))
+ {
+ loaded++;
+ if (SUCCEEDED(service->SetAddress(NULL)))
+ {
+ ULONG savedOk;
+ if (SUCCEEDED(serviceStorage->Save(&service, 1, ifc_omstorage::saveClearModified, &savedOk)))
+ {
+ navigation->CreateItem(service, 1);
+ }
+ }
+ service->Release();
+ }
+ serviceEnum->Release();
+ }
+ break;
+ }
+ else if (OMSTORAGE_E_UNKNOWN_FORMAT != hr)
+ {
+ break;
+ }
+ storage->Release();
+ }
+
+ return hr;
+}
+
+HRESULT ImportService_FromUrl(HWND hOwner)
+{
+ OPENURLDLG dlg = {0};
+ WCHAR szBuffer[4096] = {0};
+
+ dlg.hOwner = hOwner;
+ dlg.pszAddress = NULL;
+ dlg.pszBuffer = szBuffer;
+ dlg.cchBufferMax = ARRAYSIZE(szBuffer);
+ if (IDOK != ImportUrlDlg_Show(&dlg))
+ return S_FALSE;
+
+ ifc_omstorageenumerator *enumerator;
+ HRESULT hr = ImportUrl_GetEnumerator(&enumerator);
+ if (SUCCEEDED(hr))
+ {
+ Navigation *navigation;
+ hr = Plugin_GetNavigation(&navigation);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorage *serviceStorage;
+ hr = ServiceHelper_QueryStorage(&serviceStorage);
+ if (SUCCEEDED(hr))
+ {
+ ServiceHost *serviceHost;
+ hr = ServiceHost::GetCachedInstance(&serviceHost);
+ if (SUCCEEDED(hr))
+ {
+ hr = ImportUrl_LoadAddress(hOwner, szBuffer, enumerator, serviceHost, serviceStorage, navigation);
+ serviceHost->Release();
+ }
+ serviceStorage->Release();
+ }
+ navigation->Release();
+ }
+ enumerator->Release();
+ }
+ return hr;
+}
+
+static INT_PTR ImportUrlDlg_OnInit(HWND hwnd, HWND hFocus, LPARAM param)
+{
+ OPENURLDLG *poud = (OPENURLDLG*)param;
+ if (NULL != poud)
+ {
+ SetProp(hwnd, L"OPENURLDLG", poud);
+
+ HWND hAddress = GetDlgItem(hwnd, IDC_ADDRESS);
+ if (NULL != hAddress)
+ {
+ LPWSTR p;
+
+ size_t count = recentList.size();
+ if (0 == count)
+ {
+ char szKey[32] = {0}, szBuffer[4096] = {0};
+ for(int i = 1; i < 101; i++)
+ {
+ if (FAILED(StringCchPrintfA(szKey, ARRAYSIZE(szKey), "entry%d", i)) ||
+ 0 == Config_ReadStr("RecentUrl", szKey, NULL, szBuffer, ARRAYSIZE(szBuffer)) ||
+ '\0' == szBuffer[0])
+ {
+ break;
+ }
+
+ p = Plugin_MultiByteToWideChar(CP_UTF8, 0, szBuffer, -1);
+ if (NULL != p) recentList.push_back(p);
+ }
+ count = recentList.size();
+ }
+
+ for (size_t i = 0; i < count; i ++)
+ {
+ p = recentList[i];
+ if(NULL != p && L'\0' != *p)
+ SendMessage(hAddress, CB_ADDSTRING, 0, (LPARAM)p);
+ }
+
+ if (NULL != poud->pszAddress)
+ SetWindowText(hAddress, poud->pszAddress);
+ }
+
+ RECT ownerRect;
+ if (NULL != poud->hOwner && IsWindowVisible(poud->hOwner) &&
+ GetWindowRect(poud->hOwner, &ownerRect))
+ {
+ RECT myRect;
+ GetWindowRect(hwnd, &myRect);
+ LONG x = ownerRect.left + ((ownerRect.right - ownerRect.left) - (myRect.right - myRect.left))/2;
+ LONG y = ownerRect.top + ((ownerRect.bottom - ownerRect.top) - (myRect.bottom - myRect.top))/2;
+ SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+ }
+
+ return 0;
+}
+
+static void ImportUrlDlg_OnDestroy(HWND hwnd)
+{
+ RemoveProp(hwnd, L"OPENURLDLG");
+
+}
+static INT_PTR ImportUrlDlg_ReturnAddress(HWND hwnd)
+{
+ OPENURLDLG *poud = (OPENURLDLG*)GetProp(hwnd, L"OPENURLDLG");
+ if (NULL == poud) return -1;
+
+ HWND hAddress = GetDlgItem(hwnd, IDC_ADDRESS);
+ if (NULL == hAddress) return -2;
+
+ if (0 == GetWindowText(hAddress, poud->pszBuffer, poud->cchBufferMax))
+ return -3;
+
+ if (NULL != poud->pszBuffer && L'\0' != *poud->pszBuffer)
+ {
+ LPWSTR p;
+ size_t index = recentList.size();
+ while(index--)
+ {
+ p = recentList[index];
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, poud->pszBuffer, -1, p, -1))
+ {
+ Plugin_FreeString(p);
+ recentList.erase(recentList.begin() + index);
+ }
+ }
+
+ p = Plugin_CopyString(poud->pszBuffer);
+ if (NULL != p)
+ {
+ recentList.insert(recentList.begin(), p);
+ recentListModified = TRUE;
+ }
+ }
+ return IDOK;
+}
+
+static void ImportUrlDlg_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND hControl)
+{
+ switch(commandId)
+ {
+ case IDOK:
+ EndDialog(hwnd, ImportUrlDlg_ReturnAddress(hwnd));
+ break;
+ case IDCANCEL:
+ EndDialog(hwnd, IDCANCEL);
+ break;
+ }
+
+}
+static INT_PTR WINAPI ImportUrlDlg_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return ImportUrlDlg_OnInit(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: ImportUrlDlg_OnDestroy(hwnd); break;
+ case WM_COMMAND: ImportUrlDlg_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
+ }
+
+ return 0;
+
+}
+
+
+static INT_PTR ImportUrlDlg_Show(OPENURLDLG *poud)
+{
+ if (NULL == poud || NULL == poud->pszBuffer)
+ return -1;
+
+ HWND hParent = poud->hOwner;
+ if (NULL == poud->hOwner)
+ hParent = Plugin_GetLibrary();
+
+ return WASABI_API_DIALOGBOXPARAMW(IDD_OPENURL, hParent, ImportUrlDlg_DialogProc, (LPARAM)poud);
+
+}
+
+void ImportService_SaveRecentUrl()
+{
+ if (FALSE == recentListModified)
+ return;
+
+ Config_WriteSection("RecentUrl", NULL);
+
+ size_t count = recentList.size();
+ if (count > 100) count = 100;
+
+ char szKey[32], szBuffer[4096];
+ UINT entry = 1;
+
+ for (size_t i = 0; i < count; i++)
+ {
+ LPCWSTR p = recentList[i];
+ if (NULL != p && L'\0' != *p &&
+ 0 != WideCharToMultiByte(CP_UTF8, 0, p, -1, szBuffer, ARRAYSIZE(szBuffer), NULL, NULL) &&
+ SUCCEEDED(StringCchPrintfA(szKey, ARRAYSIZE(szKey), "entry%d", entry)) &&
+ SUCCEEDED(Config_WriteStr("RecentUrl", szKey, szBuffer)))
+ {
+ entry++;
+ }
+ }
+
+ recentListModified = FALSE;
+
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/jsapi2_omcom.cpp b/Src/Plugins/Library/ml_online/jsapi2_omcom.cpp
new file mode 100644
index 00000000..bebe30d3
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/jsapi2_omcom.cpp
@@ -0,0 +1,136 @@
+#include "./main.h"
+#include "./api__ml_online.h"
+#include "./jsapi2_omcom.h"
+#include "./navigation.h"
+
+#include <ifc_omservice.h>
+#include <browserView.h>
+
+#include "../Winamp/JSAPI.h"
+
+
+JSAPI2::OnlineServicesAPI::OnlineServicesAPI(const wchar_t *_key, JSAPI::ifc_info *_info)
+{
+ info = _info;
+ key = _key;
+ refCount = 1;
+}
+
+#define DISP_TABLE \
+ CHECK_ID(Login)\
+
+
+#define CHECK_ID(str) JSAPI_DISP_ENUMIFY(str),
+enum {
+ DISP_TABLE
+};
+
+#undef CHECK_ID
+#define CHECK_ID(str) if (wcscmp(rgszNames[i], L## #str) == 0) { rgdispid[i] = JSAPI_DISP_ENUMIFY(str); continue; }
+HRESULT JSAPI2::OnlineServicesAPI::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
+{
+ bool unknowns = false;
+ for (unsigned int i = 0;i != cNames;i++)
+ {
+ DISP_TABLE
+
+ rgdispid[i] = DISPID_UNKNOWN;
+ unknowns = true;
+
+ }
+ if (unknowns)
+ return DISP_E_UNKNOWNNAME;
+ else
+ return S_OK;
+}
+
+HRESULT JSAPI2::OnlineServicesAPI::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT JSAPI2::OnlineServicesAPI::GetTypeInfoCount(unsigned int FAR * pctinfo)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT JSAPI2::OnlineServicesAPI::Login(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr)
+{
+ JSAPI_VERIFY_METHOD(wFlags);
+ JSAPI_VERIFY_PARAMCOUNT(pdispparams, 1);
+ JSAPI_VERIFY_PARAMTYPE(pdispparams, 1, VT_BSTR, puArgErr);
+ JSAPI_INIT_RESULT(pvarResult, VT_BOOL);
+ JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_FALSE);
+ const wchar_t *url = JSAPI_PARAM(pdispparams, 1).bstrVal;
+
+// if (AGAVE_API_JSAPI2_SECURITY->GetActionAuthorization(L"onlineservices", L"login", key, info, JSAPI2::api_security::ACTION_DISALLOWED) == JSAPI2::api_security::ACTION_ALLOWED)
+ {
+ HWND hBrowser = info->GetHWND();
+ if (NULL != hBrowser && IsWindow(hBrowser))
+ {
+ ifc_omservice *service;
+ if (FALSE != BrowserControl_GetService(hBrowser, &service))
+ {
+ wchar_t szBuffer[4096] = {0};
+ LPCWSTR navigateUrl;
+
+ if (NULL != AGAVE_API_AUTH &&
+ 0 == AGAVE_API_AUTH->ClientToWeb(GUID_NULL, url, szBuffer, ARRAYSIZE(szBuffer)))
+ {
+ navigateUrl = szBuffer;
+ }
+ else
+ navigateUrl = url;
+
+ BrowserControl_Navigate(hBrowser, navigateUrl, TRUE);
+
+ JSAPI_SET_RESULT(pvarResult, boolVal, VARIANT_TRUE);
+ service->Release();
+ }
+ }
+ }
+ return S_OK;
+}
+
+#undef CHECK_ID
+#define CHECK_ID(str) case JSAPI_DISP_ENUMIFY(str): return str(wFlags, pdispparams, pvarResult, puArgErr);
+HRESULT JSAPI2::OnlineServicesAPI::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
+{
+ switch (dispid)
+ {
+ DISP_TABLE
+ }
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+STDMETHODIMP JSAPI2::OnlineServicesAPI::QueryInterface(REFIID riid, PVOID *ppvObject)
+{
+ if (!ppvObject)
+ return E_POINTER;
+
+ else if (IsEqualIID(riid, IID_IDispatch))
+ *ppvObject = (IDispatch *)this;
+ else if (IsEqualIID(riid, IID_IUnknown))
+ *ppvObject = this;
+ else
+ {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+
+ AddRef();
+ return S_OK;
+}
+
+ULONG JSAPI2::OnlineServicesAPI::AddRef(void)
+{
+ return InterlockedIncrement(&refCount);
+}
+
+
+ULONG JSAPI2::OnlineServicesAPI::Release(void)
+{
+ LONG lRef = InterlockedDecrement(&refCount);
+ if (lRef == 0) delete this;
+ return lRef;
+}
diff --git a/Src/Plugins/Library/ml_online/jsapi2_omcom.h b/Src/Plugins/Library/ml_online/jsapi2_omcom.h
new file mode 100644
index 00000000..970ef323
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/jsapi2_omcom.h
@@ -0,0 +1,27 @@
+#pragma once
+#include <ocidl.h>
+#include "../Winamp/JSAPI_Info.h"
+
+namespace JSAPI2
+{
+ class OnlineServicesAPI : public IDispatch
+ {
+ public:
+ OnlineServicesAPI(const wchar_t *_key, JSAPI::ifc_info *info);
+ STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
+ STDMETHOD_(ULONG, AddRef)(void);
+ STDMETHOD_(ULONG, Release)(void);
+ // *** IDispatch Methods ***
+ STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
+ STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
+ STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
+ STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
+ private:
+ const wchar_t *key;
+ volatile LONG refCount;
+ JSAPI::ifc_info *info;
+
+ STDMETHOD (Login)(WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, unsigned int FAR *puArgErr);
+ };
+}
+
diff --git a/Src/Plugins/Library/ml_online/local_menu.cpp b/Src/Plugins/Library/ml_online/local_menu.cpp
new file mode 100644
index 00000000..2548ad18
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/local_menu.cpp
@@ -0,0 +1,379 @@
+#include "./main.h"
+#include "./local_menu.h"
+#include "../nu/menuHelpers.h"
+#include "./resource.h"
+#include "./api__ml_online.h"
+#include "../../General/gen_ml/menu.h"
+
+#include <windows.h>
+#include <strsafe.h>
+
+#define MENU_SERVICECONTEXT 0
+#define MENU_GALERYCONTEXT 1
+#define MENU_RATING 2
+#define MENU_VIEW 3
+#define MENU_NAVIGATION 4
+#define MENU_TOOLBAR 5
+
+#define RATING_MARKER MAKELONG(MAKEWORD('R','A'),MAKEWORD('T','E'))
+
+#define RATING_MINSPACECX 16
+
+
+
+typedef BOOL (__cdecl *MLISSKINNEDPOPUPENABLED)(void);
+typedef HANDLE (__cdecl *MLINITSKINNEDPOPUPHOOK)(HWND /*hwnd*/, HMLIMGLST /*hmlil*/, INT /*width*/, UINT /*skinStyle*/,
+ MENUCUSTOMIZEPROC /*customProc*/, ULONG_PTR /*customParam*/);
+typedef HANDLE (__cdecl *MLREMOVESKINNEDPOPUPHOOK)(HANDLE /*hPopupHook*/);
+
+#if 0
+static BOOL Menu_IsRatingStar(HMENU hMenu, INT itemId, INT *valueOut)
+{
+ WCHAR szBuffer[8] = {0};
+ INT cchBuffer = GetMenuStringW(hMenu, itemId, szBuffer, ARRAYSIZE(szBuffer), MF_BYCOMMAND);
+ if (cchBuffer < 1 || cchBuffer > 5)
+ return FALSE;
+
+ for (INT i = 1; i < cchBuffer; i++)
+ {
+ if (szBuffer[i -1] != szBuffer[i])
+ return FALSE;
+ }
+
+ if (NULL != valueOut)
+ *valueOut = cchBuffer;
+
+ return TRUE;
+}
+
+static BOOL Menu_MeasureRating(HMENU hMenu, HDC hdc, MEASUREITEMSTRUCT *pmis)
+{
+ if (NULL == hdc || !Menu_IsRatingStar(hMenu, pmis->itemID, NULL))
+ return FALSE;
+
+ RECT rect;
+ if (!MLRating_CalcRect(Plugin_GetLibrary(), NULL, 5, &rect))
+ return FALSE;
+
+ pmis->itemHeight = rect.bottom - rect.top + 6;
+
+ TEXTMETRIC tm;
+ if (GetTextMetrics(hdc, &tm) &&
+ (UINT)(tm.tmHeight + 2) > pmis->itemHeight)
+ {
+ pmis->itemHeight = tm.tmHeight + 2;
+ }
+
+ INT spaceCX = (pmis->itemHeight > RATING_MINSPACECX) ? pmis->itemHeight : RATING_MINSPACECX;
+ pmis->itemWidth = rect.right - rect.left + (2 * spaceCX) - (GetSystemMetrics(SM_CXMENUCHECK) - 1);
+
+ return TRUE;
+}
+
+static BOOL Menu_DrawRating(HMENU hMenu, HDC hdc, DRAWITEMSTRUCT *pdis)
+{
+ INT ratingValue;
+ if (NULL == hdc || !Menu_IsRatingStar(hMenu, pdis->itemID, &ratingValue))
+ return FALSE;
+
+ INT spaceCX = ((pdis->rcItem.bottom - pdis->rcItem.top) > RATING_MINSPACECX) ?
+ (pdis->rcItem.bottom - pdis->rcItem.top) :
+ RATING_MINSPACECX;
+
+ RATINGDRAWPARAMS rdp = {0};
+ rdp.cbSize = sizeof(RATINGDRAWPARAMS);
+ rdp.hdcDst = hdc;
+ rdp.rc = pdis->rcItem;
+ rdp.rc.left += spaceCX;
+ rdp.value = ratingValue;
+ rdp.maxValue = 5;
+
+ UINT menuState = GetMenuState(hMenu, pdis->itemID, MF_BYCOMMAND);
+ rdp.trackingValue = (0 == ((MF_DISABLED | MF_GRAYED) & menuState)) ? rdp.value : 0;
+
+ rdp.fStyle = RDS_LEFT | RDS_VCENTER | RDS_HOT;
+ rdp.hMLIL = NULL;
+ rdp.index = 0;
+
+ return MLRating_Draw(Plugin_GetLibrary(), &rdp);
+}
+
+static BOOL CALLBACK Menu_CustomDrawProc(INT action, HMENU hMenu, HDC hdc, LPARAM param, ULONG_PTR user)
+{
+ switch(action)
+ {
+ case MLMENU_ACTION_MEASUREITEM:
+ if (hMenu == (HMENU)user)
+ return Menu_MeasureRating(hMenu, hdc, (MEASUREITEMSTRUCT*)param);
+ break;
+ case MLMENU_ACTION_DRAWITEM:
+ if (hMenu == (HMENU)user)
+ return MLMENU_WANT_DRAWPART;
+ break;
+ case MLMENU_ACTION_DRAWBACK:
+ break;
+ case MLMENU_ACTION_DRAWICON:
+ break;
+ case MLMENU_ACTION_DRAWTEXT:
+ if (hMenu == (HMENU)user)
+ return Menu_DrawRating(hMenu, hdc, (DRAWITEMSTRUCT*)param);
+ break;
+ }
+ return FALSE;
+}
+#endif
+
+BOOL Menu_SetRatingValue(HMENU ratingMenu, INT ratingValue)
+{
+ if (NULL == ratingMenu) return FALSE;
+
+ INT ratingList[] = { ID_RATING_VALUE_1, ID_RATING_VALUE_2, ID_RATING_VALUE_3,
+ ID_RATING_VALUE_4, ID_RATING_VALUE_5};
+ ratingValue--;
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(MENUITEMINFO);
+
+ UINT type, state;
+ for (INT i = 0; i < ARRAYSIZE(ratingList); i++)
+ {
+ mii.fMask = MIIM_STATE | MIIM_FTYPE;
+ if (GetMenuItemInfo(ratingMenu, ratingList[i], FALSE, &mii))
+ {
+ if (ratingValue == i)
+ {
+ type = mii.fType | MFT_RADIOCHECK;
+ state = mii.fState | MFS_CHECKED;
+ }
+ else
+ {
+ type = mii.fType & ~MFT_RADIOCHECK;
+ state = mii.fState & ~MFS_CHECKED;
+ }
+
+ mii.fMask = 0;
+ if (type != mii.fType)
+ {
+ mii.fType = type;
+ mii.fMask |= MIIM_FTYPE;
+ }
+
+ if (state != mii.fState)
+ {
+ mii.fState = state;
+ mii.fMask |= MIIM_STATE;
+ }
+
+ if (0 != mii.fMask)
+ SetMenuItemInfo(ratingMenu, ratingList[i], FALSE, &mii);
+ }
+ }
+ return TRUE;
+}
+
+static HMENU Menu_FindRatingMenuRecur(HMENU hMenu, MENUINFO *pmi, MENUITEMINFO *pmii)
+{
+ if (GetMenuInfo(hMenu, pmi) && RATING_MARKER == pmi->dwMenuData)
+ return hMenu;
+
+ INT count = GetMenuItemCount(hMenu);
+ for(INT i = 0; i < count; i++)
+ {
+ if (GetMenuItemInfo(hMenu, i, TRUE, pmii) && NULL != pmii->hSubMenu)
+ {
+ HMENU hRating = Menu_FindRatingMenuRecur(pmii->hSubMenu, pmi, pmii);
+ if (NULL != hRating)
+ return hRating;
+ }
+ }
+ return NULL;
+}
+
+HMENU Menu_FindRatingMenu(HMENU hMenu)
+{
+ if (NULL == hMenu)
+ return NULL;
+
+ MENUITEMINFO mii;
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_SUBMENU;
+
+ MENUINFO mi;
+ mi.cbSize = sizeof(MENUINFO);
+ mi.fMask = MIM_MENUDATA;
+
+ return Menu_FindRatingMenuRecur(hMenu, &mi, &mii);
+}
+
+static BOOL Menu_InsertRatingMenu(HMENU hDest, INT iPos, HMENU baseMenu, INT ratingValue)
+{
+ if ( 0 == MenuHelper_CopyMenuEx(hDest, iPos, baseMenu, MENU_RATING, 1))
+ return FALSE;
+
+ MENUITEMINFO mii = {0};
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask = MIIM_SUBMENU;
+ if (GetMenuItemInfo(hDest, iPos, TRUE, &mii))
+ {
+ if (NULL != mii.hSubMenu)
+ {
+ MENUINFO mi = {0};
+ mi.cbSize = sizeof(MENUINFO);
+ mi.fMask = MIM_MENUDATA;
+ mi.dwMenuData = RATING_MARKER;
+ SetMenuInfo(mii.hSubMenu, &mi);
+
+ Menu_SetRatingValue(mii.hSubMenu, ratingValue);
+ }
+ }
+ return TRUE;
+}
+
+static HMENU Menu_CreateRatingMenu(HMENU baseMenu, INT ratingValue)
+{
+ HMENU menu = GetSubMenu(baseMenu, MENU_RATING);
+ if (NULL == menu) return NULL;
+
+ menu = MenuHelper_DuplcateMenu(menu);
+ if (NULL == menu) return NULL;
+
+ MENUINFO mi = {0};
+ mi.cbSize = sizeof(MENUINFO);
+ mi.fMask = MIM_MENUDATA;
+ mi.dwMenuData = RATING_MARKER;
+ SetMenuInfo(menu, &mi);
+
+ Menu_SetRatingValue(menu, ratingValue);
+ return menu;
+}
+
+static HMENU Menu_GetServiceContext(HMENU baseMenu, UINT flags, BOOL fGaleryMode)
+{
+ HMENU menu = GetSubMenu(baseMenu, (FALSE == fGaleryMode) ? MENU_SERVICECONTEXT : MENU_GALERYCONTEXT);
+ if (NULL == menu) return NULL;
+
+ menu = MenuHelper_DuplcateMenu(menu);
+ if (NULL == menu) return NULL;
+
+ HMENU embedMenu;
+ INT inserted;
+ INT total = GetMenuItemCount(menu);
+
+ if (FALSE == fGaleryMode)
+ {
+ // rating
+ if (Menu_InsertRatingMenu(menu, 0, baseMenu, RATINGFROMMCF(flags)))
+ total++;
+ }
+
+ if (0 != (MCF_NAVIGATION & flags))
+ {// navigation
+ embedMenu = GetSubMenu(baseMenu, MENU_NAVIGATION);
+ if (NULL != embedMenu)
+ {
+ inserted = MenuHelper_CopyMenu(menu, 0, embedMenu);
+ if (inserted > 0 && total > 0 && MenuHelper_InsertSeparator(menu, inserted))
+ inserted++;
+ total += inserted;
+ }
+ }
+
+ if (0 != (MCF_VIEW & flags))
+ {// view
+ embedMenu = GetSubMenu(baseMenu, MENU_VIEW);
+ if (NULL != embedMenu)
+ {
+ inserted = MenuHelper_CopyMenu(menu, 0, embedMenu);
+ if (inserted > 0 && total > 0 && MenuHelper_InsertSeparator(menu, inserted))
+ {
+ inserted++;
+ }
+ total += inserted;
+ }
+
+ EnableMenuItem(menu, ID_VIEW_OPEN , MF_BYCOMMAND | ((0 == (MCF_VIEWACTIVE & flags)) ? MF_ENABLED : MF_DISABLED));
+ if ( 0 == (MCF_VIEWACTIVE & flags))
+ SetMenuDefaultItem(menu, ID_VIEW_OPEN, FALSE);
+ }
+
+ return menu;
+}
+
+static HMENU Menu_GetToolbarContext(HMENU baseMenu, UINT flags)
+{
+ HMENU hMenu = GetSubMenu(baseMenu, MENU_TOOLBAR);
+ return hMenu;
+}
+
+void Menu_ConvertRatingMenuStar(HMENU menu, UINT menu_id)
+{
+ MENUITEMINFOW mi = {sizeof(mi), MIIM_DATA | MIIM_TYPE, MFT_STRING};
+ wchar_t rateBuf[32], *rateStr = rateBuf;
+ mi.dwTypeData = rateBuf;
+ mi.cch = 32;
+ if(GetMenuItemInfoW(menu, menu_id, FALSE, &mi))
+ {
+ while(rateStr && *rateStr)
+ {
+ if(*rateStr == L'*') *rateStr = L'\u2605';
+ rateStr=CharNextW(rateStr);
+ }
+ SetMenuItemInfoW(menu, menu_id, FALSE, &mi);
+ }
+}
+
+HMENU Menu_GetMenu(INT menuKind, UINT flags)
+{
+ HMENU baseMenu = WASABI_API_LOADMENUW(IDR_CONTEXTMENU);
+ if (NULL == baseMenu)
+ return NULL;
+
+ HMENU rate_hmenu = GetSubMenu(baseMenu,2);
+ Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATING_VALUE_5);
+ Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATING_VALUE_4);
+ Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATING_VALUE_3);
+ Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATING_VALUE_2);
+ Menu_ConvertRatingMenuStar(rate_hmenu, ID_RATING_VALUE_1);
+
+ switch(menuKind)
+ {
+ case OMMENU_SERVICECONTEXT:
+ case OMMENU_GALERYCONTEXT:
+ return Menu_GetServiceContext(baseMenu, flags, (OMMENU_GALERYCONTEXT == menuKind));
+ case OMMENU_RATING:
+ return Menu_CreateRatingMenu(baseMenu, RATINGFROMMCF(flags));
+ case OMMENU_TOOLBAR:
+ return Menu_GetToolbarContext(baseMenu, flags);
+ }
+ return NULL;
+}
+
+void Menu_ReleaseMenu(HMENU hMenu, INT menuKind)
+{
+ if (NULL == hMenu)
+ return;
+
+ switch(menuKind)
+ {
+ case OMMENU_SERVICECONTEXT:
+ DestroyMenu(hMenu);
+ break;
+ case OMMENU_GALERYCONTEXT:
+ DestroyMenu(hMenu);
+ break;
+ case OMMENU_RATING:
+ DestroyMenu(hMenu);
+ break;
+ case OMMENU_TOOLBAR:
+ break;
+ }
+}
+
+INT DoTrackPopup(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm)
+{
+ if (NULL == hMenu)
+ return NULL;
+
+ return Menu_TrackPopupParam(Plugin_GetLibrary(), hMenu, fuFlags, x, y,
+ hwnd, lptpm, (ULONG_PTR)Menu_FindRatingMenu(hMenu));
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/local_menu.h b/Src/Plugins/Library/ml_online/local_menu.h
new file mode 100644
index 00000000..bf4257e1
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/local_menu.h
@@ -0,0 +1,40 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_MENU_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_MENU_HEADER
+
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include "../../General/gen_ml/ml_ipc_0313.h"
+
+class OmService;
+
+#define OMMENU_SERVICECONTEXT 0
+#define OMMENU_GALERYCONTEXT 1
+#define OMMENU_RATING 2
+#define OMMENU_TOOLBAR 3
+
+#define MCF_VIEW 0x00000001
+#define MCF_NAVIGATION 0x00000004
+#define MCF_VIEWACTIVE 0x00000008
+
+#define MCF_RATINGMASK 0xF0000000
+#define MCF_RATING1 0x10000000
+#define MCF_RATING2 0x20000000
+#define MCF_RATING3 0x30000000
+#define MCF_RATING4 0x40000000
+#define MCF_RATING5 0x50000000
+
+#define RATINGTOMCF(__rating) ((0x0F & (__rating)) << 24)
+#define RATINGFROMMCF(__mcf) (0x0F & ((__mcf) >> 24))
+
+HMENU Menu_GetMenu(INT menuKind, UINT flags);
+void Menu_ReleaseMenu(HMENU hMenu, INT menuKind);
+
+HMENU Menu_FindRatingMenu(HMENU hMenu);
+BOOL Menu_SetRatingValue(HMENU ratingMenu, INT ratingValue);
+INT DoTrackPopup(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm);
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_MENU_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/messageBox.cpp b/Src/Plugins/Library/ml_online/messageBox.cpp
new file mode 100644
index 00000000..03a850e1
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/messageBox.cpp
@@ -0,0 +1,193 @@
+#include "main.h"
+#include "./api__ml_online.h"
+#include "./resource.h"
+
+#include <windows.h>
+#include <strsafe.h>
+
+typedef struct __MESSAGEBOX
+{
+ HWND hParent;
+ LPCWSTR pszText;
+ LPCWSTR pszCaption;
+ UINT uType;
+ LPCWSTR pszCheck;
+ INT *checked;
+} MESSAGEBOX;
+
+static INT_PTR CALLBACK OmMessageBox_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+INT OmMessageBox(HWND hParent, LPCWSTR pszText, LPCWSTR pszCaption, UINT uType, LPCWSTR pszCheck, INT *checked)
+{
+ MESSAGEBOX instance;
+ instance.hParent = hParent;
+ instance.pszText = pszText;
+ instance.pszCaption = pszCaption;
+ instance.uType = uType;
+ instance.pszCheck = pszCheck;
+ instance.checked = checked;
+
+ return (INT)WASABI_API_DIALOGBOXPARAMW(IDD_MESSAGEBOX, hParent, OmMessageBox_DialogProc, (LPARAM)&instance);
+}
+
+static void OmMessgageBox_CenterWindow(HWND hwnd, HWND hCenter)
+{
+ if (NULL == hwnd || NULL == hCenter)
+ return;
+
+ RECT centerRect, windowRect;
+ if (!GetWindowRect(hwnd, &windowRect) || !GetWindowRect(hCenter, &centerRect))
+ return;
+ windowRect.left = centerRect.left + ((centerRect.right - centerRect.left) - (windowRect.right - windowRect.left))/2;
+ windowRect.top = centerRect.top + ((centerRect.bottom - centerRect.top) - (windowRect.bottom - windowRect.top))/2;
+
+ SetWindowPos(hwnd, NULL, windowRect.left, windowRect.top, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+static BOOL OmMessageBox_GetTextBox(HWND hwnd, LPCWSTR pszText, INT cchText, SIZE *sizeText)
+{
+ HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
+ if (NULL == hdc) return FALSE;
+
+ HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L);
+ HFONT fontOrig = (HFONT)SelectObject(hdc, font);
+
+ if (cchText < 0)
+ cchText = lstrlen(pszText);
+
+ BOOL resultOk = GetTextExtentPoint32(hdc, pszText, cchText, sizeText);
+
+ SelectObject(hdc, fontOrig);
+ ReleaseDC(hwnd, hdc);
+
+ return resultOk;
+}
+static HICON OmMessageBox_GetIcon(UINT flags)
+{
+ LPCWSTR iconName = NULL;
+ switch(0x000000F0 & flags)
+ {
+ case MB_ICONHAND: iconName = IDI_HAND; break;
+ case MB_ICONQUESTION: iconName = IDI_QUESTION; break;
+ case MB_ICONEXCLAMATION:iconName = IDI_EXCLAMATION; break;
+ case MB_ICONASTERISK: iconName = IDI_ASTERISK; break;
+ }
+ return (NULL != iconName) ? LoadIcon(NULL, iconName) : NULL;
+}
+
+static BOOL OmMessageBox_GetIconSize(UINT flags, SIZE *iconSize)
+{
+ if (NULL == iconSize) return FALSE;
+
+ iconSize->cx = 0;
+ iconSize->cy = 0;
+
+ HICON hIcon = OmMessageBox_GetIcon(flags);
+ if (NULL == hIcon)
+ return TRUE;
+
+ ICONINFO iconInfo;
+
+ if (!GetIconInfo(hIcon, &iconInfo) || FALSE == iconInfo.fIcon)
+ return FALSE;
+
+ BITMAP bm;
+ if (NULL != iconInfo.hbmColor)
+ {
+ if (FALSE == GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bm))
+ return FALSE;
+
+ iconSize->cx = bm.bmWidth;
+ iconSize->cy = bm.bmHeight;
+ }
+ else if (NULL != iconInfo.hbmMask)
+ {
+ if (FALSE == GetObject(iconInfo.hbmMask, sizeof(BITMAP), &bm))
+ return FALSE;
+
+ iconSize->cx = bm.bmWidth;
+ iconSize->cy = bm.bmHeight/2;
+ }
+
+ return TRUE;
+}
+
+static BOOL OmMessageBox_GetButtonSize(HWND hwnd, SIZE *buttonSize)
+{
+ if (NULL == buttonSize)
+ return FALSE;
+
+ if (FALSE != SendMessage(hwnd, (0x1600 + 0x0001) /*BCM_GETIDEALSIZE*/, 0, (LPARAM)buttonSize))
+ return TRUE;
+
+ return FALSE;
+}
+static INT_PTR OmMessageBox_OnInitDialog(HWND hwnd, HWND hFocus, LPARAM lParam)
+{
+ MESSAGEBOX *pmb = (MESSAGEBOX*)lParam;
+ SetWindowText(hwnd, pmb->pszCaption);
+
+ SIZE textSize;
+ SIZE maxSize;
+ MONITORINFO mi;
+ mi.cbSize = sizeof(MONITORINFO);
+
+ HMONITOR hMonitor = MonitorFromWindow(pmb->hParent, MONITOR_DEFAULTTONEAREST);
+ if (NULL != hMonitor &&
+ GetMonitorInfo(hMonitor, &mi))
+ {
+ RECT rcFrame;
+ SetRectEmpty(&rcFrame);
+ AdjustWindowRectEx(&rcFrame, GetWindowStyle(hwnd), FALSE, GetWindowStyleEx(hwnd));
+ maxSize.cx = mi.rcWork.right - mi.rcWork.left - (rcFrame.right - rcFrame.left) - 8*2;
+ maxSize.cy = mi.rcWork.bottom - mi.rcWork.top - (rcFrame.bottom - rcFrame.top) - 8*2;
+ }
+ else
+ {
+ maxSize.cx = 1200;
+ maxSize.cy = 800;
+ }
+
+ HWND hText = GetDlgItem(hwnd, IDC_TEXT);
+ if (NULL != hText)
+ {
+ if (!OmMessageBox_GetTextBox(hText, pmb->pszText, -1, &textSize))
+ ZeroMemory(&textSize, sizeof(SIZE));
+
+ SetWindowPos(hText, NULL, 0, 0, textSize.cx, textSize.cy, SWP_NOACTIVATE | SWP_NOZORDER);
+ }
+ else
+ ZeroMemory(&textSize, sizeof(SIZE));
+
+
+
+ OmMessgageBox_CenterWindow(hwnd, pmb->hParent);
+ SendMessage(hwnd, DM_REPOSITION, 0, 0L);
+ return FALSE;
+}
+
+static void OmMessageBox_OnDestroy(HWND hwnd)
+{
+
+}
+
+static void OmMessageBox_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND hControl)
+{
+ switch(commandId)
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hwnd, commandId);
+ break;
+ }
+}
+static INT_PTR CALLBACK OmMessageBox_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ case WM_INITDIALOG: return OmMessageBox_OnInitDialog(hwnd, (HWND)wParam, lParam);
+ case WM_DESTROY: OmMessageBox_OnDestroy(hwnd); break;
+ case WM_COMMAND: OmMessageBox_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break;
+ }
+ return 0;
+}
diff --git a/Src/Plugins/Library/ml_online/messageBox.h b/Src/Plugins/Library/ml_online/messageBox.h
new file mode 100644
index 00000000..4becf7f8
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/messageBox.h
@@ -0,0 +1,13 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_MESSAGEBOX_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_MESSAGEBOX_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+
+INT OmMessageBox(HWND hParent, LPCWSTR pszText, LPCWSTR pszCaption, UINT uType, LPCWSTR pszCheck, INT *checked);
+
+#endif //NULLOSFT_ONLINEMEDIA_PLUGIN_MESSAGEBOX_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/ml_online.rc b/Src/Plugins/Library/ml_online/ml_online.rc
new file mode 100644
index 00000000..133a9988
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/ml_online.rc
@@ -0,0 +1,453 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#include ""version.rc2""\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_MAINDIALOG DIALOGEX 0, 0, 225, 92
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "Back",IDC_BACK,"Button",BS_OWNERDRAW | WS_TABSTOP,0,81,32,11
+ CONTROL "Forward",IDC_FORWARD,"Button",BS_OWNERDRAW | WS_TABSTOP,36,81,40,11
+ CONTROL "Home",IDC_HOME,"Button",BS_OWNERDRAW | WS_TABSTOP,80,81,32,11
+ CONTROL "Stop",IDC_STOP,"Button",BS_OWNERDRAW | WS_TABSTOP,116,81,24,11
+ CONTROL "Refresh",IDC_REFRESH,"Button",BS_OWNERDRAW | WS_TABSTOP,144,81,32,11
+END
+
+IDD_OMPREF DIALOGEX 0, 0, 272, 247
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "'Now Playing' Service",IDC_STATIC,0,0,272,72
+ LTEXT "This allows you to specify an alternative Now Playing service to use in the 'Now Playing' node (if installed). Leave blank to reset to the default service.",IDC_STATIC,6,11,260,18
+ EDITTEXT IDC_NOWPLAYINGURL,6,33,260,13,ES_AUTOHSCROLL
+ LTEXT "Note: For now playing lookups to work when using an alternative Now Playing service, the service will need to use the available Winamp Javascript API.",IDC_STATIC,6,51,260,18,WS_DISABLED
+ GROUPBOX "Online Media",IDC_STATIC,0,76,272,146
+ GROUPBOX "General Settings",IDC_STATIC,5,87,260,30
+ CONTROL "Allow sections to auto-resize",IDC_AUTOSIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,100,107,10
+ GROUPBOX "Cache Preferences",IDC_STATIC,5,120,260,40
+ LTEXT "Keep pages cached for:",IDC_STATIC,11,132,78,8
+ CONTROL "Never",IDC_RADIO_NEVER,"Button",BS_AUTORADIOBUTTON | WS_GROUP,20,144,35,10
+ CONTROL "One Hour",IDC_RADIO_HOURLY,"Button",BS_AUTORADIOBUTTON,62,144,47,10
+ CONTROL "One Day",IDC_RADIO_DAILY,"Button",BS_AUTORADIOBUTTON,116,144,44,10
+ CONTROL "One Week",IDC_RADIO_WEEKLY,"Button",BS_AUTORADIOBUTTON,167,144,51,10
+ GROUPBOX "Bandwidth Control",IDC_STATIC,5,164,260,52
+ LTEXT "Maximum bandwidth for media in Kbytes",IDC_STATIC,11,175,131,10,SS_CENTERIMAGE
+ EDITTEXT IDC_RADIO_MAXBW,154,174,30,12,ES_AUTOHSCROLL
+ LTEXT "Minimum bandwidth for media in Kbytes\n(not used by some sections)",IDC_STATIC,11,190,129,16
+ EDITTEXT IDC_RADIO_MINBW,154,190,31,12,ES_AUTOHSCROLL
+END
+
+IDD_MESSAGEBOX DIALOGEX 0, 0, 186, 90
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "",IDC_TEXT,7,7,172,35
+END
+
+IDD_SETUPPAGE DIALOGEX 0, 0, 256, 158
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Choose Winamp's web services to experience premium media services online.",IDC_LABEL_HEADER,5,6,251,8
+ LISTBOX IDC_SERVICELIST,5,24,99,126,LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | LBS_NODATA | WS_VSCROLL | WS_TABSTOP
+ CONTROL "",IDC_PLACEHOLDER,"Static",SS_BLACKRECT | NOT WS_VISIBLE,107,24,148,126
+END
+
+IDD_SETUP_SERVICEDETAILS DIALOGEX 0, 0, 148, 126
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "",IDC_THUMBNAIL,"Static",SS_BITMAP,0,0,45,45
+ LTEXT "",IDC_TITLE,46,0,95,10
+ EDITTEXT 1316,1,46,140,80,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "line1\r\nline2",IDC_SERVICEMETA,46,10,102,26,NOT WS_VISIBLE
+END
+
+IDD_SETUP_GROUPDETAILS DIALOGEX 0, 0, 148, 126
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Group Title",IDC_TITLE,4,0,144,13
+ EDITTEXT IDC_DESCRIPTION,2,15,145,97,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | NOT WS_VISIBLE | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "to read about Winamp policies.",IDC_HELPTEXT,0,117,147,8
+END
+
+IDD_OPENURL DIALOGEX 0, 0, 220, 54
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Open Url"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ LTEXT "Enter service list address:",IDC_STATIC,5,4,210,10
+ COMBOBOX IDC_ADDRESS,5,18,210,30,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "OK",IDOK,111,36,50,13
+ PUSHBUTTON "Cancel",IDCANCEL,165,36,50,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CONTEXTMENU MENU
+BEGIN
+ POPUP "ServiceContextMenu"
+ BEGIN
+ MENUITEM SEPARATOR
+ POPUP "&Service Management"
+ BEGIN
+ MENUITEM "&New", ID_SERVICE_NEW
+ POPUP "&Import"
+ BEGIN
+ MENUITEM "&File...", ID_SERVICE_IMPORT_FILE
+ MENUITEM "&Url..", ID_SERVICE_IMPORT_URL
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "&Edit", ID_SERVICE_EDIT
+ MENUITEM SEPARATOR
+ MENUITEM "Open Containing &Folder", ID_SERVICE_LOCATE
+ MENUITEM "Open in E&xternal Editor", ID_SERVICE_EDITEXTERNAL
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Re&move", ID_SERVICE_UNSUBSCRIBE
+ MENUITEM "Reset Permi&ssions", ID_SERVICE_RESETPOLICY
+ END
+ POPUP "GaleryContextMenu"
+ BEGIN
+ POPUP "&Service Management"
+ BEGIN
+ MENUITEM "&New", ID_SERVICE_NEW
+ MENUITEM SEPARATOR
+ MENUITEM "Import &File...", ID_SERVICE_IMPORT_FILE
+ MENUITEM "Import &Url..", ID_SERVICE_IMPORT_URL
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "&Preferences...", ID_PLUGIN_PREFERENCES
+ MENUITEM "&omBrowser Options...", ID_OMBROWSER_OPTIONS
+ MENUITEM SEPARATOR
+ MENUITEM "&Help", ID_PLUGIN_HELP
+ END
+ POPUP "&Rate"
+ BEGIN
+ MENUITEM "*****", ID_RATING_VALUE_5
+ MENUITEM "****", ID_RATING_VALUE_4
+ MENUITEM "***", ID_RATING_VALUE_3
+ MENUITEM "**", ID_RATING_VALUE_2
+ MENUITEM "*", ID_RATING_VALUE_1
+ END
+ POPUP "View"
+ BEGIN
+ MENUITEM "&Open", ID_VIEW_OPEN
+ MENUITEM "Open in &New Window", ID_VIEW_OPENPOPUP
+ END
+ POPUP "Navigation"
+ BEGIN
+ MENUITEM "Back", ID_NAVIGATION_BACK
+ MENUITEM "Forward", ID_NAVIGATION_FORWARD
+ MENUITEM "Refresh", ID_NAVIGATION_REFRESH
+ MENUITEM "Stop", ID_NAVIGATION_STOP
+ END
+ POPUP "Toolbar"
+ BEGIN
+ MENUITEM "&Top Dock", ID_TOOLBAR_DOCKTOP
+ MENUITEM "&Bottom Dock", ID_TOOLBAR_DOCKBOTTOM
+ MENUITEM SEPARATOR
+ MENUITEM "Auto-&Hide", ID_TOOLBAR_AUTOHIDE
+ MENUITEM "Enable &Tab Stop", ID_TOOLBAR_TABSTOP
+ END
+END
+
+IDR_SETUPMENU MENU
+BEGIN
+ POPUP "GroupContext"
+ BEGIN
+ MENUITEM "Expand", ID_GROUP_TOGGLE
+ MENUITEM SEPARATOR
+ MENUITEM "&Select All", ID_GROUP_SELECTALL
+ MENUITEM "&Unselect All", ID_GROUP_UNSELECTALL
+ MENUITEM SEPARATOR
+ MENUITEM "&Reload", ID_GROUP_RELOAD
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_MESSAGEBOX, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 83
+ END
+
+ IDD_SETUPPAGE, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ VERTGUIDE, 104
+ TOPMARGIN, 6
+ BOTTOMMARGIN, 150
+ HORZGUIDE, 24
+ END
+
+ IDD_SETUP_SERVICEDETAILS, DIALOG
+ BEGIN
+ RIGHTMARGIN, 142
+ VERTGUIDE, 46
+ HORZGUIDE, 10
+ HORZGUIDE, 46
+ END
+
+ IDD_OPENURL, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 215
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 49
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_BROWSERACCEL ACCELERATORS
+BEGIN
+ VK_F5, ID_NAVIGATION_REFRESH, VIRTKEY, NOINVERT
+ "R", ID_NAVIGATION_REFRESH, VIRTKEY, CONTROL, NOINVERT
+ VK_F11, ID_WINDOW_FULLSCREEN, VIRTKEY, NOINVERT
+ VK_F4, ID_WINDOW_CLOSE, VIRTKEY, CONTROL, NOINVERT
+ VK_F5, ID_NAVIGATION_REFRESH_COMPLETELY, VIRTKEY, CONTROL, NOINVERT
+ "R", ID_NAVIGATION_REFRESH_COMPLETELY, VIRTKEY, SHIFT, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// HTML
+//
+
+IDR_HTML_EDITOR HTML "resources\\pages\\serviceEditor.htm"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_ONLINE_SERVICES "Online Services"
+ IDS_NO_INTERNET_CONNECTION
+ "The media library feature you are attempting to use requires an internet connection. Please make sure you are connected to the internet and try again."
+ IDS_SORRY "Sorry"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_PLUGIN_NAME "Nullsoft Online Services v%d.%02d"
+ 65535 "{D006C700-557E-43c7-A580-B4C50C56957A}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_DEFAULT_SERVICENAME "Unknown name"
+ IDS_MORE "More..."
+ IDS_HOME "Home"
+ IDS_HOME_DESCRIPTION "Navigate to the main service page"
+ IDS_BACK "Back"
+ IDS_BACK_DESCRIPTION "Go back one page"
+ IDS_FORWARD "Forward"
+ IDS_FORWARD_DESCRIPTION "Go forward one page"
+ IDS_REFRESH "Refresh"
+ IDS_REFRESH_DESCRIPTION "Reload current page"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_STOP "Stop"
+ IDS_STOP_DESCRIPTION "Stop loading this page"
+ IDS_SEPARATOR "Separator"
+ IDS_SPACE "Space"
+ IDS_FLEXSPACE "Flexible Space"
+ IDS_MORE_DESCRIPTION "Show more items"
+ IDS_RATED "Rating:"
+ IDS_PLEASE_WAIT "Please wait..."
+ IDS_SERVICE_CHECKINGVERSION "Checking service version"
+ IDS_SERVICE_GETINFO "Get Info..."
+ IDS_SERVICE_GETINFO_DESCRIPTION "Get service info"
+ IDS_SERVICE_REPORT "Report..."
+ IDS_SERVICE_REPORT_DESCRIPTION "Report service"
+ IDS_SERVICE_UNSUBSCRIBE "Remove"
+ IDS_SERVICE_UNSUBSCRIBE_DESCRIPTION "Remove service"
+ IDS_RATING_0 "not rated"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_RATING_1 "1 star"
+ IDS_RATING_2 "2 stars"
+ IDS_RATING_3 "3 stars"
+ IDS_RATING_4 "4 stars"
+ IDS_RATING_5 "5 stars"
+ IDS_RATING_CHANGETO "Set to:"
+ IDS_RATING_CURRENT "Rated:"
+ IDS_SETUPPAGE_TITLE "Choose Online Services"
+ IDS_SETUP_EMPTYGROUP "( empty )"
+ IDS_SETUP_LOADINGGROUP "Loading..."
+ IDS_SERVICEGROUP_FEATURED "Featured"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_SERVICEGROUP_KNOWN "Already Subscribed"
+ IDS_SETUP_GROUPLOADFAILED "Error loading data"
+ IDS_SERVICE_BYAUTHOR " by: "
+ IDS_SERVICE_LASTUPDATED " updated: "
+ IDS_SERVICEGROUP_FEATUREDLONG "Featured Services"
+ IDS_SERVICEGROUP_KNOWNLONG "Already Subscribed Services"
+ IDS_SERVICEGROUP_FEATURED_DESC
+ "This group contains some great services that we handpicked especially for you - including Winamp Charts & Add-ons. Also, if at any time you would like to try some other services, feel free to discover them by selecting the Online Services page in the Media Library."
+ IDS_SERVICEGROUP_KNOWN_DESC
+ "Services that you have already subscribed to."
+ IDS_MESSAGEBOX_UNSUBSCRIBE
+ "Are you sure you want to remove %s from your Online Services?"
+ IDS_MESSAGEBOX_UNSUBSCRIBE_CAPTION "Confirm Service Remove"
+ IDS_MESSAGEBOX_RESETTODEFAULT
+ "Are you sure you want to remove your current Online Services and restore the default Online Services?"
+ IDS_MESSAGEBOX_RESETTODEFAULT_CAPTION
+ "Confirm Restore to Default Services"
+ IDS_DOWNLOADSERVICE_JOB "Loading featured services"
+ IDS_SAVESERVICE_JOB "Saving services"
+ IDS_CACHEICONS_JOB "Caching service icons"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_REGISTERINGSERVICE_JOB "Registering services"
+ IDS_MESSAGEBOX_NAMECHANGED_CAPTION "Service Name Changed"
+ IDS_MESSAGEBOX_NAMECHANGED "The service %s has been renamed to %s."
+ IDS_MESSAGEBOX_POLICYRESET_CAPTION "Confirm Permissions Reset"
+ IDS_MESSAGEBOX_POLICYRESET
+ "Are you sure you want to reset permissions for %s service?"
+ IDS_COLLAPSE "Collapse"
+ IDS_EXPAND "Expand"
+ IDS_SECURE_CONNECTION "Show Page Identification"
+ IDS_SCRIPT_ERROR "Show Script Errors"
+ IDS_SCRIPT_ERROR_DESCRIPTION "Page executed with some errors"
+ IDS_ENCRYPTION_MIXED "Mixed security"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_ENCRYPTION_40BIT "40-bit security"
+ IDS_ENCRYPTION_56BIT "56-bit security"
+ IDS_ENCRYPTION_FORTEZZA "Fortezza security"
+ IDS_ENCRYPTION_128BIT "128-bit security"
+ IDS_CONNECTION_UNSECURE "Connection Not Encrypted"
+ IDS_CONNECTION_ENCRYPTED "Connection Encrypted"
+ IDS_NAVIGATING "Navigating..."
+ IDS_MESSAGEBOX_FORCEREMOVE
+ "Service %s was cancelled and will be removed from your Online Services."
+ IDS_MESSAGEBOX_FORCEREMOVE_CAPTION "Service Cancelled"
+ IDS_HISTORY "Recent pages"
+ IDS_HISTORY_DESCRIPTION "Recent pages"
+ IDS_CURRENT_PAGE "Current Page"
+ IDS_CLICKHERE "Click here"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_MESSAGEBOX_DISCOVERBUSY
+ "Service discovery in progress. Please wait till completion and try again."
+ IDS_MESSAGEBOX_DISCOVERBUSY_CAPTION "Operation In Progress"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_USERSERVICE_NAME "My Service"
+ IDS_FILEFILTER_ALL "All Files"
+ IDS_FILEFILTER_ALLKNOWN "All Supported Files"
+ IDS_IMPORT_FILES "Import Service..."
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#include "version.rc2"
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/Library/ml_online/ml_online.sln b/Src/Plugins/Library/ml_online/ml_online.sln
new file mode 100644
index 00000000..733c80a3
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/ml_online.sln
@@ -0,0 +1,30 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ml_online", "ml_online.vcxproj", "{E40EADDB-F488-43CE-A451-B0D805C09A94}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Debug|Win32.Build.0 = Debug|Win32
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Debug|x64.ActiveCfg = Debug|x64
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Debug|x64.Build.0 = Debug|x64
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Release|Win32.ActiveCfg = Release|Win32
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Release|Win32.Build.0 = Release|Win32
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Release|x64.ActiveCfg = Release|x64
+ {E40EADDB-F488-43CE-A451-B0D805C09A94}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {87AC7BBF-61AD-484A-A3A1-FA39CE18F1E7}
+ EndGlobalSection
+EndGlobal
diff --git a/Src/Plugins/Library/ml_online/ml_online.vcxproj b/Src/Plugins/Library/ml_online/ml_online.vcxproj
new file mode 100644
index 00000000..8475fcd7
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/ml_online.vcxproj
@@ -0,0 +1,395 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{E40EADDB-F488-43CE-A451-B0D805C09A94}</ProjectGuid>
+ <RootNamespace>ml_online</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;ML_ONLINE_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <ModuleDefinitionFile>
+ </ModuleDefinitionFile>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;ML_ONLINE_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;ML_ONLINE_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;..\..\..\;..\..\..\wasabi;..\..\..\agave;..\..\..\ombrowser;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;ML_ONLINE_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Manifest>
+ <OutputManifestFile>$(IntDir)$(TargetName)$(TargetExt).intermediate.manifest</OutputManifestFile>
+ </Manifest>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ml_online.rc" />
+ <ResourceCompile Include="png.rc" />
+ <ResourceCompile Include="testPages.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="resources\pages\serviceEditor.htm">
+ <DeploymentContent>true</DeploymentContent>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\General\gen_ml\menu.cpp" />
+ <ClCompile Include="..\..\..\nu\ConfigCOM.cpp">
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)%(Filename)1.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)%(Filename)1.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)%(Filename)1.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)%(Filename)1.obj</ObjectFileName>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp">
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)%(Filename)2.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)%(Filename)2.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)%(Filename)2.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)%(Filename)2.obj</ObjectFileName>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\menuHelpers.cpp" />
+ <ClCompile Include="..\..\..\nu\menushortcuts.cpp" />
+ <ClCompile Include="..\..\..\nu\sort.cpp" />
+ <ClCompile Include="..\..\..\nu\trace.cpp" />
+ <ClCompile Include="..\..\..\nu\windowsTheme.cpp" />
+ <ClCompile Include="..\..\..\Winamp\JSAPI_CallbackParameters.cpp" />
+ <ClCompile Include="browserEvent.cpp" />
+ <ClCompile Include="commands.cpp" />
+ <ClCompile Include="common.cpp" />
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="external.cpp" />
+ <ClCompile Include="forceUrl.cpp" />
+ <ClCompile Include="handler.cpp" />
+ <ClCompile Include="importFile.cpp" />
+ <ClCompile Include="importUrl.cpp" />
+ <ClCompile Include="JnetCOM.cpp" />
+ <ClCompile Include="JSAPI2_Creator.cpp" />
+ <ClCompile Include="jsapi2_omcom.cpp" />
+ <ClCompile Include="local_menu.cpp" />
+ <ClCompile Include="Main.cpp" />
+ <ClCompile Include="messageBox.cpp" />
+ <ClCompile Include="navigation.cpp" />
+ <ClCompile Include="OMCOM.cpp" />
+ <ClCompile Include="Preferences.cpp" />
+ <ClCompile Include="serviceHelper.cpp" />
+ <ClCompile Include="serviceHost.cpp" />
+ <ClCompile Include="Setup\setup.cpp" />
+ <ClCompile Include="Setup\setupDetails.cpp" />
+ <ClCompile Include="Setup\setupDetailsGroup.cpp" />
+ <ClCompile Include="Setup\setupGroup.cpp" />
+ <ClCompile Include="Setup\setupGroupFilter.cpp" />
+ <ClCompile Include="Setup\setupGroupList.cpp" />
+ <ClCompile Include="Setup\setupImage.cpp" />
+ <ClCompile Include="Setup\setupListbox.cpp" />
+ <ClCompile Include="Setup\setupListboxLabel.cpp" />
+ <ClCompile Include="Setup\setupLog.cpp" />
+ <ClCompile Include="Setup\setupPage.cpp" />
+ <ClCompile Include="Setup\setupPageWnd.cpp" />
+ <ClCompile Include="Setup\setupRecord.cpp" />
+ <ClCompile Include="Setup\setupServicePanel.cpp" />
+ <ClCompile Include="wasabi.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\General\gen_ml\menu.h" />
+ <ClInclude Include="..\..\..\jnetlib\asyncdns.h" />
+ <ClInclude Include="..\..\..\jnetlib\connection.h" />
+ <ClInclude Include="..\..\..\jnetlib\httpget.h" />
+ <ClInclude Include="..\..\..\jnetlib\listen.h" />
+ <ClInclude Include="..\..\..\jnetlib\netinc.h" />
+ <ClInclude Include="..\..\..\jnetlib\util.h" />
+ <ClInclude Include="..\..\..\nu\ConfigCOM.h" />
+ <ClInclude Include="browserEvent.h" />
+ <ClInclude Include="BufferCache.h" />
+ <ClInclude Include="commands.h" />
+ <ClInclude Include="common.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="external.h" />
+ <ClInclude Include="forceUrl.h" />
+ <ClInclude Include="handler.h" />
+ <ClInclude Include="import.h" />
+ <ClInclude Include="JnetCOM.h" />
+ <ClInclude Include="JSAPI2_Creator.h" />
+ <ClInclude Include="jsapi2_omcom.h" />
+ <ClInclude Include="local_menu.h" />
+ <ClInclude Include="Main.h" />
+ <ClInclude Include="messageBox.h" />
+ <ClInclude Include="navigation.h" />
+ <ClInclude Include="OMCOM.h" />
+ <ClInclude Include="Preferences.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="serviceHelper.h" />
+ <ClInclude Include="serviceHost.h" />
+ <ClInclude Include="Setup\setupDetails.h" />
+ <ClInclude Include="Setup\setupGroup.h" />
+ <ClInclude Include="Setup\SetupGroupFilter.h" />
+ <ClInclude Include="Setup\setupGroupList.h" />
+ <ClInclude Include="Setup\setupImage.h" />
+ <ClInclude Include="Setup\setupListbox.h" />
+ <ClInclude Include="Setup\setupListboxLabel.h" />
+ <ClInclude Include="Setup\setupLog.h" />
+ <ClInclude Include="Setup\setupPage.h" />
+ <ClInclude Include="Setup\setupRecord.h" />
+ <ClInclude Include="Setup\setupServicePanel.h" />
+ <ClInclude Include="api__ml_online.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/ml_online.vcxproj.filters b/Src/Plugins/Library/ml_online/ml_online.vcxproj.filters
new file mode 100644
index 00000000..6963e519
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/ml_online.vcxproj.filters
@@ -0,0 +1,301 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="browserEvent.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="commands.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="common.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="external.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="forceUrl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="handler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="importFile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="importUrl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="JnetCOM.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="jsapi2_omcom.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="JSAPI2_Creator.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="local_menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="messageBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="navigation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="OMCOM.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Preferences.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="serviceHelper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="serviceHost.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupDetails.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupDetailsGroup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupGroup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupGroupFilter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupGroupList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wasabi.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupServicePanel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupRecord.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupPageWnd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupPage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupLog.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupListboxLabel.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupListbox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Setup\setupImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\ConfigCOM.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Winamp\JSAPI_CallbackParameters.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\MediaLibraryInterface.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\General\gen_ml\menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\menuHelpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\menushortcuts.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\sort.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\trace.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\windowsTheme.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="browserEvent.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="BufferCache.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="commands.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="common.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="external.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="forceUrl.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="handler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="import.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="JnetCOM.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="JSAPI2_Creator.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="jsapi2_omcom.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="local_menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="messageBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="navigation.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="OMCOM.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Preferences.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="serviceHelper.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api__ml_online.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupServicePanel.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupRecord.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupPage.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupLog.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupListboxLabel.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupListbox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupImage.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupGroupList.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\SetupGroupFilter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupGroup.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Setup\setupDetails.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="serviceHost.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\jnetlib\listen.h">
+ <Filter>Header Files\jnetlibws</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\jnetlib\asyncdns.h">
+ <Filter>Header Files\jnetlibws</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\jnetlib\connection.h">
+ <Filter>Header Files\jnetlibws</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\jnetlib\httpget.h">
+ <Filter>Header Files\jnetlibws</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\ConfigCOM.h">
+ <Filter>Header Files\nu</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\General\gen_ml\menu.h">
+ <Filter>Header Files\gen_ml</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\jnetlib\netinc.h">
+ <Filter>Header Files\jnetlibws</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\jnetlib\util.h">
+ <Filter>Header Files\jnetlibws</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="ml_online.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="png.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="testPages.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{e3d16f42-4b26-4321-936a-0243e6c4f69c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{cd0589d8-5b12-49c6-9677-6d79580e8c60}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{92b2e12e-b041-4908-b10a-e7156ed3fc62}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="HTM Files">
+ <UniqueIdentifier>{4b199e78-6958-4855-8ac9-9d3e16a24d84}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\jnetlibws">
+ <UniqueIdentifier>{888befc6-d04c-4686-907e-b74a8a1742f8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\nu">
+ <UniqueIdentifier>{981def8f-a0e5-4e28-a401-8dcf96712819}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\gen_ml">
+ <UniqueIdentifier>{b7e447b3-b0b4-48d9-9da1-a65fb3f61161}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="resources\pages\serviceEditor.htm">
+ <Filter>HTM Files</Filter>
+ </None>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/navigation.cpp b/Src/Plugins/Library/ml_online/navigation.cpp
new file mode 100644
index 00000000..72019329
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/navigation.cpp
@@ -0,0 +1,1472 @@
+#include "main.h"
+#include "./navigation.h"
+#include "./resource.h"
+#include "./api__ml_online.h"
+#include "./local_menu.h"
+#include "./commands.h"
+#include "./config.h"
+
+#include "../omBrowser/browserView.h"
+#include "../winamp/wa_ipc.h"
+
+#include "./serviceHost.h"
+#include "./serviceHelper.h"
+#include "./browserEvent.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omserviceeditor.h>
+#include <storageIni.h>
+#include <ifc_omserviceenum.h>
+#include <ifc_mlnavigationhelper.h>
+#include <ifc_omserviceeventmngr.h>
+#include <ifc_ombrowserwndmngr.h>
+#include <ifc_ombrowserwndenum.h>
+#include <ifc_ombrowsereventmngr.h>
+
+#include "../../General/gen_ml/menu.h"
+#include "../../General/gen_ml/ml_ipc_0313.h"
+
+#include <vector>
+#include "../nu/sort.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <algorithm>
+
+#define NAVITEM_PREFIX L"om_svc_"
+#define E_NAVITEM_UNKNOWN E_NOINTERFACE
+
+typedef struct __NAVASYNCPARAM
+{
+ Navigation *instance;
+ NavigationCallback callback;
+ ULONG_PTR param;
+}NAVASYNCPARAM;
+
+
+
+static BOOL Navigation_CheckInvariantName(LPCWSTR pszInvarian)
+{
+ INT cchInvariant = (NULL != pszInvarian) ? lstrlen(pszInvarian) : 0;
+ INT cchPrefix = ARRAYSIZE(NAVITEM_PREFIX) - 1;
+ return (cchInvariant > cchPrefix &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, 0, NAVITEM_PREFIX, cchPrefix, pszInvarian, cchPrefix));
+}
+
+
+Navigation::Navigation()
+ : ref(1), cookie(0), hRoot(NULL), hLibrary(NULL)
+{
+}
+
+Navigation::~Navigation()
+{
+}
+
+HRESULT Navigation::CreateInstance(Navigation **instance)
+{
+ if (NULL == instance) return E_POINTER;
+
+ HRESULT hr;
+
+ Navigation *navigation = new Navigation();
+ if (NULL != navigation)
+ {
+ hr = navigation->Initialize();
+ if (FAILED(hr))
+ {
+ navigation->Release();
+ navigation = NULL;
+ }
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+
+ *instance = navigation;
+ return hr;
+}
+
+size_t Navigation::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t Navigation::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int Navigation::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_MlNavigationCallback))
+ *object = static_cast<ifc_mlnavigationcallback*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+HRESULT Navigation::Initialize()
+{
+ hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ if (0 == cookie)
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->RegisterCallback(this, &cookie);
+ navHelper->Release();
+ }
+ }
+
+ ifc_omservice *service;
+
+ MLNavCtrl_BeginUpdate(hLibrary, NUF_LOCK_TOP);
+
+ // TODO make thie configurable?
+ if (SUCCEEDED(ServiceHelper_Create(ROOTSERVICE_ID, MAKEINTRESOURCE(IDS_ONLINE_SERVICES),
+ NULL, L"http://client.winamp.com/services", SVCF_SPECIAL | SVCF_SUBSCRIBED, 1, FALSE, &service)))
+ {
+ hRoot = CreateItemInt(NULL, service);
+ service->Release();
+ }
+
+ if (NULL == hRoot)
+ {
+ MLNavCtrl_EndUpdate(hLibrary);
+ return E_FAIL;
+ }
+
+
+ ifc_omserviceenum *enumerator;
+ if (SUCCEEDED(ServiceHelper_Load(&enumerator)))
+ {
+ ifc_omservice *service;
+ std::vector<ifc_omservice*> serviceList;
+ while (S_OK == enumerator->Next(1, &service, NULL))
+ {
+ if (S_OK == ServiceHelper_IsSubscribed(service))
+ {
+ serviceList.push_back(service);
+ }
+ else
+ service->Release();
+ }
+ enumerator->Release();
+
+ size_t count = serviceList.size();
+ Order(serviceList);
+ for(size_t i =0; i < count; i++)
+ {
+ service = serviceList[i];
+ CreateItemInt(hRoot, service);
+ service->Release();
+ }
+ }
+
+ MLNavCtrl_EndUpdate(hLibrary);
+
+ return S_OK;
+}
+
+HRESULT Navigation::Finish()
+{
+ if (0 != cookie)
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->UnregisterCallback(cookie);
+ navHelper->Release();
+ }
+ }
+
+ if (NULL != OMBROWSERMNGR)
+ {
+ OMBROWSERMNGR->Finish();
+ }
+
+ return S_OK;
+}
+
+HRESULT Navigation::SaveOrder()
+{
+ if (NULL == hRoot || NULL == hLibrary)
+ return E_UNEXPECTED;
+
+ LPSTR buffer = NULL;
+ INT count = MLNavItem_GetChildrenCount(hLibrary, hRoot);
+ if (count > 0)
+ {
+ size_t bufferMax = 11 * count;
+ buffer = Plugin_MallocAnsiString(bufferMax);
+ if (NULL == buffer) return E_OUTOFMEMORY;
+ *buffer = '\0';
+
+ LPSTR cursor = buffer;
+ size_t remaining = bufferMax;
+
+ NAVITEM item;
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_PARAM;
+ item.hItem = MLNavItem_GetChild(hLibrary, hRoot);
+ while (NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service)
+ {
+ UINT serviceFlags;
+ if (SUCCEEDED(service->GetFlags(&serviceFlags)) &&
+ 0 == (SVCF_SPECIAL & serviceFlags))
+ {
+ if (cursor == buffer ||
+ SUCCEEDED(StringCchCopyExA(cursor, remaining, ";", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE)))
+ {
+ StringCchPrintfExA(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, "%d", service->GetId());
+ }
+ }
+ }
+ }
+ item.hItem = MLNavItem_GetNext(hLibrary, item.hItem);
+ }
+
+ }
+
+ Config_WriteStr("Navigation", "order", buffer);
+ Plugin_FreeAnsiString(buffer);
+
+ return S_OK;
+}
+
+//static int __fastcall Navigation_OrderComparer(const void *elem1, const void *elem2, const void *context)
+//{
+// std::vector<UINT> *orderList = (std::vector<UINT>*)context;
+//
+// UINT serviceId;
+// size_t index1, index2;
+// size_t count = orderList->size();
+//
+// serviceId = (*(ifc_omservice**)elem1)->GetId();
+// for (index1 = 0; index1 < count && serviceId != orderList->at(index1); index1++);
+//
+// serviceId = (*(ifc_omservice**)elem2)->GetId();
+// for (index2 = 0; index2 < count && serviceId != orderList->at(index2); index2++);
+//
+// return (INT)(index1 - index2);
+//}
+class Navigation_OrderComparer
+{
+public:
+ Navigation_OrderComparer(const void* ctx)
+ : context(ctx)
+ {
+ }
+
+ bool operator()(const void* elem1, const void* elem2)
+ {
+ std::vector<UINT>* orderList = (std::vector<UINT>*)context;
+
+ UINT serviceId;
+ size_t index1, index2;
+ size_t count = orderList->size();
+
+ serviceId = ((ifc_omservice*)elem1)->GetId();
+ for (index1 = 0; index1 < count && serviceId != orderList->at(index1); index1++);
+
+ serviceId = ((ifc_omservice*)elem2)->GetId();
+ for (index2 = 0; index2 < count && serviceId != orderList->at(index2); index2++);
+
+ return (INT)(index1 - index2) < 0;
+ }
+
+
+private:
+ const void* context;
+};
+
+HRESULT Navigation::Order(std::vector<ifc_omservice*> &list)
+{
+ size_t listSize = list.size();
+
+ if (listSize < 2)
+ return S_FALSE;
+
+ //if (NULL == list) return E_INVALIDARG;
+
+ size_t bufferMax = 16384;
+ LPSTR buffer = Plugin_MallocAnsiString(bufferMax);
+ if (NULL == buffer) return E_OUTOFMEMORY;
+
+ UINT len = Config_ReadStr("Navigation", "order", NULL, buffer, (UINT)bufferMax);
+ std::vector<UINT> orderList;
+
+ LPCSTR end = buffer + len;
+ LPCSTR block = buffer;
+ LPCSTR cursor = block;
+ for(;;)
+ {
+ if (cursor == end || ';' == *cursor)
+ {
+ if (block != cursor)
+ {
+ INT serviceId;
+ if (FALSE != StrToIntExA(block, STIF_DEFAULT, &serviceId))
+ orderList.push_back(serviceId);
+ }
+
+ if (cursor == end) break;
+ cursor++;
+ block = cursor;
+ }
+ cursor++;
+ }
+
+
+ if (0 != orderList.size())
+ {
+ //nu::qsort(list, listSize, sizeof(ifc_omservice*), &orderList, Navigation_OrderComparer);
+ std::sort(list.begin(), list.end(), Navigation_OrderComparer(&orderList));
+ }
+
+ Plugin_FreeAnsiString(buffer);
+ return S_OK;
+}
+
+typedef struct __IMAGEAPCPARAM
+{
+ LPWSTR name;
+ INT index;
+} IMAGEAPCPARAM;
+
+static void CALLBACK Navigtaion_ImageChangedApc(Navigation *instance, ULONG_PTR param)
+{
+ IMAGEAPCPARAM *image = (IMAGEAPCPARAM*)param;
+ if (NULL == image) return;
+
+ instance->ImageChanged(image->name, image->index);
+
+ Plugin_FreeString(image->name);
+ free(image);
+}
+
+
+void Navigation::ImageChanged(LPCWSTR pszName, INT index)
+{
+ if (NULL == hRoot || NULL == hLibrary || NULL == pszName)
+ return;
+
+
+ DWORD libraryTID = GetWindowThreadProcessId(hLibrary, NULL);
+ DWORD currentTID = GetCurrentThreadId();
+ if (libraryTID != currentTID)
+ {
+ if (NULL != OMUTILITY)
+ {
+ IMAGEAPCPARAM *param = (IMAGEAPCPARAM*)calloc(1, sizeof(IMAGEAPCPARAM));
+ if (NULL != param)
+ {
+ param->name = Plugin_CopyString(pszName);
+ param->index = index;
+ if (FAILED(PostMainThreadCallback(Navigtaion_ImageChangedApc, (ULONG_PTR)param)))
+ {
+ free(param);
+ }
+ }
+ }
+ return;
+ }
+
+ WCHAR szBuffer[2048] = {0};
+ NAVITEM item = {0};
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_TEXTINVARIANT | NIMF_PARAM;
+ item.pszInvariant = szBuffer;
+ item.cchInvariantMax = ARRAYSIZE(szBuffer);
+
+ item.hItem = hRoot;
+
+ while (NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item) &&
+ FALSE != Navigation_CheckInvariantName(item.pszInvariant))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service &&
+ SUCCEEDED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer))) &&
+ CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, szBuffer, -1, pszName, -1))
+ {
+
+ item.iImage = index;
+ item.iSelectedImage = index;
+ item.mask = NIMF_IMAGE | NIMF_IMAGESEL;
+ MLNavItem_SetInfo(hLibrary, &item);
+ return;
+ }
+ }
+
+ item.hItem = (HNAVITEM)SENDMLIPC(hLibrary,
+ (item.hItem == hRoot) ? ML_IPC_NAVITEM_GETCHILD : ML_IPC_NAVITEM_GETNEXT,
+ (WPARAM)item.hItem);
+
+ }
+}
+
+
+
+BOOL Navigation::ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result)
+{
+ if (msg < ML_MSG_TREE_BEGIN || msg > ML_MSG_TREE_END)
+ return FALSE;
+
+ HRESULT hr;
+
+ switch(msg)
+ {
+ case ML_MSG_TREE_ONCREATEVIEW:
+ {
+ HWND hView;
+ hr = OnCreateView(GetMessageItem(msg, param1), (HWND)param2, &hView);
+ *result = (SUCCEEDED(hr)) ? (INT_PTR)hView : NULL;
+ }
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_CONTEXTMENU:
+ hr = OnContextMenu(GetMessageItem(msg, param1), (HWND)param2, MAKEPOINTS(param3));
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_ONDELETE:
+ hr = OnDeleteItem(GetMessageItem(msg, param1));
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_ONENDTITLEEDIT:
+ hr = OnEndTitleEdit(GetMessageItem(msg, param1), (LPCWSTR)param2);
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_TREE_ONKEYDOWN:
+ hr = OnKeyDown(GetMessageItem(msg, param1), (NMTVKEYDOWN*)param2);
+ *result = SUCCEEDED(hr);
+ return TRUE;
+
+ case ML_MSG_NAVIGATION_ONDESTROY:
+ OnControlDestroy();
+ *result = 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+HNAVITEM Navigation::GetActive(ifc_omservice **serviceOut)
+{
+ ifc_omservice *service;
+ HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL;
+ if (NULL == hActive || FAILED(GetService(hActive, &service)))
+ {
+ hActive = NULL;
+ service = NULL;
+ }
+
+ if (NULL != serviceOut)
+ *serviceOut = service;
+ else if (NULL != service)
+ service->Release();
+
+ return hActive;
+}
+
+HWND Navigation::GetActiveView(ifc_omservice **serviceOut)
+{
+ HWND hView = (NULL != hLibrary) ? ((HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0)) : NULL;
+ if (NULL != hView)
+ {
+ WCHAR szBuffer[128] = {0};
+ if (!GetClassName(hView, szBuffer, ARRAYSIZE(szBuffer)) ||
+ CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, szBuffer, -1,
+ L"Nullsoft_omBrowserView", -1))
+ {
+ hView = NULL;
+ }
+ }
+
+ ifc_omservice *service;
+ if (NULL == hView || FALSE == BrowserView_GetService(hView, &service))
+ {
+ hView = NULL;
+ service = NULL;
+ }
+
+ if (NULL != serviceOut)
+ *serviceOut = service;
+ else if (NULL != service)
+ service->Release();
+
+ return hView;
+}
+
+HRESULT Navigation::SelectItem(HNAVITEM hItem, LPCWSTR pszUrl)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+
+ ifc_omservice *service;
+ HRESULT hr = GetService(hItem, &service);
+ if (FAILED(hr)) return hr;
+
+ hr = SelectItemInt(hItem, service->GetId(), pszUrl);
+ service->Release();
+
+ return hr;
+}
+
+HRESULT Navigation::DeleteItem(HNAVITEM hItem)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ ifc_omservice *service;
+ if (FAILED(GetService(hItem, &service)))
+ return E_FAIL;
+
+ HRESULT hr;
+ UINT serviceFlags;
+ if (FAILED(service->GetFlags(&serviceFlags))) serviceFlags = 0;
+ if (0 == (SVCF_SPECIAL & serviceFlags))
+ {
+ MLNavCtrl_BeginUpdate(hLibrary, 0);
+ HNAVITEM hSelection = MLNavCtrl_GetSelection(hLibrary);
+ if (hSelection == hItem)
+ {
+ HNAVITEM hNext = MLNavItem_GetNext(hLibrary, hItem);
+ if (NULL == hNext)
+ hNext = MLNavItem_GetPrevious(hLibrary, hItem);
+
+ if (NULL != hNext)
+ {
+ MLNavItem_Select(hLibrary, hNext);
+ }
+ }
+
+ BOOL result = MLNavCtrl_DeleteItem(hLibrary, hItem);
+ hr = (FALSE != result) ? S_OK : E_FAIL;
+
+ MLNavCtrl_EndUpdate(hLibrary);
+ }
+ else
+ {
+ hr = E_FAIL;
+ }
+
+ service->Release();
+
+ return hr;
+}
+
+HRESULT Navigation::DeleteAll()
+{
+ if (NULL == hRoot || NULL == hLibrary) return E_UNEXPECTED;
+
+ std::vector<HNAVITEM> itemList;
+ HNAVITEM hItem = MLNavItem_GetChild(hLibrary, hRoot);
+ while (NULL != hItem)
+ {
+ itemList.push_back(hItem);
+ hItem = MLNavItem_GetNext(hLibrary, hItem);
+ }
+
+ MLNavCtrl_BeginUpdate(hLibrary, 0);
+
+ NAVITEM item;
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_PARAM;
+
+ size_t index = itemList.size();
+ while(index--)
+ {
+ item.hItem = itemList[index];
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service)
+ {
+ service->AddRef();
+
+ UINT serviceFlags;
+ if (SUCCEEDED(service->GetFlags(&serviceFlags)) &&
+ 0 == (SVCF_SPECIAL & serviceFlags) &&
+ FALSE != MLNavCtrl_DeleteItem(hLibrary, item.hItem))
+ {
+ }
+
+ service->Release();
+ }
+ }
+ }
+
+ MLNavCtrl_EndUpdate(hLibrary);
+ return S_OK;
+}
+HRESULT Navigation::InitializeBrowser()
+{
+ if (NULL == OMBROWSERMNGR)
+ return E_UNEXPECTED;
+
+ HWND hWinamp = Plugin_GetWinamp();
+
+ HRESULT hr = OMBROWSERMNGR->Initialize(NULL, hWinamp);
+ if (SUCCEEDED(hr))
+ {
+ if (S_OK == hr)
+ {
+ ifc_ombrowsereventmngr *eventManager;
+ if (SUCCEEDED(OMBROWSERMNGR->QueryInterface(IFC_OmBrowserEventManager, (void**)&eventManager)))
+ {
+ BrowserEvent *eventHandler;
+ if (SUCCEEDED(BrowserEvent::CreateInstance(&eventHandler)))
+ {
+ eventManager->RegisterHandler(eventHandler);
+ eventHandler->Release();
+ }
+ eventManager->Release();
+ }
+ }
+ }
+ return hr;
+}
+
+HRESULT Navigation::CreatePopup(HNAVITEM hItem, HWND *hwnd)
+{
+ if (NULL == hwnd) return E_POINTER;
+ *hwnd = NULL;
+
+ if (NULL == hLibrary) return E_UNEXPECTED;
+ if (NULL == hItem) return E_INVALIDARG;
+
+ HRESULT hr;
+
+ ifc_omservice *service;
+ hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ HWND hWinamp = Plugin_GetWinamp();
+
+ hr = InitializeBrowser();
+ if (SUCCEEDED(hr))
+ {
+ RECT rect;
+ HWND hFrame = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
+ if (NULL == hFrame) hFrame = hLibrary;
+ if (NULL == hFrame || FALSE == GetWindowRect(hFrame, &rect))
+ {
+ hr = E_FAIL;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ rect.left += 16;
+ rect.top += 16;
+
+ hr = OMBROWSERMNGR->CreatePopup(service, rect.left, rect.top,
+ rect.right - rect.left, rect.bottom - rect.top, hWinamp, NULL, 0, hwnd);
+ }
+ }
+ service->Release();
+ }
+ return hr;
+}
+
+static void CALLBACK Navigation_AsyncCallback(ULONG_PTR data)
+{
+ NAVASYNCPARAM *async = (NAVASYNCPARAM*)data;
+ if (NULL == async) return;
+
+ async->callback(async->instance, async->param);
+ if (NULL != async->instance)
+ async->instance->Release();
+ free(async);
+}
+
+HRESULT Navigation::PostMainThreadCallback(NavigationCallback callback, ULONG_PTR param)
+{
+ if (NULL == callback)
+ return E_INVALIDARG;
+
+ NAVASYNCPARAM *async = (NAVASYNCPARAM*)calloc(1, sizeof(NAVASYNCPARAM));
+ if (NULL == async) return E_OUTOFMEMORY;
+
+ async->instance = this;
+ async->callback = callback;
+ async->param = param;
+ this->AddRef();
+
+ HRESULT hr;
+ if (NULL == OMUTILITY)
+ hr = E_FAIL;
+ else
+ {
+ hr = OMUTILITY->PostMainThreadCallback(Navigation_AsyncCallback, (ULONG_PTR)async);
+ if (FAILED(hr))
+ {
+ Release();
+ free(async);
+ }
+ }
+
+ return hr;
+}
+
+static void CALLBACK Navigation_CreateItemAsyncCallback(Navigation *instance, ULONG_PTR param)
+{
+ ifc_omservice *service= (ifc_omservice*)param;
+
+ if (NULL != service)
+ {
+ if (NULL != instance)
+ instance->CreateItem(service);
+
+ service->Release();
+ }
+}
+
+HRESULT Navigation::CreateItemAsync(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;;
+
+ service->AddRef();
+
+ HRESULT hr = PostMainThreadCallback(Navigation_CreateItemAsyncCallback, (ULONG_PTR)service);
+ if (FAILED(hr))
+ service->Release();
+
+ return hr;
+}
+
+
+HRESULT Navigation::GetService(HNAVITEM hItem, ifc_omservice **service)
+{
+ WCHAR szBuffer[64] = {0};
+
+ if (NULL == service) return E_POINTER;
+ *service = NULL;
+
+ if (NULL == hLibrary || NULL == hItem)
+ return E_INVALIDARG;
+
+ NAVITEM itemInfo;
+ itemInfo.cbSize = sizeof(NAVITEM);
+ itemInfo.hItem = hItem;
+ itemInfo.pszInvariant = szBuffer;
+ itemInfo.cchInvariantMax = ARRAYSIZE(szBuffer);
+ itemInfo.mask = NIMF_PARAM | NIMF_TEXTINVARIANT;
+
+ if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo))
+ return E_FAIL;
+
+ if (FALSE == Navigation_CheckInvariantName(szBuffer))
+ return E_NAVITEM_UNKNOWN;
+
+ *service = (ifc_omservice*)itemInfo.lParam;
+ (*service)->AddRef();
+ return S_OK;
+}
+
+static void CALLBACK Navigtaion_UpdateServiceApc(Dispatchable *object, ULONG_PTR param1, ULONG_PTR param2)
+{
+ Navigation *navigation = (Navigation*)object;
+ if (NULL != navigation)
+ {
+ ifc_omservice *service = (ifc_omservice*)param1;
+ navigation->UpdateService(service, (UINT)param2);
+ if (NULL != service) service->Release();
+ }
+}
+
+HRESULT Navigation::UpdateService(ifc_omservice *service, UINT modifiedFlags)
+{
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ DWORD libraryTID = GetWindowThreadProcessId(hLibrary, NULL);
+ DWORD currentTID = GetCurrentThreadId();
+ if (libraryTID != currentTID)
+ {
+ if (NULL != OMUTILITY)
+ {
+ service->AddRef();
+ if (FAILED(OMUTILITY->PostMainThreadCallback2(Navigtaion_UpdateServiceApc, this, (ULONG_PTR)service, (ULONG_PTR)modifiedFlags)))
+ service->Release();
+ }
+ return E_PENDING;
+ }
+
+ HNAVITEM hItem = FindService(service->GetId(), NULL);
+ if (NULL == hItem)
+ return E_FAIL;
+
+ if (0 != (ifc_omserviceeditor::modifiedFlags & modifiedFlags) &&
+ S_FALSE == ServiceHelper_IsSubscribed(service))
+ {
+ DeleteItem(hItem);
+ return S_OK;
+ }
+
+ NAVITEM itemInfo;
+ itemInfo.cbSize = sizeof(NAVITEM);
+ itemInfo.hItem = hItem;
+
+ itemInfo.mask = NIMF_IMAGE;
+ if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo))
+ itemInfo.iImage= -1;
+
+ itemInfo.mask = 0;
+
+ WCHAR szName[512] = {0};
+ if (0 != (ifc_omserviceeditor::modifiedName & modifiedFlags) &&
+ SUCCEEDED(service->GetName(szName, ARRAYSIZE(szName))))
+ {
+ itemInfo.mask |= NIMF_TEXT;
+ itemInfo.pszText = szName;
+ }
+
+
+ if (0 != (ifc_omserviceeditor::modifiedIcon & modifiedFlags))
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ INT iImage;
+ WCHAR szIcon[1024] = {0};
+ if (FAILED(service->GetIcon(szIcon, ARRAYSIZE(szIcon))) ||
+ FAILED(navHelper->QueryIndex(szIcon, &iImage, NULL)))
+ {
+ iImage = -1;
+ }
+
+ if (itemInfo.iImage != iImage)
+ {
+ itemInfo.mask |= NIMF_IMAGE | NIMF_IMAGESEL;
+ itemInfo.iImage = iImage;
+ itemInfo.iSelectedImage = iImage;
+ }
+
+ navHelper->Release();
+ }
+ }
+
+ if (0 != itemInfo.mask)
+ {
+ if (FALSE == MLNavItem_SetInfo(hLibrary, &itemInfo))
+ return E_FAIL;
+ }
+
+ NAVITEMINAVLIDATE invalidate;
+ invalidate.hItem = hItem;
+ invalidate.fErase = FALSE;
+ invalidate.prc = NULL;
+ MLNavItem_Invalidate(hLibrary, &invalidate);
+
+ return S_OK;
+}
+
+HNAVITEM Navigation::FindService(UINT serviceId, ifc_omservice **serviceOut)
+{
+ if (NULL == hRoot || NULL == hLibrary)
+ {
+ if (NULL != serviceOut) *serviceOut = NULL;
+ return NULL;
+ }
+
+ WCHAR szBuffer[128] = {0};
+ NAVITEM item = {0};
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_TEXTINVARIANT | NIMF_PARAM;
+ item.pszInvariant = szBuffer;
+ item.cchInvariantMax = ARRAYSIZE(szBuffer);
+
+ item.hItem = hRoot;
+
+ while (NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item) &&
+ FALSE != Navigation_CheckInvariantName(item.pszInvariant))
+ {
+ ifc_omservice *service = (ifc_omservice*)item.lParam;
+ if (NULL != service && serviceId == service->GetId())
+ {
+ if (NULL != serviceOut)
+ {
+ *serviceOut = service;
+ service->AddRef();
+ }
+ return item.hItem;
+ }
+ }
+
+ item.hItem = (HNAVITEM)SENDMLIPC(hLibrary,
+ (item.hItem == hRoot) ? ML_IPC_NAVITEM_GETCHILD : ML_IPC_NAVITEM_GETNEXT,
+ (WPARAM)item.hItem);
+
+ }
+
+ if (NULL != serviceOut) *serviceOut = NULL;
+ return NULL;
+}
+
+HRESULT Navigation::ShowService(UINT serviceId, LPCWSTR pszUrl)
+{
+ ifc_omservice *service;
+ HNAVITEM hItem = FindService(serviceId, &service);
+ if (NULL == hItem) return E_FAIL;
+
+ HRESULT hr = SelectItemInt(hItem, serviceId, pszUrl);
+ service->Release();
+
+ return hr;
+}
+
+HNAVITEM Navigation::CreateItem(ifc_omservice *service, int altMode)
+{
+ if (NULL == hLibrary || NULL == hRoot) return NULL;
+ return CreateItemInt(hRoot, service, altMode);
+}
+
+HRESULT Navigation::GenerateServiceName(LPWSTR pszBuffer, INT cchBufferMax)
+{
+ if (NULL == pszBuffer) return E_POINTER;
+ *pszBuffer = L'\0';
+
+ if (NULL == hLibrary || NULL == hRoot) return E_UNEXPECTED;
+
+ if (FAILED(Plugin_CopyResString(pszBuffer, cchBufferMax, MAKEINTRESOURCE(IDS_USERSERVICE_NAME))))
+ return E_UNEXPECTED;
+
+ INT cchName = lstrlen(pszBuffer);
+ LPWSTR pszFormat = pszBuffer + cchName;
+ INT cchFormatMax = cchBufferMax - cchName;
+
+ WCHAR szText[512] = {0};
+ NAVITEM item = {0};
+
+ item.cbSize = sizeof(item);
+ item.mask = NIMF_TEXT;
+ item.pszText = szText;
+ item.cchTextMax = ARRAYSIZE(szText);
+
+ BOOL fFound = TRUE;
+
+ for(INT index = 1; FALSE != fFound; index++)
+ {
+ fFound = FALSE;
+ if (FAILED(StringCchPrintf(pszFormat, cchFormatMax, L" %d", index)))
+ {
+ pszFormat = L'\0';
+ return E_FAIL;
+ }
+
+ item.hItem = MLNavItem_GetChild(hLibrary, hRoot);
+ while(NULL != item.hItem)
+ {
+ if (FALSE != MLNavItem_GetInfo(hLibrary, &item) &&
+ CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, item.pszText, -1, pszBuffer, -1))
+ {
+ fFound = TRUE;
+ break;
+ }
+ item.hItem = MLNavItem_GetNext(hLibrary, item.hItem);
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT Navigation::CreateUserService(HNAVITEM *itemOut)
+{
+ HRESULT hr;
+
+ if (NULL != itemOut)
+ *itemOut = NULL;
+
+ if (NULL == hRoot) return E_FAIL;
+
+ INT serviceId = 710;
+ while(NULL != FindService(serviceId, NULL)) serviceId++;
+
+ WCHAR szName[256] = {0};
+ if (FAILED(GenerateServiceName(szName, ARRAYSIZE(szName))))
+ return E_FAIL;
+
+ ifc_omservice *service;
+ hr = ServiceHelper_Create(serviceId, szName, NULL, L"about:blank", SVCF_SUBSCRIBED | SVCF_PREAUTHORIZED, 2, TRUE, &service);
+
+ if (SUCCEEDED(hr))
+ {
+ HNAVITEM hItem = CreateItem(service, 1);
+ if (NULL == hItem)
+ {
+ hr = E_FAIL;
+ }
+ else
+ {
+ if (NULL != itemOut)
+ *itemOut = hItem;
+ }
+
+ service->Release();
+ }
+
+ return hr;
+}
+
+typedef struct __ICONPATCHREC
+{
+ LPCWSTR iconName;
+ LPCWSTR resourceName;
+} ICONPATCHREC;
+
+const static ICONPATCHREC szIconPatch[] =
+{
+ //{ L"11000", MAKEINTRESOURCEW(IDR_ICON_AOL)},
+ { L"11001", MAKEINTRESOURCEW(IDR_ICON_SHOUTCASTRADIO)},
+ /*{ L"11002", MAKEINTRESOURCEW(IDR_ICON_SHOUTCASTTV)},
+ { L"11003", MAKEINTRESOURCEW(IDR_ICON_WINAMPMUSIC)},
+ { L"11004", MAKEINTRESOURCEW(IDR_ICON_SINGINGFISH)},
+ { L"11005", MAKEINTRESOURCEW(IDR_ICON_MUSICNOW)},
+ { L"11006", MAKEINTRESOURCEW(IDR_ICON_AOL_GAMES)},
+ { L"11007", MAKEINTRESOURCEW(IDR_ICON_IN2TV)},
+ { L"11008", MAKEINTRESOURCEW(IDR_ICON_WINAMREMOTE)},*/
+};
+
+HRESULT Navigation::PatchIconName(LPWSTR pszIcon, UINT cchMaxIconLen, ifc_omservice *service)
+{
+ if (NULL == pszIcon) return E_INVALIDARG;
+ if (L'\0' == *pszIcon) return S_FALSE;
+
+ for(INT i = 0; i < ARRAYSIZE(szIconPatch); i++)
+ {
+ if (CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, szIconPatch[i].iconName, -1, pszIcon, -1))
+ {
+ if (IS_INTRESOURCE(szIconPatch[i].resourceName))
+ {
+ return Plugin_MakeResourcePath(pszIcon, cchMaxIconLen, RT_RCDATA, szIconPatch[i].resourceName, RESPATH_COMPACT);
+ }
+ return StringCchCopy(pszIcon, cchMaxIconLen, szIconPatch[i].resourceName);
+ }
+ }
+
+ if (FALSE == PathIsURL(pszIcon) && FALSE != PathIsRelative(pszIcon))
+ {
+ WCHAR szTemp[2048] = {0};
+ HRESULT hr = StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszIcon);
+ if (SUCCEEDED(hr))
+ {
+ ServiceHost *serviceHost;
+ hr = ServiceHost::GetCachedInstance(&serviceHost);
+ if (SUCCEEDED(hr))
+ {
+ WCHAR szBase[MAX_PATH] = {0};
+ if (SUCCEEDED(serviceHost->GetBasePath(service, szBase, ARRAYSIZE(szBase))))
+ {
+ if (L'\0' != szBase[0] && FALSE != PathIsRelative(szBase))
+ {
+ LPCWSTR pszUser = (NULL != WASABI_API_APP) ? WASABI_API_APP->path_getUserSettingsPath() : NULL;
+ if (NULL != pszUser)
+ {
+ StringCchCopy(pszIcon, cchMaxIconLen, pszUser);
+ }
+ PathAppend(pszIcon, szBase);
+ }
+ else
+ {
+ StringCchCopy(pszIcon, cchMaxIconLen, szBase);
+ }
+
+ PathAppend(pszIcon, szTemp);
+ }
+ serviceHost->Release();
+ }
+ }
+ if (FAILED(hr))
+ return hr;
+ }
+
+ return S_FALSE;
+}
+
+HNAVITEM Navigation::CreateItemInt(HNAVITEM hParent, ifc_omservice *service, int altMode)
+{
+ if (!altMode && (S_OK != ServiceHelper_IsSubscribed(service)))
+ return NULL;
+
+ WCHAR szName[256] = {0}, szInvariant[64] = {0};
+ if (FAILED(service->GetName(szName, ARRAYSIZE(szName))))
+ return NULL;
+
+ if (L'\0' == szName[0])
+ WASABI_API_LNGSTRINGW_BUF(IDS_DEFAULT_SERVICENAME, szName, ARRAYSIZE(szName));
+
+ if (FAILED(StringCchPrintf(szInvariant, ARRAYSIZE(szInvariant), NAVITEM_PREFIX L"%u", service->GetId())))
+ return NULL;
+
+ NAVINSERTSTRUCT nis = {0};
+ nis.hInsertAfter = NULL;
+ nis.hParent = hParent;
+
+ INT iIcon = -1;
+ ifc_mlnavigationhelper *navHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ WCHAR szIcon[2048] = {0};
+ if (FAILED(service->GetIcon(szIcon, ARRAYSIZE(szIcon))) ||
+ FAILED(PatchIconName(szIcon, ARRAYSIZE(szIcon), service)) ||
+ FAILED(navHelper->QueryIndex(szIcon, &iIcon, NULL)))
+ {
+ iIcon = -1;
+ }
+ navHelper->Release();
+ }
+
+ nis.item.cbSize = sizeof(NAVITEM);
+ nis.item.mask = NIMF_TEXT | NIMF_STYLE | NIMF_TEXTINVARIANT | NIMF_PARAM | NIMF_IMAGE | NIMF_IMAGESEL;
+
+ nis.item.id = 0;
+ nis.item.pszText = szName;
+ nis.item.pszInvariant = szInvariant;
+ nis.item.lParam = (LPARAM)service;
+
+ nis.item.style = 0;
+ UINT serviceFlags;
+ if (FAILED(service->GetFlags(&serviceFlags)))
+ serviceFlags = 0;
+
+ if (0 != (SVCF_SPECIAL & serviceFlags))
+ {
+ nis.item.style |= (NIS_HASCHILDREN | NIS_ALLOWCHILDMOVE);
+ iIcon = -1;
+ }
+
+ nis.item.styleMask = nis.item.style;
+
+ nis.item.iImage = iIcon;
+ nis.item.iSelectedImage = iIcon;
+
+
+ HNAVITEM hItem = MLNavCtrl_InsertItem(hLibrary, &nis);
+ if (NULL != hItem)
+ {
+ ServiceHost *serviceHost;
+ if (SUCCEEDED(ServiceHost::GetCachedInstance(&serviceHost)))
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEventMngr, (void**)&eventManager)))
+ {
+ eventManager->RegisterHandler(serviceHost);
+ eventManager->Release();
+ }
+
+ serviceHost->Release();
+ }
+
+ service->AddRef();
+ }
+
+ return hItem;
+}
+
+HRESULT Navigation::SelectItemInt(HNAVITEM hItem, UINT serviceId, LPCWSTR pszUrl)
+{
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ if (NULL != pszUrl && L'\0' != *pszUrl)
+ {
+ HRESULT hr = forceUrl.Set(serviceId, pszUrl);
+ if (FAILED(hr)) return hr;
+ }
+ else
+ {
+ forceUrl.Remove(serviceId);
+ }
+
+ if (FALSE == MLNavItem_Select(hLibrary, hItem))
+ {
+ forceUrl.Remove(serviceId);
+ return E_FAIL;
+ }
+
+ return S_OK;
+
+}
+HNAVITEM Navigation::GetMessageItem(INT msg, INT_PTR param1)
+{
+ return (msg < ML_MSG_NAVIGATION_FIRST) ?
+ MLNavCtrl_FindItemById(hLibrary, param1) :
+ (HNAVITEM)param1;
+}
+
+HRESULT Navigation::OnCreateView(HNAVITEM hItem, HWND hParent, HWND *hView)
+{
+ if (NULL == hView) return E_POINTER;
+ *hView = NULL;
+
+ if (NULL == hLibrary) return E_UNEXPECTED;
+ if (NULL == hItem || NULL == hParent) return E_INVALIDARG;
+
+ HRESULT hr;
+
+ ifc_omservice *service;
+ hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ hr = InitializeBrowser();
+ if (SUCCEEDED(hr))
+ {
+ LPWSTR pszUrl;
+ if (S_OK != forceUrl.Peek(service->GetId(), &pszUrl))
+ pszUrl = NULL;
+
+ hr = OMBROWSERMNGR->CreateView(service, hParent, pszUrl, 0, hView);
+ forceUrl.FreeString(pszUrl);
+ }
+
+ service->Release();
+ }
+ return hr;
+}
+
+HRESULT Navigation::OnContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts)
+{
+ if (NULL == hItem || NULL == hHost)
+ return E_INVALIDARG;
+
+ HWND hLibrary = Plugin_GetLibrary();
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ HRESULT hr;
+ ifc_omservice *service, *activeService;
+ hr = GetService(hItem, &service);
+ if (FAILED(hr)) return hr;
+
+ POINT pt;
+ POINTSTOPOINT(pt, pts);
+ if (-1 == pt.x || -1 == pt.y)
+ {
+ NAVITEMGETRECT itemRect;
+ itemRect.fItem = FALSE;
+ itemRect.hItem = hItem;
+ if (MLNavItem_GetRect(hLibrary, &itemRect))
+ {
+ MapWindowPoints(hHost, HWND_DESKTOP, (POINT*)&itemRect.rc, 2);
+ pt.x = itemRect.rc.left + 2;
+ pt.y = itemRect.rc.top + 2;
+ }
+ }
+
+ HWND activeView = GetActiveView(&activeService);
+ if (NULL == activeView) activeService = NULL;
+
+ INT menuKind = (hItem == hRoot) ? OMMENU_GALERYCONTEXT : OMMENU_SERVICECONTEXT;
+
+ UINT menuFlags = MCF_VIEW;
+ if (NULL != activeService && activeService->GetId() == service->GetId())
+ menuFlags |= MCF_VIEWACTIVE;
+
+ UINT rating;
+ if (OMMENU_SERVICECONTEXT == menuKind && SUCCEEDED(service->GetRating(&rating)))
+ menuFlags |= RATINGTOMCF(rating);
+
+ HMENU hMenu = Menu_GetMenu(menuKind, menuFlags);
+ if (NULL != hMenu)
+ {
+ INT commandId = DoTrackPopup(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
+ pt.x, pt.y, hHost, NULL);
+
+ Menu_ReleaseMenu(hMenu, menuKind);
+
+ if (0 != commandId && FALSE != Command_ProcessService(activeView, service, commandId, NULL))
+ commandId = 0;
+
+ if (0 != commandId && NULL != activeView && FALSE != Command_ProcessView(activeView, commandId, NULL))
+ commandId = 0;
+
+ if (0 != commandId && FALSE != Command_ProcessGeneral(commandId, NULL))
+ commandId = 0;
+ }
+
+ if (NULL != activeService)
+ activeService->Release();
+
+ service->Release();
+
+ return hr;
+}
+
+HRESULT Navigation::OnEndTitleEdit(HNAVITEM hItem, LPCWSTR pszNewTitle)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ HRESULT hr;
+
+ ifc_omservice *service;
+ hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ if (NULL != pszNewTitle)
+ {
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ hr = editor->SetName(pszNewTitle, FALSE);
+ editor->Release();
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ ServiceHelper_Save(service);
+
+ service->Release();
+ }
+ return hr;
+}
+
+HRESULT Navigation::OnDeleteItem(HNAVITEM hItem)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ WCHAR szBuffer[2048] = {0};
+ NAVITEM itemInfo = {0};
+
+ itemInfo.cbSize = sizeof(itemInfo);
+ itemInfo.hItem = hItem;
+ itemInfo.pszInvariant = szBuffer;
+ itemInfo.cchInvariantMax = ARRAYSIZE(szBuffer);
+ itemInfo.mask = NIMF_PARAM | NIMF_TEXTINVARIANT | NIMF_IMAGE;
+
+ if (FALSE == MLNavItem_GetInfo(hLibrary, &itemInfo))
+ return E_FAIL;
+ if (FALSE == Navigation_CheckInvariantName(szBuffer))
+ return E_NAVITEM_UNKNOWN;
+
+ ifc_omservice *service = (ifc_omservice*)itemInfo.lParam;
+
+ if (NULL != service)
+ {
+ if (SUCCEEDED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer))))
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->ReleaseIndex(szBuffer);
+ navHelper->Release();
+ }
+ }
+
+ ifc_ombrowserwndmngr *windowManager;
+ if (NULL != OMBROWSERMNGR && SUCCEEDED(OMBROWSERMNGR->QueryInterface(IFC_OmBrowserWindowManager, (void**)&windowManager)))
+ {
+ UINT serviceId = service->GetId();
+
+ ifc_ombrowserwndenum *windowEnum;
+ if (SUCCEEDED(windowManager->Enumerate(NULL, &serviceId, &windowEnum)))
+ {
+ HWND hwnd;
+ while (S_OK == windowEnum->Next(1, &hwnd, NULL))
+ {
+ DestroyWindow(hwnd);
+ }
+ windowEnum->Release();
+ }
+
+ windowManager->Release();
+ }
+
+ ServiceHost *serviceHost;
+ if (SUCCEEDED(ServiceHost::GetCachedInstance(&serviceHost)))
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEventMngr, (void**)&eventManager)))
+ {
+ eventManager->UnregisterHandler(serviceHost);
+ eventManager->Release();
+ }
+ serviceHost->Release();
+ }
+
+ itemInfo.mask = NIMF_PARAM;
+ itemInfo.lParam = 0L;
+ MLNavItem_SetInfo(hLibrary, &itemInfo);
+
+ service->Release();
+ }
+
+
+
+ return S_OK;
+}
+
+HRESULT Navigation::OnKeyDown(HNAVITEM hItem, NMTVKEYDOWN *pnmkd)
+{
+ if (NULL == hItem) return E_INVALIDARG;
+ if (NULL == hLibrary) return E_UNEXPECTED;
+
+ ifc_omservice *service;
+ HRESULT hr = GetService(hItem, &service);
+ if (SUCCEEDED(hr))
+ {
+ switch(pnmkd->wVKey)
+ {
+ case VK_DELETE:
+ {
+ BOOL fSuccess;
+ ifc_omservice *activeService;
+ HWND activeView = GetActiveView(&activeService);
+ if (IsWindow(activeView))
+ {
+ Command_ProcessService(activeView, service, ID_SERVICE_UNSUBSCRIBE, &fSuccess);
+ }
+ }
+ break;
+ }
+
+ service->Release();
+ }
+ return hr;
+}
+
+HRESULT Navigation::OnControlDestroy()
+{
+ SaveOrder();
+ return S_OK;
+}
+
+#define CBCLASS Navigation
+START_DISPATCH;
+CB(ADDREF, AddRef)
+CB(RELEASE, Release)
+CB(QUERYINTERFACE, QueryInterface)
+VCB(API_IMAGECHANGED, ImageChanged)
+END_DISPATCH;
+#undef CBCLASS \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/navigation.h b/Src/Plugins/Library/ml_online/navigation.h
new file mode 100644
index 00000000..c25682df
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/navigation.h
@@ -0,0 +1,96 @@
+#ifndef NULLOSFT_ONLINEMEDIA_PLUGIN_NAVIGATION_HEADER
+#define NULLOSFT_ONLINEMEDIA_PLUGIN_NAVIGATION_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+#include <ifc_mlnavigationcallback.h>
+#include "./forceUrl.h"
+#include <vector>
+
+class ifc_omservice;
+class forceUrl;
+typedef LPVOID HNAVITEM;
+
+#define ROOTSERVICE_ID 777
+
+typedef void (CALLBACK *NavigationCallback)(Navigation* /*instance*/, ULONG_PTR /*param*/);
+
+class Navigation : public ifc_mlnavigationcallback
+{
+protected:
+ Navigation();
+ ~Navigation();
+
+public:
+ static HRESULT CreateInstance(Navigation **instance);
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_mlnavigationcallback */
+ void ImageChanged(LPCWSTR pszName, INT index);
+
+public:
+ HRESULT Finish();
+
+ BOOL ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result);
+
+ HNAVITEM GetActive(ifc_omservice **serviceOut);
+ HWND GetActiveView(ifc_omservice **serviceOut);
+
+ HRESULT SelectItem(HNAVITEM hItem, LPCWSTR pszUrl);
+ HRESULT DeleteItem(HNAVITEM hItem);
+ HRESULT DeleteAll();
+
+ HRESULT GenerateServiceName(LPWSTR pszBuffer, INT cchBufferMax);
+
+ HRESULT CreatePopup(HNAVITEM hItem, HWND *hwnd);
+
+ HRESULT GetService(HNAVITEM hItem, ifc_omservice **service);
+ HRESULT UpdateService(ifc_omservice *service, UINT modifiedFlags);
+ HNAVITEM FindService(UINT serviceId, ifc_omservice **serviceOut);
+ HRESULT ShowService(UINT serviceId, LPCWSTR pszUrl);
+
+ HNAVITEM CreateItem(ifc_omservice *service, int altMode = 0);
+ HRESULT CreateItemAsync(ifc_omservice *service);
+ HRESULT CreateUserService(HNAVITEM *itemOut);
+
+protected:
+ HRESULT Initialize();
+ HRESULT InitializeBrowser();
+ HRESULT SaveOrder();
+ HRESULT Order(std::vector<ifc_omservice *> &list);
+
+ HNAVITEM GetMessageItem(INT msg, INT_PTR param1);
+ HNAVITEM CreateItemInt(HNAVITEM hParent, ifc_omservice *service, int altMode = 0);
+ HRESULT SelectItemInt(HNAVITEM hItem, UINT serviceId, LPCWSTR pszUrl);
+
+ HRESULT PatchIconName(LPWSTR pszIcon, UINT cchMaxIconLen, ifc_omservice *service);
+
+ HRESULT OnCreateView(HNAVITEM hItem, HWND hParent, HWND *hView);
+ HRESULT OnContextMenu(HNAVITEM hItem, HWND hHost, POINTS pts);
+ HRESULT OnEndTitleEdit(HNAVITEM hItem, LPCWSTR pszNewTitle);
+ HRESULT OnDeleteItem(HNAVITEM hItem);
+ HRESULT OnKeyDown(HNAVITEM hItem, NMTVKEYDOWN *pnmkd);
+ HRESULT OnControlDestroy();
+
+ HRESULT PostMainThreadCallback(NavigationCallback callback, ULONG_PTR param);
+
+protected:
+ size_t ref;
+ HNAVITEM hRoot;
+ HWND hLibrary;
+ UINT cookie;
+ ForceUrl forceUrl;
+
+protected:
+ RECVS_DISPATCH;
+};
+
+#endif // NULLOSFT_ONLINEMEDIA_PLUGIN_NAVIGATION_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/png.rc b/Src/Plugins/Library/ml_online/png.rc
new file mode 100644
index 00000000..a1468d05
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/png.rc
@@ -0,0 +1,27 @@
+#include "resource.h"
+/////////////////////////////////////////////////////////////////////////////
+//
+// Data
+//
+IDR_SERVICE64X64_IMAGE RCDATA
+".\\resources\\service64x64.png"
+IDR_ICON_DEFAULT RCDATA
+".\\resources\\iconDefault.png"
+//IDR_ICON_AOL RCDATA
+//".\\resources\\iconAol.png"
+//IDR_ICON_AOL_GAMES RCDATA
+//".\\resources\\iconAolGames.png"
+//IDR_ICON_IN2TV RCDATA
+//".\\resources\\iconIn2Tv.png"
+//IDR_ICON_MUSICNOW RCDATA
+//".\\resources\\iconMusicNow.png"
+IDR_ICON_SHOUTCASTRADIO RCDATA
+".\\resources\\iconShoutcastRadio.png"
+//IDR_ICON_SHOUTCASTTV RCDATA
+//".\\resources\\iconShoutcastTv.png"
+//IDR_ICON_SINGINGFISH RCDATA
+//".\\resources\\iconSingingfish.png"
+//IDR_ICON_WINAMPMUSIC RCDATA
+//".\\resources\\iconWaMusic.png"
+//IDR_ICON_WINAMREMOTE RCDATA
+//".\\resources\\iconWaRemote.png"
diff --git a/Src/Plugins/Library/ml_online/resource.h b/Src/Plugins/Library/ml_online/resource.h
new file mode 100644
index 00000000..db9fbbde
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resource.h
@@ -0,0 +1,206 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ml_online.rc
+//
+#define IDS_NULLSOFT_ONLINE_SERVICES 0
+#define IDS_ONLINE_SERVICES 1
+#define IDOK 1
+#define IDS_NO_INTERNET_CONNECTION 2
+#define IDCANCEL 2
+#define IDS_SORRY 3
+#define IDR_CONTEXTMENU 4
+#define IDD_MAINDIALOG 101
+#define IDD_OMPREF 102
+#define IDD_OPENURL 103
+#define IDS_USERSERVICE_NAME 104
+#define IDS_FILEFILTER_ALL 105
+#define IDS_FILEFILTER_ALLKNOWN 106
+#define IDS_IMPORT_FILES 109
+#define IDS_DEFAULT_SERVICENAME 148
+#define IDS_MORE 151
+#define IDS_HOME 152
+#define IDS_HOME_DESCRIPTION 153
+#define IDS_BACK 154
+#define IDS_BACK_DESCRIPTION 155
+#define IDS_FORWARD 156
+#define IDS_FORWARD_DESCRIPTION 157
+#define IDS_REFRESH 158
+#define IDS_REFRESH_DESCRIPTION 159
+#define IDS_STOP 160
+#define IDS_STOP_DESCRIPTION 161
+#define IDS_SEPARATOR 162
+#define IDS_SPACE 163
+#define IDS_FLEXSPACE 164
+#define IDS_MORE_DESCRIPTION 165
+#define IDS_RATED 166
+#define IDS_PLEASE_WAIT 167
+#define IDS_SERVICE_CHECKINGVERSION 168
+#define IDS_SERVICE_GETINFO 169
+#define IDS_SERVICE_GETINFO_DESCRIPTION 170
+#define IDS_SERVICE_REPORT 171
+#define IDS_SERVICE_REPORT_DESCRIPTION 172
+#define IDS_SERVICE_UNSUBSCRIBE 173
+#define IDS_SERVICE_UNSUBSCRIBE_DESCRIPTION 174
+#define IDS_RATING_0 175
+#define IDS_RATING_1 176
+#define IDS_RATING_2 177
+#define IDS_RATING_3 178
+#define IDS_RATING_4 179
+#define IDS_RATING_5 180
+#define IDS_RATING_CHANGETO 181
+#define IDS_RATING_CURRENT 183
+#define IDD_MESSAGEBOX 186
+#define IDD_SETUPPAGE 187
+#define IDS_SETUPPAGE_TITLE 188
+#define IDS_SETUP_EMPTYGROUP 189
+#define IDS_SETUP_LOADINGGROUP 190
+#define IDS_SERVICEGROUP_FEATURED 191
+#define IDS_SERVICEGROUP_KNOWN 192
+#define IDD_SETUP_SERVICEDETAILS 192
+#define IDS_SETUP_GROUPLOADFAILED 193
+#define IDS_SERVICE_BYAUTHOR 194
+#define IDS_SERVICE_LASTUPDATED 195
+#define IDD_SETUP_GROUPDETAILS 196
+#define IDS_SERVICEGROUP_FEATUREDLONG 197
+#define IDS_SERVICEGROUP_KNOWNLONG 198
+#define IDS_SERVICEGROUP_FEATURED_DESC 199
+#define IDS_SERVICEGROUP_KNOWN_DESC 200
+#define IDS_MESSAGEBOX_UNSUBSCRIBE 201
+#define IDS_MESSAGEBOX_UNSUBSCRIBE_CAPTION 202
+#define IDS_MESSAGEBOX_RESETTODEFAULT 203
+#define IDS_MESSAGEBOX_RESETTODEFAULT_CAPTION 204
+#define IDS_DOWNLOADSERVICE_JOB 205
+#define IDS_SAVESERVICE_JOB 206
+#define IDS_CACHEICONS_JOB 207
+#define IDS_REGISTERINGSERVICE_JOB 210
+#define IDS_MESSAGEBOX_NAMECHANGED_CAPTION 212
+#define IDS_MESSAGEBOX_NAMECHANGED 213
+#define IDR_SETUPMENU 213
+#define IDS_MESSAGEBOX_POLICYRESET_CAPTION 214
+#define IDS_MESSAGEBOX_POLICYRESET 215
+#define IDS_COLLAPSE 216
+#define IDS_EXPAND 217
+#define IDS_SECURE_CONNECTION 219
+#define IDS_SCRIPT_ERROR 220
+#define IDS_SCRIPT_ERROR_DESCRIPTION 221
+#define IDS_ENCRYPTION_UNSECURE 222
+#define IDS_ENCRYPTION_MIXED 223
+#define IDS_ENCRYPTION_40BIT 224
+#define IDS_ENCRYPTION_56BIT 225
+#define IDS_ENCRYPTION_FORTEZZA 226
+#define IDS_ENCRYPTION_128BIT 227
+#define IDS_CONNECTION_UNSECURE 229
+#define IDS_CONNECTION_ENCRYPTED 230
+#define IDR_HTML1 230
+#define IDS_NAVIGATING 231
+#define IDS_MESSAGEBOX_FORCEREMOVE 232
+#define IDS_MESSAGEBOX_FORCEREMOVE_CAPTION 233
+#define IDS_HISTORY 234
+#define IDS_HISTORY_DESCRIPTION 235
+#define IDS_CURRENT_PAGE 236
+#define IDR_BROWSERACCEL 237
+#define IDS_READMORE 238
+#define IDS_CLICKHERE 239
+#define IDS_MESSAGEBOX_DISCOVERBUSY 240
+#define IDS_MESSAGEBOX_DISCOVERBUSY_CAPTION 241
+#define IDR_HTML_EDITOR 243
+#define IDC_ADDRESS 1000
+#define IDC_REFRESH 1007
+#define IDC_RADIO_DAILY 1035
+#define IDC_RADIO_WEEKLY 1036
+#define IDC_RADIO_NEVER 1037
+#define IDC_AUTOSIZE 1052
+#define IDC_RADIO_HOURLY 1060
+#define IDC_NOWPLAYINGURL 1069
+#define IDC_RADIO_MAXBW 1071
+#define IDC_RADIO_MINBW 1072
+#define IDC_BACK 1310
+#define IDC_FORWARD 1311
+#define IDC_TEXT 1311
+#define IDC_HOME 1312
+#define IDC_SERVICELIST 1312
+#define IDC_STOP 1313
+#define IDC_LABEL_HEADER 1313
+#define IDC_PLACEHOLDER 1315
+#define IDC_DESCRIPTION 1316
+#define IDC_TITLE 1317
+#define IDC_THUMBNAIL 1318
+#define IDC_SERVICEMETA 1319
+#define IDC_STATUS 1321
+#define IDC_BUTTON1 1322
+#define IDC_HELPTEXT 1323
+#define IDR_CURTAINPROGRESS_IMAGE 20000
+#define IDR_TOOLBARLARGE_IMAGE 20001
+#define IDR_TOOLBARPROGRESS_IMAGE 20002
+#define IDR_SERVICE64X64_IMAGE 20003
+#define IDR_ICON_DEFAULT 20010
+#define IDR_ICON_AOL 20011
+#define IDR_ICON_AOL_GAMES 20012
+#define IDR_ICON_IN2TV 20013
+#define IDR_ICON_MUSICNOW 20014
+#define IDR_ICON_SHOUTCASTRADIO 20015
+#define IDR_ICON_SHOUTCASTTV 20016
+#define IDR_ICON_SINGINGFISH 20017
+#define IDR_ICON_WINAMPMUSIC 20018
+#define IDR_ICON_WINAMREMOTE 20019
+#define IDR_MENUARROW_IMAGE 20020
+#define ID_RATING_VALUE_5 40106
+#define ID_RATING_VALUE_4 40107
+#define ID_RATING_VALUE_3 40108
+#define ID_RATING_VALUE_2 40109
+#define ID_RATING_VALUE_1 40110
+#define ID_SERVICE_REPORT 40112
+#define ID_SERVICE_UNSUBSCRIBE 40113
+#define ID_SERVICE_GETINFO 40114
+#define ID_GALERY_OPENVIEW 40119
+#define ID_PLUGIN_PREFERENCES 40123
+#define ID_VIEW_OPEN 40127
+#define ID_NAVIGATION_HOME 40134
+#define ID_NAVIGATION_BACK 40135
+#define ID_NAVIGATION_FORWARD 40136
+#define ID_NAVIGATION_STOP 40138
+#define ID_SERVICEMANAGER_RESET 40141
+#define ID_NAVIGATION_REFRESH 40142
+#define ID_NAVIGATION_HISTORY 40143
+#define ID_TOOLBAR_DOCKTOP 40147
+#define ID_TOOLBAR_DOCKBOTTOM 40148
+#define ID_TOOLBAR_AUTOHIDE 40149
+#define ID_TOOLBAR_TABSTOP 40151
+#define ID_SERVICE_RESETPOLICY 40154
+#define ID_GROUP_COLLAPSE 40161
+#define ID_GROUP_SELECTALL 40162
+#define ID_GROUP_UNSELECTALL 40163
+#define ID_GROUP_TOGGLE 40164
+#define ID_GROUP_RELOAD 40166
+#define ID_BROWSER_SECURECONNECTION 40167
+#define ID_BROWSER_SCRIPTERROR 40168
+#define ID_WINDOW_FULLSCREEN 40169
+#define ID_WINDOW_CLOSE 40170
+#define ID_NAVIGATION_REFRESH_COMPLETELY 40171
+#define ID_VIEW_OPENINNEWWINDOW 40174
+#define ID_VIEW_OPENPOPUP 40175
+#define ID_GALERYCONTEXTMENU_HELP 40176
+#define ID_PLUGIN_HELP 40177
+#define ID_SERVICE_NEW 40178
+#define ID_SERVICE_IMPORT_FILE 40179
+#define ID_SERVICE_IMPORT_URL 40180
+#define ID_SERVICE_EDIT 40181
+#define ID_SERVICE_RELOAD 40182
+#define ID_SERVICE_RESETPERMISSIONS 40183
+#define ID_SERVICE_DELETE 40184
+#define ID_SERVICE_DELETEALL 40185
+#define ID_SERVICE_LOCATE 40186
+#define ID_SERVICE_EDITEXTERNAL 40187
+#define ID_OMBROWSER_OPTIONS 40188
+#define IDS_PLUGIN_NAME 65534
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 244
+#define _APS_NEXT_COMMAND_VALUE 40189
+#define _APS_NEXT_CONTROL_VALUE 1324
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Src/Plugins/Library/ml_online/resources/iconAol.png b/Src/Plugins/Library/ml_online/resources/iconAol.png
new file mode 100644
index 00000000..c48a9cda
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconAol.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconAolGames.png b/Src/Plugins/Library/ml_online/resources/iconAolGames.png
new file mode 100644
index 00000000..d5dce983
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconAolGames.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconDefault.png b/Src/Plugins/Library/ml_online/resources/iconDefault.png
new file mode 100644
index 00000000..6a2d99e6
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconDefault.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconIn2Tv.png b/Src/Plugins/Library/ml_online/resources/iconIn2Tv.png
new file mode 100644
index 00000000..5df2581b
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconIn2Tv.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconMusicNow.png b/Src/Plugins/Library/ml_online/resources/iconMusicNow.png
new file mode 100644
index 00000000..ca645c9e
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconMusicNow.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconShoutcastRadio.png b/Src/Plugins/Library/ml_online/resources/iconShoutcastRadio.png
new file mode 100644
index 00000000..72ea4648
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconShoutcastRadio.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconShoutcastTv.png b/Src/Plugins/Library/ml_online/resources/iconShoutcastTv.png
new file mode 100644
index 00000000..85fcfbca
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconShoutcastTv.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconSingingfish.png b/Src/Plugins/Library/ml_online/resources/iconSingingfish.png
new file mode 100644
index 00000000..a3b7c57f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconSingingfish.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconWaMusic.png b/Src/Plugins/Library/ml_online/resources/iconWaMusic.png
new file mode 100644
index 00000000..32ea227a
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconWaMusic.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/iconWaRemote.png b/Src/Plugins/Library/ml_online/resources/iconWaRemote.png
new file mode 100644
index 00000000..70143fe1
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/iconWaRemote.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/resources/pages/serviceEditor.htm b/Src/Plugins/Library/ml_online/resources/pages/serviceEditor.htm
new file mode 100644
index 00000000..ea7c3bf2
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/pages/serviceEditor.htm
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Online Services Editor</title>
+ <meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema"/>
+ <meta content="True" name="vs_snapToGrid"/>
+ <meta content="False" name="vs_showGrid"/>
+ <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
+ <script src="webdev.js" type="text/javascript"></script>
+ <style type="text/css">#Checkbox1 {width: 321px; top: 160px; left: 64px;}</style>
+</head>
+<body style="overflow-y: auto; overflow-x: auto; overflow: visible; FONT-FAMILY: MS Shell Dlg 2;" onload="WebDevEditor_Init();">
+ <h2 align="center">Online Services Editor</h2>
+ <div align="left" nowrap>
+ <div id="DIV1" style="border: 1px solid gray; clear: both; DISPLAY: block; left: 50%; float: none; visibility: visible; margin-left: -204px; overflow: visible; width: 408px; color: black; position: absolute; height: 150px; background-color: dimgray; top: 126px;" align="center" nowrap ms_positioning="GridLayout">
+ <div style="z-index: 110; left: 16px; position: absolute; top: 14px">Id:</div>
+ <input id="scvedt_edt_id" style="z-index: 101; left: 64px; position: absolute; top: 14px" readonly maxlength="16" size="9" tabindex="1"/>
+ <div style="z-index: 102; left: 16px; position: absolute; top: 46px">Name:</div>
+ <input id="scvedt_edt_name" style="z-index: 103; left: 64px; position: absolute; top: 46px; width: 322px;" maxlength="512" size="48" tabindex="2"/>
+ <div style="z-index: 104; left: 16px; position: absolute; top: 78px">Url:</div>
+ <input id="scvedt_edt_url" style="z-index: 105; left: 64px; position: absolute; top: 78px; width: 321px;" maxlength="2048" size="48" name="Text1" tabindex="3"/>
+ <div style="z-index: 106; left: 16px; position: absolute; top: 110px">Icon:</div>
+ <input id="scvedt_edt_icon" style="z-index: 107; left: 64px; position: absolute; top: 110px; width: 321px;" maxlength="512" size="48" name="Text2" tabindex="4"/>
+ <div align = "left" style="z-index: 108; position: absolute; width: 320px; left: 25px;" >
+ <input id="svcedt_btn_save" style="z-index: 109; left: 200px; width: 88px; position: absolute; top: 180px; height: 24px; right: 118px;" onclick="WebDevEditor_Save();" type="submit" value="Save" name="buttonOk" tabindex="6"/>
+ <input id="svcedt_btn_close" style="z-index: 110; left: 297px; width: 88px; position: absolute; top: 180px; height: 24px" onclick="WebDevEditor_Close();" type="button" value="Cancel" name="buttonCancel" tabindex="7"/>
+ </div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/resources/pages/webdev.js b/Src/Plugins/Library/ml_online/resources/pages/webdev.js
new file mode 100644
index 00000000..96eb0ab9
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/pages/webdev.js
@@ -0,0 +1,95 @@
+function GetUrlParam(name)
+{
+ name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
+ var regexS = "[\\?&]"+name+"=([^&#]*)";
+ var regex = new RegExp( regexS );
+ var results = regex.exec( window.location.href );
+ if( results == null )
+ return "";
+ else
+ return results[1];
+}
+
+function WebDev_OpenService(serviceId, forceUrl)
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else if (false == window.external.WebDev.serviceOpen(serviceId, forceUrl))
+ alert("Unable to open service");
+}
+function WebDev_OpenDocumentation()
+{
+ WebDev_OpenService(701, null);
+}
+
+function WebDev_OpenJSAPI2Test()
+{
+ WebDev_OpenService(702, null);
+}
+
+function WebDev_CreateService()
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else if (false == window.external.WebDev.serviceCreate())
+ alert("Unable to create service");
+}
+
+function WebDevEditor_Init(url)
+{
+ var serviceId = parseInt(GetUrlParam("serviceId"), 10);
+
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else
+ {
+ var info = window.external.WebDev.serviceGetInfo(serviceId);
+ if (null == info)
+ {
+ alert("Unable to get service information");
+ }
+ else
+ {
+ document.getElementById("scvedt_edt_id").value = info.id;
+ document.getElementById("scvedt_edt_name").value = info.name;
+ document.getElementById("scvedt_edt_url").value = info.url;
+ document.getElementById("scvedt_edt_icon").value = info.icon;
+ }
+ }
+}
+
+function WebDevEditor_Save()
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else
+ {
+ var serviceId = parseInt(document.getElementById("scvedt_edt_id").value, 10);
+ if (0 != serviceId)
+ {
+ var serviceName = document.getElementById("scvedt_edt_name").value;
+ var serviceUrl = document.getElementById("scvedt_edt_url").value;
+ var serviceIcon = document.getElementById("scvedt_edt_icon").value;
+ if (false == window.external.WebDev.serviceSetInfo(serviceId, serviceName, serviceIcon, serviceUrl, true))
+ {
+ alert("Unable to set service info");
+ }
+ if (false == window.external.WebDev.serviceOpen(serviceId, 1))
+ {
+ alert("Unable to navigate");
+ }
+ }
+ }
+}
+
+function WebDevEditor_Close()
+{
+ if (typeof(window.external.WebDev) == "undefined")
+ alert("Cannot access Webdev Api");
+ else
+ {
+ var serviceId = parseInt(document.getElementById("scvedt_edt_id").value, 10);
+ if (0 == serviceId || false == window.external.WebDev.serviceOpen(serviceId, 1))
+ alert("Unable to navigate");
+ }
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/resources/service64x64.png b/Src/Plugins/Library/ml_online/resources/service64x64.png
new file mode 100644
index 00000000..8aaea002
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/resources/service64x64.png
Binary files differ
diff --git a/Src/Plugins/Library/ml_online/serviceHelper.cpp b/Src/Plugins/Library/ml_online/serviceHelper.cpp
new file mode 100644
index 00000000..e2f24bea
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/serviceHelper.cpp
@@ -0,0 +1,1232 @@
+#include "main.h"
+#include "./serviceHelper.h"
+#include "./navigation.h"
+#include "./api__ml_online.h"
+#include "./resource.h"
+#include "./serviceHost.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omfilestorage.h>
+#include <ifc_omwebstorage.h>
+#include <ifc_omserviceeditor.h>
+#include <ifc_omserviceeventmngr.h>
+#include <ifc_omservicecommand.h>
+#include <ifc_omserviceenum.h>
+#include <ifc_omxmlserviceenum.h>
+#include <ifc_omservicecopier.h>
+#include <ifc_mlnavigationhelper.h>
+#include <browserUiCommon.h>
+
+#include <vector>
+
+#include <wininet.h>
+#include <strsafe.h>
+
+typedef std::vector<ifc_omstorageasync*> AsyncList;
+
+struct SERVICEHELPER
+{
+ SERVICEHELPER()
+ : discoverAsync(NULL)
+ {}
+
+ ifc_omstorageasync *discoverAsync;
+ AsyncList versionChecks;
+ CRITICAL_SECTION lock;
+};
+
+static SERVICEHELPER *serviceHelper = NULL;
+
+static void ServiceHelper_Lock()
+{
+ if (NULL != serviceHelper)
+ EnterCriticalSection(&serviceHelper->lock);
+}
+
+static void ServiceHelper_Unlock()
+{
+ if (NULL != serviceHelper)
+ LeaveCriticalSection(&serviceHelper->lock);
+}
+
+static void CALLBACK ServiceHelper_Uninitialize()
+{
+ if (NULL == serviceHelper)
+ return;
+
+ ServiceHelper_Lock();
+
+ ifc_omstorage *storage = NULL;;
+ if (SUCCEEDED(ServiceHelper_QueryWebStorage(&storage)))
+ {
+ if (NULL != serviceHelper->discoverAsync)
+ {
+ storage->RequestAbort(serviceHelper->discoverAsync, TRUE);
+ storage->EndLoad(serviceHelper->discoverAsync, NULL);
+ serviceHelper->discoverAsync->Release();
+ serviceHelper->discoverAsync = NULL;
+ }
+
+ size_t index = serviceHelper->versionChecks.size();
+ while(index--)
+ {
+ ifc_omstorageasync *async = serviceHelper->versionChecks[index];
+ storage->RequestAbort(async, TRUE);
+ storage->EndLoad(async, NULL);
+ }
+ serviceHelper->versionChecks.clear();
+
+ storage->Release();
+ }
+
+ ServiceHelper_Unlock();
+ DeleteCriticalSection(&serviceHelper->lock);
+
+ //free(serviceHelper);
+ delete serviceHelper;
+ serviceHelper = NULL;
+}
+
+HRESULT ServiceHelper_Initialize()
+{
+ if (NULL != serviceHelper)
+ return S_FALSE;
+
+ //serviceHelper = (SERVICEHELPER*)calloc(1, sizeof(SERVICEHELPER));
+ serviceHelper = new SERVICEHELPER();
+
+ if (NULL == serviceHelper)
+ return E_OUTOFMEMORY;
+ InitializeCriticalSection(&serviceHelper->lock);
+
+ Plugin_RegisterUnloadCallback(ServiceHelper_Uninitialize);
+
+ return S_OK;
+}
+
+HRESULT ServiceHelper_QueryStorage(ifc_omstorage **storage)
+{
+ if (NULL == storage) return E_POINTER;
+
+ if (NULL == OMSERVICEMNGR)
+ {
+ *storage = NULL;
+ return E_UNEXPECTED;
+ }
+
+ return OMSERVICEMNGR->QueryStorage(&SUID_OmStorageIni, storage);
+}
+
+HRESULT ServiceHelper_QueryWebStorage(ifc_omstorage **storage)
+{
+ if (NULL == storage) return E_POINTER;
+
+ if (NULL == OMSERVICEMNGR)
+ {
+ *storage = NULL;
+ return E_UNEXPECTED;
+ }
+
+ return OMSERVICEMNGR->QueryStorage(&SUID_OmStorageUrl, storage);
+}
+
+HRESULT ServiceHelper_Create(UINT serviceId, LPCWSTR pszName, LPCWSTR pszIcon, LPCWSTR pszUrl, UINT flags, UINT generation, BOOL fSave, ifc_omservice **serviceOut)
+{
+ if (NULL == serviceOut)
+ return E_POINTER;
+
+ *serviceOut = NULL;
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ ServiceHost *serviceHost;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ HRESULT hr = OMSERVICEMNGR->CreateService(serviceId, serviceHost, serviceOut);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omserviceeditor *editor;
+ if (SUCCEEDED((*serviceOut)->QueryInterface(IFC_OmServiceEditor, (void**)&editor)))
+ {
+ WCHAR szBuffer[4096] = {0};
+ editor->BeginUpdate();
+ if (NULL != pszName && IS_INTRESOURCE(pszName))
+ {
+ if (NULL != WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszName, szBuffer, ARRAYSIZE(szBuffer)))
+ editor->SetName(szBuffer, FALSE);
+ }
+ else
+ editor->SetName(pszName, FALSE);
+
+ if (NULL != pszIcon && IS_INTRESOURCE(pszIcon))
+ {
+ if (SUCCEEDED(Plugin_MakeResourcePath(szBuffer, ARRAYSIZE(szBuffer), RT_RCDATA, pszIcon, RESPATH_COMPACT)))
+ editor->SetIcon(szBuffer, FALSE);
+ }
+ else
+ editor->SetIcon(pszIcon, FALSE);
+
+ if (NULL != pszUrl && IS_INTRESOURCE(pszUrl))
+ {
+ if (SUCCEEDED(Plugin_MakeResourcePath(szBuffer, ARRAYSIZE(szBuffer), RT_HTML, pszUrl, RESPATH_TARGETIE | RESPATH_COMPACT)))
+ editor->SetUrl(szBuffer, FALSE);
+ }
+ else
+ editor->SetUrl(pszUrl, FALSE);
+
+ editor->SetGeneration(generation);
+
+ editor->SetFlags(flags, 0xFFFFFFFF);
+
+ if (FALSE != fSave)
+ {
+ hr = ServiceHelper_Save(*serviceOut);
+ if (FAILED(hr))
+ {
+ (*serviceOut)->Release();
+ *serviceOut = NULL;
+ }
+ }
+ else
+ {
+ editor->SetModified(0, (UINT)-1);
+ }
+ editor->EndUpdate();
+ editor->Release();
+ }
+ }
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+
+ return hr;
+}
+
+HRESULT ServiceHelper_Save(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ if (S_OK == ServiceHelper_IsSpecial(service))
+ return S_FALSE;
+
+ HRESULT hr;
+ ifc_omstorage *storage;
+ hr = ServiceHelper_QueryStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+
+ UINT serviceFlags;
+ if (SUCCEEDED(service->GetFlags(&serviceFlags)) && 0 != (SVCF_AUTOUPGRADE & serviceFlags))
+ {
+ hr = storage->Save(&service, 1, ifc_omstorage::saveClearModified, NULL);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omserviceeditor *editor;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEditor, (void**)&editor)))
+ {
+ editor->BeginUpdate();
+ editor->SetFlags(0, SVCF_AUTOUPGRADE);
+ editor->SetModified(0, ifc_omserviceeditor::modifiedFlags);
+ editor->EndUpdate();
+ editor->Release();
+ }
+ }
+ }
+ else
+ {
+ hr = storage->Save(&service, 1, ifc_omstorage::saveClearModified | ifc_omstorage::saveModifiedOnly, NULL);
+ }
+
+ storage->Release();
+ }
+ return hr;
+}
+
+HRESULT ServiceHelper_Delete(ifc_omservice *service, UINT flags)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == OMSERVICEMNGR)
+ return E_UNEXPECTED;
+
+ if (S_OK == ServiceHelper_IsSpecial(service))
+ return S_FALSE;
+
+ HRESULT hr;
+
+ if (S_OK == ServiceHelper_IsSubscribed(service))
+ {
+ hr = ServiceHelper_Subscribe(service, FALSE, ((SHF_NOTIFY | SHF_VERBAL) & flags));
+ if (S_OK != hr) return hr;
+ }
+
+ ifc_omstorage *storage = NULL;
+ hr = ServiceHelper_QueryStorage(&storage);
+ if (SUCCEEDED(hr) && storage)
+ {
+ hr = storage->Delete(&service, 1, NULL);
+ storage->Release();
+ }
+ return hr;
+}
+
+HRESULT ServiceHelper_SetFlags(ifc_omservice *service, UINT flags, UINT flagsMask)
+{
+ if (NULL ==service)
+ return E_INVALIDARG;
+
+ UINT serviceFlags;
+ HRESULT hr = service->GetFlags(&serviceFlags);
+ if (FAILED(hr)) return hr;
+
+ if ((flags & flagsMask) == (serviceFlags & flagsMask))
+ return S_FALSE;
+
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ editor->SetFlags(flags, flagsMask);
+ editor->Release();
+ }
+
+ return hr;
+}
+HRESULT ServiceHelper_IsSpecial(ifc_omservice *service)
+{
+ if (NULL == service) return E_INVALIDARG;
+
+ UINT flags;
+ HRESULT hr = service->GetFlags(&flags);
+ if (FAILED(hr)) return hr;
+
+ return (0 != (SVCF_SPECIAL & flags)) ? S_OK : S_FALSE;
+}
+
+HRESULT ServiceHelper_IsSubscribed(ifc_omservice *service)
+{
+ if (NULL == service) return E_INVALIDARG;
+
+ //return S_OK;
+
+ UINT flags;
+ HRESULT hr = service->GetFlags(&flags);
+ if (FAILED(hr)) return hr;
+
+ return (0 != (SVCF_SUBSCRIBED & flags)) ? S_OK : S_FALSE;
+}
+
+HRESULT ServiceHelper_IsModified(ifc_omservice *service)
+{
+ if (NULL == service) return E_INVALIDARG;
+
+ HRESULT hr;
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ UINT modified;
+ hr = editor->GetModified(&modified);
+ if (SUCCEEDED(hr))
+ {
+ hr = (0 == modified) ? S_FALSE : S_OK;
+ }
+ editor->Release();
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_MarkModified(ifc_omservice *service, UINT modifiedFlag, UINT modifiedMask)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ HRESULT hr;
+ ifc_omserviceeditor *editor;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ hr = editor->SetModified(modifiedFlag, modifiedMask);
+ editor->Release();
+ }
+ return hr;
+}
+HRESULT ServiceHelper_Load(ifc_omserviceenum **enumerator)
+{
+ if (NULL == enumerator)
+ return E_POINTER;
+
+ HRESULT hr;
+ ifc_omstorage *storage;
+ hr = ServiceHelper_QueryStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ ServiceHost *serviceHost;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ hr = storage->Load(L"*.ini", serviceHost, enumerator);
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+
+ storage->Release();
+ }
+
+ if (FAILED(hr))
+ *enumerator = NULL;
+
+ return hr;
+}
+
+HRESULT ServiceHelper_UpdateIcon(ifc_omserviceeditor *editor, LPCWSTR pszImage)
+{
+ WCHAR szBuffer[8192];
+ szBuffer[0] = L'\0';
+
+ if (NULL == editor)
+ return E_INVALIDARG;
+
+ ifc_omservice *service;
+ if (SUCCEEDED(editor->QueryInterface(IFC_OmService, (void**)&service)))
+ {
+ if (FAILED(service->GetIcon(szBuffer, ARRAYSIZE(szBuffer))))
+ szBuffer[0] = L'\0';
+ service->Release();
+ }
+
+ HRESULT hr = editor->SetIcon(pszImage, FALSE);
+ if (FAILED(hr) || S_FALSE == hr) return hr;
+
+ if (L'\0' != szBuffer)
+ {
+ ifc_mlnavigationhelper *navHelper;
+ if (SUCCEEDED(OMUTILITY->GetMlNavigationHelper(Plugin_GetLibrary(), &navHelper)))
+ {
+ navHelper->ReleaseIndex(szBuffer);
+ navHelper->Release();
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT ServiceHelper_Find(UINT serviceId, ifc_omservice **serviceOut)
+{
+ if (NULL == serviceOut) return E_POINTER;
+ *serviceOut = NULL;
+
+ if (0 == serviceId)
+ return E_INVALIDARG;
+
+
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ HNAVITEM hItem = navigation->FindService(serviceId, serviceOut);
+ navigation->Release();
+ if (NULL != hItem)
+ return S_OK;
+ }
+
+ ifc_omserviceenum *enumerator;
+ HRESULT hr = ServiceHelper_Load(&enumerator);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ hr = S_FALSE;
+ while (S_OK == enumerator->Next(1, &service, NULL))
+ {
+ if (service->GetId() == serviceId)
+ {
+ *serviceOut = service;
+ hr = S_OK;
+ break;
+ }
+ service->Release();
+ }
+ enumerator->Release();
+ }
+ return hr;
+}
+
+HRESULT ServiceHelper_SetRating(ifc_omservice *service, UINT rating, UINT flags)
+{
+ if (NULL == service)
+ return E_POINTER;
+
+ ifc_omserviceeditor *editor;
+ HRESULT hr;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr))
+ {
+ hr = editor->SetRating(rating);
+ if (S_OK == hr)
+ {
+ if (0 != (SHF_NOTIFY & flags))
+ {
+ // send notification
+
+ WCHAR szUrl[2048] = {0}, szClient[128] = {0};
+ if (NULL == OMBROWSERMNGR || FAILED(OMBROWSERMNGR->GetClientId(szClient, ARRAYSIZE(szClient))))
+ szClient[0] = L'\0';
+
+ hr = StringCchPrintf(szUrl, ARRAYSIZE(szUrl),
+ L"http://services.winamp.com/svc/rating?svc_id=%u&unique_id=%s&rating=%d",
+ service->GetId(), szClient, rating*2);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = ServiceHelper_PostNotificationUrl(szUrl);
+ }
+
+ }
+
+ if (0 != (SHF_SAVE & flags))
+ ServiceHelper_Save(service);
+
+ }
+ editor->Release();
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_PostNotificationUrl(LPCWSTR pszUrl)
+{
+ HRESULT hr;
+ ifc_omstorage *storage;
+ hr = ServiceHelper_QueryWebStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omstorageasync *async;
+ hr = storage->BeginLoad(pszUrl, NULL, NULL, NULL, &async);
+ if(SUCCEEDED(hr))
+ {
+ async->Release();
+ }
+ storage->Release();
+ }
+
+ return hr;
+}
+HRESULT ServiceHelper_ResetPermissions(ifc_omservice *service, UINT flags)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (0 != (SHF_VERBAL & flags))
+ {
+ WCHAR szText[1024] = {0}, szFormat[512] = {0}, szName[128] = {0};
+
+ service->GetName(szName, ARRAYSIZE(szName));
+ WASABI_API_LNGSTRINGW_BUF(IDS_MESSAGEBOX_POLICYRESET, szFormat, ARRAYSIZE(szFormat));
+ StringCchPrintf(szText, ARRAYSIZE(szText), szFormat, szName);
+
+ if (IDNO == Plugin_MessageBox(szText, MAKEINTRESOURCE(IDS_MESSAGEBOX_POLICYRESET_CAPTION),
+ MB_ICONQUESTION | MB_YESNO))
+ {
+ return S_FALSE;
+ }
+ }
+
+ if (NULL == AGAVE_API_JSAPI2_SECURITY)
+ return E_UNEXPECTED;
+
+ WCHAR szBuffer[64] = {0};
+ if (FAILED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), L"%u", service->GetId())))
+ return E_FAIL;
+
+ AGAVE_API_JSAPI2_SECURITY->ResetAuthorization(szBuffer);
+ return S_OK;
+}
+
+HRESULT ServiceHelper_IsPreAuthorized(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ UINT serviceFlags;
+ HRESULT hr = service->GetFlags(&serviceFlags);
+ if (SUCCEEDED(hr))
+ {
+ hr = (0 != (SVCF_PREAUTHORIZED & serviceFlags)) ? S_OK : S_FALSE;
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_Subscribe(ifc_omservice *service, BOOL subscribe, UINT flags)
+{
+ if (NULL == service)
+ return E_POINTER;
+
+ if ((TRUE == subscribe) == (S_OK == ServiceHelper_IsSubscribed(service)))
+ return S_FALSE;
+
+ HRESULT hr;
+
+ if (FALSE == subscribe && 0 != (SHF_VERBAL & flags))
+ {
+ WCHAR szText[1024] = {0}, szFormat[512] = {0}, szName[128] = {0};
+ if (FAILED(service->GetName(szName, ARRAYSIZE(szName))))
+ szName[0] = L'\0';
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_MESSAGEBOX_UNSUBSCRIBE, szFormat, ARRAYSIZE(szFormat));
+ StringCchPrintf(szText,ARRAYSIZE(szText), szFormat, szName);
+
+ if (IDNO == Plugin_MessageBox(szText, MAKEINTRESOURCE(IDS_MESSAGEBOX_UNSUBSCRIBE_CAPTION),
+ MB_ICONQUESTION | MB_YESNO))
+ {
+ return S_FALSE;
+ }
+ }
+
+ ifc_omserviceeditor *editor = NULL;
+ hr = service->QueryInterface(IFC_OmServiceEditor, (void**)&editor);
+ if (SUCCEEDED(hr) && editor)
+ {
+ hr = editor->SetFlags((FALSE == subscribe) ? 0 : SVCF_SUBSCRIBED, SVCF_SUBSCRIBED);
+ if (S_OK == hr)
+ {
+ if (0 != (SHF_SAVE & flags))
+ ServiceHelper_Save(service);
+ }
+ editor->Release();
+
+ if (FALSE == subscribe)
+ ServiceHelper_Delete(service, 0);
+ }
+
+ if (S_OK == hr)
+ {
+ if (NULL == AGAVE_API_JSAPI2_SECURITY)
+ {
+ WCHAR szBuffer[64] = {0};
+ if (SUCCEEDED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), L"%u", service->GetId())))
+ {
+ AGAVE_API_JSAPI2_SECURITY->ResetAuthorization(szBuffer);
+ }
+ }
+
+ if (0 != (SHF_NOTIFY & flags))
+ {
+ LPWSTR pszUrl;
+ UINT serviceId = service->GetId();
+ LPCWSTR action = (FALSE != subscribe) ? L"add" : L"remove";;
+ if (NULL != action && SUCCEEDED(Plugin_BuildActionUrl(&pszUrl, action, &serviceId, 1)))
+ {
+ hr = ServiceHelper_PostNotificationUrl(pszUrl);
+ Plugin_FreeString(pszUrl);
+ }
+ }
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_ResetSubscription(UINT flags)
+{
+ if (S_OK == ServiceHelper_IsDiscovering())
+ {
+ if (0 != (SHF_VERBAL & flags))
+ {
+ Plugin_MessageBox(MAKEINTRESOURCE(IDS_MESSAGEBOX_DISCOVERBUSY),
+ MAKEINTRESOURCE(IDS_MESSAGEBOX_DISCOVERBUSY_CAPTION),
+ MB_ICONASTERISK | MB_OK);
+ }
+
+ return E_PENDING;
+ }
+
+ if (0 != (SHF_VERBAL & flags))
+ {
+ if (IDNO == Plugin_MessageBox(MAKEINTRESOURCE(IDS_MESSAGEBOX_RESETTODEFAULT),
+ MAKEINTRESOURCE(IDS_MESSAGEBOX_RESETTODEFAULT_CAPTION),
+ MB_ICONQUESTION | MB_YESNO))
+ {
+ return S_FALSE;
+ }
+ }
+
+ HRESULT hr;
+
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ navigation->DeleteAll();
+ navigation->Release();
+ }
+
+ ifc_omstorage *storage;
+ hr = ServiceHelper_QueryStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omserviceenum *enumerator;
+ hr = ServiceHelper_Load(&enumerator);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omservice *service;
+ std::vector<UINT> removeList;
+ while(S_OK == enumerator->Next(1, &service, NULL))
+ {
+ if (S_OK == ServiceHelper_IsSubscribed(service))
+ removeList.push_back(service->GetId());
+
+ ServiceHelper_ResetPermissions(service, 0);
+ storage->Delete(&service, 1, NULL);
+ service->Release();
+ }
+ enumerator->Release();
+
+ if (0 != removeList.size())
+ {
+ LPWSTR pszUrl;
+ if (SUCCEEDED(Plugin_BuildActionUrl(&pszUrl, L"remove", removeList.data(), removeList.size())))
+ {
+ ServiceHelper_PostNotificationUrl(pszUrl);
+ Plugin_FreeString(pszUrl);
+ }
+ }
+ }
+ storage->Release();
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // discover new services
+ hr = ServiceHelper_BeginDiscover(L"http://services.winamp.com/svc/default.php");
+ }
+
+ return hr;
+}
+
+HRESULT ServiceHelper_IsDiscovering()
+{
+ HRESULT hr;
+ ServiceHelper_Lock();
+
+ if (NULL == serviceHelper)
+ {
+ hr = E_UNEXPECTED;
+ }
+ else
+ {
+ hr = (NULL != serviceHelper->discoverAsync) ? S_OK : S_FALSE;
+ }
+
+ ServiceHelper_Unlock();
+
+ return hr;
+}
+
+static void CALLBACK ServiceHelper_DiscoverComplete(ifc_omstorageasync *result)
+{
+ if (NULL == result)
+ return;
+
+ SERVICEHELPER *helper;
+ if (FAILED(result->GetData((void**)&helper)) || NULL == helper)
+ return;
+
+ ServiceHelper_Lock();
+
+ ifc_omstorage *webStorage;
+ HRESULT hr = ServiceHelper_QueryWebStorage(&webStorage);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omserviceenum *serviceEnum;
+ if (SUCCEEDED(webStorage->EndLoad(result, &serviceEnum)))
+ {
+ Navigation *navigation;
+ if (FAILED(Plugin_GetNavigation(&navigation)))
+ navigation = NULL;
+
+ ifc_omstorage *localStorage;
+ if (SUCCEEDED(ServiceHelper_QueryStorage(&localStorage)))
+ {
+ std::vector<UINT> registerList;
+
+ ifc_omservice *service;
+ while(S_OK == serviceEnum->Next(1, &service, NULL))
+ {
+ ifc_omserviceeditor *editor;
+ if (SUCCEEDED(service->QueryInterface(IFC_OmServiceEditor, (void**)&editor)))
+ {
+ if (SUCCEEDED(editor->SetFlags(SVCF_SUBSCRIBED, SVCF_SUBSCRIBED)))
+ registerList.push_back(service->GetId());
+
+ editor->Release();
+ }
+
+ if (SUCCEEDED(localStorage->Save(&service, 1, ifc_omstorage::saveClearModified, NULL)))
+ {
+ navigation->CreateItemAsync(service);
+ }
+
+ service->Release();
+ }
+
+ if (0 != registerList.size())
+ {
+ LPWSTR pszUrl;
+ if (SUCCEEDED(Plugin_BuildActionUrl(&pszUrl, L"add", registerList.data(), registerList.size())))
+ {
+ ServiceHelper_PostNotificationUrl(pszUrl);
+ Plugin_FreeString(pszUrl);
+ }
+ }
+ }
+
+ if (NULL != navigation)
+ navigation->Release();
+
+ serviceEnum->Release();
+ }
+ webStorage->Release();
+ }
+
+
+ if (serviceHelper->discoverAsync == result)
+ {
+ serviceHelper->discoverAsync->Release();
+ serviceHelper->discoverAsync = NULL;
+ }
+
+ ServiceHelper_Unlock();
+}
+
+HRESULT ServiceHelper_BeginDiscover(LPCWSTR address)
+{
+ ifc_omstorage *storage;
+ HRESULT hr = ServiceHelper_QueryWebStorage(&storage);
+ if (FAILED(hr)) return hr;
+
+ ServiceHost *serviceHost;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ ServiceHelper_Lock();
+
+ if (NULL == serviceHelper->discoverAsync)
+ hr = storage->BeginLoad(address, serviceHost, ServiceHelper_DiscoverComplete, serviceHelper, &serviceHelper->discoverAsync);
+ else
+ hr = E_PENDING;
+
+ ServiceHelper_Unlock();
+
+ storage->Release();
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+
+ return hr;
+}
+HRESULT ServiceHelper_GetDetailsUrl(LPWSTR pszBuffer, UINT cchBufferMax, ifc_omservice *service, BOOL fLite)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ if (0 == service)
+ return E_INVALIDARG;
+
+ HRESULT hr;
+ size_t remaining = cchBufferMax;
+ LPWSTR cursor = pszBuffer;
+
+ hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ L"http://services.winamp.com/svc/details?svc_id=%u", service->GetId());
+
+ WCHAR szClient[128] = {0};
+ if (SUCCEEDED(hr) &&
+ NULL != OMBROWSERMNGR &&
+ SUCCEEDED(OMBROWSERMNGR->GetClientId(szClient, ARRAYSIZE(szClient))) &&
+ L'\0' != szClient[0])
+ {
+ hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE,
+ L"&unique_id=%s", szClient);
+ }
+
+ if (FALSE != fLite && SUCCEEDED(hr))
+ {
+ hr = StringCchCopyEx(cursor, remaining, L"&detail=lite", &cursor, &remaining, STRSAFE_NULL_ON_FAILURE);
+ }
+ return hr;
+}
+static void CALLBACK ServiceHelper_VersionCheckComplete(ifc_omstorageasync *result)
+{
+ if (NULL == result)
+ return;
+
+ ifc_omservice *target;
+ if (FAILED(result->GetData((void**)&target)) || NULL == target)
+ return;
+
+ ServiceHelper_Lock();
+
+ size_t index = serviceHelper->versionChecks.size();
+ while(index--)
+ {
+ if (result == serviceHelper->versionChecks[index])
+ {
+ auto it = serviceHelper->versionChecks.begin() + index;
+ if (it < serviceHelper->versionChecks.end())
+ {
+ it = serviceHelper->versionChecks.erase(it);
+ }
+ break;
+ }
+ }
+
+ ServiceHelper_Unlock();
+
+ UINT originalFlags;
+ if (FAILED(target->GetFlags(&originalFlags)))
+ originalFlags = 0;
+
+ ifc_omserviceeditor *editor;
+ if (SUCCEEDED(target->QueryInterface(IFC_OmServiceEditor, (void**)&editor)))
+ editor->BeginUpdate();
+ else
+ editor = NULL;
+
+
+ ifc_omstorage *storage;
+ HRESULT hr = ServiceHelper_QueryWebStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ ifc_omserviceenum *serviceEnum;
+ if (SUCCEEDED(storage->EndLoad(result, &serviceEnum)))
+ {
+ ifc_omservice *source = NULL;
+ while(S_OK == serviceEnum->Next(1, &source, NULL))
+ {
+ if (source->GetId() == target->GetId())
+ break;
+
+ source->Release();
+ source = NULL;
+ }
+
+ ifc_omxmlserviceenum *xmlServiceEnum;
+ if (SUCCEEDED(serviceEnum->QueryInterface(IFC_OmXmlServiceEnum, (void**)&xmlServiceEnum)))
+ {
+ UINT statusCode;
+ if (SUCCEEDED(xmlServiceEnum->GetStatusCode(&statusCode)))
+ {
+ switch(statusCode)
+ {
+ case 410: // force remove
+ if (NULL != target)
+ {
+ WCHAR szText[1024] = {0}, szFormat[512] = {0}, szName[128] = {0};
+ target->GetName(szName, ARRAYSIZE(szName));
+ WASABI_API_LNGSTRINGW_BUF(IDS_MESSAGEBOX_FORCEREMOVE, szFormat, ARRAYSIZE(szFormat));
+ StringCchPrintf(szText,ARRAYSIZE(szText), szFormat, szName);
+ Plugin_MessageBox(szText, MAKEINTRESOURCE(IDS_MESSAGEBOX_FORCEREMOVE_CAPTION), MB_ICONINFORMATION | MB_OK);
+ ServiceHelper_Delete(target, 0);
+ target->Release();
+ target = NULL;
+ }
+ break;
+
+ case 200:
+ if (NULL != source)
+ {
+ ifc_omservicecopier *copier;
+ if (SUCCEEDED(source->QueryInterface(IFC_OmServiceCopier, (void**)&copier)))
+ {
+ UINT targetFlags, sourceFlags;
+ if (FAILED(target->GetFlags(&targetFlags))) targetFlags = 0;
+ if (SUCCEEDED(source->GetFlags(&sourceFlags)))
+ targetFlags |= sourceFlags;
+
+ copier->CopyTo(target, NULL);
+ copier->Release();
+
+ editor->SetFlags(targetFlags, targetFlags);
+ }
+
+ }
+ break;
+ }
+ }
+ xmlServiceEnum->Release();
+ }
+
+ if (NULL != source)
+ source->Release();
+
+ serviceEnum->Release();
+ }
+ storage->Release();
+ }
+
+ if (NULL != target && NULL != AGAVE_API_JSAPI2_SECURITY)
+ {
+ WCHAR szBuffer[64] = {0};
+ if (SUCCEEDED(StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%u", target->GetId())))
+ {
+ UINT flags = 0;
+ if (SUCCEEDED(target->GetFlags(&flags)))
+ {
+ bool bypassEnabled = (0 != (SVCF_PREAUTHORIZED & flags));
+ AGAVE_API_JSAPI2_SECURITY->SetBypass(szBuffer, bypassEnabled);
+ }
+ }
+ }
+
+ if (NULL != editor)
+ {
+ editor->SetFlags(SVCF_VALIDATED, SVCF_VALIDATED | SVCF_VERSIONCHECK);
+
+ if (NULL != target)
+ {
+ UINT targetFlags;
+ if (FAILED(target->GetFlags(&targetFlags)))
+ targetFlags = 0;
+
+ if ((targetFlags & ~ifc_omservice::RuntimeFlagsMask) == (originalFlags & ~ifc_omservice::RuntimeFlagsMask))
+ editor->SetModified(0, ifc_omserviceeditor::modifiedFlags);
+ }
+ editor->EndUpdate();
+ editor->Release();
+ }
+
+ if (NULL != target)
+ {
+ ifc_omserviceeventmngr *eventManager;
+ if (SUCCEEDED(target->QueryInterface(IFC_OmServiceEventMngr, (void**)&eventManager)))
+ {
+ eventManager->Signal_CommandStateChange(&CMDGROUP_SERVICE, SVCCOMMAND_BLOCKNAV);
+ eventManager->Release();
+ }
+
+ ServiceHelper_Save(target);
+ target->Release();
+ }
+
+}
+
+HRESULT ServiceHelper_BeginVersionCheck(ifc_omservice *service)
+{
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ if (NULL == serviceHelper)
+ return E_UNEXPECTED;
+
+ HRESULT hr = S_OK;
+ ServiceHelper_Lock();
+ size_t index = serviceHelper->versionChecks.size();
+ while(index--)
+ {
+ ifc_omstorageasync *async = serviceHelper->versionChecks[index];
+ UINT serviceId;
+ if (SUCCEEDED(async->GetData((void**)&serviceId)))
+ {
+ if (serviceId == service->GetId())
+ {
+ hr = S_FALSE;
+ }
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ ifc_omstorage *storage;
+ HRESULT hr = ServiceHelper_QueryWebStorage(&storage);
+ if (SUCCEEDED(hr))
+ {
+ WCHAR szAddress[2048] = {0};
+ hr = ServiceHelper_GetDetailsUrl(szAddress, ARRAYSIZE(szAddress), service, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ ServiceHost *serviceHost;
+ if (FAILED(ServiceHost::GetCachedInstance(&serviceHost)))
+ serviceHost = NULL;
+
+ ifc_omserviceeditor *editor;
+ if (FAILED(service->QueryInterface(IFC_OmServiceEditor, (void**)&editor)))
+ editor = NULL;
+ else
+ {
+ editor->BeginUpdate();
+ editor->SetFlags(SVCF_VERSIONCHECK, SVCF_VERSIONCHECK);
+ }
+ service->AddRef();
+ serviceHelper->versionChecks.push_back(NULL);
+ //ifc_omstorageasync **pAsync = serviceHelper->versionChecks.end() - 1;
+ ifc_omstorageasync** pAsync = (serviceHelper->versionChecks.size() ?
+ &serviceHelper->versionChecks.at(serviceHelper->versionChecks.size() - 1) : 0);
+ hr = storage->BeginLoad(szAddress, serviceHost, ServiceHelper_VersionCheckComplete, service, pAsync);
+ if (FAILED(hr))
+ {
+ service->Release();
+ serviceHelper->versionChecks.pop_back();
+ if (NULL != editor)
+ editor->SetFlags(0, SVCF_VERSIONCHECK);
+ }
+
+ if (NULL != serviceHost)
+ serviceHost->Release();
+
+ if (NULL != editor)
+ {
+ editor->EndUpdate();
+ editor->Release();
+ }
+ }
+ storage->Release();
+ }
+
+ }
+
+ ServiceHelper_Unlock();
+
+ return hr;
+}
+static void CALLBACK ServiceHelper_UpdateOperationInfoApc(ULONG_PTR param)
+{
+ HWND hBrowser = (HWND)param;
+ if (NULL != hBrowser && IsWindow(hBrowser))
+ {
+ ServiceHelper_UpdateOperationInfo(hBrowser);
+ }
+}
+
+HRESULT ServiceHelper_UpdateOperationInfo(HWND hBrowser)
+{
+ if (NULL == hBrowser)
+ return E_INVALIDARG;
+
+ DWORD currentTID = GetCurrentThreadId();
+ DWORD winampTID = GetWindowThreadProcessId(Plugin_GetWinamp(), NULL);
+ if (NULL != winampTID && winampTID != currentTID)
+ {
+ HRESULT hr;
+ if (NULL != OMUTILITY)
+ hr = OMUTILITY->PostMainThreadCallback(ServiceHelper_UpdateOperationInfoApc, (ULONG_PTR)hBrowser);
+ else
+ hr = E_FAIL;
+ return hr;
+ }
+
+ ifc_omservice *service;
+ if (FALSE == BrowserControl_GetService(hBrowser, &service))
+ return E_FAIL;
+
+ UINT flags;
+ if (SUCCEEDED(service->GetFlags(&flags)))
+ {
+ WCHAR szText[128] = {0}, szTitle[128] = {0};
+ OPERATIONINFO operation = {0};
+ operation.cbSize = sizeof(operation);
+ operation.mask = NBCOM_FLAGS;
+
+ if (0 == (SVCF_VERSIONCHECK & flags))
+ {
+ operation.flags = NBCOF_HIDEWIDGET;
+ }
+ else
+ {
+ operation.mask |= (NBCOM_TITLE | NBCOM_TEXT);
+ operation.flags = NBCOF_SHOWWIDGET;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_SERVICE_CHECKINGVERSION, szTitle, ARRAYSIZE(szTitle));
+ operation.title = szTitle;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_PLEASE_WAIT, szText, ARRAYSIZE(szText));
+ operation.text = szText;
+ }
+ BrowserControl_ShowOperation(hBrowser, &operation);
+ }
+ service->Release();
+
+ return S_OK;
+}
+static void CALLBACK ServiceHelper_ShowWindowTimer(UINT_PTR eventId, DWORD elapsedMs, ULONG_PTR data)
+{
+ Plugin_KillTimer(eventId);
+ HWND hTarget = (HWND)data;
+
+ if (hTarget == Plugin_GetLibrary())
+ PostMessage(hTarget, WM_ML_IPC, 0, ML_IPC_ENSURE_VISIBLE);
+ else
+ ShowWindow(hTarget, SW_SHOWNORMAL);
+}
+
+HRESULT ServiceHelper_ShowService(UINT serviceId, UINT showMode)
+{
+ HRESULT hr;
+
+ BOOL serviceFound = FALSE;
+ ifc_omservice *service;
+ if (0 != serviceId && ROOTSERVICE_ID != serviceId &&
+ S_OK == ServiceHelper_Find(serviceId, &service) &&
+ NULL != service)
+ {
+ if (S_OK == ServiceHelper_IsSubscribed(service))
+ serviceFound = TRUE;
+
+ service->Release();
+ }
+
+ WCHAR szBuffer[INTERNET_MAX_URL_LENGTH] = {0};
+ LPCWSTR pszUrl = NULL;
+
+ if (FALSE == serviceFound)
+ {
+ if (0 != serviceId && ROOTSERVICE_ID != serviceId &&
+ SUCCEEDED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer),
+ L"http://client.winamp.com/service/detail/gallery/%u", serviceId)))
+ {
+ pszUrl = szBuffer;
+ }
+ serviceId = ROOTSERVICE_ID;
+ }
+
+ Navigation *navigation;
+ hr = Plugin_GetNavigation(&navigation);
+ if (FAILED(hr)) return hr;
+
+ if (SHOWMODE_POPUP == showMode)
+ {
+ showMode = SHOWMODE_ENSUREVISIBLE;
+ if (FALSE != serviceFound)
+ {
+ HNAVITEM hItem = navigation->FindService(serviceId, NULL);
+ if (NULL != hItem)
+ {
+ HWND hPopup;
+ hr = navigation->CreatePopup(hItem, &hPopup);
+ if (SUCCEEDED(hr))
+ {
+ Plugin_SetTimer(100, ServiceHelper_ShowWindowTimer, (ULONG_PTR)hPopup);
+ showMode = SHOWMODE_POPUP;
+ }
+ }
+ }
+ }
+
+ if (SHOWMODE_POPUP != showMode)
+ {
+ hr = navigation->ShowService(serviceId, pszUrl);
+ if (SUCCEEDED(hr))
+ {
+ if (SHOWMODE_ENSUREVISIBLE == showMode)
+ Plugin_SetTimer(100, ServiceHelper_ShowWindowTimer, (ULONG_PTR)Plugin_GetLibrary());
+ }
+ }
+
+ navigation->Release();
+ return hr;
+} \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/serviceHelper.h b/Src/Plugins/Library/ml_online/serviceHelper.h
new file mode 100644
index 00000000..fe09a685
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/serviceHelper.h
@@ -0,0 +1,57 @@
+#ifndef NULLSOFT_ONLINEMEDIA_PLUGIN_SERVICE_HELPER_HEADER
+#define NULLSOFT_ONLINEMEDIA_PLUGIN_SERVICE_HELPER_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <wtypes.h>
+
+class ifc_omservice;
+class ifc_omserviceenum;
+class ifc_omserviceeditor;
+class ifc_omstorage;
+
+#include <ifc_omstorageasync.h>
+
+#define SHF_VERBAL 0x00000001
+#define SHF_NOTIFY 0x00000002
+#define SHF_SAVE 0x00000004
+
+HRESULT ServiceHelper_Initialize();
+
+HRESULT ServiceHelper_QueryStorage(ifc_omstorage **storage);
+HRESULT ServiceHelper_QueryWebStorage(ifc_omstorage **storage);
+HRESULT ServiceHelper_Load(ifc_omserviceenum **enumerator);
+
+HRESULT ServiceHelper_Create(UINT serviceId, LPCWSTR pszName, LPCWSTR pszIcon, LPCWSTR pszUrl, UINT flags, UINT generation, BOOL fSave, ifc_omservice **serviceOut);
+HRESULT ServiceHelper_Save(ifc_omservice *service);
+HRESULT ServiceHelper_Delete(ifc_omservice *service, UINT flags);
+HRESULT ServiceHelper_UpdateIcon(ifc_omserviceeditor *editor, LPCWSTR pszImage);
+HRESULT ServiceHelper_Find(UINT serviceId, ifc_omservice **serviceOut);
+
+HRESULT ServiceHelper_SetFlags(ifc_omservice *service, UINT flags, UINT flagsMask);
+HRESULT ServiceHelper_IsSpecial(ifc_omservice *service);
+HRESULT ServiceHelper_IsPreAuthorized(ifc_omservice *service);
+HRESULT ServiceHelper_IsSubscribed(ifc_omservice *service);
+HRESULT ServiceHelper_IsModified(ifc_omservice *service);
+HRESULT ServiceHelper_MarkModified(ifc_omservice *service, UINT modifiedFlag, UINT modifiedMask);
+
+HRESULT ServiceHelper_SetRating(ifc_omservice *service, UINT rating, UINT flags);
+HRESULT ServiceHelper_Subscribe(ifc_omservice *service, BOOL subscribe, UINT flags);
+HRESULT ServiceHelper_ResetPermissions(ifc_omservice *service, UINT flags);
+HRESULT ServiceHelper_ResetSubscription(UINT flags);
+HRESULT ServiceHelper_BeginDiscover(LPCWSTR address);
+HRESULT ServiceHelper_IsDiscovering();
+HRESULT ServiceHelper_BeginVersionCheck(ifc_omservice *service);
+
+HRESULT ServiceHelper_GetDetailsUrl(LPWSTR pszBuffer, UINT cchBufferMax, ifc_omservice *service, BOOL fLite);
+HRESULT ServiceHelper_PostNotificationUrl(LPCWSTR pszUrl);
+HRESULT ServiceHelper_UpdateOperationInfo(HWND hBrowser);
+
+#define SHOWMODE_NORMAL 0
+#define SHOWMODE_ENSUREVISIBLE 1
+#define SHOWMODE_POPUP 2
+HRESULT ServiceHelper_ShowService(UINT serviceId, UINT showMode);
+
+#endif //NULLSOFT_ONLINEMEDIA_PLUGIN_SERVICE_HELPER_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/serviceHost.cpp b/Src/Plugins/Library/ml_online/serviceHost.cpp
new file mode 100644
index 00000000..e907e345
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/serviceHost.cpp
@@ -0,0 +1,394 @@
+#include "main.h"
+#include "./serviceHost.h"
+#include "./api__ml_online.h"
+#include "./resource.h"
+#include "./external.h"
+#include "./navigation.h"
+#include "./commands.h"
+#include "./serviceHelper.h"
+
+#include <ifc_omservice.h>
+#include <ifc_omserviceeditor.h>
+#include <ifc_omservicecommand.h>
+
+#include <ifc_omstoragehelper.h>
+#include <ifc_omstoragehandlerenum.h>
+#include <ifc_omfilestorage.h>
+
+#include "../winamp/IWasabiDispatchable.h"
+#include "../winamp/JSAPI_Info.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+
+
+#define IS_INVALIDISPATCH(__disp) (((IDispatch *)1) == (__disp) || NULL == (__disp))
+
+static ServiceHost *cachedInstance = NULL;
+
+static void CALLBACK StorageHandler_ReadAuth(ifc_omservice *service, LPCWSTR pszKey, LPCWSTR pszValue)
+{
+ INT iVal;
+ UINT flags = (NULL != pszValue &&
+ FALSE != StrToIntEx(pszValue, STIF_SUPPORT_HEX, &iVal) &&
+ 0 != iVal) ?
+ SVCF_USECLIENTOWEB : 0;
+
+ ServiceHelper_SetFlags(service, flags, SVCF_USECLIENTOWEB);
+}
+
+
+static void CALLBACK StorageHandler_ReadBypass(ifc_omservice *service, LPCWSTR pszKey, LPCWSTR pszValue)
+{
+ INT iVal;
+ UINT flags = (NULL != pszValue &&
+ FALSE != StrToIntEx(pszValue, STIF_SUPPORT_HEX, &iVal) &&
+ 0 != iVal) ?
+ SVCF_PREAUTHORIZED : 0;
+
+ ServiceHelper_SetFlags(service, flags, SVCF_PREAUTHORIZED);
+
+}
+
+static void CALLBACK StorageHandler_ReadSubscribed(ifc_omservice *service, LPCWSTR pszKey, LPCWSTR pszValue)
+{
+ INT iVal;
+ UINT flags = (NULL != pszValue &&
+ FALSE != StrToIntEx(pszValue, STIF_SUPPORT_HEX, &iVal) &&
+ 0 != iVal) ?
+ SVCF_SUBSCRIBED : 0;
+
+ flags |= SVCF_AUTOUPGRADE;
+ ServiceHelper_SetFlags(service, flags, SVCF_SUBSCRIBED | SVCF_AUTOUPGRADE);
+
+}
+
+static const ifc_omstoragehelper::TemplateRecord szStorageExtXml[] =
+{
+ { L"auth", StorageHandler_ReadAuth },
+ { L"bypass", StorageHandler_ReadBypass },
+};
+
+static const ifc_omstoragehelper::TemplateRecord szStorageExtIni[] =
+{
+ { L"subscribed", StorageHandler_ReadSubscribed },
+};
+
+ServiceHost::ServiceHost()
+ : ref(1), storageExtXml(NULL), storageExtIni(NULL)
+{
+
+}
+
+ServiceHost::~ServiceHost()
+{
+ if (NULL != storageExtXml)
+ storageExtXml->Release();
+
+ if (NULL != storageExtIni)
+ storageExtIni->Release();
+}
+
+HRESULT ServiceHost::CreateInstance(ServiceHost **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+ *instance = new ServiceHost();
+ if (NULL == instance) return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+HRESULT ServiceHost::GetCachedInstance(ServiceHost **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+
+ if (NULL == cachedInstance)
+ {
+ HRESULT hr = CreateInstance(&cachedInstance);
+ if (FAILED(hr))
+ {
+ *instance = NULL;
+ return hr;
+ }
+ }
+
+ cachedInstance->AddRef();
+ *instance = cachedInstance;
+ return S_OK;
+}
+
+HRESULT ServiceHost::ReleseCache()
+{
+ if (NULL == cachedInstance)
+ return S_FALSE;
+
+ ServiceHost *t = cachedInstance;
+ cachedInstance = NULL;
+ t->Release();
+
+ return S_OK;
+}
+
+size_t ServiceHost::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+size_t ServiceHost::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+int ServiceHost::QueryInterface(GUID interface_guid, void **object)
+{
+ if (NULL == object) return E_POINTER;
+
+ if (IsEqualIID(interface_guid, IFC_OmServiceHost))
+ *object = static_cast<ifc_omservicehost*>(this);
+ else if (IsEqualIID(interface_guid, IFC_OmServiceEvent))
+ *object = static_cast<ifc_omserviceevent*>(this);
+ else if (IsEqualIID(interface_guid, IFC_OmStorageExt))
+ *object = static_cast<ifc_omstorageext*>(this);
+ else
+ {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+
+ if (NULL == *object)
+ return E_UNEXPECTED;
+
+ AddRef();
+ return S_OK;
+}
+
+
+HRESULT ServiceHost::GetExternal(ifc_omservice *service, IDispatch **ppDispatch)
+{
+ if (NULL == ppDispatch)
+ return E_POINTER;
+
+ if (NULL != *ppDispatch)
+ {
+ // try to connect our external
+ IWasabiDispatchable *pWasabi;
+ if (SUCCEEDED((*ppDispatch)->QueryInterface(IID_IWasabiDispatchable, (void**)&pWasabi)))
+ {
+ JSAPI::ifc_info *pInfo;
+ if (SUCCEEDED(pWasabi->QueryDispatchable(JSAPI::IID_JSAPI_ifc_info, (Dispatchable**)&pInfo)))
+ {
+ ExternalDispatch *pExternal;
+ if (SUCCEEDED(ExternalDispatch::CreateInstance(&pExternal)))
+ {
+ pInfo->AddAPI(pExternal->GetName(), pExternal);
+ pExternal->Release();
+ }
+ pInfo->Release();
+ }
+ pWasabi->Release();
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT ServiceHost::GetBasePath(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ return StringCchCopy(pszBuffer, cchBufferMax, L".\\Plugins\\ml\\omServices");
+}
+
+HRESULT ServiceHost::GetDefaultName(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ if (NULL == pszBuffer)
+ return E_POINTER;
+
+ if (NULL == service)
+ return E_INVALIDARG;
+
+ return StringCchPrintf(pszBuffer, cchBufferMax, L"omService_{%010u}.ini", service->GetId());
+}
+
+HRESULT ServiceHost::QueryCommandState(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId)
+{
+ if (NULL == service || NULL == commandGroup)
+ return E_NOTIMPL;
+
+ if (IsEqualGUID(*commandGroup, CMDGROUP_SERVICE))
+ {
+ switch(commandId)
+ {
+ case SVCCOMMAND_SHOWINFO:
+ case SVCCOMMAND_REPORT:
+ case SVCCOMMAND_UNSUBSCRIBE:
+ case SVCCOMMAND_RATE:
+ if (S_FALSE == ServiceHelper_IsSpecial(service))
+ {
+ return CMDSTATE_ENABLED;
+ }
+ return CMDSTATE_UNKNOWN;
+
+ case SVCCOMMAND_BLOCKNAV:
+ {
+ UINT flags;
+ if (FAILED(service->GetFlags(&flags)))
+ flags = 0;
+
+ HRESULT state = (0 == (SVCF_VERSIONCHECK & flags)) ?
+ CMDSTATE_DISABLED : CMDSTATE_ENABLED;
+
+ ServiceHelper_UpdateOperationInfo(hBrowser);
+ return state;
+ }
+ break;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+HRESULT ServiceHost::ExecuteCommand(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId, ULONG_PTR commandArg)
+{
+ if (IsEqualGUID(CMDGROUP_SERVICE, *commandGroup))
+ {
+ if (S_OK != ServiceHelper_IsSpecial(service))
+ {
+ switch(commandId)
+ {
+ case SVCCOMMAND_SHOWINFO: Command_ShowServiceInfo(service); return S_OK;
+ case SVCCOMMAND_REPORT: Command_ReportService(service); return S_OK;
+ case SVCCOMMAND_UNSUBSCRIBE: Command_UnsubscribeService(service); return S_OK;
+ case SVCCOMMAND_RATE: Command_SetServiceRating(service, (UINT)commandArg); return S_OK;
+ }
+ }
+ }
+
+ return E_NOTIMPL;
+}
+
+void ServiceHost::ServiceChange(ifc_omservice *service, UINT nModified)
+{
+ if (NULL == service) return;
+
+ Navigation *navigation;
+ if (SUCCEEDED(Plugin_GetNavigation(&navigation)))
+ {
+ navigation->UpdateService(service, nModified);
+ navigation->Release();
+ }
+
+}
+
+HRESULT ServiceHost::EnumerateStorageExt(const GUID *storageId, ifc_omstoragehandlerenum **enumerator)
+{
+ if (NULL == storageId)
+ return E_INVALIDARG;
+
+ if (IsEqualGUID(SUID_OmStorageXml, *storageId))
+ {
+ if (NULL == storageExtXml)
+ {
+ ifc_omstoragehelper *storageHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetStorageHelper(&storageHelper)))
+ {
+ if (FAILED(storageHelper->CreateEnumerator(szStorageExtXml, ARRAYSIZE(szStorageExtXml), &storageExtXml)))
+ storageExtXml = NULL;
+ storageHelper->Release();
+
+ }
+
+ if (NULL == storageExtXml)
+ return E_FAIL;
+ }
+
+ *enumerator = storageExtXml;
+ storageExtXml->AddRef();
+ return S_OK;
+ }
+ else if (IsEqualGUID(SUID_OmStorageIni, *storageId))
+ {
+ if (NULL == storageExtIni)
+ {
+ ifc_omstoragehelper *storageHelper;
+ if (NULL != OMUTILITY && SUCCEEDED(OMUTILITY->GetStorageHelper(&storageHelper)))
+ {
+ if (FAILED(storageHelper->CreateEnumerator(szStorageExtIni, ARRAYSIZE(szStorageExtIni), &storageExtIni)))
+ storageExtIni = NULL;
+ storageHelper->Release();
+
+ }
+
+ if (NULL == storageExtIni)
+ return E_FAIL;
+ }
+
+ *enumerator = storageExtIni;
+ storageExtIni->AddRef();
+ return S_OK;
+
+ }
+ return E_NOTIMPL;
+}
+
+HRESULT ServiceHost::GetUrl(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax)
+{
+ UINT flags;
+ if (NULL != service && SUCCEEDED(service->GetFlags(&flags)) &&
+ 0 != (SVCF_USECLIENTOWEB & flags) && NULL != AGAVE_API_AUTH)
+ {
+ LPWSTR pszUrl = Plugin_CopyString(pszBuffer);
+ if (NULL == pszUrl) return E_OUTOFMEMORY;
+
+ HRESULT hr(E_NOTIMPL);
+
+ if (0 == AGAVE_API_AUTH->ClientToWeb(GUID_NULL, pszUrl, pszBuffer, cchBufferMax))
+ hr = S_OK;
+
+ Plugin_FreeString(pszUrl);
+ return hr;
+ }
+
+ return E_NOTIMPL;
+}
+
+#define CBCLASS ServiceHost
+START_MULTIPATCH;
+ START_PATCH(MPIID_OMSVCHOST)
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, ADDREF, AddRef);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, RELEASE, Release);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, QUERYINTERFACE, QueryInterface);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETEXTERNAL, GetExternal);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETBASEPATH, GetBasePath);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETDEFAULTNAME, GetDefaultName);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_QUERYCOMMANDSTATE, QueryCommandState);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_EXECUTECOMMAND, ExecuteCommand);
+ M_CB(MPIID_OMSVCHOST, ifc_omservicehost, API_GETURL, GetUrl);
+
+ NEXT_PATCH(MPIID_OMSVCEVENT)
+ M_CB(MPIID_OMSVCEVENT, ifc_omserviceevent, ADDREF, AddRef);
+ M_CB(MPIID_OMSVCEVENT, ifc_omserviceevent, RELEASE, Release);
+ M_CB(MPIID_OMSVCEVENT, ifc_omserviceevent, QUERYINTERFACE, QueryInterface);
+ M_VCB(MPIID_OMSVCEVENT, ifc_omserviceevent, API_SERVICECHANGE, ServiceChange);
+
+ NEXT_PATCH(MPIID_OMSTRGEXT)
+ M_CB(MPIID_OMSTRGEXT, ifc_omstorageext, ADDREF, AddRef);
+ M_CB(MPIID_OMSTRGEXT, ifc_omstorageext, RELEASE, Release);
+ M_CB(MPIID_OMSTRGEXT, ifc_omstorageext, QUERYINTERFACE, QueryInterface);
+ M_CB(MPIID_OMSTRGEXT, ifc_omstorageext, API_ENUMERATE, EnumerateStorageExt);
+
+
+ END_PATCH
+END_MULTIPATCH;
+#undef CBCLASS
diff --git a/Src/Plugins/Library/ml_online/serviceHost.h b/Src/Plugins/Library/ml_online/serviceHost.h
new file mode 100644
index 00000000..1e292b9f
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/serviceHost.h
@@ -0,0 +1,75 @@
+#ifndef NULLSOFT_ONLINEMEDIA_PLUGIN_SERVICE_HOST_HEADER
+#define NULLSOFT_ONLINEMEDIA_PLUGIN_SERVICE_HOST_HEADER
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+#pragma once
+#endif
+
+#include <ifc_omservicehost.h>
+#include <ifc_omserviceevent.h>
+#include <ifc_omstorageext.h>
+#include <bfc/multipatch.h>
+
+#define SVCF_SUBSCRIBED 0x00000001
+
+// runtime flags
+#define SVCF_SPECIAL 0x00010000
+#define SVCF_USECLIENTOWEB 0x00020000
+#define SVCF_VALIDATED 0x00040000
+#define SVCF_VERSIONCHECK 0x00080000
+#define SVCF_PREAUTHORIZED 0x00100000
+#define SVCF_AUTOUPGRADE 0x00200000
+
+#define MPIID_OMSVCHOST 10
+#define MPIID_OMSVCEVENT 20
+#define MPIID_OMSTRGEXT 30
+
+class ifc_omstoragehandlerenum;
+
+class ServiceHost : public MultiPatch<MPIID_OMSVCHOST, ifc_omservicehost>,
+ public MultiPatch<MPIID_OMSVCEVENT, ifc_omserviceevent>,
+ public MultiPatch<MPIID_OMSTRGEXT, ifc_omstorageext>
+{
+
+protected:
+ ServiceHost();
+ ~ServiceHost();
+
+public:
+ static HRESULT CreateInstance(ServiceHost **instance);
+ static HRESULT GetCachedInstance(ServiceHost **instance);
+ static HRESULT ReleseCache();
+
+public:
+ /* Dispatchable */
+ size_t AddRef();
+ size_t Release();
+ int QueryInterface(GUID interface_guid, void **object);
+
+ /* ifc_omservicehost */
+ HRESULT GetExternal(ifc_omservice *service, IDispatch **ppDispatch);
+ HRESULT GetBasePath(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax);
+ HRESULT GetDefaultName(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax);
+ HRESULT QueryCommandState(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId);
+ HRESULT ExecuteCommand(ifc_omservice *service, HWND hBrowser, const GUID *commandGroup, UINT commandId, ULONG_PTR commandArg);
+ HRESULT GetUrl(ifc_omservice *service, LPWSTR pszBuffer, UINT cchBufferMax);
+
+ /* ifc_omsvceventhandler */
+ void ServiceChange(ifc_omservice *service, UINT nModified);
+
+ /* ifc_omstorageext */
+ HRESULT EnumerateStorageExt(const GUID *storageId, ifc_omstoragehandlerenum **enumerator);
+
+protected:
+ ULONG ref;
+ ifc_omstoragehandlerenum *storageExtXml;
+ ifc_omstoragehandlerenum *storageExtIni;
+
+protected:
+ RECVS_MULTIPATCH;
+};
+
+
+
+
+#endif //NULLSOFT_ONLINEMEDIA_PLUGIN_SERVICE_HOST_HEADER \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/testPages.rc b/Src/Plugins/Library/ml_online/testPages.rc
new file mode 100644
index 00000000..9ea955e3
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/testPages.rc
@@ -0,0 +1,7 @@
+#include "./resource.h"
+/////////////////////////////////////////////////////////////////////////////
+//
+// HTML
+//
+
+WEBDEV.JS HTML ".\\resources\\pages\\webdev.js" \ No newline at end of file
diff --git a/Src/Plugins/Library/ml_online/version.rc2 b/Src/Plugins/Library/ml_online/version.rc2
new file mode 100644
index 00000000..dfc40599
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "../../../Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,0,3,0
+ PRODUCTVERSION WINAMP_PRODUCTVER
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Winamp SA"
+ VALUE "FileDescription", "Winamp Media Library Plug-in"
+ VALUE "FileVersion", "2,0,3,0"
+ VALUE "InternalName", "Nullsoft Online Services"
+ VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "ml_online.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/Src/Plugins/Library/ml_online/wasabi.cpp b/Src/Plugins/Library/ml_online/wasabi.cpp
new file mode 100644
index 00000000..e6edf6ae
--- /dev/null
+++ b/Src/Plugins/Library/ml_online/wasabi.cpp
@@ -0,0 +1,144 @@
+#include "main.h"
+#include "./api__ml_online.h"
+#include <api/service/waservicefactory.h>
+#include "../nu/Singleton.h"
+#include "handler.h"
+#include "JSAPI2_Creator.h"
+
+static ULONG wasabiRef = 0;
+
+api_service *WASABI_API_SVC = NULL;
+api_application *WASABI_API_APP = NULL;
+api_config *AGAVE_API_CONFIG = NULL;
+api_language *WASABI_API_LNG = NULL;
+api_explorerfindfile *WASABI_API_EXPLORERFINDFILE = NULL;
+api_memmgr *WASABI_API_MEMMNGR = NULL;
+svc_imageLoader *pngLoaderApi = NULL;
+obj_ombrowser *browserManager = NULL;
+ifc_omservicemanager *serviceManager = NULL;
+ifc_omutility *omUtility = NULL;
+JSAPI2::api_security *AGAVE_API_JSAPI2_SECURITY = NULL;
+api_auth *AGAVE_API_AUTH = 0;
+static JSAPI2Factory jsapi2Factory;
+HINSTANCE WASABI_API_LNG_HINST = NULL;
+HINSTANCE WASABI_API_ORIG_HINST = NULL;
+static BOOL fDefaultsLoaded = FALSE;
+static OnlineServicesURIHandler uri_handler;
+static SingletonServiceFactory<svc_urihandler, OnlineServicesURIHandler> uri_handler_factory;
+
+EXTERN_C const GUID pngLoaderGUID =
+{ 0x5e04fb28, 0x53f5, 0x4032, { 0xbd, 0x29, 0x3, 0x2b, 0x87, 0xec, 0x37, 0x25 } };
+
+void *Wasabi_QueryInterface(REFGUID interfaceGuid)
+{
+ waServiceFactory *serviceFactory = WASABI_API_SVC->service_getServiceByGuid(interfaceGuid);
+ return (NULL != serviceFactory) ? serviceFactory->getInterface() : NULL;
+}
+
+void Wasabi_ReleaseInterface(REFGUID interfaceGuid, void *pInstance)
+{
+ if (NULL == pInstance) return;
+ waServiceFactory *serviceFactory = WASABI_API_SVC->service_getServiceByGuid(interfaceGuid);
+ if (NULL != serviceFactory) serviceFactory->releaseInterface(pInstance);
+}
+
+
+HRESULT WasabiApi_Initialize(HINSTANCE hInstance, api_service *serviceApi)
+{
+ if (NULL != WASABI_API_SVC)
+ return S_FALSE;
+
+ fDefaultsLoaded = FALSE;
+
+ WASABI_API_SVC = serviceApi;
+ if ((api_service *)1 == WASABI_API_SVC)
+ WASABI_API_SVC = NULL;
+
+ if (NULL == WASABI_API_SVC)
+ return E_FAIL;
+
+ WASABI_API_ORIG_HINST = hInstance;
+ WASABI_API_LNG_HINST = WASABI_API_ORIG_HINST;
+
+ WasabiApi_AddRef();
+ return S_OK;
+}
+
+HRESULT WasabiApi_LoadDefaults()
+{
+ if (NULL == WASABI_API_SVC)
+ return E_UNEXPECTED;
+
+ if (FALSE != fDefaultsLoaded)
+ return S_FALSE;
+
+ WASABI_API_APP = QueryWasabiInterface(api_application, applicationApiServiceGuid);
+ WASABI_API_LNG = QueryWasabiInterface(api_language, languageApiGUID);
+ WASABI_API_EXPLORERFINDFILE = QueryWasabiInterface(api_explorerfindfile, ExplorerFindFileApiGUID);
+ AGAVE_API_CONFIG = QueryWasabiInterface(api_config, AgaveConfigGUID);
+ AGAVE_API_JSAPI2_SECURITY = QueryWasabiInterface(JSAPI2::api_security, JSAPI2::api_securityGUID);
+ OMBROWSERMNGR = QueryWasabiInterface(obj_ombrowser, OBJ_OmBrowser);
+ OMSERVICEMNGR = QueryWasabiInterface(ifc_omservicemanager, IFC_OmServiceManager);
+ OMUTILITY = QueryWasabiInterface(ifc_omutility, IFC_OmUtility);
+ AGAVE_API_AUTH = QueryWasabiInterface(api_auth, AuthApiGUID);
+
+ if (NULL != WASABI_API_LNG)
+ WASABI_API_LNG_HINST = WASABI_API_LNG->StartLanguageSupport(WASABI_API_ORIG_HINST, MlOnlineLangGUID);
+
+ WASABI_API_SVC->service_register(&jsapi2Factory);
+ uri_handler_factory.Register(WASABI_API_SVC, &uri_handler);
+
+ fDefaultsLoaded = TRUE;
+ return S_OK;
+}
+
+static void WasabiApi_Uninitialize()
+{
+ if (NULL != WASABI_API_SVC)
+ {
+ ReleaseWasabiInterface(applicationApiServiceGuid, WASABI_API_APP);
+ ReleaseWasabiInterface(languageApiGUID, WASABI_API_LNG);
+ ReleaseWasabiInterface(ExplorerFindFileApiGUID, WASABI_API_EXPLORERFINDFILE);
+ ReleaseWasabiInterface(AgaveConfigGUID, AGAVE_API_CONFIG);
+ ReleaseWasabiInterface(memMgrApiServiceGuid, WASABI_API_MEMMNGR);
+ ReleaseWasabiInterface(pngLoaderGUID, WASABI_API_PNGLOADER);
+ ReleaseWasabiInterface(JSAPI2::api_securityGUID, AGAVE_API_JSAPI2_SECURITY);
+ ReleaseWasabiInterface(AuthApiGUID, AGAVE_API_AUTH);
+ ReleaseWasabiInterface(OBJ_OmBrowser, OMBROWSERMNGR);
+ ReleaseWasabiInterface(IFC_OmServiceManager, OMSERVICEMNGR);
+ ReleaseWasabiInterface(IFC_OmUtility, OMUTILITY);
+ WASABI_API_SVC->service_deregister(&jsapi2Factory);
+ uri_handler_factory.Deregister(WASABI_API_SVC);
+ }
+
+ WASABI_API_SVC = NULL;
+ WASABI_API_APP = NULL;
+ WASABI_API_LNG = NULL;
+ WASABI_API_EXPLORERFINDFILE = NULL;
+ AGAVE_API_CONFIG = NULL;
+ AGAVE_API_JSAPI2_SECURITY = NULL;
+ AGAVE_API_AUTH = NULL;
+ OMBROWSERMNGR = NULL;
+ OMSERVICEMNGR = NULL;
+ OMUTILITY = NULL;
+
+ fDefaultsLoaded = FALSE;
+}
+
+ULONG WasabiApi_AddRef()
+{
+ return InterlockedIncrement((LONG*)&wasabiRef);
+}
+
+ULONG WasabiApi_Release()
+{
+ if (0 == wasabiRef)
+ return wasabiRef;
+
+ LONG r = InterlockedDecrement((LONG*)&wasabiRef);
+ if (0 == r)
+ {
+ WasabiApi_Uninitialize();
+ }
+ return r;
+} \ No newline at end of file