winamp/Src/Plugins/Library/ml_nowplaying/navigation.cpp
2024-09-24 14:54:57 +02:00

684 lines
16 KiB
C++

#include "main.h"
#include "./navigation.h"
#include "./resource.h"
#include "./wasabi.h"
#include "./service.h"
#include "../omBrowser/browserView.h"
#include "../winamp/wa_ipc.h"
#include "../replicant/nu/Autowide.h"
#include "../gen_ml/ml_ipc_0313.h"
#include "./local_menu.h"
#include "../gen_ml/menu.h"
#include <strsafe.h>
#define NAVITEM_PREFIX L"nowplaying_svc_"
#define E_NAVITEM_UNKNOWN E_NOINTERFACE
typedef struct __NAVENUMRESULT
{
HNAVITEM hItem;
OmService *service;
UINT serviceId;
LPCWSTR pszPrefix;
INT cchPrefix;
HWND hLibrary;
NAVITEM itemInfo;
WCHAR szBuffer[256];
} NAVENUMRESULT;
typedef struct __FORCEURLDATA
{
UINT serviceId;
LPWSTR url;
} FORCEURLDATA;
#define FORCEURLPROP L"MLNOWPLAYING_FORCEURL"
static void Navigation_RemoveForceUrl()
{
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary) return;
FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
RemoveProp(hLibrary, FORCEURLPROP);
if (NULL != data)
{
Plugin_FreeString(data->url);
free(data);
}
}
static HRESULT Navigation_SetForceUrl(UINT serviceId, LPCWSTR pszUrl)
{
if (NULL == pszUrl) return E_INVALIDARG;
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary) return E_FAIL;
FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
if (NULL != data)
{
Plugin_FreeString(data->url);
if (data->serviceId != serviceId)
{
free(data);
data = NULL;
}
}
if (NULL == data)
{
data = (FORCEURLDATA*)calloc(1, sizeof(FORCEURLDATA));
if (NULL == data) return E_OUTOFMEMORY;
data->serviceId = serviceId;
}
data->url = Plugin_CopyString(pszUrl);
if (NULL == data->url || FALSE == SetProp(hLibrary, FORCEURLPROP, data))
{
Navigation_RemoveForceUrl();
return E_FAIL;
}
return S_OK;
}
static HRESULT Navigation_GetForceUrl(UINT serviceId, const wchar_t **ppszUrl)
{
if (NULL == ppszUrl) return E_POINTER;
*ppszUrl = NULL;
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary) return E_FAIL;
FORCEURLDATA *data = (FORCEURLDATA*)GetProp(hLibrary, FORCEURLPROP);
if (NULL == data || data->serviceId != serviceId)
return E_NOINTERFACE;
*ppszUrl = data->url;
return S_OK;
}
static INT Navigation_GetIconIndex(LPCWSTR pszImage)
{
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary) return -1;
HMLIMGLST hmlilNavigation = MLNavCtrl_GetImageList(hLibrary);
if (NULL == hmlilNavigation) return -1;
MLIMAGESOURCE mlis;
ZeroMemory(&mlis, sizeof(mlis));
mlis.cbSize = sizeof(mlis);
mlis.hInst = NULL;
mlis.bpp = 24;
mlis.lpszName = pszImage;
mlis.type = SRC_TYPE_PNG;
mlis.flags = ISF_FORCE_BPP | ISF_PREMULTIPLY | ISF_LOADFROMFILE;
MLIMAGELISTITEM item;
ZeroMemory(&item, sizeof(item));
item.cbSize = sizeof(item);
item.hmlil = hmlilNavigation;
item.filterUID = MLIF_FILTER3_UID;
item.pmlImgSource = &mlis;
return MLImageList_Add(hLibrary, &item);
}
static HNAVITEM Navigation_CreateItem(HWND hLibrary, HNAVITEM hParent, OmService *service)
{
if (NULL == hLibrary || NULL == service)
return NULL;
WCHAR szName[256] = {0}, szInvariant[64] = {0};
if (FAILED(service->GetName(szName, ARRAYSIZE(szName))))
return NULL;
if (FAILED(StringCchPrintf(szInvariant, ARRAYSIZE(szInvariant), NAVITEM_PREFIX L"%u", service->GetId())))
return NULL;
NAVINSERTSTRUCT nis = {0};
nis.hInsertAfter = NULL;
nis.hParent = hParent;
WCHAR szIcon[512] = {0};
INT iIcon = (SUCCEEDED(service->GetIcon(szIcon, ARRAYSIZE(szIcon)))) ?
Navigation_GetIconIndex(szIcon) : -1;
nis.item.cbSize = sizeof(NAVITEM);
nis.item.mask = NIMF_TEXT | NIMF_STYLE | NIMF_TEXTINVARIANT | NIMF_PARAM;
if (-1 != iIcon)
nis.item.mask |= (NIMF_IMAGE | NIMF_IMAGESEL);
nis.item.id = 0;
nis.item.pszText = szName;
nis.item.pszInvariant = szInvariant;
nis.item.style = NIS_ALLOWCHILDMOVE;
nis.item.styleMask = nis.item.style;
nis.item.lParam = (LPARAM)service;
nis.item.iImage = iIcon;
nis.item.iSelectedImage = iIcon;
HNAVITEM hItem = MLNavCtrl_InsertItem(hLibrary, &nis);
if (NULL != hItem)
service->AddRef();
return hItem;
}
static HNAVITEM Navigation_GetMessageItem(INT msg, INT_PTR param1)
{
HWND hLibrary = Plugin_GetLibrary();
HNAVITEM hItem = (msg < ML_MSG_NAVIGATION_FIRST) ? MLNavCtrl_FindItemById(hLibrary, param1) : (HNAVITEM)param1;
return hItem;
}
static HRESULT Navigation_GetService(HWND hLibrary, HNAVITEM hItem, OmService **service)
{
WCHAR szBuffer[64] = {0};
if (NULL == service) return E_POINTER;
*service = NULL;
if (NULL == hLibrary || NULL == hItem) return E_INVALIDARG;
NAVITEM itemInfo = {0};
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;
INT cchInvariant = lstrlen(szBuffer);
INT cchPrefix = ARRAYSIZE(NAVITEM_PREFIX) - 1;
if (cchInvariant <= cchPrefix ||
CSTR_EQUAL != CompareString(CSTR_INVARIANT, 0, NAVITEM_PREFIX, cchPrefix, szBuffer, cchPrefix))
{
return E_NAVITEM_UNKNOWN;
}
*service = (OmService*)itemInfo.lParam;
(*service)->AddRef();
return S_OK;
}
static BOOL CALLBACK Navigation_ItemEnumerator(HNAVITEM hItem, LPARAM param)
{
if (NULL == hItem) return TRUE;
NAVENUMRESULT *result = (NAVENUMRESULT*)param;
if (NULL == result) return FALSE;
result->itemInfo .hItem = hItem;
if (FALSE != MLNavItem_GetInfo(result->hLibrary, &result->itemInfo) &&
CSTR_EQUAL == CompareString(CSTR_INVARIANT, NORM_IGNORECASE, result->itemInfo.pszInvariant, result->cchPrefix,
result->pszPrefix, result->cchPrefix))
{
OmService *service = (OmService*)result->itemInfo.lParam;
if (NULL != service && service->GetId() == result->serviceId)
{
result->hItem = hItem;
result->service = service;
service->AddRef();
return FALSE;
}
}
return TRUE;
}
static HRESULT Navigation_CreateView(HNAVITEM hItem, HWND hParent, HWND *hView)
{
if (NULL == hView) return E_POINTER;
*hView = NULL;
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary) return E_UNEXPECTED;
if (NULL == hItem || NULL == hParent) return E_INVALIDARG;
HRESULT hr;
OmService *service = NULL;
hr = Navigation_GetService(hLibrary, hItem, &service);
if (SUCCEEDED(hr))
{
if (NULL == OMBROWSERMNGR)
hr = E_UNEXPECTED;
if (SUCCEEDED(hr))
{
hr = OMBROWSERMNGR->Initialize(NULL, Plugin_GetWinamp());
if (SUCCEEDED(hr))
{
LPCWSTR forceUrl;
if (FAILED(Navigation_GetForceUrl(service->GetId(), &forceUrl)))
forceUrl = NULL;
hr = OMBROWSERMNGR->CreateView(service, hParent, forceUrl, 0, hView);
Navigation_RemoveForceUrl();
}
}
wchar_t nowplayingurl[1024] = {0};
// May 2022 - this service url is dead and would need either fixing up or replacing
lstrcpynW(nowplayingurl, AutoWide(g_config->ReadString("nowplayingurl", "http://client.winamp.com/nowplaying")), ARRAYSIZE(nowplayingurl));
service->SetUrl(nowplayingurl[0] ? nowplayingurl : SERVICE_HOMEURL);
service->Release();
}
return hr;
}
static BOOL Navigation_GetViewRect(RECT *rect)
{
if (NULL == rect) return FALSE;
HWND hWinamp = Plugin_GetWinamp();
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hWinamp || NULL == hLibrary)
return FALSE;
HWND hFrame = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
if (NULL == hFrame)
hFrame = hLibrary;
return GetWindowRect(hFrame, rect);
}
static HRESULT Navigation_CreatePopup(HNAVITEM hItem, HWND *hWindow)
{
if (NULL == hWindow) return E_POINTER;
*hWindow = NULL;
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary) return E_UNEXPECTED;
if (NULL == hItem) return E_INVALIDARG;
HRESULT hr;
OmService *service;
hr = Navigation_GetService(hLibrary, hItem, &service);
if (SUCCEEDED(hr))
{
HWND hWinamp = Plugin_GetWinamp();
if (NULL == OMBROWSERMNGR)
hr = E_UNEXPECTED;
if (SUCCEEDED(hr))
{
hr = OMBROWSERMNGR->Initialize(NULL, hWinamp);
if (SUCCEEDED(hr))
{
RECT rect;
if (FALSE == Navigation_GetViewRect(&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, hWindow);
}
}
}
service->Release();
}
return hr;
}
static void Navigation_OnDestroy()
{
Navigation_RemoveForceUrl();
if (NULL != OMBROWSERMNGR)
{
OMBROWSERMNGR->Finish();
}
}
static void Navigation_OpenPreferences()
{
winampMediaLibraryPlugin *(*gp)();
gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(GetModuleHandle(L"ml_online.dll"), "winampGetMediaLibraryPlugin");
if (gp)
{
winampMediaLibraryPlugin *mlplugin = gp();
if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
{
mlplugin->MessageProc(ML_MSG_CONFIG, 0, 0, 0);
}
else
SendMessage(Plugin_GetWinamp(), WM_WA_IPC, (WPARAM)-1, IPC_OPENPREFSTOPAGE);
}
else
SendMessage(Plugin_GetWinamp(), WM_WA_IPC, (WPARAM)-1, IPC_OPENPREFSTOPAGE);
}
static HRESULT Navigation_ShowContextMenu(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;
OmService *service;
hr = Navigation_GetService(hLibrary, 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;
}
}
HMENU hMenu = Menu_GetMenu(MENU_NAVIGATIONCONTEXT);
if (NULL != hMenu)
{
INT commandId = Menu_TrackPopup(hLibrary, hMenu,
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD,
pt.x, pt.y, hHost, NULL);
Menu_ReleaseMenu(hMenu, MENU_NAVIGATIONCONTEXT);
switch(commandId)
{
case ID_NAVIGATION_OPEN:
MLNavItem_Select(hLibrary, hItem);
break;
case ID_NAVIGATION_OPENNEWWINDOW:
{
HWND hWindow;
if (SUCCEEDED(Navigation_CreatePopup(hItem, &hWindow)))
{
ShowWindow(hWindow, SW_SHOWNORMAL);
}
}
break;
case ID_NAVIGATION_HELP:
SENDWAIPC(Plugin_GetWinamp(), IPC_OPEN_URL, L"https://help.winamp.com/hc/articles/8105304048660-The-Winamp-Media-Library");
break;
case ID_PLUGIN_PREFERENCES:
Navigation_OpenPreferences();
break;
}
}
service->Release();
return hr;
}
BOOL Navigation_Initialize(void)
{
OmService *service;
HWND hLibrary = Plugin_GetLibrary();
MLNavCtrl_BeginUpdate(hLibrary, NUF_LOCK_TOP);
if (SUCCEEDED(OmService::CreateInstance(&service)))
{
HNAVITEM hParent = NULL;
Navigation_CreateItem(hLibrary, hParent, service);
service->Release();
}
MLNavCtrl_EndUpdate(hLibrary);
return TRUE;
}
static void Navigation_OnDeleteItem(HNAVITEM hItem)
{
if (NULL == hItem) return;
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary) return;
OmService *service;
if (SUCCEEDED(Navigation_GetService(hLibrary, hItem, &service)))
{
NAVITEM itemInfo;
itemInfo.cbSize = sizeof(NAVITEM);
itemInfo.hItem = hItem;
itemInfo.mask = NIMF_PARAM;
itemInfo.lParam = 0L;
MLNavItem_SetInfo(hLibrary, &itemInfo);
service->Release(); // create
service->Release(); // Navigation_GetService
}
}
BOOL Navigation_ProcessMessage(INT msg, INT_PTR param1, INT_PTR param2, INT_PTR param3, INT_PTR *result)
{
if (msg == ML_MSG_NO_CONFIG)
{
if (!GetModuleHandle(L"ml_online.dll"))
{
*result = TRUE;
return TRUE;
}
}
else if (msg == ML_MSG_CONFIG)
{
Navigation_OpenPreferences();
*result = TRUE;
return TRUE;
}
if (msg < ML_MSG_TREE_BEGIN || msg > ML_MSG_TREE_END)
return FALSE;
switch(msg)
{
case ML_MSG_TREE_ONCREATEVIEW:
{
HWND hView;
HNAVITEM hItem = Navigation_GetMessageItem(msg, param1);
HRESULT hr = Navigation_CreateView(hItem, (HWND)param2, &hView);
if (SUCCEEDED(hr))
{
*result = (INT_PTR)hView;
return TRUE;
}
}
break;
case ML_MSG_NAVIGATION_ONDESTROY:
Navigation_OnDestroy();
break;
case ML_MSG_NAVIGATION_CONTEXTMENU:
{
HNAVITEM hItem = Navigation_GetMessageItem(msg, param1);
HRESULT hr = Navigation_ShowContextMenu(hItem, (HWND)param2, MAKEPOINTS(param3));
if (SUCCEEDED(hr))
{
*result = TRUE;
return TRUE;
}
}
break;
case ML_MSG_NAVIGATION_ONDELETE:
{
HNAVITEM hItem = Navigation_GetMessageItem(msg, param1);
Navigation_OnDeleteItem(hItem);
break;
}
}
return FALSE;
}
HNAVITEM Navigation_FindService(UINT serviceId, OmService **serviceOut)
{
NAVENUMRESULT result;
result.hItem = NULL;
result.service = NULL;
result.serviceId = serviceId;
result.pszPrefix = NAVITEM_PREFIX;
result.cchPrefix = lstrlen(result.pszPrefix);
result.hLibrary = Plugin_GetLibrary();
result.itemInfo.cbSize = sizeof(result.itemInfo);
result.itemInfo.mask = NIMF_TEXTINVARIANT | NIMF_PARAM;
result.itemInfo.cchInvariantMax = ARRAYSIZE(result.szBuffer);
result.itemInfo.pszInvariant = result.szBuffer;
NAVCTRLENUMPARAMS param;
param.enumProc = Navigation_ItemEnumerator;
param.hItemStart = NULL;
param.lParam = (LPARAM)&result;
if (NULL != result.hLibrary)
MLNavCtrl_EnumItems(result.hLibrary, &param);
if (NULL != serviceOut)
*serviceOut = result.service;
else if (NULL != result.service)
result.service->Release();
return result.hItem;
}
HRESULT Navigation_ShowService(UINT serviceId, LPCWSTR pszUrl, UINT navFlags)
{
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary)
return E_FAIL;
OmService *service;
HNAVITEM hItem = Navigation_FindService(serviceId, &service);
if (NULL == hItem)
return E_FAIL;
OmService *activeService;
HWND hView = Navigation_GetActiveView(&activeService);
if (NULL == hView || activeService->GetId() != service->GetId())
{
hView = NULL;
activeService = NULL;
}
HRESULT hr = S_OK;
if (NULL != hView)
{
if (NULL == pszUrl && 0 != (NAVFLAG_FORCEACTIVE & navFlags))
pszUrl = NAVIGATE_HOME;
if (NULL != pszUrl && FALSE == BrowserView_Navigate(hView, pszUrl, TRUE))
hr = E_FAIL;
}
else
{
if (NULL != pszUrl)
hr = Navigation_SetForceUrl(serviceId, pszUrl);
else
Navigation_RemoveForceUrl();
if (SUCCEEDED(hr) && FALSE == MLNavItem_Select(hLibrary, hItem))
{
Navigation_RemoveForceUrl();
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
if (0 != (NAVFLAG_ENSUREITEMVISIBLE & navFlags))
MLNavItem_EnsureVisible(hLibrary, hItem);
if (0 != (NAVFLAG_ENSUREMLVISIBLE & navFlags))
SENDMLIPC(hLibrary, ML_IPC_ENSURE_VISIBLE, 0L);
}
service->Release();
if (NULL != activeService)
activeService->Release();
return hr;
}
HNAVITEM Navigation_GetActive(OmService **serviceOut)
{
HWND hLibrary = Plugin_GetLibrary();
OmService *service;
HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL;
if (NULL == hActive || FAILED(Navigation_GetService(hLibrary, hActive, &service)))
{
hActive = NULL;
service = NULL;
}
if (NULL != serviceOut)
*serviceOut = service;
return hActive;
}
HWND Navigation_GetActiveView(OmService **serviceOut)
{
HWND hLibrary = Plugin_GetLibrary();
if (NULL == hLibrary)
{
if (NULL != serviceOut) *serviceOut = NULL;
return NULL;
}
HWND hView =((HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0));
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;
}
OmService *service;
HNAVITEM hActive = (NULL != hLibrary) ? MLNavCtrl_GetSelection(hLibrary) : NULL;
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;
}