generate.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. #include "../gracenote/gracenote.h"
  2. #include "api__ml_plg.h"
  3. #include <windows.h>
  4. #include "resource.h"
  5. #include "../../General/gen_ml/ml.h"
  6. #include "../winamp/wa_ipc.h"
  7. #include "../Agave/Language/api_language.h"
  8. #include "../nu/MediaLibraryInterface.h"
  9. #include "../nu/ComboBox.h"
  10. #include "main.h"
  11. #include <shlwapi.h>
  12. #include <assert.h>
  13. #include "playlist.h"
  14. #include <atlbase.h>
  15. #include "IDScanner.h"
  16. //#include "../Wasabi/bfc/util/timefmt.h"
  17. //#include <bfc/util/timefmt.h>
  18. #include <strsafe.h> // should be last
  19. HWND hwndDlgCurrent = 0;
  20. bool optionsVisible = true;
  21. bool isGenerating = false;
  22. int originalWidth = 877;
  23. //#define DIALOG_WIDTH_OPTIONS 877 // use originalWidth instead
  24. #define DIALOG_WIDTH_NO_OPTIONS 610
  25. #define DIALOG_HIDDEN_COLUMN_ID 4
  26. // Pass in 0 for width or height in order to preserve its current dimension
  27. void SizeWindow(HWND hwnd, int width, int height)
  28. {
  29. if (width == 0 || height == 0) // Preserve only if one of the items is 0
  30. {
  31. RECT windowRect;
  32. GetWindowRect(hwnd, &windowRect);
  33. if (width == 0) // Preserve the width
  34. width = windowRect.right - windowRect.left;
  35. if (height == 0) // Preserve the height
  36. height = windowRect.bottom - windowRect.top;
  37. }
  38. SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  39. }
  40. void ClientResize(HWND hWnd, int nWidth, int nHeight)
  41. {
  42. RECT rcClient, rcWind;
  43. POINT ptDiff;
  44. GetClientRect(hWnd, &rcClient);
  45. GetWindowRect(hWnd, &rcWind);
  46. ptDiff.x = (rcWind.right - rcWind.left) - rcClient.right;
  47. ptDiff.y = (rcWind.bottom - rcWind.top) - rcClient.bottom;
  48. MoveWindow(hWnd,rcWind.left, rcWind.top, nWidth + ptDiff.x, nHeight + ptDiff.y, TRUE);
  49. }
  50. void SetMarqueeProgress(bool isMarquee)
  51. {
  52. HWND hwndProgress = GetDlgItem(hwndDlgCurrent,IDC_PROGRESS_GENERATE);
  53. static long state = GetWindowLongW(hwndProgress, GWL_STYLE); // Capture the initial state of the progress bar
  54. if (isMarquee) // Set it to marquee style
  55. {
  56. SetWindowLong (hwndProgress, GWL_STYLE, GetWindowLong(hwndProgress, GWL_STYLE) | PBS_MARQUEE);
  57. //SendMessage(hwndProgress, PBM_SETMARQUEE, 1, 10);
  58. SendMessage(hwndProgress, PBM_SETMARQUEE, 1, 30);
  59. }
  60. else // Restore the normal progress bar
  61. {
  62. SetWindowLong (hwndProgress, GWL_STYLE, state);
  63. //SendMessage(hwndProgress, WM_PAINT, 0, 0);
  64. InvalidateRect(hwndProgress, 0, 1); // Force a repaint of the marquee after turning it off because there are stuck pixels in XP
  65. }
  66. }
  67. // Sets the query check state as well as enabling all the controls involved
  68. void SetMLQueryCheckState(HWND hwndDlg, unsigned int checked)
  69. {
  70. // Get the handles to all the child controls we want to enable / disable
  71. HWND hwndButtonMlQuery = GetDlgItem(hwndDlg, IDC_BUTTON_ML_QUERY);
  72. HWND hwndEditMlQuery = GetDlgItem(hwndDlg, IDC_EDIT_ML_QUERY);
  73. HWND hwndButtonRestoreQueryDefault = GetDlgItem(hwndDlg, IDC_BUTTON_RESTORE_QUERY_DEFAULT);
  74. if (checked) // enable all the controls related to ML query
  75. {
  76. CheckDlgButton(hwndDlg, IDC_CHECK_ML_QUERY, TRUE);
  77. EnableWindow (hwndButtonMlQuery, TRUE );
  78. EnableWindow (hwndEditMlQuery, TRUE );
  79. EnableWindow (hwndButtonRestoreQueryDefault, TRUE );
  80. useMLQuery = true;
  81. }
  82. else // disable all the controls related to ML query
  83. {
  84. CheckDlgButton(hwndDlg, IDC_CHECK_ML_QUERY, FALSE);
  85. EnableWindow (hwndButtonMlQuery, FALSE );
  86. EnableWindow (hwndEditMlQuery, FALSE );
  87. EnableWindow (hwndButtonRestoreQueryDefault, FALSE );
  88. useMLQuery = false;
  89. }
  90. }
  91. void SetButtonsEnabledState(bool enabled_flag)
  92. {
  93. int itemIds[] =
  94. {
  95. IDC_BUTTON_PLAY_NOW,
  96. IDC_BUTTON_ENQUEUE_NOW,
  97. IDC_BUTTON_SAVEAS,
  98. IDC_BUTTON_REGENERATE
  99. };
  100. for(int i = 0; i < sizeof(itemIds) / sizeof(itemIds[0]); i++)
  101. EnableWindow(GetDlgItem(hwndDlgCurrent, itemIds[i]), enabled_flag);
  102. }
  103. void ToggleOptions(bool reset)
  104. {
  105. if (reset)
  106. optionsVisible = false;
  107. else
  108. optionsVisible = !optionsVisible; // Toggle the options visible state
  109. // to resolve tabbing issues when in the collapsed
  110. // state we need to disable some of the controls (dro)
  111. int itemIds[] = {
  112. IDC_RADIO_PLAYLIST_ITEMS,
  113. IDC_RADIO_PLAYLIST_LENGTH,
  114. IDC_RADIO_PLAYLIST_SIZE,
  115. IDC_COMBO_LENGTH,
  116. IDC_CHECK_USE_SEED,
  117. IDC_CHECK_MULTIPLE_ARTISTS,
  118. IDC_CHECK_MULTIPLE_ALBUMS,
  119. IDC_CHECK_ML_QUERY,
  120. IDC_EDIT_ML_QUERY,
  121. IDC_BUTTON_ML_QUERY,
  122. IDC_BUTTON_RESTORE_QUERY_DEFAULT
  123. };
  124. for(int i = 0; i < sizeof(itemIds) / sizeof(itemIds[0]); i++)
  125. EnableWindow(GetDlgItem(hwndDlgCurrent, itemIds[i]), optionsVisible);
  126. SetMLQueryCheckState(hwndDlgCurrent, useMLQuery);
  127. if (optionsVisible)
  128. {
  129. SizeWindow(hwndDlgCurrent, originalWidth, 0); // Resize the window to the correct width
  130. SetDlgItemText(hwndDlgCurrent, IDC_BUTTON_OPTIONS, WASABI_API_LNGSTRINGW(IDS_OPTIONS)); // Set the dialog button to show the correct options mode
  131. }
  132. else
  133. {
  134. SizeWindow(hwndDlgCurrent, DIALOG_WIDTH_NO_OPTIONS, 0);
  135. SetDlgItemText(hwndDlgCurrent, IDC_BUTTON_OPTIONS, WASABI_API_LNGSTRINGW(IDS_NO_OPTIONS)); // Set the dialog button to show the correct options mode
  136. }
  137. }
  138. // ToDo: Make this more human readable
  139. void FormatToMinutesAndSeconds(const int lengthInSeconds, wchar_t *buff, const size_t cchBuf)
  140. {
  141. //StringCchPrintfW(buff, cchBuf, L"%d:%02d", lengthInSeconds / 60, lengthInSeconds % 60);
  142. int total_length_s = lengthInSeconds;
  143. int uncert = 0;
  144. // Minutes and seconds
  145. if (total_length_s < 60*60) StringCchPrintfW(buff, 64, L"%s%u:%02u", uncert ? L"~" : L"", total_length_s / 60, total_length_s % 60);
  146. // Hours minutes and seconds
  147. else if (total_length_s < 60*60*24) StringCchPrintfW(buff, 64, L"%s%u:%02u:%02u", uncert ? L"~" : L"", total_length_s / 60 / 60, (total_length_s / 60) % 60, total_length_s % 60);
  148. else
  149. {
  150. wchar_t days[16] = {0};
  151. int total_days = total_length_s / (60 * 60 * 24); // Calculate days
  152. total_length_s -= total_days * 60 * 60 * 24; // Remove days from length
  153. StringCchPrintfW(buff, 64,
  154. //WASABI_API_LNGSTRINGW(IDS_LENGTH_DURATION_STRING),
  155. L"%s%u %s+%u:%02u:%02u",
  156. ((uncert) ? L"~" : L""), total_days, // Approximate
  157. WASABI_API_LNGSTRINGW_BUF(total_days == 1 ? IDS_DAY : IDS_DAYS, days, 16), // Days
  158. total_length_s / 60 / 60, // Hours
  159. (total_length_s / 60) % 60, // Minutes
  160. total_length_s % 60); // Seconds
  161. }
  162. }
  163. // Refreashed the statistics about the generated playlist
  164. void UpdateStats(void)
  165. {
  166. const int MAX_STATS = 512;
  167. wchar_t stats[MAX_STATS] = {0};
  168. wchar_t lengthText[MAX_STATS] = {0};
  169. wchar_t sizeText[MAX_STATS] = {0};
  170. int count = (int)currentPlaylist.GetNumItems();
  171. uint64_t length = currentPlaylist.GetPlaylistLengthMilliseconds();
  172. uint64_t size = currentPlaylist.GetPlaylistSizeBytes();
  173. // Add the seed stats?
  174. if (useSeed == TRUE)
  175. {
  176. count += (int)seedPlaylist.GetNumItems();
  177. length += seedPlaylist.GetPlaylistLengthMilliseconds();
  178. size += seedPlaylist.GetPlaylistSizeBytes();
  179. }
  180. FormatToMinutesAndSeconds((int)(length / 1000), lengthText, MAX_STATS); // / 1000 because we have it in milliseconds and not seconds
  181. StrFormatByteSizeW(size, sizeText, MAX_STATS); // Get the human readable formatting for filesize
  182. StringCchPrintf(stats, MAX_STATS, WASABI_API_LNGSTRINGW(IDS_STATS), count, lengthText, sizeText);
  183. SetDlgItemText(hwndDlgCurrent, IDC_STATIC_STATS, stats); // Set the dialog button to show the correct options mode
  184. }
  185. // Update the progress to the current
  186. static void doProgressBar(HWND h, int x, int t=-1) {
  187. h = GetDlgItem(h,IDC_PROGRESS_GENERATE);
  188. if(t!=-1 && SendMessage(h,PBM_GETRANGE,0,0) != t)
  189. SendMessage(h,PBM_SETRANGE32,0,t);
  190. SendMessage(h,PBM_SETPOS,x,0);
  191. }
  192. // Update the status while id scanner is active
  193. static void FillStatus(HWND hwndDlg)
  194. {
  195. long state, track, tracks;
  196. if (scanner.GetStatus(&state, &track, &tracks))
  197. {
  198. static int x=0;
  199. wchar_t *ticker;
  200. switch (x++)
  201. {
  202. case 0: ticker=L""; break;
  203. case 1: ticker=L"."; break;
  204. case 2: ticker=L".."; break;
  205. default: ticker=L"...";
  206. }
  207. x%=4;
  208. wchar_t status[1024]=L"";
  209. switch (state)
  210. {
  211. case IDScanner::STATE_ERROR:
  212. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_INITIALIZING,status,1024);
  213. KillTimer(hwndDlg, 1);
  214. doProgressBar(hwndDlg,0,1);
  215. ShowErrorDlg(hwndDlg);
  216. break;
  217. case IDScanner::STATE_IDLE:
  218. WASABI_API_LNGSTRINGW_BUF(IDS_IDLE,status,1024);
  219. doProgressBar(hwndDlg,0);
  220. break;
  221. case IDScanner::STATE_INITIALIZING:
  222. StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_INITIALIZING), ticker);
  223. doProgressBar(hwndDlg,0);
  224. break;
  225. case IDScanner::STATE_SYNC:
  226. StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_SYNC), track, tracks, ticker);
  227. doProgressBar(hwndDlg,track,tracks);
  228. break;
  229. case IDScanner::STATE_METADATA:
  230. StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_METADATA), track, tracks, ticker);
  231. doProgressBar(hwndDlg,track,tracks);
  232. break;
  233. case IDScanner::STATE_MUSICID:
  234. StringCchPrintfW(status, 1024, WASABI_API_LNGSTRINGW(IDS_MUSICID), track, tracks, ticker);
  235. doProgressBar(hwndDlg,track,tracks);
  236. break;
  237. case IDScanner::STATE_DONE:
  238. if (!isGenerating) // Only set the done state if the gneeration has not started yet
  239. {
  240. WASABI_API_LNGSTRINGW_BUF(IDS_DONE,status,1024);
  241. doProgressBar(hwndDlg,0,0); // Turn off the progress bar to 0
  242. }
  243. KillTimer(hwndDlg, 1);
  244. break;
  245. }
  246. if (!isGenerating) // Only set the done state if the gneeration has not started yet
  247. {
  248. SetDlgItemTextW(hwndDlg, IDC_STATIC_PROGRESS_STATE, status);
  249. }
  250. }
  251. }
  252. // Function calls appropriate items when a generation is requested
  253. void Regenerate(HWND hwndDlg)
  254. {
  255. SendMessage(GetDlgItem(hwndDlgCurrent, IDC_LIST_RESULTS2),LVM_DELETEALLITEMS,0,0); // Clear the listview of all playlist items
  256. SetTimer(hwndDlg, 1, 500, 0); // Set the progress timer for the scanner
  257. StartScan();
  258. MoreLikeTheseSongs(&seedPlaylist);
  259. }
  260. // Function draws in colors for the seed listview items
  261. LRESULT CustomDrawListViewColors(LPARAM lParam)
  262. {
  263. LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
  264. switch(lplvcd->nmcd.dwDrawStage)
  265. {
  266. case CDDS_PREPAINT : //Before the paint cycle begins
  267. return CDRF_NOTIFYITEMDRAW; //request notifications for individual listview items
  268. case CDDS_ITEMPREPAINT: //Before an item is drawn
  269. if (lplvcd->nmcd.dwItemSpec < seedPlaylist.entries.size()) // Check how many seeds we have, thats how we know which rows in the view to color paint
  270. {
  271. if (useSeed == TRUE)
  272. {
  273. lplvcd->clrText = RGB(0,0,255); // Color seed tracks blue
  274. }
  275. else
  276. {
  277. lplvcd->clrText = RGB(100,100,100); // Color seed tracks a faded grey
  278. }
  279. }
  280. return CDRF_NEWFONT;
  281. break;
  282. }
  283. return CDRF_DODEFAULT;
  284. }
  285. int SetRadioControlsState(HWND hwndDlg)
  286. {
  287. // Set the radio buttons for playlist length type, items or minutes
  288. if(plLengthType == PL_ITEMS)
  289. {
  290. CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_ITEMS,TRUE);
  291. SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_ITEMS, 0);
  292. SetPlLengthTypeComboToItems(hwndDlg, plItems);
  293. }
  294. else if(plLengthType == PL_MINUTES)
  295. {
  296. CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_LENGTH,TRUE);
  297. SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_LENGTH, 0);
  298. SetPlLengthTypeComboToMinutes(hwndDlg, plMinutes);
  299. }
  300. else if(plLengthType == PL_MEGABYTES)
  301. {
  302. CheckDlgButton(hwndDlg,IDC_RADIO_PLAYLIST_SIZE,TRUE);
  303. SendMessage(hwndDlg, WM_COMMAND, IDC_RADIO_PLAYLIST_SIZE, 0);
  304. SetPlLengthTypeComboToMegabytes(hwndDlg, plMegabytes);
  305. }
  306. return 0;
  307. }
  308. // Update the combo box contents depending on which lengthType we are using
  309. int UpdateComboLength(HWND hwndDlg)
  310. {
  311. const int BUF_SIZE = 32;
  312. ComboBox combo(hwndDlg, IDC_COMBO_LENGTH);
  313. wchar_t buf[BUF_SIZE] = {0};
  314. combo.GetEditText(buf, BUF_SIZE);
  315. switch(plLengthType)
  316. {
  317. case PL_ITEMS:
  318. plItems = _wtoi(buf);
  319. return 0;
  320. break;
  321. case PL_MINUTES:
  322. plMinutes = _wtoi(buf);
  323. return 0;
  324. break;
  325. case PL_MEGABYTES:
  326. plMegabytes = _wtoi(buf);
  327. return 0;
  328. break;
  329. }
  330. return 1;
  331. }
  332. LRESULT tab_fix_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  333. {
  334. if(uMsg == WM_CHAR)
  335. {
  336. if(wParam == VK_TAB)
  337. {
  338. SendMessage(hwndDlgCurrent, WM_NEXTDLGCTL, (GetAsyncKeyState(VK_SHIFT)&0x8000), FALSE);
  339. return TRUE;
  340. }
  341. }
  342. return CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"tab_fix_proc"), hwndDlg, uMsg, wParam, lParam);
  343. }
  344. // this will prevent the hidden column (for making the headers work better)
  345. // from appearing as sizeable / disabled (as it effectively is)
  346. LRESULT header_block_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  347. {
  348. if(uMsg == WM_SETCURSOR)
  349. {
  350. HDHITTESTINFO hitTest;
  351. GetCursorPos(&hitTest.pt);
  352. ScreenToClient(hwndDlg, &hitTest.pt);
  353. hitTest.flags = hitTest.iItem = 0;
  354. SendMessage(hwndDlg, HDM_HITTEST, FALSE, (LPARAM)&hitTest);
  355. if(hitTest.iItem == DIALOG_HIDDEN_COLUMN_ID || hitTest.iItem == -1)
  356. {
  357. SetCursor(LoadCursor(NULL, IDC_ARROW));
  358. return TRUE;
  359. }
  360. }
  361. return CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"header_block_proc"), hwndDlg, uMsg, wParam, lParam);
  362. }
  363. INT_PTR CALLBACK GenerateProcedure(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  364. {
  365. switch (msg)
  366. {
  367. case WM_INITDIALOG:
  368. {
  369. RECT r;
  370. GetWindowRect(hwndDlg, &r);
  371. originalWidth = r.right - r.left;
  372. // bit hacky but it will resolve issues with tabbing and the combobox in a
  373. // dropdown style still not 100% sure why it's failing to work though (dro)
  374. HWND combobox = GetWindow(GetDlgItem(hwndDlg, IDC_COMBO_LENGTH), GW_CHILD);
  375. SetPropW(combobox, L"tab_fix_proc",(HANDLE)SetWindowLongPtrW(combobox, GWLP_WNDPROC, (LONG_PTR)tab_fix_proc));
  376. hwndDlgCurrent = hwndDlg; // Set the global so that we have a window open
  377. // this will make sure that we've got thr aacplus logo shown even when using a localised version
  378. SendDlgItemMessage(hwndDlg,IDC_LOGO,STM_SETIMAGE,IMAGE_BITMAP,
  379. (LPARAM)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_GN_LOGO),IMAGE_BITMAP,0,0,LR_SHARED));
  380. BoldStatusText(GetDlgItem(hwndDlg, IDC_STATIC_PROGRESS_STATE) );
  381. SetRadioControlsState(hwndDlg); // Set the playlist length state
  382. if(multipleArtists)
  383. CheckDlgButton(hwndDlg,IDC_CHECK_MULTIPLE_ARTISTS,TRUE);
  384. if(multipleAlbums)
  385. CheckDlgButton(hwndDlg,IDC_CHECK_MULTIPLE_ALBUMS,TRUE);
  386. if(useSeed)
  387. CheckDlgButton(hwndDlg,IDC_CHECK_USE_SEED,TRUE);
  388. // Set up the colums for the playlist listing
  389. #define ListView_InsertColumnW(hwnd, iCol, pcol) \
  390. (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol))
  391. //SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
  392. // Add the columns to the listbox
  393. HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST_RESULTS2);
  394. ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
  395. LVCOLUMNW lvc = {0, };
  396. lvc.mask = LVCF_TEXT|LVCF_WIDTH;
  397. lvc.pszText = WASABI_API_LNGSTRINGW(IDS_TITLE); // Initialize the columns of the listview
  398. lvc.cx = 160;
  399. ListView_InsertColumnW(hwndlist, 0, &lvc);
  400. lvc.pszText = WASABI_API_LNGSTRINGW(IDS_LENGTH);
  401. lvc.cx = 80;
  402. ListView_InsertColumnW(hwndlist, 1, &lvc);
  403. lvc.pszText = WASABI_API_LNGSTRINGW(IDS_SIZE);
  404. lvc.cx = 80;
  405. ListView_InsertColumnW(hwndlist, 2, &lvc);
  406. lvc.pszText = WASABI_API_LNGSTRINGW(IDS_SEED);
  407. lvc.cx = 80;
  408. ListView_InsertColumnW(hwndlist, 3, &lvc);
  409. lvc.pszText = 0;
  410. lvc.cx = 0;
  411. ListView_InsertColumnW(hwndlist, DIALOG_HIDDEN_COLUMN_ID, &lvc);
  412. // Autosize the columns taking the header into consideration
  413. ListView_SetColumnWidth(hwndlist,0,LVSCW_AUTOSIZE_USEHEADER);
  414. ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE_USEHEADER);
  415. ListView_SetColumnWidth(hwndlist,2,LVSCW_AUTOSIZE_USEHEADER);
  416. ListView_SetColumnWidth(hwndlist,3,LVSCW_AUTOSIZE_USEHEADER);
  417. HWND hwndListHeader = ListView_GetHeader(hwndlist);
  418. SetPropW(hwndListHeader, L"header_block_proc",(HANDLE)SetWindowLongPtrW(hwndListHeader, GWLP_WNDPROC, (LONG_PTR)header_block_proc));
  419. // Background color for highlighting seed tracks.
  420. //hbrBkcolor = CreateSolidBrush ( RGB(255,0,0) );
  421. BoldStatusText(GetDlgItem(hwndDlg, IDC_STATIC_STATS) );
  422. // Populate the query textbox
  423. SetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, mlQuery); // Set the text for the query
  424. // Disable the regenerate button because we will be scanning the library and generating on initialization
  425. //EnableWindow (GetDlgItem(hwndDlgCurrent, IDC_BUTTON_REGENERATE), FALSE ); // This is for initialization
  426. SetButtonsEnabledState(false);
  427. // Set up the window with the options hidden
  428. ToggleOptions(true);
  429. // Show the window since we are modeless
  430. POINT pt = {(LONG)GetPrivateProfileInt(L"ml_plg", L"generate_x",-1, mediaLibrary.GetWinampIniW()),
  431. (LONG)GetPrivateProfileInt(L"ml_plg", L"generate_y",-1, mediaLibrary.GetWinampIniW())};
  432. if (!windowOffScreen(hwndDlg, pt))
  433. SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
  434. else
  435. ShowWindow(hwndDlg, SW_SHOW);
  436. Regenerate(hwndDlg);
  437. if (WASABI_API_APP) // Add direct mousewheel support for the main tracklist view of seed and generated tracks
  438. WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(hwndDlg, IDC_LIST_RESULTS2), TRUE);
  439. return TRUE;
  440. }
  441. break;
  442. // Trying to change the background color of seed tracks here
  443. /*case WM_CTLCOLORSTATIC:
  444. {
  445. HDC hdc = (HDC) wParam;
  446. HWND hwndStatic = (HWND) lParam;
  447. if ( hwndStatic == GetDlgItem ( hwndDlg, IDC_LIST_RESULTS2 ))
  448. {
  449. SetBkMode ( hdc, TRANSPARENT );
  450. return (LRESULT) hbrBkcolor;
  451. }
  452. }
  453. break;*/
  454. case WM_TIMER:
  455. FillStatus(hwndDlg);
  456. break;
  457. case WM_COMMAND:
  458. switch (LOWORD(wParam))
  459. {
  460. case IDCANCEL:
  461. {
  462. RECT rect = {0};
  463. GetWindowRect(hwndDlg, &rect);
  464. char buf[16] = {0};
  465. StringCchPrintfA(buf, 16, "%d", rect.left);
  466. WritePrivateProfileStringA("ml_plg", "generate_x", buf, mediaLibrary.GetWinampIni());
  467. StringCchPrintfA(buf, 16, "%d", rect.top);
  468. WritePrivateProfileStringA("ml_plg", "generate_y", buf, mediaLibrary.GetWinampIni());
  469. EndDialog(hwndDlg, 0);
  470. hwndDlgCurrent = 0; // Set to null so new instance can be opened
  471. WriteSettingsToIni(hwndDlg);
  472. // We need to free up our seed tracks because we no longer require them
  473. seedPlaylist.Clear(); // Clear the global seed list
  474. }
  475. break;
  476. case IDC_BUTTON_CANCEL:
  477. SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0);
  478. break;
  479. case IDC_BUTTON_REGENERATE:
  480. Regenerate(hwndDlg);
  481. break;
  482. case IDC_RADIO_PLAYLIST_ITEMS:
  483. SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_ITEMS));
  484. plLengthType = PL_ITEMS; // Set to # of items
  485. SetPlLengthTypeComboToItems(hwndDlg, plItems);
  486. break;
  487. case IDC_RADIO_PLAYLIST_LENGTH:
  488. SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_MINUTES));
  489. plLengthType = PL_MINUTES; // Set to minutes
  490. SetPlLengthTypeComboToMinutes(hwndDlg, plMinutes);
  491. break;
  492. case IDC_RADIO_PLAYLIST_SIZE:
  493. SetDlgItemText(hwndDlg, IDC_LENGTH_TYPE, WASABI_API_LNGSTRINGW(IDS_MEGABYTES));
  494. plLengthType = PL_MEGABYTES; // Set to megabytes
  495. SetPlLengthTypeComboToMegabytes(hwndDlg, plMegabytes);
  496. break;
  497. case IDC_COMBO_LENGTH:
  498. {
  499. UpdateComboLength(hwndDlg);
  500. }
  501. break;
  502. case IDC_BUTTON_OPTIONS:
  503. ToggleOptions(false);
  504. break;
  505. case IDC_BUTTON_PLAY_NOW:
  506. playPlaylist(currentPlaylist, false, 0, /*seed,*/ useSeed); // Play the current playlist taking the seed track into consideration
  507. SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done
  508. break;
  509. case IDC_BUTTON_ENQUEUE_NOW:
  510. playPlaylist(currentPlaylist, true, 0, /*seed,*/ useSeed); // Enqueue the current playlist taking the seed track into consideration
  511. SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done
  512. break;
  513. case IDC_BUTTON_SAVEAS:
  514. {
  515. // ToDo spawn a dialog to save the current playlist as a ML playlist
  516. int save_result = WASABI_API_DIALOGBOXPARAM(IDD_ADD_PLAYLIST, hwndDlg, AddPlaylistDialogProc, (LPARAM)&seedPlaylist/*seed*/);
  517. if (save_result == IDOK) // If the user accepted that playlist dialog then go ahead and close up everything
  518. {
  519. SendMessage(hwndDlg, WM_COMMAND, IDCANCEL, 0); // Close up the dialog because we are done
  520. }
  521. }
  522. break;
  523. case IDC_BUTTON_ML_QUERY:
  524. {
  525. char temp[1024] = {0};
  526. GetDlgItemTextA(hwndDlg, IDC_EDIT_ML_QUERY, temp, sizeof(temp) - 1); // Retreive the current custom ML query
  527. ml_editview meq = {hwndDlg, (temp[0] == 0) ? DEFAULT_ML_QUERY : temp, "ML Query", -1}; // Create the editview
  528. meq.name = WASABI_API_LNGSTRING(IDS_ML_QUERY); // Set a custom title
  529. if(!(int)SendMessage(plugin.hwndLibraryParent, WM_ML_IPC, (LPARAM)&meq, ML_IPC_EDITVIEW))
  530. return 0; // Spawn the edit view
  531. SetDlgItemTextA(hwndDlg, IDC_EDIT_ML_QUERY, meq.query); // Set the text back to the edited query
  532. }
  533. break;
  534. case IDC_BUTTON_RESTORE_QUERY_DEFAULT:
  535. SetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, _T(DEFAULT_ML_QUERY)); // Set the text back to the edited query
  536. break;
  537. case IDC_CHECK_USE_SEED:
  538. useSeed = IsDlgButtonChecked(hwndDlg,IDC_CHECK_USE_SEED);
  539. UpdateStats(); // Update the track stats, because the seed status can change them
  540. RedrawWindow(GetDlgItem(hwndDlg,IDC_LIST_RESULTS2), 0, 0, RDW_INVALIDATE); // Refresh the colors in the list view
  541. break;
  542. case IDC_CHECK_MULTIPLE_ARTISTS: // Set the multiple tracks per artist option when checked
  543. multipleArtists = IsDlgButtonChecked(hwndDlg, IDC_CHECK_MULTIPLE_ARTISTS);
  544. break;
  545. case IDC_CHECK_MULTIPLE_ALBUMS: // Set the multiple tracks per album option when checked
  546. multipleAlbums = IsDlgButtonChecked(hwndDlg, IDC_CHECK_MULTIPLE_ALBUMS);
  547. break;
  548. case IDC_CHECK_ML_QUERY:
  549. SetMLQueryCheckState(hwndDlg, IsDlgButtonChecked(hwndDlg, IDC_CHECK_ML_QUERY));
  550. break;
  551. case IDC_EDIT_ML_QUERY:
  552. if (HIWORD(wParam) == EN_CHANGE)
  553. {
  554. GetDlgItemTextW(hwndDlg, IDC_EDIT_ML_QUERY, mlQuery, MAX_ML_QUERY_SIZE); // Set the text back to the edited query
  555. break;
  556. }
  557. }
  558. break;
  559. case WM_NOTIFY:
  560. if(((LPNMHDR)lParam)->code == HDN_BEGINTRACKW || ((LPNMHDR)lParam)->code == HDN_BEGINTRACKA ||
  561. ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGW || ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGA)
  562. {
  563. LPNMHEADER pNMHeader = (LPNMHEADER)lParam;
  564. if(pNMHeader->iItem == DIALOG_HIDDEN_COLUMN_ID)
  565. {
  566. SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)TRUE);
  567. return TRUE;
  568. }
  569. }
  570. else if(((LPNMHDR)lParam)->code == NM_CUSTOMDRAW) // Notify for List View custom redraw (seed track colors)
  571. {
  572. #if defined(_WIN64)
  573. SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, (LONG)CustomDrawListViewColors(lParam));
  574. #else
  575. SetWindowLong(hwndDlg, DWL_MSGRESULT, (LONG)CustomDrawListViewColors(lParam));
  576. #endif
  577. return TRUE;
  578. }
  579. {
  580. const int controls[] =
  581. {
  582. IDC_LIST_RESULTS2,
  583. };
  584. if (WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, msg, wParam, lParam, controls, ARRAYSIZE(controls)) != FALSE)
  585. {
  586. return TRUE;
  587. }
  588. }
  589. break;
  590. case WM_DESTROY:
  591. {
  592. if (WASABI_API_APP)
  593. WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(GetDlgItem(hwndDlg, IDC_LIST_RESULTS2), FALSE);
  594. }
  595. break;
  596. }
  597. return 0;
  598. }
  599. int AddResultListItem(Playlist *playlist, int index, int position, bool seed)
  600. {
  601. const unsigned int MAX_INFO = 256;
  602. wchar_t filename[MAX_INFO] = {0};
  603. wchar_t info[MAX_INFO] = {0};
  604. wchar_t *seedText = 0;
  605. LVITEMW lvi={LVIF_TEXT, position, 0};
  606. playlist->GetItem(index,filename,MAX_INFO);
  607. // Add the title column
  608. playlist->GetItemTitle(index, info, MAX_INFO);
  609. lvi.pszText=info;
  610. lvi.cchTextMax=sizeof(info) / sizeof(*info);
  611. SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_INSERTITEMW,0,(LPARAM)&lvi);
  612. // Add the length column
  613. int length = playlist->GetItemLengthMilliseconds(index);
  614. if (length <= 0)
  615. StringCchCopyW(info, MAX_INFO, WASABI_API_LNGSTRINGW(IDS_UNKNOWN));
  616. else
  617. FormatToMinutesAndSeconds(length / 1000, info, MAX_INFO); // / 1000 because we have it in milliseconds and not seconds
  618. lvi.pszText=info;
  619. lvi.cchTextMax=sizeof(info) / sizeof(*info);
  620. lvi.iSubItem = 1;
  621. SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi);
  622. // Add the size column
  623. int size = playlist->GetItemSizeBytes(index);
  624. if (size <= 0)
  625. StringCchCopyW(info, MAX_INFO, WASABI_API_LNGSTRINGW(IDS_UNKNOWN));
  626. else
  627. StrFormatByteSizeW(size, info, MAX_INFO);
  628. lvi.pszText=info;
  629. lvi.cchTextMax=sizeof(info) / sizeof(*info);
  630. lvi.iSubItem = 2;
  631. SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi);
  632. // Add the seed track column
  633. if (seed == true)
  634. seedText = WASABI_API_LNGSTRINGW(IDS_YES);
  635. else
  636. seedText = WASABI_API_LNGSTRINGW(IDS_NO);
  637. lvi.pszText=seedText;
  638. lvi.cchTextMax=sizeof(seedText) / sizeof(*seedText);
  639. lvi.iSubItem = 3;
  640. SendMessage(GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2),LVM_SETITEMW,0,(LPARAM)&lvi);
  641. return 0;
  642. }
  643. void CantPopulateResults(void)
  644. {
  645. wchar_t message[256] = {0};
  646. WASABI_API_LNGSTRINGW_BUF(IDS_EXCUSE_ME, message, 256);
  647. MessageBoxW(hwndDlgCurrent, message, WASABI_API_LNGSTRINGW(IDS_NULLSOFT_PLAYLIST_GENERATOR), MB_OK | MB_ICONINFORMATION);
  648. }
  649. void PopulateResults(Playlist *playlist)
  650. {
  651. // Add all of the seed tracks to the listview
  652. int listLength = (playlist) ? (int)playlist->GetNumItems() : 0;
  653. int seedLength = (int)seedPlaylist.GetNumItems();
  654. for (int i = 0; i < seedLength; i++)
  655. {
  656. AddResultListItem(&seedPlaylist, i, i, true);
  657. }
  658. // Add all of the generated tracks to the listview
  659. for (int i = 0; i < listLength; i++)
  660. {
  661. AddResultListItem(playlist, i, seedLength + i, false);
  662. }
  663. // After we are done populating the data then we can size the columns accordingly
  664. HWND hwndlist = GetDlgItem(hwndDlgCurrent,IDC_LIST_RESULTS2);
  665. ListView_SetColumnWidth(hwndlist,0,(listLength ? LVSCW_AUTOSIZE : LVSCW_AUTOSIZE_USEHEADER));
  666. ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE_USEHEADER);
  667. ListView_SetColumnWidth(hwndlist,2,LVSCW_AUTOSIZE_USEHEADER);
  668. ListView_SetColumnWidth(hwndlist,3,LVSCW_AUTOSIZE_USEHEADER);
  669. // Refresh the playlist stats
  670. UpdateStats();
  671. // Change the progress status to read done 'generated'
  672. SetDlgItemText(hwndDlgCurrent,IDC_STATIC_PROGRESS_STATE, WASABI_API_LNGSTRINGW(IDS_DONE));
  673. SetMarqueeProgress(false); // Turn the marquee off because we are actually generating the tracks
  674. //EnableWindow (GetDlgItem(hwndDlgCurrent, IDC_BUTTON_REGENERATE), TRUE );
  675. SetButtonsEnabledState(true); // Renable the buttons
  676. }