winamp/Src/Plugins/General/gen_ml/main.cpp
2024-09-24 14:54:57 +02:00

1173 lines
34 KiB
C++

#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;
}
};