aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/General/gen_ml/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/General/gen_ml/main.cpp')
-rw-r--r--Src/Plugins/General/gen_ml/main.cpp1172
1 files changed, 1172 insertions, 0 deletions
diff --git a/Src/Plugins/General/gen_ml/main.cpp b/Src/Plugins/General/gen_ml/main.cpp
new file mode 100644
index 00000000..8fc9069c
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/main.cpp
@@ -0,0 +1,1172 @@
+#include "main.h"
+#include <windowsx.h>
+#include <time.h>
+#include <rpc.h>
+#include "../winamp/gen.h"
+#include "resource.h"
+#include "childwnd.h"
+#include "config.h"
+#include "../winamp/ipc_pe.h"
+#include "../winamp/wa_dlg.h"
+#include "../winamp/strutil.h"
+#include "ml.h"
+#include "ml_ipc.h"
+#include "./folderbrowser.h"
+#include "./mldwm.h"
+
+#ifndef _ML_HEADER_IMPMLEMENT
+#define _ML_HEADER_IMPMLEMENT
+#endif // _ML_HEADER_IMPMLEMENT
+#include "ml_ipc_0313.h"
+#undef _ML_HEADER_IMPMLEMENT
+
+#include "sendto.h"
+#include "../gen_hotkeys/wa_hotkeys.h"
+#include "MediaLibraryCOM.h"
+#include "../nu/CCVersion.h"
+#include "../nu/AutoWideFn.h"
+#include "../nu/htmlcontainer2.h"
+#include <shlwapi.h>
+
+#include "api__gen_ml.h"
+#include <api/service/waServiceFactory.h>
+#include "./navigation.h"
+//#include "./skinnedwnd.h"
+#include "./skinning.h"
+#include "../nu/ServiceWatcher.h"
+#include "MusicID.h"
+#include <tataki/export.h>
+#include <strsafe.h>
+#include "../Winamp/wasabicfg.h"
+
+// {6B0EDF80-C9A5-11d3-9F26-00C04F39FFC6}
+static const GUID library_guid =
+{ 0x6b0edf80, 0xc9a5, 0x11d3, { 0x9f, 0x26, 0x0, 0xc0, 0x4f, 0x39, 0xff, 0xc6 } };
+
+int m_calling_getfileinfo;
+int IPC_GETMLWINDOW, IPC_LIBRARY_SENDTOMENU, IPC_GET_ML_HMENU;
+int config_use_ff_scrollbars=1, config_use_alternate_colors=0;
+LARGE_INTEGER freq;
+C_Config *g_config;
+
+embedWindowState myWindowState;
+prefsDlgRecW myPrefsItem, myPrefsItemPlug;
+
+DEFINE_EXTERNAL_SERVICE(api_service, WASABI_API_SVC);
+DEFINE_EXTERNAL_SERVICE(api_application, WASABI_API_APP);
+DEFINE_EXTERNAL_SERVICE(api_language, WASABI_API_LNG);
+DEFINE_EXTERNAL_SERVICE(obj_ombrowser, AGAVE_OBJ_BROWSER);
+DEFINE_EXTERNAL_SERVICE(api_mldb, AGAVE_API_MLDB);
+DEFINE_EXTERNAL_SERVICE(api_syscb, WASABI_API_SYSCB);
+DEFINE_EXTERNAL_SERVICE(api_threadpool, AGAVE_API_THREADPOOL);
+DEFINE_EXTERNAL_SERVICE(api_decodefile, AGAVE_API_DECODE);
+DEFINE_EXTERNAL_SERVICE(wnd_api, WASABI_API_WND);
+DEFINE_EXTERNAL_SERVICE(api_skin, WASABI_API_SKIN);
+DEFINE_EXTERNAL_SERVICE(api_config, AGAVE_API_CONFIG);
+DEFINE_EXTERNAL_SERVICE(api_palette, WASABI_API_PALETTE);
+#ifndef IGNORE_API_GRACENOTE
+DEFINE_EXTERNAL_SERVICE(api_gracenote, AGAVE_API_GRACENOTE);
+#endif
+DEFINE_EXTERNAL_SERVICE(JSAPI2::api_security, AGAVE_API_JSAPI2_SECURITY);
+
+ifc_configitem *ieDisableSEH = 0;
+
+// wasabi based services for localisation support
+HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
+
+static ServiceWatcher serviceWatcher;
+#ifndef IGNORE_API_GRACENOTE
+MusicIDCOM musicIDCOM;
+#endif
+
+void config();
+void quit();
+int init();
+
+BOOL init2(void);
+
+extern "C"
+{
+ HWND g_hwnd, g_ownerwnd;
+
+ extern winampGeneralPurposePlugin plugin =
+ {
+ GPPHDR_VER_U,
+ "nullsoft(gen_ml.dll)",
+ init,
+ config,
+ quit,
+ };
+};
+HWND g_PEWindow;
+
+HMENU wa_main_menu = NULL;
+HMENU wa_windows_menu = NULL;
+HMENU wa_playlists_cmdmenu = NULL;
+HMENU last_playlistsmenu = NULL;
+HMENU last_viewmenu = NULL;
+int last_viewmenu_insert = 0;
+int g_safeMode = 0, sneak = 0;
+
+HCURSOR hDragNDropCursor;
+int profile = 0;
+
+wchar_t pluginPath[MAX_PATH] = {0};
+static wchar_t preferencesName[128];
+
+
+HMENU g_context_menus;
+
+extern C_ItemList m_plugins;
+extern HNAVCTRL hNavigation;
+
+//xp theme disabling shit
+static HMODULE m_uxdll;
+HRESULT (__stdcall *SetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
+BOOL (__stdcall *IsAppThemed)(void);
+
+template <class api_T>
+void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
+{
+ if (WASABI_API_SVC)
+ {
+ waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ api_t = reinterpret_cast<api_T *>( factory->getInterface() );
+ }
+}
+
+template <class api_T>
+void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
+{
+ if (WASABI_API_SVC && api_t)
+ {
+ waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t);
+ if (factory)
+ factory->releaseInterface(api_t);
+ }
+ api_t = NULL;
+}
+
+bool IsVisible()
+{
+ return g_hwnd && IsWindowVisible(g_ownerwnd);
+}
+
+void MLVisibleChanged(BOOL fVisible)
+{
+ static BOOL visible = FALSE;
+ if (fVisible != visible)
+ {
+ visible = fVisible;
+ plugin_SendMessage(ML_MSG_MLVISIBLE, visible, 0, 0);
+ }
+}
+
+BOOL MlWindow_SetMinimizedMode(BOOL fMinimized)
+{
+ if (FALSE != fMinimized)
+ return SetPropW(g_ownerwnd, L"MLWindow_MinimizedMode", (HANDLE)1);
+
+ RemovePropW(g_ownerwnd, L"MLWindow_MinimizedMode");
+ return TRUE;
+}
+
+BOOL MlWindow_IsMinimizedMode(void)
+{
+ return (0 != GetPropW(g_ownerwnd, L"MLWindow_MinimizedMode"));
+}
+
+void toggleVisible(int closecb)
+{
+ BOOL fVisible, fMinimized;
+ HWND rootWindow;
+ fVisible = (0 != (WS_VISIBLE & GetWindowLongPtrW(g_ownerwnd, GWL_STYLE)));//IsWindowVisible(g_ownerwnd);
+
+ rootWindow = GetAncestor(g_ownerwnd, GA_ROOT);
+ if (NULL == rootWindow || rootWindow == g_ownerwnd)
+ {
+ rootWindow = (HWND)(HWND)SENDWAIPC(plugin.hwndParent, IPC_GETDIALOGBOXPARENT, 0);
+ if (NULL == rootWindow)
+ rootWindow = plugin.hwndParent;
+ }
+
+ fMinimized = IsIconic(rootWindow);
+
+ if (FALSE != fVisible || 1 == closecb)
+ {
+ if (FALSE == fMinimized && FALSE != fVisible)
+ {
+ HWND hwndFocus = GetFocus();
+
+ if (hwndFocus == g_ownerwnd || IsChild(g_ownerwnd, hwndFocus))
+ SendMessageW(plugin.hwndParent, WM_COMMAND, WINAMP_NEXT_WINDOW, 0);
+
+ ShowWindow(g_ownerwnd, SW_HIDE);
+ }
+ }
+ else
+ {
+ if (init2() && FALSE == fMinimized && FALSE == fVisible)
+ {
+ ShowWindow(g_ownerwnd, SW_SHOWNORMAL);
+ // make sure that we focus the tree to work around some modern skin quirks
+ if(closecb != 2)
+ {
+ SetFocus(NavCtrlI_GetHWND(hNavigation));
+ }
+ else
+ {
+ // delay the focusing on loading as some machines are too fast and
+ // may cause the wrong view to the selected (root instead of child)
+ PostMessage(g_ownerwnd,WM_ML_IPC,0,ML_IPC_FOCUS_TREE);
+ }
+ }
+ }
+
+ if (FALSE != fMinimized && 1 != closecb)
+ {
+ MlWindow_SetMinimizedMode(TRUE);
+
+ if (NULL != g_config)
+ g_config->WriteInt(L"visible", (FALSE == fVisible));
+
+ UINT menuFlags = (FALSE == fVisible) ? MF_CHECKED : MF_UNCHECKED;
+ menuFlags |= MF_BYCOMMAND;
+
+ INT szMenu[] = { 0, 4, };
+ for (INT i = 0; i < ARRAYSIZE(szMenu); i++)
+ {
+ HMENU hMenu = (HMENU)SendMessage(plugin.hwndParent, WM_WA_IPC, szMenu[i], IPC_GET_HMENU);
+ if (NULL != hMenu)
+ CheckMenuItem(hMenu, WA_MENUITEM_ID, menuFlags);
+ }
+ }
+}
+
+
+static WNDPROC wa_oldWndProc;
+
+static BOOL Winamp_OnIPC(HWND hwnd, UINT uMsg, INT_PTR param, LRESULT *pResult)
+{
+ if (IPC_GETMLWINDOW == uMsg && IPC_GETMLWINDOW > 65536)
+ {
+ if (param == -1 && !g_hwnd) init2();
+ *pResult = (LRESULT)g_hwnd;
+ return TRUE;
+ }
+ else if (IPC_LIBRARY_SENDTOMENU == uMsg && IPC_LIBRARY_SENDTOMENU > 65536)
+ {
+ librarySendToMenuStruct *s = (librarySendToMenuStruct*)param;
+ if (!s || s->mode == 0)
+ {
+ *pResult = 0xFFFFFFFF;
+ return TRUE;
+ }
+ if (s->mode == 1)
+ {
+ if (!s->ctx[0])
+ {
+ if (!g_hwnd) init2();
+ SendToMenu *stm = new SendToMenu();
+ if (s->build_start_id && s->build_end_id)
+ {
+ stm->buildmenu(s->build_hMenu, s->data_type, s->ctx[1], s->ctx[2], s->build_start_id, s->build_end_id);
+ }
+ else
+ {
+ stm->buildmenu(s->build_hMenu, s->data_type, s->ctx[1], s->ctx[2]);
+ }
+ s->ctx[0] = (intptr_t)stm;
+ *pResult = 0xFFFFFFFF;
+ return TRUE;
+ }
+ }
+ else if (s->mode == 2)
+ {
+ SendToMenu *stm = (SendToMenu *)s->ctx[0];
+ if (stm && stm->isourcmd(s->menu_id))
+ {
+ *pResult = 0xFFFFFFFF;
+ return TRUE;
+ }
+ }
+ else if (s->mode == 3)
+ {
+ SendToMenu *stm = (SendToMenu *)s->ctx[0];
+ if (stm)
+ {
+ *pResult = stm->handlecmd(s->hwnd, s->menu_id, s->data_type, s->data);
+ return TRUE;
+ }
+ }
+ else if (s->mode == 4)
+ {
+ delete (SendToMenu *)s->ctx[0];
+ s->ctx[0] = 0;
+ }
+ *pResult = TRUE;
+ return TRUE;
+ }
+ else if (IPC_GET_ML_HMENU == uMsg && IPC_GET_ML_HMENU > 65536)
+ {
+ *pResult = (LRESULT)g_context_menus;
+ return TRUE;
+ }
+
+ switch(uMsg)
+ {
+ case IPC_CB_RESETFONT:
+ PostMessageW(g_hwnd, WM_DISPLAYCHANGE, 0, 0);
+ break;
+
+ case IPC_CB_GETTOOLTIPW:
+ if (param == 16 && g_config->ReadInt(L"attachlbolt", 0))
+ {
+ static wchar_t tlStr[64];
+ *pResult = (LRESULT)WASABI_API_LNGSTRINGW_BUF(IDS_TOGGLE_LIBRARY,tlStr,64);
+ return TRUE;
+ }
+ break;
+
+ case IPC_GET_EXTENDED_FILE_INFO_HOOKABLE:
+ if (!m_calling_getfileinfo)
+ {
+ extendedFileInfoStruct *extendedInfo;
+ extendedInfo = (extendedFileInfoStruct*)param;
+ if (NULL != extendedInfo &&
+ NULL != extendedInfo->filename &&
+ NULL != extendedInfo->metadata)
+ {
+ if (plugin_SendMessage(ML_IPC_HOOKEXTINFO, param, 0, 0))
+ {
+ *pResult = 1;
+ return TRUE;
+ }
+ }
+ }
+ break;
+
+ case IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE:
+ if (!m_calling_getfileinfo)
+ {
+ extendedFileInfoStructW *extendedInfo;
+ extendedInfo = (extendedFileInfoStructW*)param;
+ if (NULL != extendedInfo &&
+ NULL != extendedInfo->filename &&
+ NULL != extendedInfo->metadata)
+ {
+ if (plugin_SendMessage(ML_IPC_HOOKEXTINFOW, param, 0, 0))
+ {
+ *pResult = 1;
+ return TRUE;
+ }
+ }
+ }
+ break;
+
+ case IPC_HOOK_TITLES:
+ if (NULL != param)
+ {
+ waHookTitleStruct *hookTitle;
+ hookTitle = (waHookTitleStruct*)param;
+ if (NULL != hookTitle->filename &&
+ plugin_SendMessage(ML_IPC_HOOKTITLE, param, 0, 0))
+ {
+ *pResult = 1;
+ return TRUE;
+ }
+ }
+ break;
+
+ case IPC_HOOK_TITLESW:
+ if (NULL != param)
+ {
+ waHookTitleStructW *hookTitle;
+ hookTitle = (waHookTitleStructW*)param;
+ if (NULL != hookTitle->filename &&
+ plugin_SendMessage(ML_IPC_HOOKTITLEW, param, 0, 0))
+ {
+ *pResult = 1;
+ return TRUE;
+ }
+ }
+ break;
+
+ case IPC_ADD_PREFS_DLG:
+ case IPC_ADD_PREFS_DLGW:
+ if (param && !((prefsDlgRec*)param)->where)
+ {
+ prefsDlgRec *p = (prefsDlgRec *)param;
+ // we use the dialog proc for the preferences to determine the hinstance of the module and
+ // use that to then determine if we set it as a child of the media library preference node
+ // it also handles localised versions of the preference pages as the dialog proceedure is
+ // going to be in the true plug-in dll and so can be matched to the main ml plugins list!
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ if(VirtualQuery(p->proc, &mbi, sizeof(mbi)))
+ {
+ int i = m_plugins.GetSize();
+ while (i-- > 0)
+ {
+ winampMediaLibraryPlugin *mlplugin = (winampMediaLibraryPlugin *)m_plugins.Get(i);
+ if (mlplugin->hDllInstance == (HINSTANCE)mbi.AllocationBase)
+ {
+ p->where = (intptr_t)(INT_PTR)&myPrefsItem;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case IPC_CB_ONSHOWWND:
+ if ((HWND)param == g_ownerwnd) MLVisibleChanged(TRUE);
+ break;
+
+ case IPC_HOOK_OKTOQUIT:
+ {
+ if (plugin_SendMessage(ML_MSG_NOTOKTOQUIT, 0, 0, 0))
+ {
+ *pResult = 0;
+ return TRUE;
+ }
+ }
+ break;
+
+ case IPC_PLAYING_FILEW:
+ plugin_SendMessage(ML_MSG_PLAYING_FILE, param, 0, 0);
+ break;
+
+ case IPC_WRITECONFIG:
+ plugin_SendMessage(ML_MSG_WRITE_CONFIG, param, 0, 0);
+ break;
+ }
+ return FALSE;
+}
+
+static LRESULT WINAPI wa_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg)
+ {
+ // far from ideal fix but deals with differing plugin load orders (mainly from FAT32 drives)
+ // and not being able to unload/clean up properly the scrollbar bitmaps used - DRO 29/09/07
+ case WM_CLOSE:
+ SkinnedScrollWnd_Quit();
+ break;
+ case WM_WA_IPC:
+ {
+ LRESULT result = 0;
+ if (Winamp_OnIPC(hwndDlg, (UINT)lParam, (INT_PTR)wParam, &result)) return result;
+ break;
+ }
+ case WM_SIZE:
+ if (wParam == SIZE_RESTORED)
+ {
+ if (FALSE != MlWindow_IsMinimizedMode())
+ {
+ MlWindow_SetMinimizedMode(FALSE);
+ int showCommand = (0 != g_config->ReadInt(L"visible", 1)) ? SW_SHOWNA : SW_HIDE;
+ ShowWindow(g_ownerwnd, showCommand);
+ }
+ }
+ break;
+ case WM_COMMAND:
+ case WM_SYSCOMMAND:
+ {
+ WORD lowP = LOWORD(wParam);
+ if (lowP == WA_MENUITEM_ID || lowP == WINAMP_LIGHTNING_CLICK)
+ {
+ if (lowP != WINAMP_LIGHTNING_CLICK || g_config->ReadInt(L"attachlbolt", 0))
+ {
+ toggleVisible();
+ return 0;
+ }
+ }
+ #if 0 // no radio - don't delete yet - tag will need to do this in ml_online
+ else if (lowP == WINAMP_VIDEO_TVBUTTON) // && g_config->ReadInt("attachtv",1))
+ {
+ if (!g_hwnd || !IsWindowVisible(g_ownerwnd)) toggleVisible();
+ PostMessage(g_ownerwnd, WM_NEXTDLGCTL, (WPARAM)g_hwnd, TRUE);
+ HWND hwndTree = GetTreeHWND(g_hwnd);
+ HTREEITEM hti = findByParam(hwndTree, TREE_INTERNET_VIDEO, TVI_ROOT);
+ if (hti)
+ {
+ TreeView_SelectItem(hwndTree, hti);
+ return 0;
+ }
+ }
+ #endif
+ // done like this since ml_online can't really subclass winamp to get the notification
+ else if (lowP == WINAMP_VIDEO_TVBUTTON)
+ {
+ if (!g_hwnd || !IsWindowVisible(g_ownerwnd)) toggleVisible();
+ HNAVITEM hDefItem = NavCtrlI_FindItemByName(hNavigation, LOCALE_USER_DEFAULT, NICF_INVARIANT_I | NICF_DISPLAY_I | NICF_IGNORECASE_I, L"Shoutcast TV", -1);
+
+ if(!hDefItem)
+ {
+ // work with the localised version of the Online Services root (if there...)
+ wchar_t OSName[64] = {L"Online Services"};
+ WASABI_API_LNG->GetStringFromGUIDW(MlOnlineLangGUID,WASABI_API_ORIG_HINST,1,OSName,64);
+
+ // just incase the localised dll was there but the file was missing the translation
+ if(!lstrcmpiW(OSName,L"Error loading string"))
+ lstrcpynW(OSName,L"Online Services",64);
+
+ hDefItem = NavCtrlI_FindItemByName(hNavigation, LOCALE_USER_DEFAULT, NICF_INVARIANT_I | NICF_DISPLAY_I | NICF_IGNORECASE_I, OSName, -1);
+ }
+ if (hDefItem)
+ {
+ NavItemI_Select(hDefItem);
+ NavCtrlI_Show(hNavigation, SW_SHOWNA);
+ }
+ else
+ {
+ wchar_t titleStr[128] = {0};
+ MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_ONLINE_SERVICES_NOT_PRESENT),
+ WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SWITCHING_TO_VIEW,titleStr,128),0);
+ }
+ return 0;
+ }
+ if (lowP == WINAMP_SHOWLIBRARY)
+ {
+ if (!g_hwnd || !IsWindowVisible(g_hwnd))
+ toggleVisible((2 == HIWORD(wParam) ? 2 : 0));
+ }
+ else if (lowP == WINAMP_CLOSELIBRARY)
+ {
+ if (g_hwnd && IsWindowVisible(g_ownerwnd)) toggleVisible();
+ }
+ }
+ break;
+ case WM_DWMCOMPOSITIONCHANGED:
+ if (IsWindow(g_hwnd)) PostMessageW(g_hwnd, WM_DWMCOMPOSITIONCHANGED, 0, 0L);
+ break;
+ }
+
+ return CallWindowProcW(wa_oldWndProc, hwndDlg, uMsg, wParam, lParam);
+}
+
+INT_PTR CALLBACK dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL init2(void)
+{
+ if (!g_hwnd)
+ {
+ WADlg_init(plugin.hwndParent);
+
+ //xp theme disabling shit
+ m_uxdll = LoadLibraryA("uxtheme.dll");
+ if (m_uxdll)
+ {
+ IsAppThemed = (BOOL (__stdcall *)(void))GetProcAddress(m_uxdll, "IsAppThemed");
+ SetWindowTheme = (HRESULT (__stdcall *)(struct HWND__ *, LPCWSTR , LPCWSTR ))GetProcAddress(m_uxdll, "SetWindowTheme");
+ }
+ else
+ {
+ IsAppThemed = NULL;
+ SetWindowTheme = NULL;
+ }
+
+ g_context_menus = WASABI_API_LOADMENU(IDR_CONTEXTMENUS);
+
+ // 02/11/08 DrO
+ // defaults were 100,100,500,400 and not visible but these now make it align when opened under
+ // a clean install starting with a classic skin and is also visible on start now as with modern
+ myWindowState.r.left = g_config->ReadInt(L"mw_xpos", 301);
+ myWindowState.r.top = g_config->ReadInt(L"mw_ypos", 29);
+ myWindowState.r.right = myWindowState.r.left + g_config->ReadInt(L"mw_width", 500);
+ myWindowState.r.bottom = myWindowState.r.top + g_config->ReadInt(L"mw_height", 348);
+ SET_EMBED_GUID((&myWindowState), library_guid);
+
+ g_ownerwnd = (HWND)SendMessage(plugin.hwndParent, WM_WA_IPC, (LPARAM) & myWindowState, IPC_GET_EMBEDIF);
+ if (!g_ownerwnd) return FALSE;
+
+ if (NULL != WASABI_API_APP) WASABI_API_APP->app_registerGlobalWindow(g_ownerwnd);
+
+ SetWindowTextW(g_ownerwnd, WASABI_API_LNGSTRINGW(IDS_WINAMP_LIBRARY));
+ g_hwnd = WASABI_API_CREATEDIALOGW(IDD_MAIN, g_ownerwnd, dialogProc);
+ if (!g_hwnd)
+ {
+ DestroyWindow(g_ownerwnd);
+ g_ownerwnd = NULL;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+wchar_t WINAMP_INI[MAX_PATH] = {0}, WINAMP_INI_DIR[MAX_PATH] = {0};
+MediaLibraryCOM mediaLibraryCOM;
+IDispatch *winampExternal = 0;
+
+void TAG_FMT_EXT(const wchar_t *filename, void *f, void *ff, void *p, wchar_t *out, int out_len, int extended)
+{
+ waFormatTitleExtended fmt;
+ fmt.filename=filename;
+ fmt.useExtendedInfo=extended;
+ fmt.out = out;
+ fmt.out_len = out_len;
+ fmt.p = p;
+ fmt.spec = 0;
+ *(void **)&fmt.TAGFUNC = f;
+ *(void **)&fmt.TAGFREEFUNC = ff;
+ *out = 0;
+
+ int oldCallingGetFileInfo=m_calling_getfileinfo;
+ m_calling_getfileinfo=1;
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&fmt, IPC_FORMAT_TITLE_EXTENDED);
+ m_calling_getfileinfo=oldCallingGetFileInfo;
+}
+
+wchar_t *itemrecordTagFunc(wchar_t *tag, void * p) //return 0 if not found
+{
+ itemRecord *t = (itemRecord *)p;
+ char buf[128] = {0};
+ char *value = NULL;
+
+ if (!_wcsicmp(tag, L"artist")) value = t->artist;
+ else if (!_wcsicmp(tag, L"album")) value = t->album;
+ else if (!_wcsicmp(tag, L"filename")) value = t->filename;
+ else if (!_wcsicmp(tag, L"title")) value = t->title;
+ else if ( !_wcsicmp( tag, L"ext" ) ) value = t->ext;
+ else if (!_wcsicmp(tag, L"year"))
+ {
+ if (t->year > 0)
+ {
+ StringCchPrintfA(buf, 128, "%04d", t->year);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"genre")) value = t->genre;
+ else if (!_wcsicmp(tag, L"comment")) value = t->comment;
+ else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track"))
+ {
+ if (t->track > 0)
+ {
+ StringCchPrintfA(buf, 128, "%02d", t->track);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"rating")) value = getRecordExtendedItem(t, "RATING");
+ else if (!_wcsicmp(tag, L"playcount")) value = getRecordExtendedItem(t, "PLAYCOUNT");
+ else if (!_wcsicmp(tag, L"bitrate")) value = getRecordExtendedItem(t, "BITRATE");
+ else
+ return 0;
+
+ if (!value) return reinterpret_cast<wchar_t *>(-1);
+ else return AutoWideDup(value);
+}
+
+wchar_t *itemrecordWTagFunc(wchar_t *tag, void * p) //return 0 if not found
+{
+ itemRecordW *t = (itemRecordW *)p;
+ wchar_t buf[128] = {0};
+ wchar_t *value = NULL;
+
+ // TODO: more fields
+ if (!_wcsicmp(tag, L"artist")) value = t->artist;
+ else if (!_wcsicmp(tag, L"album")) value = t->album;
+ else if (!_wcsicmp(tag, L"albumartist")) value = t->albumartist;
+ else if (!_wcsicmp(tag, L"category")) value = t->category;
+ else if (!_wcsicmp(tag, L"comment")) value = t->comment;
+ else if (!_wcsicmp(tag, L"composer")) value = t->composer;
+ else if (!_wcsicmp(tag, L"publisher")) value = t->publisher;
+ else if (!_wcsicmp(tag, L"filename")) value = t->filename;
+ else if (!_wcsicmp(tag, L"title")) value = t->title;
+ else if (!_wcsicmp(tag, L"year"))
+ {
+ if (t->year > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%04d", t->year);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"genre")) value = t->genre;
+ else if (!_wcsicmp(tag, L"comment")) value = t->comment;
+ else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track"))
+ {
+ if (t->track > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%02d", t->track);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"rating"))
+ {
+ if (t->rating > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%d", t->rating);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"playcount"))
+ {
+ if (t->playcount > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%d", t->playcount);
+ value = buf;
+ }
+ }
+ else if (!_wcsicmp(tag, L"bitrate"))
+ {
+ if (t->bitrate > 0)
+ {
+ StringCchPrintfW(buf, 128, L"%d", t->bitrate);
+ value = buf;
+ }
+ }
+ else
+ return 0;
+
+ if (!value) return reinterpret_cast<wchar_t *>(-1);
+ else return _wcsdup(value);
+}
+
+
+void fieldTagFuncFree(wchar_t * tag, void * p)
+{
+ free(tag);
+}
+
+void main_playItemRecordList(itemRecordList *obj, int enqueue, int startplaying)
+{
+ if (obj->Size && !enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE);
+
+ int x;
+ wchar_t title[2048]=L"";
+
+ for (x = 0; x < obj->Size; x ++)
+ {
+ if (obj->Items[x].filename && *obj->Items[x].filename)
+ {
+ AutoWideFn wfn( obj->Items[ x ].filename );
+
+ TAG_FMT_EXT(wfn, itemrecordTagFunc, fieldTagFuncFree, (void*)&obj->Items[x], title, 2048, 1);
+
+ {
+ enqueueFileWithMetaStructW s;
+ s.filename = wfn;
+ s.title = title;
+ s.ext = NULL;
+ s.length = obj->Items[x].length;
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW);
+ }
+ }
+ }
+
+ if (obj->Size && !enqueue && startplaying) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY);
+}
+
+void main_playItemRecordListW(itemRecordListW *obj, int enqueue, int startplaying)
+{
+ if (obj->Size && !enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE);
+
+ int x;
+ wchar_t title[2048]=L"";
+
+ for (x = 0; x < obj->Size; x ++)
+ {
+ if (obj->Items[x].filename && *obj->Items[x].filename)
+ {
+ TAG_FMT_EXT(obj->Items[x].filename, itemrecordWTagFunc, fieldTagFuncFree, (void*)&obj->Items[x], title, 2048, 1);
+ {
+ enqueueFileWithMetaStructW s;
+ s.filename = obj->Items[x].filename;
+ s.title = title;
+ s.ext = NULL;
+ s.length = obj->Items[x].length;
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW);
+ }
+ }
+ }
+
+ if (obj->Size && !enqueue && startplaying) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY);
+}
+
+void OpenMediaLibraryPreferences()
+{
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItem, IPC_OPENPREFSTOPAGE);
+}
+
+int AddTreeImageBmp(int resourceId)
+{
+ HMLIMGLST hmlilNavigation = MLNavCtrl_GetImageList(g_hwnd);
+ MLIMAGESOURCE mlis = {sizeof(MLIMAGESOURCE),0};
+ MLIMAGELISTITEM item = {0};
+ item.cbSize = sizeof(MLIMAGELISTITEM);
+ item.hmlil = hmlilNavigation;
+ item.filterUID = MLIF_FILTER1_UID;
+ item.pmlImgSource = &mlis;
+
+ mlis.hInst = WASABI_API_ORIG_HINST;
+ mlis.bpp = 24;
+ mlis.lpszName = MAKEINTRESOURCEW(resourceId);
+ mlis.type = SRC_TYPE_BMP;
+ mlis.flags = ISF_FORCE_BPP;
+ return MLImageList_Add(g_hwnd, &item);
+}
+
+void SkinnedScrollWnd_Init();
+void SkinnedScrollWnd_Quit();
+int init()
+{
+ wchar_t g_path[MAX_PATH] = {0};
+ QueryPerformanceFrequency(&freq);
+
+ WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
+ if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1)
+ return GEN_INIT_FAILURE;
+
+ HTMLContainer2_Initialize();
+ Tataki::Init(WASABI_API_SVC);
+
+ // loader so that we can get the localisation service api for use
+ ServiceBuild(WASABI_API_LNG, languageApiGUID);
+ ServiceBuild(WASABI_API_SYSCB, syscbApiServiceGuid);
+ ServiceBuild(AGAVE_API_DECODE, decodeFileGUID);
+ ServiceBuild(AGAVE_API_JSAPI2_SECURITY, JSAPI2::api_securityGUID);
+ ServiceBuild(AGAVE_OBJ_BROWSER, OBJ_OmBrowser);
+ #ifndef IGNORE_API_GRACENOTE
+ ServiceBuild(AGAVE_API_GRACENOTE, gracenoteApiGUID);
+ #endif
+ ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceBuild(WASABI_API_PALETTE, PaletteManagerGUID);
+ ServiceBuild(AGAVE_API_THREADPOOL, ThreadPoolGUID);
+ // no guarantee that AGAVE_API_MLDB will be available yet, so we'll start a watcher for it
+ serviceWatcher.WatchWith(WASABI_API_SVC);
+ serviceWatcher.WatchFor(&AGAVE_API_MLDB, mldbApiGuid);
+ serviceWatcher.WatchFor(&WASABI_API_SKIN, skinApiServiceGuid);
+ serviceWatcher.WatchFor(&WASABI_API_WND,wndApiServiceGuid);
+ WASABI_API_SYSCB->syscb_registerCallback(&serviceWatcher);
+ SkinnedScrollWnd_Init();
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,GenMlLangGUID);
+
+ // Build plugin description string...
+ static wchar_t szDescription[256];
+ StringCchPrintfW(szDescription, ARRAYSIZE(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_NULLSOFT_ML_STR),
+ LOWORD(PLUGIN_VERSION) >> 8,
+ PLUGIN_VERSION & 0xFF);
+ plugin.description = (char*)szDescription;
+
+ DispatchInfo dispatchInfo;
+ dispatchInfo.name = L"MediaLibrary";
+ dispatchInfo.dispatch = &mediaLibraryCOM;
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&dispatchInfo, IPC_ADD_DISPATCH_OBJECT);
+
+ #ifndef IGNORE_API_GRACENOTE
+ dispatchInfo.name = L"MusicID";
+ dispatchInfo.dispatch = &musicIDCOM;
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&dispatchInfo, IPC_ADD_DISPATCH_OBJECT);
+ #endif
+
+ IPC_LIBRARY_SENDTOMENU = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE);
+ IPC_GETMLWINDOW = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibraryGetWnd", IPC_REGISTER_WINAMP_IPCMESSAGE);
+ IPC_GET_ML_HMENU = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibraryGetHmenu", IPC_REGISTER_WINAMP_IPCMESSAGE);
+
+ lstrcpynW(WINAMP_INI, (wchar_t*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIFILEW), MAX_PATH);
+ lstrcpynW(WINAMP_INI_DIR, (wchar_t*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW), MAX_PATH);
+
+ PathCombineW(g_path, WINAMP_INI_DIR, L"Plugins");
+ CreateDirectoryW(g_path, NULL);
+
+ wchar_t *dir = (wchar_t*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW);
+ if (dir == (wchar_t *)1 || dir == 0)
+ lstrcpynW(pluginPath, g_path, MAX_PATH);
+ else
+ lstrcpynW(pluginPath, dir, MAX_PATH);
+
+ hDragNDropCursor = LoadCursor(plugin.hDllInstance, MAKEINTRESOURCE(ML_IDC_DRAGDROP));
+ profile = GetPrivateProfileIntW(L"winamp", L"profile", 0, WINAMP_INI);
+
+ wchar_t configName[1024 + 32] = {0};
+ StringCchPrintfW(configName, 1024 + 32, L"%s\\gen_ml.ini", g_path);
+ g_config = new C_Config(configName);
+ config_use_ff_scrollbars = g_config->ReadInt(L"ffsb", 1);
+ config_use_alternate_colors = g_config->ReadInt(L"alternate_items", 1);
+
+ int vis = g_config->ReadInt(L"visible", 1);
+ wa_main_menu = (HMENU)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_HMENU);
+ wa_windows_menu = (HMENU)SendMessage(plugin.hwndParent, WM_WA_IPC, 4, IPC_GET_HMENU);
+ wa_playlists_cmdmenu = NULL;
+ if (wa_main_menu || wa_windows_menu)
+ {
+ if (wa_main_menu)
+ {
+ MENUITEMINFOW i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, vis ? (UINT)MFS_CHECKED : 0, WA_MENUITEM_ID};
+ int prior_item = GetMenuItemID(wa_main_menu,9);
+ if(prior_item <= 0) prior_item = GetMenuItemID(wa_main_menu,8);
+ i.dwTypeData = WASABI_API_LNGSTRINGW(IDS_ML_ALT_L_SHORTCUT);
+
+ // append before the video menu entry (more reliable than inserting into position '9' in the menu
+ InsertMenuItemW(wa_main_menu, prior_item, FALSE, &i);
+ SendMessage(plugin.hwndParent, WM_WA_IPC, 1, IPC_ADJUST_OPTIONSMENUPOS);
+ }
+
+ if (wa_windows_menu)
+ {
+ MENUITEMINFOW i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, vis ? (UINT)MFS_CHECKED : 0, WA_MENUITEM_ID};
+ int prior_item = GetMenuItemID(wa_windows_menu,3);
+ if(prior_item <= 0) prior_item = GetMenuItemID(wa_windows_menu,2);
+ i.dwTypeData = WASABI_API_LNGSTRINGW(IDS_ML_ALT_L_SHORTCUT);
+ InsertMenuItemW(wa_windows_menu, prior_item, FALSE, &i);
+ SendMessage(plugin.hwndParent, WM_WA_IPC, 1, IPC_ADJUST_FFWINDOWSMENUPOS);
+ }
+ }
+
+ // subclass the winamp window to get our leet menu item to work
+ wa_oldWndProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(plugin.hwndParent, GWLP_WNDPROC, (LONGX86)(LONG_PTR)wa_newWndProc);
+
+ myPrefsItem.dlgID = IDD_PREFSFR;
+ myPrefsItem.name = WASABI_API_LNGSTRINGW_BUF(IDS_MEDIA_LIBRARY,preferencesName,128);
+ myPrefsItem.proc = (void*)PrefsProc;
+ myPrefsItem.hInst = WASABI_API_LNG_HINST;
+ myPrefsItem.where = -6; // to become root based item
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItem, IPC_ADD_PREFS_DLGW);
+
+ myPrefsItemPlug.dlgID = IDD_MLPLUGINS;
+ myPrefsItemPlug.name = preferencesName;
+ myPrefsItemPlug.proc = (void*)PluginsProc;
+ myPrefsItemPlug.hInst = WASABI_API_LNG_HINST;
+ myPrefsItemPlug.where = 1;
+ SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItemPlug, IPC_ADD_PREFS_DLGW);
+
+ g_PEWindow = (HWND)SendMessage(plugin.hwndParent, WM_WA_IPC, IPC_GETWND_PE, IPC_GETWND);
+ g_safeMode = SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_IS_SAFEMODE);
+
+ // we're gonna go ahead and make this directory just to be safe.
+ // if a plugin tries to use it as an INI directory but it doesn't exist, things go wrong
+ wchar_t mldir[MAX_PATH] = {0};
+ PathCombineW(mldir, g_path, L"ml");
+ CreateDirectoryW(mldir, NULL);
+ PathCombineW(mldir, mldir, L"views");
+ CreateDirectoryW(mldir, NULL);
+
+ //add general hotkey
+ int m_genhotkeys_add_ipc = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"GenHotkeysAdd", IPC_REGISTER_WINAMP_IPCMESSAGE);
+
+ static genHotkeysAddStruct ghas = {
+ (char*)_wcsdup(WASABI_API_LNGSTRINGW(IDS_ML_GHK_STR)),
+ HKF_BRING_TO_FRONT|HKF_UNICODE_NAME,
+ WM_COMMAND,
+ WA_MENUITEM_ID,
+ 0,
+ // specifically set the id str now so that it'll work correctly with whatever lngpack is in use
+ "ML: Show/Hide Media Library"
+ };
+ if (m_genhotkeys_add_ipc > 65536) PostMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&ghas, m_genhotkeys_add_ipc); //post so gen_hotkeys will catch it if not inited yet
+
+ init2();
+
+ // register the art view window classes
+ {
+ extern void InitSmoothScrollList();
+ extern void InitHeaderIconList();
+ InitSmoothScrollList();
+ InitHeaderIconList();
+ RegisterFolderBrowserControl(plugin.hDllInstance);
+ }
+
+ NavCtrlI_BeginUpdate(hNavigation, NUF_LOCK_NONE_I);
+ loadMlPlugins();
+
+#if 0
+ #ifdef _DEBUG
+ #define BETA
+ #endif
+ #ifdef BETA
+ sneak = GetPrivateProfileIntW(L"winamp", L"sneak", 0, WINAMP_INI);
+ if (!(sneak & 1))
+ {
+ NAVINSERTSTRUCT nis = {0};
+ nis.item.cbSize = sizeof(NAVITEM);
+ nis.item.pszText = L"Winamp Labs";
+ nis.item.pszInvariant = L"winamp_labs";
+ nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL | NIMF_STYLE;
+ nis.item.iImage = nis.item.iSelectedImage = AddTreeImageBmp(IDB_TREEITEM_LABS);
+ nis.item.style = NIS_BOLD;
+ nis.hInsertAfter = NCI_FIRST;
+ NAVITEM nvItem = {sizeof(NAVITEM),0,NIMF_ITEMID,};
+ nvItem.hItem = MLNavCtrl_InsertItem(g_hwnd, &nis);
+ }
+ #endif
+#endif
+
+ NavCtrlI_EndUpdate(hNavigation);
+
+ if (SW_SHOWMINIMIZED == SENDWAIPC(plugin.hwndParent, IPC_INITIAL_SHOW_STATE, 0))
+ {
+ MlWindow_SetMinimizedMode(TRUE);
+ }
+ else if (0 != vis)
+ {
+ PostMessageW(plugin.hwndParent, WM_COMMAND, MAKEWPARAM(WINAMP_SHOWLIBRARY, 2), 0L);
+ }
+
+ return GEN_INIT_SUCCESS;
+}
+
+
+void quit()
+{
+ serviceWatcher.StopWatching();
+ serviceWatcher.Clear();
+
+ MlWindow_SetMinimizedMode(FALSE);
+
+ if (g_ownerwnd)
+ {
+ g_config->WriteInt(L"mw_xpos", myWindowState.r.left);
+ g_config->WriteInt(L"mw_ypos", myWindowState.r.top);
+ g_config->WriteInt(L"mw_width", myWindowState.r.right - myWindowState.r.left);
+ g_config->WriteInt(L"mw_height", myWindowState.r.bottom - myWindowState.r.top);
+
+ if (NULL != WASABI_API_APP) WASABI_API_APP->app_unregisterGlobalWindow(g_ownerwnd);
+ DestroyWindow(g_ownerwnd);
+ g_ownerwnd = NULL;
+ }
+
+ // unload any services from ml_ plugins before unloading the plugins
+ ServiceRelease(AGAVE_API_MLDB, mldbApiGuid);
+
+ #ifndef IGNORE_API_GRACENOTE
+ musicIDCOM.Quit();
+ #endif
+
+ unloadMlPlugins();
+
+ WADlg_close();
+
+ if (g_config)
+ {
+ delete g_config;
+ g_config = NULL;
+ }
+
+ if (m_uxdll)
+ {
+ FreeLibrary(m_uxdll);
+ m_uxdll = NULL;
+ }
+ SkinnedScrollWnd_Quit();
+ #ifndef IGNORE_API_GRACENOTE
+ ServiceRelease(AGAVE_API_GRACENOTE, gracenoteApiGUID);
+ #endif
+ ServiceRelease(WASABI_API_SYSCB, syscbApiServiceGuid);
+ ServiceRelease(AGAVE_API_DECODE, decodeFileGUID);
+ ServiceRelease(AGAVE_API_JSAPI2_SECURITY, JSAPI2::api_securityGUID);
+ ServiceRelease(AGAVE_OBJ_BROWSER, OBJ_OmBrowser);
+ ServiceRelease(WASABI_API_LNG, languageApiGUID);
+ ServiceRelease(WASABI_API_WND, wndApiServiceGuid);
+ ServiceRelease(WASABI_API_SKIN, skinApiServiceGuid);
+ ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
+ ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceRelease(WASABI_API_PALETTE, PaletteManagerGUID);
+
+ Tataki::Quit();
+
+ HTMLContainer2_Uninitialize();
+
+ ServiceRelease(AGAVE_API_THREADPOOL, ThreadPoolGUID);
+}
+
+void config()
+{
+ OpenMediaLibraryPreferences();
+}
+
+INT MediaLibrary_TrackPopupEx(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, HMLIMGLST hmlil,
+ INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam)
+{
+ if (NULL == hMenu)
+ return NULL;
+
+ return IsSkinnedPopupEnabled(FALSE) ?
+ TrackSkinnedPopupMenuEx(hMenu, fuFlags, x, y, hwnd, lptpm, hmlil, width, skinStyle, customProc, customParam) :
+ TrackPopupMenuEx(hMenu, fuFlags, x, y, hwnd, lptpm);
+}
+
+
+INT MediaLibrary_TrackPopup(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd)
+{
+ return MediaLibrary_TrackPopupEx(hMenu, fuFlags, x, y, hwnd, NULL, NULL, 0, SMS_USESKINFONT, NULL, NULL);
+}
+
+ HANDLE MediaLibrary_InitSkinnedPopupHook(HWND hwnd, HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam)
+ {
+ if (FALSE == IsSkinnedPopupEnabled(FALSE))
+ return FALSE;
+
+ return InitSkinnedPopupHook(hwnd, hmlil, width, skinStyle, customProc, customParam);
+ }
+
+
+
+BOOL
+MediaLibrary_OpenUrl(HWND ownerWindow, const wchar_t *url, BOOL forceExternal)
+{
+ BOOL result;
+ HCURSOR cursor;
+
+ cursor = LoadCursor(NULL, IDC_APPSTARTING);
+ if (NULL != cursor)
+ cursor = SetCursor(cursor);
+
+ if (FALSE != forceExternal)
+ {
+ HINSTANCE instance;
+
+ if (NULL == ownerWindow)
+ ownerWindow = (HWND)SENDWAIPC(plugin.hwndParent, IPC_GETDIALOGBOXPARENT, 0);
+
+ instance = ShellExecuteW(ownerWindow, L"open", url, NULL, NULL, SW_SHOWNORMAL);
+ result = ((INT_PTR)instance > 32) ? TRUE: FALSE;
+ }
+ else
+ {
+ SENDWAIPC(plugin.hwndParent, IPC_OPEN_URL, url);
+ result = TRUE;
+ }
+
+ if (NULL != cursor)
+ SetCursor(cursor);
+
+ return result;
+}
+
+BOOL
+MediaLibrary_OpenHelpUrl(const wchar_t *helpUrl)
+{
+ HWND ownerWindow;
+
+ ownerWindow = (HWND)SENDWAIPC(plugin.hwndParent, IPC_GETDIALOGBOXPARENT, 0);
+
+ return MediaLibrary_OpenUrl(ownerWindow, helpUrl, FALSE);
+}
+
+extern "C"
+{
+ int getFileInfo(const char *filename, const char *metadata, char *dest, int len)
+ {
+ m_calling_getfileinfo = 1;
+ dest[0] = 0;
+ extendedFileInfoStruct efis = {
+ filename,
+ metadata,
+ dest,
+ (size_t)len,
+ };
+ int r = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM) & efis, IPC_GET_EXTENDED_FILE_INFO); //will return 1 if wa2 supports this IPC call
+ m_calling_getfileinfo = 0;
+ return r;
+ }
+
+ __declspec(dllexport) winampGeneralPurposePlugin *winampGetGeneralPurposePlugin()
+ {
+ return &plugin;
+ }
+};
+