1
0

gen_classicart.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935
  1. /* gen_classicart
  2. version 0.5, February 7th, 2010
  3. version 0.4, February 4th, 2010
  4. version 0.3, November 9th, 2009
  5. version 0.2, February 27th, 2008
  6. Copyright (C) 2008 Will Fisher
  7. This software is provided 'as-is', without any express or implied
  8. warranty. In no event will the authors be held liable for any damages
  9. arising from the use of this software.
  10. Permission is granted to anyone to use this software for any purpose,
  11. including commercial applications, and to alter it and redistribute it
  12. freely, subject to the following restrictions:
  13. 1. The origin of this software must not be misrepresented; you must not
  14. claim that you wrote the original software. If you use this software
  15. in a product, an acknowledgment in the product documentation would be
  16. appreciated but is not required.
  17. 2. Altered source versions must be plainly marked as such, and must not be
  18. misrepresented as being the original software.
  19. 3. This notice may not be removed or altered from any source distribution.
  20. Will Fisher [email protected]
  21. ---------------------------------------------------------------------------
  22. Changes from v0.2 by Darren Owen aka DrO
  23. * Added Alt+A global shortcut to toggle the album art window
  24. * Album art window is now included in Winamp's ctrl+tab feature (broken in some 5.5x builds)
  25. * Double-click will now open the folder (like the native album art window does)
  26. * Changing menu id to be dynamic rather than hard-coded to show use of IPC_REGISTER_LOWORD_COMMAND
  27. * Made background colour match the media library album art views (override with waBkClr=1 in the plugin's ini section)
  28. * Made compatible to deal with Winamp being minimised on startup so the window won't appear briefly
  29. * Implemented winampUninstallPlugin(..) support
  30. ---------------------------------------------------------------------------
  31. Changes from v0.3 by Darren Owen aka DrO
  32. * Added localisation support (with example en-us language file included in the installer)
  33. * Adding an option to show the album art when clicking on an item in the playlist editor without having to play it
  34. * Fixed the main menu item not working via the system menu
  35. * Plugin will now only work on 5.53+ clients to ensure the Alt+A global shortcut will work (bug-fix for v0.3)
  36. * Fixed issue with api.h not containing all of the required headers (only affects compiling source code)
  37. ---------------------------------------------------------------------------
  38. Changes from v0.4 by Darren Owen aka DrO
  39. * Added detection of being used under a modern skin so the album art window and menu item can be automatically hidden (default: on)
  40. * Added in ability for the tracking action to work via the keyboard actions along with button clicking
  41. * Tweaked the menu item text so the items (at least with en-us translations) is better consistant with the rest of Winamp (if that's possible, heh)
  42. * Added config menu into the Winamp preferences so it's possible to access the options when the album art window isn't shown
  43. */
  44. #include <windows.h>
  45. #include <shlwapi.h>
  46. #include <strsafe.h>
  47. #include "../winamp/gen.h"
  48. #include "../winamp/wa_ipc.h"
  49. #include "../winamp/ipc_pe.h"
  50. #define WA_DLG_IMPLEMENT
  51. #include "../winamp/wa_dlg.h"
  52. #include "api.h"
  53. #include "resource.h"
  54. #define ID_PE_SCUP 40289
  55. #define ID_PE_SCDOWN 40290
  56. #define PLUGIN_NAME "Album Art Viewer"
  57. #define PLUGIN_VERSION "0.6"
  58. int init();
  59. void quit();
  60. void config();
  61. winampGeneralPurposePlugin plugin =
  62. {
  63. GPPHDR_VER,
  64. "nullsoft(gen_classicart.dll)",
  65. init,
  66. config,
  67. quit,
  68. };
  69. // winampGeneralPurposePlugin plugin = {GPPHDR_VER,PLUGIN_NAME" v"PLUGIN_VERSION,init,config,quit};
  70. static api_memmgr* WASABI_API_MEMMGR;
  71. static api_service* WASABI_API_SVC;
  72. static api_albumart* AGAVE_API_ALBUMART;
  73. static api_application* WASABI_API_APP;
  74. static api_language* WASABI_API_LNG;
  75. HINSTANCE WASABI_API_LNG_HINST = 0,
  76. WASABI_API_ORIG_HINST = 0;
  77. embedWindowState myWndState={0};
  78. HWND myWnd=NULL,
  79. myWndChild=NULL;
  80. HMENU menu=NULL,
  81. context_menu=NULL;
  82. WNDPROC oldWndProc=NULL,
  83. oldPlaylistWndProc=NULL;
  84. BOOL no_uninstall=TRUE,
  85. modernloaded=FALSE,
  86. last_artwindow_open=FALSE;
  87. int artwindow_open=0,
  88. lockAspect=1,
  89. autoHide=0,
  90. waBkClr=0,
  91. on_click=0,
  92. clickTrack=0,
  93. hidemodern=1;
  94. UINT WINAMP_ARTVIEW_MENUID=0xa1ba;
  95. char* INI_FILE=NULL;
  96. RECT lastWnd={0};
  97. HDC cacheDC = NULL;
  98. COLORREF bgcolour = RGB(0,0,0);
  99. static INT_PTR CALLBACK art_dlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
  100. static LRESULT WINAPI SubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  101. static LRESULT WINAPI SubclassPlaylistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  102. void viewArtWindow(BOOL show);
  103. void addAccelerators(HWND hwnd, ACCEL* accel, int accel_size, int translate_mode);
  104. // this is used to identify the skinned frame to allow for embedding/control by modern skins if needed
  105. // {8B9052B2-2782-4ac8-BA8E-E3DEDBF0BDB5}
  106. static const GUID ArtViewerGUID =
  107. { 0x8b9052b2, 0x2782, 0x4ac8, { 0xba, 0x8e, 0xe3, 0xde, 0xdb, 0xf0, 0xbd, 0xb5 } };
  108. // this is used to identify the language dll to be used when this is running under a language pack
  109. // {EAD1E933-6D75-4c2c-B9C4-B4D7F06B7D8D}
  110. static const GUID GenClassicArtGUID =
  111. { 0xead1e933, 0x6d75, 0x4c2c, { 0xb9, 0xc4, 0xb4, 0xd7, 0xf0, 0x6b, 0x7d, 0x8d } };
  112. BOOL ArtView_SetMinimised(BOOL fMinimized)
  113. {
  114. if(fMinimized == TRUE)
  115. {
  116. return SetPropW(myWnd,L"AAMinMode",(HANDLE)TRUE);
  117. }
  118. RemovePropW(myWnd,L"AAMinMode");
  119. return TRUE;
  120. }
  121. UINT ver = -1;
  122. UINT GetWinampVersion(HWND winamp)
  123. {
  124. if(ver == -1)
  125. {
  126. return (ver = SendMessage(winamp,WM_WA_IPC,0,IPC_GETVERSION));
  127. }
  128. return ver;
  129. }
  130. template <class api_T>
  131. void ServiceBuild(api_T *&api_t, GUID factoryGUID_t){
  132. if (WASABI_API_SVC){
  133. waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t);
  134. if (factory){
  135. api_t = (api_T *)factory->getInterface();
  136. }
  137. }
  138. }
  139. BOOL DetectModernSkinLoaded(void)
  140. {
  141. wchar_t skindir[MAX_PATH]={0};
  142. SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)skindir,IPC_GETSKINW);
  143. StringCchCat(skindir,MAX_PATH,L"\\skin.xml");
  144. return PathFileExists(skindir);
  145. }
  146. void InsertItemIntoMainMenu(void){
  147. MENUITEMINFO i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, MFS_UNCHECKED, WINAMP_ARTVIEW_MENUID};
  148. i.dwTypeData = WASABI_API_LNGSTRINGW(IDS_ALBUM_ART_MENU);
  149. InsertMenuItem(menu, 10 + (int)SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)0,IPC_ADJUST_OPTIONSMENUPOS), TRUE, &i);
  150. SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)1,IPC_ADJUST_OPTIONSMENUPOS);
  151. }
  152. int init()
  153. {
  154. if(GetWinampVersion(plugin.hwndParent) < 0x5053)
  155. {
  156. // this is due to the api_application dependancy to allow for registering a hotkey correctly
  157. MessageBoxA(plugin.hwndParent,"This plug-in requires Winamp v5.9 and up for it to work.\t\n"
  158. "Please upgrade your Winamp client to be able to use this.",
  159. plugin.description,MB_OK|MB_ICONINFORMATION);
  160. return GEN_INIT_FAILURE;
  161. }
  162. else
  163. {
  164. WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
  165. if(WASABI_API_SVC != NULL)
  166. {
  167. INI_FILE = (char*)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GETINIFILE);
  168. ServiceBuild(WASABI_API_MEMMGR,memMgrApiServiceGuid);
  169. if(WASABI_API_MEMMGR == NULL) return 1;
  170. ServiceBuild(AGAVE_API_ALBUMART,albumArtGUID);
  171. if(AGAVE_API_ALBUMART == NULL) return 1;
  172. ServiceBuild(WASABI_API_APP,applicationApiServiceGuid);
  173. if(WASABI_API_APP == NULL) return 1;
  174. ServiceBuild(WASABI_API_LNG,languageApiGUID);
  175. if(WASABI_API_LNG == NULL) return 1;
  176. WASABI_API_START_LANG(plugin.hDllInstance,GenClassicArtGUID);
  177. static char pluginTitle[MAX_PATH] = {0};
  178. StringCchPrintfA(pluginTitle, MAX_PATH, WASABI_API_LNGSTRING(IDS_PLUGIN_NAME), PLUGIN_VERSION);
  179. plugin.description = pluginTitle;
  180. WADlg_init(plugin.hwndParent);
  181. // subclass main window
  182. oldWndProc = (WNDPROC)SetWindowLongPtrW(plugin.hwndParent, GWLP_WNDPROC, (LONG_PTR)SubclassProc);
  183. oldPlaylistWndProc = (WNDPROC)SetWindowLongPtrW((HWND)SendMessage(plugin.hwndParent,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND),
  184. GWLP_WNDPROC, (LONG_PTR)SubclassPlaylistProc);
  185. // do this dynamically (if on an older client then we'd need to check for a return of 1 and set an arbitrary default)
  186. WINAMP_ARTVIEW_MENUID = SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_REGISTER_LOWORD_COMMAND);
  187. // add our menu option
  188. menu = (HMENU)SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)0,IPC_GET_HMENU);
  189. if(hidemodern){
  190. if(!DetectModernSkinLoaded())
  191. {
  192. InsertItemIntoMainMenu();
  193. }
  194. else
  195. {
  196. modernloaded=TRUE;
  197. }
  198. }
  199. else
  200. {
  201. InsertItemIntoMainMenu();
  202. }
  203. // load values from ini file
  204. lockAspect = GetPrivateProfileIntA("gen_classicart","wnd_lock_aspect",lockAspect,INI_FILE);
  205. myWndState.r.top = GetPrivateProfileIntA("gen_classicart","wnd_top",0,INI_FILE);
  206. myWndState.r.bottom = GetPrivateProfileIntA("gen_classicart","wnd_bottom",0,INI_FILE);
  207. myWndState.r.left = GetPrivateProfileIntA("gen_classicart","wnd_left",0,INI_FILE);
  208. myWndState.r.right = GetPrivateProfileIntA("gen_classicart","wnd_right",0,INI_FILE);
  209. artwindow_open = GetPrivateProfileIntA("gen_classicart","wnd_open",artwindow_open,INI_FILE);
  210. autoHide = GetPrivateProfileIntA("gen_classicart","wnd_auto_hide",autoHide,INI_FILE);
  211. waBkClr = GetPrivateProfileIntA("gen_classicart","waBkClr",waBkClr,INI_FILE);
  212. clickTrack = GetPrivateProfileIntA("gen_classicart","clickTrack",clickTrack,INI_FILE);
  213. hidemodern = GetPrivateProfileIntA("gen_classicart","hidemodern",hidemodern,INI_FILE);
  214. last_artwindow_open = GetPrivateProfileIntA("gen_classicart","wnd_open_last",last_artwindow_open,INI_FILE);
  215. // create window
  216. myWndState.flags = EMBED_FLAGS_NOWINDOWMENU;
  217. myWnd = (HWND)SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)&myWndState,IPC_GET_EMBEDIF);
  218. WASABI_API_APP->app_registerGlobalWindow(myWnd);
  219. myWndChild = WASABI_API_CREATEDIALOGW(IDD_DIALOG,myWnd,art_dlgproc);
  220. SET_EMBED_GUID((&myWndState),ArtViewerGUID);
  221. SetWindowText(myWnd,WASABI_API_LNGSTRINGW(IDS_ALBUM_ART));
  222. if(SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_INITIAL_SHOW_STATE) == SW_SHOWMINIMIZED && artwindow_open)
  223. {
  224. // we are starting minimised so process as needed (keep our window hidden)
  225. MENUITEMINFO i = {sizeof(i), MIIM_STATE , MFT_STRING, MFS_UNCHECKED, WINAMP_ARTVIEW_MENUID};
  226. SetMenuItemInfo(menu, WINAMP_ARTVIEW_MENUID, FALSE, &i);
  227. ArtView_SetMinimised(TRUE);
  228. }
  229. else
  230. {
  231. if(artwindow_open) viewArtWindow(TRUE);
  232. }
  233. // not working correctly at the moment
  234. ACCEL accel = {FVIRTKEY|FALT,'A',WINAMP_ARTVIEW_MENUID};
  235. addAccelerators(myWndChild,&accel,1,TRANSLATE_MODE_GLOBAL);
  236. context_menu = WASABI_API_LOADMENUW(IDR_MENU1);
  237. return GEN_INIT_SUCCESS;
  238. }
  239. }
  240. return GEN_INIT_FAILURE;
  241. }
  242. void quit()
  243. {
  244. if(no_uninstall)
  245. {
  246. // save window state
  247. #define WritePrivateProfileInt(key, val) \
  248. { \
  249. char zzval[10] = {0}; \
  250. StringCchPrintfA(zzval,10,"%d",val); \
  251. WritePrivateProfileStringA("gen_classicart",key,zzval,INI_FILE); \
  252. }
  253. GetWindowRect(myWnd,&myWndState.r);
  254. WritePrivateProfileInt("wnd_top",myWndState.r.top);
  255. WritePrivateProfileInt("wnd_bottom",myWndState.r.bottom);
  256. WritePrivateProfileInt("wnd_left",myWndState.r.left);
  257. WritePrivateProfileInt("wnd_right",myWndState.r.right);
  258. WritePrivateProfileInt("wnd_open",artwindow_open);
  259. WritePrivateProfileInt("wnd_lock_aspect",lockAspect);
  260. WritePrivateProfileInt("wnd_auto_hide",autoHide);
  261. WritePrivateProfileInt("clickTrack",clickTrack);
  262. WritePrivateProfileInt("hidemodern",hidemodern);
  263. // reset the state so if we're using a classic skin then it'll work if the window is disabled
  264. if(modernloaded==FALSE) last_artwindow_open = artwindow_open;
  265. WritePrivateProfileInt("wnd_open_last",last_artwindow_open);
  266. }
  267. WASABI_API_APP->app_unregisterGlobalWindow(myWnd);
  268. ArtView_SetMinimised(FALSE);
  269. DestroyWindow(myWnd);
  270. WADlg_close();
  271. // restores the original winamp window proc now that we are closing and if the window was subclassed
  272. if(GetWindowLongPtr(plugin.hwndParent,GWLP_WNDPROC) == (LONG_PTR)SubclassProc){
  273. SetWindowLongPtr(plugin.hwndParent,GWLP_WNDPROC,(LONG_PTR)oldWndProc);
  274. }
  275. // restores the original playlist window proc now that we are closing and if the window was subclassed
  276. HWND pe_wnd = (HWND)SendMessage(plugin.hwndParent,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND);
  277. if(GetWindowLongPtr(pe_wnd,GWLP_WNDPROC) == (LONG_PTR)SubclassPlaylistProc){
  278. SetWindowLongPtr(pe_wnd,GWLP_WNDPROC,(LONG_PTR)oldPlaylistWndProc);
  279. }
  280. }
  281. int config_open = 0;
  282. void config()
  283. {
  284. /*if(!config_open){
  285. char message[256]={0}, tmp[128]={0};
  286. config_open = 1;
  287. StringCchPrintfA(message,256,WASABI_API_LNGSTRING(IDS_ABOUT_STRING),WASABI_API_LNGSTRING_BUF(IDS_ALBUM_ART,tmp,128));
  288. MessageBoxA(0,message,plugin.description,0);
  289. config_open = 0;
  290. }
  291. else{
  292. SetActiveWindow(FindWindowA("#32770",plugin.description));
  293. }*/
  294. HWND list = FindWindowEx(GetParent(GetFocus()),0,L"ListBox",0);
  295. HMENU popup = GetSubMenu(WASABI_API_LOADMENUW(IDR_MENU1),0);
  296. RECT r = {0};
  297. wchar_t temp[MAX_PATH] = {0};
  298. DeleteMenu(popup,ID_CONTEXTMENU_GETALBUMART,MF_BYCOMMAND);
  299. DeleteMenu(popup,ID_CONTEXTMENU_REFRESH,MF_BYCOMMAND);
  300. DeleteMenu(popup,ID_CONTEXTMENU_OPENFOLDER,MF_BYCOMMAND);
  301. MENUITEMINFO i = {sizeof(i),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING,MFS_UNCHECKED|MFS_DISABLED,1};
  302. StringCchPrintf(temp,MAX_PATH,WASABI_API_LNGSTRINGW(IDS_PLUGIN_NAME),TEXT(PLUGIN_VERSION));
  303. i.dwTypeData = temp;
  304. InsertMenuItem(popup, 0, TRUE, &i);
  305. i.wID=2;
  306. i.fState=0;
  307. i.fType=MFT_SEPARATOR;
  308. InsertMenuItem(popup, -1, TRUE, &i);
  309. i.dwTypeData = WASABI_API_LNGSTRINGW(IDS_ABOUT);
  310. i.wID=4;
  311. i.fType=MFT_STRING;
  312. InsertMenuItem(popup, -1, TRUE, &i);
  313. SendMessage(list,LB_GETITEMRECT,SendMessage(list,LB_GETCURSEL,1,0),(LPARAM)&r);
  314. ClientToScreen(list,(LPPOINT)&r);
  315. CheckMenuItem(popup,ID_CONTEXTMENU_LOCKASPECTRATIO,(lockAspect?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  316. CheckMenuItem(popup,ID_CONTEXTMENU_AUTOHIDE,(autoHide?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  317. CheckMenuItem(popup,ID_CONTEXTMENU_CLICKTRACK,(clickTrack?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  318. CheckMenuItem(popup,ID_CONTEXTMENU_AUTO_HIDE_MODERN,(hidemodern?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  319. switch(TrackPopupMenu(popup,TPM_RETURNCMD|TPM_LEFTBUTTON,r.left,r.top,0,list,NULL)){
  320. case 4:
  321. {
  322. char message[256]={0}, tmp[128]={0};
  323. StringCchPrintfA(message,256,WASABI_API_LNGSTRING(IDS_ABOUT_STRING),WASABI_API_LNGSTRING_BUF(IDS_ALBUM_ART,tmp,128));
  324. MessageBoxA(list,message,plugin.description,0);
  325. }
  326. break;
  327. case ID_CONTEXTMENU_LOCKASPECTRATIO:
  328. lockAspect = (!lockAspect);
  329. if(cacheDC) DeleteDC(cacheDC); cacheDC = NULL;
  330. InvalidateRect(myWndChild,NULL,TRUE);
  331. break;
  332. case ID_CONTEXTMENU_AUTOHIDE:
  333. autoHide = (!autoHide);
  334. break;
  335. case ID_CONTEXTMENU_CLICKTRACK:
  336. clickTrack = (!clickTrack);
  337. break;
  338. case ID_CONTEXTMENU_AUTO_HIDE_MODERN:
  339. hidemodern = (!hidemodern);
  340. break;
  341. }
  342. // clean up as we're sharing this menu
  343. DestroyMenu(popup);
  344. }
  345. static LRESULT WINAPI SubclassProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  346. {
  347. // handles the item being selected through the main window menu
  348. // including via the windows taskbar menu as it'll fail otherwise
  349. if((msg == WM_COMMAND || msg == WM_SYSCOMMAND) && LOWORD(wParam) == WINAMP_ARTVIEW_MENUID)
  350. {
  351. if(artwindow_open) PostMessage(myWndChild,WM_CLOSE,0,0);
  352. else PostMessage(myWndChild,WM_USER+1,0,0);
  353. }
  354. else if(msg == WM_WA_IPC)
  355. {
  356. if(lParam == IPC_CB_MISC && (wParam == IPC_CB_MISC_TITLE || wParam == IPC_CB_MISC_TITLE_RATING))
  357. {
  358. // art change
  359. PostMessage(myWndChild,WM_USER,0,0);
  360. }
  361. else if(lParam == IPC_SKIN_CHANGED && hidemodern)
  362. {
  363. // need to check this when doing classic->modern as it causes the pledit to be hidden
  364. if(DetectModernSkinLoaded()){
  365. if(modernloaded==FALSE){
  366. last_artwindow_open = artwindow_open;
  367. if(last_artwindow_open==TRUE){
  368. PostMessage(myWndChild,WM_CLOSE,0,0);
  369. }
  370. DeleteMenu(menu,WINAMP_ARTVIEW_MENUID,MF_BYCOMMAND);
  371. SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)-1,IPC_ADJUST_OPTIONSMENUPOS);
  372. }
  373. modernloaded=TRUE;
  374. }
  375. else{
  376. if(modernloaded==TRUE)
  377. {
  378. InsertItemIntoMainMenu();
  379. if(last_artwindow_open==TRUE)
  380. {
  381. PostMessage(myWndChild,WM_USER+1,0,0);
  382. }
  383. }
  384. modernloaded=FALSE;
  385. }
  386. }
  387. }
  388. // the purpose of this is show our gen frame window if we started in a minimised state
  389. // as showing then hiding will otherwise cause a window to flash briefly on screen
  390. else if(msg == WM_SIZE)
  391. {
  392. if (wParam == SIZE_RESTORED)
  393. {
  394. if(GetPropW(myWnd,L"AAMinMode"))
  395. {
  396. ShowWindow(myWnd,SW_SHOWNA);
  397. ArtView_SetMinimised(FALSE);
  398. }
  399. }
  400. }
  401. return CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam);
  402. }
  403. int IsInPlaylistArea(HWND playlist_wnd, int mode)
  404. {
  405. POINT pt = {0};
  406. RECT rc = {0};
  407. GetCursorPos(&pt);
  408. GetClientRect(playlist_wnd,&rc);
  409. ScreenToClient(playlist_wnd,&pt);
  410. // this corrects so the selection works correctly on the selection boundary
  411. pt.y -= 2;
  412. if(!mode)
  413. {
  414. // corrects for the window area so it only happens if a selection happens
  415. rc.top += 18;
  416. rc.left += 12;
  417. rc.right -= 19;
  418. rc.bottom -= 40;
  419. return PtInRect(&rc,pt);
  420. }
  421. else
  422. {
  423. rc.bottom -= 13;
  424. rc.top = rc.bottom - 19;
  425. rc.left += 14;
  426. for(int i = 0; i < 4; i++)
  427. {
  428. rc.right = rc.left + 22;
  429. if(PtInRect(&rc,pt))
  430. {
  431. return 1;
  432. }
  433. else{
  434. rc.left = rc.right + 7;
  435. }
  436. }
  437. return 0;
  438. }
  439. }
  440. static LRESULT WINAPI SubclassPlaylistProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  441. {
  442. LRESULT ret=0;
  443. // this will detect false clicks such as when the menus are shown in the classic playlist editor
  444. if(msg == WM_LBUTTONDOWN)
  445. {
  446. if(IsInPlaylistArea(hwnd,1))
  447. {
  448. on_click = 1;
  449. }
  450. }
  451. ret = CallWindowProc(oldPlaylistWndProc, hwnd, msg, wParam, lParam);
  452. // this will then handle the detection of a proper click in the playlist editor so
  453. // if enabled then we can get and show the album art for the selected playlist item
  454. if(msg == WM_LBUTTONDOWN && !(GetKeyState(VK_MENU)&0x1000) &&
  455. !(GetKeyState(VK_SHIFT)&0x1000) &&
  456. !(GetKeyState(VK_CONTROL)&0x1000))
  457. {
  458. if(!on_click && clickTrack)
  459. {
  460. POINT pt = {0};
  461. INT index = 0;
  462. GetCursorPos(&pt);
  463. ScreenToClient(hwnd,&pt);
  464. // this corrects so the selection works correctly on the selection boundary
  465. pt.y -= 2;
  466. index = SendMessage(hwnd,WM_WA_IPC,IPC_PE_GETIDXFROMPOINT,(LPARAM)&pt);
  467. if(IsInPlaylistArea(hwnd,0))
  468. {
  469. // bounds check things
  470. if(index < SendMessage(hwnd,WM_WA_IPC,IPC_PE_GETINDEXTOTAL,0))
  471. {
  472. // art change to show the selected item in the playlist editor
  473. PostMessage(myWndChild,WM_USER,0,(LPARAM)SendMessage(plugin.hwndParent,WM_WA_IPC,index,IPC_GETPLAYLISTFILEW));
  474. }
  475. }
  476. }
  477. else
  478. {
  479. // needs to do an increment for the next click will be a false
  480. if(on_click == 1)
  481. {
  482. on_click = 2;
  483. }
  484. else{
  485. on_click = 0;
  486. }
  487. }
  488. }
  489. else if(msg == WM_COMMAND)
  490. {
  491. // this is used for tracking the selection of items in the playlist editor
  492. // so we can update the album art when doing a single up/down selection
  493. if((LOWORD(wParam) == ID_PE_SCUP || LOWORD(wParam) == ID_PE_SCDOWN) && clickTrack)
  494. {
  495. // only do on up/down without any other keyboard accelerators pressed
  496. if(!(GetKeyState(VK_MENU)&0x1000) &&
  497. !(GetKeyState(VK_SHIFT)&0x1000) &&
  498. !(GetKeyState(VK_CONTROL)&0x1000))
  499. {
  500. if(SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_PLAYLIST_GET_SELECTED_COUNT))
  501. {
  502. int sel = SendMessage(plugin.hwndParent,WM_WA_IPC,-1,IPC_PLAYLIST_GET_NEXT_SELECTED);
  503. if(sel != -1)
  504. {
  505. // art change to show the selected item in the playlist editor
  506. PostMessage(myWndChild,WM_USER,0,(LPARAM)SendMessage(plugin.hwndParent,WM_WA_IPC,sel,IPC_GETPLAYLISTFILEW));
  507. }
  508. }
  509. }
  510. }
  511. }
  512. return ret;
  513. }
  514. ARGB32 * loadImg(const void * data, int len, int *w, int *h, bool ldata=false)
  515. {
  516. FOURCC imgload = svc_imageLoader::getServiceType();
  517. int n = WASABI_API_SVC->service_getNumServices(imgload);
  518. for(int i=0; i<n; i++)
  519. {
  520. waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
  521. if(sf)
  522. {
  523. svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
  524. if(l)
  525. {
  526. if(l->testData(data,len))
  527. {
  528. ARGB32* ret;
  529. if(ldata) ret = l->loadImageData(data,len,w,h);
  530. else ret = l->loadImage(data,len,w,h);
  531. sf->releaseInterface(l);
  532. return ret;
  533. }
  534. sf->releaseInterface(l);
  535. }
  536. }
  537. }
  538. return NULL;
  539. }
  540. ARGB32 * loadRrc(int id, wchar_t * sec, int *w, int *h, bool data=false)
  541. {
  542. DWORD size=0;
  543. HGLOBAL resourceHandle = WASABI_API_LOADRESFROMFILEW(sec,MAKEINTRESOURCE(id),&size);
  544. if(resourceHandle)
  545. {
  546. ARGB32* ret = loadImg(resourceHandle,size,w,h,data);
  547. UnlockResource(resourceHandle);
  548. return ret;
  549. }
  550. return NULL;
  551. }
  552. void adjustbmp(ARGB32 * p, int len, COLORREF fg)
  553. {
  554. ARGB32 * end = p+len;
  555. while (p < end)
  556. {
  557. int a = (*p>>24)&0xff ;
  558. int b = a*((*p&0xff) * (fg&0xff)) / (0xff*0xff);
  559. int g = a*(((*p>>8)&0xff) * ((fg>>8)&0xff)) / (0xff*0xff);
  560. int r = a*(((*p>>16)&0xff) * ((fg>>16)&0xff)) / (0xff*0xff);
  561. *p = (a<<24) | (r&0xff) | ((g&0xff)<<8) | ((b&0xff)<<16);
  562. p++;
  563. }
  564. }
  565. void DrawArt(HDC dc, HWND hwndDlg, ARGB32 * cur_image, int cur_w, int cur_h)
  566. {
  567. RECT dst, wnd;
  568. GetWindowRect(hwndDlg,&wnd);
  569. wnd.right = wnd.right - wnd.left;
  570. wnd.left = 0;
  571. wnd.bottom = wnd.bottom - wnd.top;
  572. wnd.top = 0;
  573. if(!memcmp(&lastWnd,&wnd,sizeof(RECT)) && cacheDC)
  574. {
  575. BitBlt(dc,0,0,wnd.right,wnd.bottom,cacheDC,0,0,SRCCOPY);
  576. return;
  577. }
  578. // create cacheDC
  579. if(cacheDC) DeleteDC(cacheDC);
  580. cacheDC = CreateCompatibleDC(dc);
  581. HBITMAP hbm = CreateCompatibleBitmap(dc,wnd.right,wnd.bottom);
  582. SelectObject(cacheDC,hbm);
  583. DeleteObject(hbm);
  584. lastWnd = wnd;
  585. if(!lockAspect) dst = wnd;
  586. else
  587. {
  588. // maintain 'square' stretching, fill in dst
  589. double aspX = (double)(wnd.right)/(double)cur_w;
  590. double aspY = (double)(wnd.bottom)/(double)cur_h;
  591. double asp = min(aspX, aspY);
  592. int newW = (int)(cur_w*asp);
  593. int newH = (int)(cur_h*asp);
  594. dst.left = (wnd.right - newW)/2;
  595. dst.top = (wnd.bottom - newH)/2;
  596. dst.right = dst.left + newW;
  597. dst.bottom = dst.top + newH;
  598. }
  599. // fill the background to black
  600. HBRUSH brush = CreateSolidBrush(bgcolour);
  601. FillRect(cacheDC,&wnd,brush);
  602. DeleteObject(brush);
  603. //SkinBitmap(cur_image, cur_w, cur_h).stretchToRect(&DCCanvas(cacheDC), &dst);
  604. HDC srcDC = CreateCompatibleDC(dc);
  605. BITMAPINFO bmi = {0};
  606. bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  607. bmi.bmiHeader.biWidth = cur_w;
  608. bmi.bmiHeader.biHeight = -cur_h;
  609. bmi.bmiHeader.biPlanes = 1;
  610. bmi.bmiHeader.biBitCount = 32;
  611. bmi.bmiHeader.biCompression = BI_RGB;
  612. void *bits = 0;
  613. HBITMAP srcBMP = CreateDIBSection(srcDC, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
  614. memcpy(bits, cur_image, cur_w*cur_h*4);
  615. HBITMAP oldSrcBM = (HBITMAP)SelectObject(srcDC,srcBMP);
  616. BLENDFUNCTION blendFn;
  617. blendFn.BlendOp = AC_SRC_OVER;
  618. blendFn.BlendFlags = 0;
  619. blendFn.SourceConstantAlpha = 255;
  620. blendFn.AlphaFormat = AC_SRC_ALPHA;
  621. AlphaBlend(cacheDC,
  622. dst.left, dst.top,
  623. dst.right-dst.left, dst.bottom-dst.top,
  624. srcDC,
  625. 0, 0,
  626. cur_w, cur_h,
  627. blendFn);
  628. BitBlt(dc,0,0,wnd.right,wnd.bottom,cacheDC,0,0,SRCCOPY);
  629. SelectObject(srcDC,oldSrcBM);
  630. DeleteObject(srcBMP);
  631. DeleteDC(srcDC);
  632. }
  633. static INT_PTR CALLBACK art_dlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  634. {
  635. static ARGB32 * cur_image;
  636. static int cur_w, cur_h;
  637. static bool closed;
  638. switch(msg)
  639. {
  640. case WM_INITDIALOG:
  641. closed = 0;
  642. cur_image = 0;
  643. bgcolour = WADlg_getColor(WADLG_ITEMBG);
  644. PostMessage(hwndDlg,WM_USER,0,0);
  645. break;
  646. case WM_USER+1:
  647. viewArtWindow(TRUE);
  648. closed=0;
  649. break;
  650. case WM_DISPLAYCHANGE:
  651. WADlg_init(plugin.hwndParent);
  652. bgcolour = WADlg_getColor(WADLG_ITEMBG);
  653. case WM_USER:
  654. {
  655. wchar_t *filename = (!lParam?(wchar_t *)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME):(wchar_t*)lParam);
  656. if(cur_image) WASABI_API_MEMMGR->sysFree(cur_image); cur_image = 0;
  657. if (AGAVE_API_ALBUMART->GetAlbumArt(filename, L"cover", &cur_w, &cur_h, &cur_image) != ALBUMART_SUCCESS)
  658. {/*
  659. SkinBitmap b(L"winamp.cover.notfound");
  660. if(!b.isInvalid())
  661. {
  662. cur_w = b.getWidth();
  663. cur_h = b.getHeight();
  664. cur_image = (ARGB32*)WASABI_API_MEMMGR->sysMalloc(cur_w * cur_h * sizeof(ARGB32));
  665. memcpy(cur_image,b.getBits(),cur_w * cur_h * sizeof(ARGB32));
  666. }
  667. else*/
  668. {
  669. cur_image = loadRrc(IDR_IMAGE_NOTFOUND,L"PNG",&cur_w, &cur_h,true);
  670. if(cur_image) adjustbmp(cur_image, cur_w*cur_h, WADlg_getColor(WADLG_ITEMFG));
  671. }
  672. if(!waBkClr) bgcolour = WADlg_getColor(WADLG_ITEMBG);
  673. if(autoHide && !closed && msg != WM_DISPLAYCHANGE)
  674. viewArtWindow(FALSE);
  675. }
  676. else
  677. {
  678. if(waBkClr) bgcolour = RGB(0,0,0);
  679. if(autoHide && !closed && msg != WM_DISPLAYCHANGE)
  680. viewArtWindow(TRUE);
  681. }
  682. if(cacheDC) DeleteDC(cacheDC); cacheDC = NULL;
  683. InvalidateRect(hwndDlg,NULL,TRUE);
  684. }
  685. break;
  686. case WM_DESTROY:
  687. if(cur_image) WASABI_API_MEMMGR->sysFree(cur_image); cur_image = 0;
  688. if(cacheDC) DeleteDC(cacheDC); cacheDC = NULL;
  689. break;
  690. case WM_PAINT:
  691. {
  692. if (cur_image)
  693. {
  694. PAINTSTRUCT psPaint={0};
  695. HDC dc = BeginPaint(hwndDlg, &psPaint);
  696. DrawArt(dc,hwndDlg,cur_image,cur_w,cur_h);
  697. EndPaint(hwndDlg, &psPaint);
  698. }
  699. }
  700. break;
  701. case WM_ERASEBKGND:
  702. {
  703. if (cur_image)
  704. {
  705. HDC dc = (HDC)wParam;
  706. DrawArt(dc,hwndDlg,cur_image,cur_w,cur_h);
  707. return 1;
  708. }
  709. }
  710. break;
  711. case WM_SIZE:
  712. if(cacheDC) DeleteDC(cacheDC); cacheDC = NULL;
  713. InvalidateRect(hwndDlg,NULL,TRUE);
  714. break;
  715. case WM_CLOSE:
  716. closed=1;
  717. viewArtWindow(FALSE);
  718. break;
  719. case WM_LBUTTONDBLCLK:
  720. PostMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(ID_CONTEXTMENU_OPENFOLDER,0),0);
  721. break;
  722. case WM_RBUTTONDOWN:
  723. {
  724. HMENU menu = GetSubMenu(context_menu,0);
  725. POINT p;
  726. GetCursorPos(&p);
  727. CheckMenuItem(menu,ID_CONTEXTMENU_LOCKASPECTRATIO,(lockAspect?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  728. CheckMenuItem(menu,ID_CONTEXTMENU_AUTOHIDE,(autoHide?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  729. CheckMenuItem(menu,ID_CONTEXTMENU_CLICKTRACK,(clickTrack?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  730. CheckMenuItem(menu,ID_CONTEXTMENU_AUTO_HIDE_MODERN,(hidemodern?MF_CHECKED:MF_UNCHECKED)|MF_BYCOMMAND);
  731. TrackPopupMenu(menu,TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,p.x,p.y,0,hwndDlg,NULL);
  732. }
  733. break;
  734. case WM_COMMAND:
  735. // this is used when 'Alt+A' is pressed by the user as part of the registered global shortcut
  736. if(LOWORD(wParam) == WINAMP_ARTVIEW_MENUID)
  737. {
  738. if(artwindow_open) PostMessage(myWndChild,WM_CLOSE,0,0);
  739. else PostMessage(myWndChild,WM_USER+1,0,0);
  740. }
  741. switch(LOWORD(wParam))
  742. {
  743. case ID_CONTEXTMENU_GETALBUMART:
  744. {
  745. wchar_t *filename = (wchar_t *)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME);
  746. if(filename && *filename)
  747. {
  748. wchar_t artist[1024],album[1024];
  749. extendedFileInfoStructW a = {filename,L"artist",artist,1024};
  750. SendMessage(plugin.hwndParent,WM_WA_IPC,(LPARAM)&a,IPC_GET_EXTENDED_FILE_INFOW);
  751. a.metadata = L"album";
  752. a.ret = album;
  753. SendMessage(plugin.hwndParent,WM_WA_IPC,(LPARAM)&a,IPC_GET_EXTENDED_FILE_INFOW);
  754. artFetchData d = {sizeof(d),hwndDlg,artist,album,0};
  755. int r = (int)SendMessage(plugin.hwndParent,WM_WA_IPC,(LPARAM)&d,IPC_FETCH_ALBUMART);
  756. if(r == 0 && d.imgData && d.imgDataLen) // success, save art in correct location
  757. {
  758. AGAVE_API_ALBUMART->SetAlbumArt(filename,L"cover",0,0,d.imgData,d.imgDataLen,d.type);
  759. WASABI_API_MEMMGR->sysFree(d.imgData);
  760. SendMessage(hwndDlg,WM_USER,0,0);
  761. }
  762. }
  763. }
  764. break;
  765. case ID_CONTEXTMENU_LOCKASPECTRATIO:
  766. lockAspect = (!lockAspect);
  767. if(cacheDC) DeleteDC(cacheDC); cacheDC = NULL;
  768. InvalidateRect(hwndDlg,NULL,TRUE);
  769. break;
  770. case ID_CONTEXTMENU_REFRESH:
  771. SendMessage(hwndDlg,WM_USER,0,0);
  772. break;
  773. case ID_CONTEXTMENU_OPENFOLDER:
  774. {
  775. wchar_t *filename = (wchar_t *)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GET_PLAYING_FILENAME);
  776. if(filename && *filename)
  777. {
  778. wchar_t fn[MAX_PATH];
  779. lstrcpynW(fn,filename,MAX_PATH);
  780. PathRemoveFileSpecW(fn);
  781. ShellExecuteW(NULL,L"open",fn,NULL,NULL,SW_SHOW);
  782. }
  783. }
  784. break;
  785. case ID_CONTEXTMENU_AUTOHIDE:
  786. autoHide = (!autoHide);
  787. break;
  788. case ID_CONTEXTMENU_CLICKTRACK:
  789. clickTrack = (!clickTrack);
  790. break;
  791. case ID_CONTEXTMENU_AUTO_HIDE_MODERN:
  792. hidemodern = (!hidemodern);
  793. break;
  794. }
  795. break;
  796. }
  797. return 0;
  798. }
  799. void viewArtWindow(BOOL show)
  800. {
  801. artwindow_open=show;
  802. MENUITEMINFO i = {sizeof(i), MIIM_STATE , MFT_STRING, (artwindow_open?MFS_CHECKED:MFS_UNCHECKED), WINAMP_ARTVIEW_MENUID};
  803. SetMenuItemInfo(menu, WINAMP_ARTVIEW_MENUID, FALSE, &i);
  804. ShowWindow(myWnd,(artwindow_open?SW_SHOW:SW_HIDE));
  805. }
  806. void addAccelerators(HWND hwnd, ACCEL* accel, int accel_size, int translate_mode)
  807. {
  808. HACCEL hAccel = CreateAcceleratorTable(accel,accel_size);
  809. if (hAccel) WASABI_API_APP->app_addAccelerators(hwnd, &hAccel, accel_size, translate_mode);
  810. }
  811. #ifdef __cplusplus
  812. extern "C" {
  813. #endif
  814. __declspec( dllexport ) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin()
  815. {
  816. return &plugin;
  817. }
  818. __declspec(dllexport) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param)
  819. {
  820. // prompt to remove our settings with default as no (just incase)
  821. if(MessageBoxA(hwndDlg,WASABI_API_LNGSTRING(IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS),
  822. plugin.description,MB_YESNO|MB_DEFBUTTON2) == IDYES)
  823. {
  824. WritePrivateProfileStringA("gen_classicart",0,0,INI_FILE);
  825. no_uninstall = FALSE;
  826. }
  827. // as we're doing too much in subclasses, etc we cannot allow for on-the-fly removal so need to do a normal reboot
  828. return GEN_PLUGIN_UNINSTALL_REBOOT;
  829. }
  830. #ifdef __cplusplus
  831. }
  832. #endif