editinfo.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. #include "main.h"
  2. #include "../Agave/Language/api_language.h"
  3. #include "../nu/ListView.h"
  4. #include "../Winamp/strutil.h"
  5. #include "resource.h"
  6. #include <time.h>
  7. #define MAKESAFE(x) ((x)?(x):L"")
  8. extern W_ListView resultlist;
  9. extern itemRecordListW itemCache;
  10. volatile int no_lv_update = 0;
  11. //////////////// info editor fun
  12. // must be one of OUR item records (since we free it)
  13. static void UpdateItemRecordFromDB(itemRecordW *song)
  14. {
  15. // look in the database for the updated song info
  16. EnterCriticalSection(&g_db_cs);
  17. nde_scanner_t s = NDE_Table_CreateScanner(g_table);
  18. if (NDE_Scanner_LocateNDEFilename(s, MAINTABLE_ID_FILENAME, FIRST_RECORD, song->filename))
  19. {
  20. // now we can actually update the itemCache itemRecordW from the value in the db
  21. itemRecordW item = {0};
  22. itemRecordListW obj = {&item, 0, 1};
  23. ScannerRefToObjCacheNFNW(s, &obj, false);
  24. item.filename = song->filename;
  25. song->filename = NULL; // set to NULL so freeRecord doesn't delete the filename string
  26. freeRecord(song); // delete old item
  27. *song = item; // replace with our new (BETTER :) item
  28. }
  29. NDE_Table_DestroyScanner(g_table, s);
  30. LeaveCriticalSection(&g_db_cs);
  31. }
  32. //physically update metadata in a given file
  33. int updateFileInfo(const wchar_t *filename, const wchar_t *metadata, wchar_t *data)
  34. {
  35. extendedFileInfoStructW efis = {
  36. filename,
  37. metadata,
  38. data ? data : L"",
  39. data ? wcslen(data) : 0,
  40. };
  41. return SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&efis, IPC_SET_EXTENDED_FILE_INFOW);
  42. }
  43. static int m_upd_nb, m_stopped, m_upd_nb_all, m_upd_nb_cur;
  44. static nde_scanner_t m_scanner;
  45. // sets part and parts to -1 or 0 on fail/missing (e.g. parts will be -1 on "1", but 0 on "1/")
  46. void ParseIntSlashInt(wchar_t *string, int *part, int *parts)
  47. {
  48. *part = -1;
  49. *parts = -1;
  50. if (string && string[0])
  51. {
  52. *part = _wtoi(string);
  53. while (string && *string && *string != '/')
  54. {
  55. string++;
  56. }
  57. if (string && *string == '/')
  58. {
  59. string++;
  60. *parts = _wtoi(string);
  61. }
  62. }
  63. }
  64. // TODO: benski> can this copy-and-paste code be factored?
  65. static INT_PTR CALLBACK updateFiles_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  66. {
  67. switch (uMsg)
  68. {
  69. case WM_INITDIALOG:
  70. {
  71. SetWindowTextW(hwndDlg, WASABI_API_LNG->GetStringW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  72. GetModuleHandleW(L"winamp.exe"), IDS_UPDATING_FILES));
  73. SetWindowLong(GetDlgItem(hwndDlg, IDC_STATUS), GWL_STYLE, (GetWindowLong(GetDlgItem(hwndDlg, IDC_STATUS), GWL_STYLE)&~SS_CENTER) | SS_LEFTNOWORDWRAP);
  74. SetTimer(hwndDlg, 0x123, 30, NULL);
  75. m_upd_nb = 0;
  76. EnterCriticalSection(&g_db_cs);
  77. m_scanner = NDE_Table_CreateScanner(g_table);
  78. m_stopped = 0;
  79. SendDlgItemMessage(hwndDlg, IDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, m_upd_nb_all));
  80. m_upd_nb_cur = 0;
  81. // show window and restore last position as applicable
  82. POINT pt = {g_config->ReadInt(L"scan_x", -1), g_config->ReadInt(L"scan_y", -1)};
  83. if (!windowOffScreen(hwndDlg, pt))
  84. SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
  85. break;
  86. }
  87. case WM_TIMER:
  88. if (wParam == 0x123 && !m_stopped)
  89. {
  90. unsigned int start_t = GetTickCount();
  91. again:
  92. {
  93. int l = resultlist.GetCount();
  94. while (m_upd_nb < l && !resultlist.GetSelected(m_upd_nb))
  95. m_upd_nb++;
  96. if (m_upd_nb >= l)
  97. {
  98. //done
  99. EndDialog(hwndDlg, 1);
  100. break;
  101. }
  102. int i = m_upd_nb++;
  103. itemRecordW *song = itemCache.Items + i;
  104. HWND hwndParent = GetParent(hwndDlg);
  105. wchar_t stattmp[512] = {0};
  106. wchar_t *p = scanstr_backW(song->filename, L"\\", song->filename - 1) + 1;
  107. wsprintfW(stattmp, WASABI_API_LNG->GetStringW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  108. GetModuleHandleW(L"winamp.exe"), IDS_UPDATING_X), p);
  109. SetDlgItemTextW(hwndDlg, IDC_STATUS, stattmp);
  110. SendDlgItemMessage(hwndDlg, IDC_PROGRESS1, PBM_SETPOS, m_upd_nb_cur, 0);
  111. m_upd_nb_cur++;
  112. int updtagz = !!IsDlgButtonChecked(hwndParent, IDC_CHECK1);
  113. if (!NDE_Scanner_LocateNDEFilename(m_scanner, MAINTABLE_ID_FILENAME, FIRST_RECORD, song->filename))
  114. {
  115. break;
  116. }
  117. NDE_Scanner_Edit(m_scanner);
  118. #define CHECK_AND_COPY(IDCHECK, ID, field, item) if (IsDlgButtonChecked(hwndParent, IDCHECK)) {\
  119. wchar_t blah[2048] = {0}; GetDlgItemTextW(hwndParent, ID, blah, 2048);\
  120. if (wcscmp(MAKESAFE(item), blah))\
  121. { if (blah[0]) db_setFieldStringW(m_scanner, field, blah);\
  122. else db_removeField(m_scanner, field);\
  123. ndestring_release(item);\
  124. item = ndestring_wcsdup(blah);}}
  125. CHECK_AND_COPY(IDC_CHECK_ARTIST, IDC_EDIT_ARTIST, MAINTABLE_ID_ARTIST, song->artist);
  126. CHECK_AND_COPY(IDC_CHECK_TITLE, IDC_EDIT_TITLE, MAINTABLE_ID_TITLE, song->title);
  127. CHECK_AND_COPY(IDC_CHECK_ALBUM, IDC_EDIT_ALBUM, MAINTABLE_ID_ALBUM, song->album);
  128. CHECK_AND_COPY(IDC_CHECK_COMMENT, IDC_EDIT_COMMENT, MAINTABLE_ID_COMMENT, song->comment);
  129. CHECK_AND_COPY(IDC_CHECK_GENRE, IDC_EDIT_GENRE, MAINTABLE_ID_GENRE, song->genre);
  130. CHECK_AND_COPY(IDC_CHECK_ALBUMARTIST, IDC_EDIT_ALBUMARTIST, MAINTABLE_ID_ALBUMARTIST, song->albumartist);
  131. CHECK_AND_COPY(IDC_CHECK_PUBLISHER, IDC_EDIT_PUBLISHER, MAINTABLE_ID_PUBLISHER, song->publisher);
  132. CHECK_AND_COPY(IDC_CHECK_COMPOSER, IDC_EDIT_COMPOSER, MAINTABLE_ID_COMPOSER, song->composer);
  133. CHECK_AND_COPY(IDC_CHECK_CATEGORY, IDC_EDIT_CATEGORY, MAINTABLE_ID_CATEGORY, song->category);
  134. #define CHECK_AND_COPY_EXTENDED(IDCHECK, ID, field, name) if (IsDlgButtonChecked(hwndParent, IDCHECK)) {\
  135. wchar_t blah[2048] = {0}; GetDlgItemTextW(hwndParent, ID, blah, 2048);\
  136. wchar_t *oldData = getRecordExtendedItem_fast(song, name);\
  137. if (wcscmp(MAKESAFE(oldData), blah))\
  138. { if (blah[0]) db_setFieldStringW(m_scanner, field, blah);\
  139. else db_removeField(m_scanner, field);\
  140. wchar_t *nde_blah = ndestring_wcsdup(blah);\
  141. setRecordExtendedItem(song, name, nde_blah);\
  142. ndestring_release(nde_blah);}}
  143. CHECK_AND_COPY_EXTENDED(IDC_CHECK_DIRECTOR, IDC_EDIT_DIRECTOR, MAINTABLE_ID_DIRECTOR, extended_fields.director);
  144. CHECK_AND_COPY_EXTENDED(IDC_CHECK_PRODUCER, IDC_EDIT_PRODUCER, MAINTABLE_ID_PRODUCER, extended_fields.producer);
  145. CHECK_AND_COPY_EXTENDED(IDC_CHECK_PODCAST_CHANNEL, IDC_EDIT_PODCAST_CHANNEL, MAINTABLE_ID_PODCASTCHANNEL, extended_fields.podcastchannel);
  146. #define CHECK_AND_COPY_EXTENDED_PC(IDCHECK, field, name) \
  147. wchar_t blah[2048] = {0}; StringCchPrintfW(blah, 2048, L"%d", (IsDlgButtonChecked(hwndParent, IDCHECK) == BST_CHECKED));\
  148. wchar_t *oldData = getRecordExtendedItem_fast(song, name);\
  149. if (wcscmp(MAKESAFE(oldData), blah))\
  150. { if (blah[0]) db_setFieldInt(m_scanner, field, _wtoi(blah));\
  151. else db_removeField(m_scanner, field);\
  152. wchar_t *nde_blah = ndestring_wcsdup(blah);\
  153. setRecordExtendedItem(song, name, nde_blah);\
  154. ndestring_release(nde_blah);}
  155. CHECK_AND_COPY_EXTENDED_PC(IDC_CHECK_PODCAST, MAINTABLE_ID_ISPODCAST, extended_fields.ispodcast);
  156. if (IsDlgButtonChecked(hwndParent, IDC_CHECK_TRACK))
  157. {
  158. wchar_t blah[64] = {0};
  159. GetDlgItemTextW(hwndParent, IDC_EDIT_TRACK, blah, 64);
  160. int track, tracks;
  161. ParseIntSlashInt(blah, &track, &tracks);
  162. if (tracks <= 0) tracks = -1;
  163. if (track <= 0) track = -1;
  164. if (song->track != track || song->tracks != tracks)
  165. {
  166. if (track>0) db_setFieldInt(m_scanner, MAINTABLE_ID_TRACKNB, track);
  167. else db_removeField(m_scanner, MAINTABLE_ID_TRACKNB);
  168. if (tracks>0) db_setFieldInt(m_scanner, MAINTABLE_ID_TRACKS, tracks);
  169. else db_removeField(m_scanner, MAINTABLE_ID_TRACKS);
  170. song->track = track;
  171. song->tracks = tracks;
  172. }
  173. }
  174. if (IsDlgButtonChecked(hwndParent, IDC_CHECK_DISC))
  175. {
  176. wchar_t blah[64] = {0};
  177. GetDlgItemTextW(hwndParent, IDC_EDIT_DISC, blah, 64);
  178. int disc, discs;
  179. ParseIntSlashInt(blah, &disc, &discs);
  180. if (discs <= 0) discs = -1;
  181. if (disc <= 0) disc = -1;
  182. if (song->disc != disc || song->discs != discs)
  183. {
  184. if (disc>0) db_setFieldInt(m_scanner, MAINTABLE_ID_DISC, disc);
  185. else db_removeField(m_scanner, MAINTABLE_ID_DISC);
  186. if (discs>0) db_setFieldInt(m_scanner, MAINTABLE_ID_DISCS, discs);
  187. else db_removeField(m_scanner, MAINTABLE_ID_DISCS);
  188. song->disc = disc;
  189. song->discs = discs;
  190. }
  191. }
  192. if (IsDlgButtonChecked(hwndParent, IDC_CHECK_YEAR))
  193. {
  194. wchar_t blah[64] = {0};
  195. GetDlgItemText(hwndParent, IDC_EDIT_YEAR, blah, 64);
  196. int n = _wtoi(blah);
  197. if (n <= 0) n = -1;
  198. if (song->year != n)
  199. {
  200. if (n > 0) db_setFieldInt(m_scanner, MAINTABLE_ID_YEAR, n);
  201. else db_removeField(m_scanner, MAINTABLE_ID_YEAR);
  202. song->year = n;
  203. }
  204. }
  205. if (IsDlgButtonChecked(hwndParent, IDC_CHECK_BPM))
  206. {
  207. wchar_t blah[64] = {0};
  208. GetDlgItemText(hwndParent, IDC_EDIT_BPM, blah, 64);
  209. int n = _wtoi(blah);
  210. if (n <= 0) n = -1;
  211. if (song->bpm != n)
  212. {
  213. if (n > 0) db_setFieldInt(m_scanner, MAINTABLE_ID_BPM, n);
  214. else db_removeField(m_scanner, MAINTABLE_ID_BPM);
  215. song->bpm = n;
  216. }
  217. }
  218. if (IsDlgButtonChecked(hwndParent, IDC_CHECK_RATING))
  219. {
  220. int n = SendDlgItemMessage(hwndParent, IDC_COMBO_RATING, CB_GETCURSEL, 0, 0), rating = -1;
  221. if (n != CB_ERR && (n >= 0 && n < 5))
  222. {
  223. rating = 5 - n;
  224. }
  225. /*int n = atoi(blah);
  226. if (n < 0 || n > 5) n = -1;*/
  227. if (song->rating != rating)
  228. {
  229. if (rating > 0) db_setFieldInt(m_scanner, MAINTABLE_ID_RATING, n);
  230. else db_removeField(m_scanner, MAINTABLE_ID_RATING);
  231. song->rating = rating;
  232. }
  233. }
  234. if (updtagz)
  235. {
  236. m_stopped = 1;
  237. retry:
  238. if (updateFileInfo(song->filename, DB_FIELDNAME_title, song->title)) // if this returns 0, then this format doesnt even support extended
  239. {
  240. updateFileInfo(song->filename, DB_FIELDNAME_artist, song->artist);
  241. updateFileInfo(song->filename, DB_FIELDNAME_album, song->album);
  242. updateFileInfo(song->filename, DB_FIELDNAME_comment, song->comment);
  243. updateFileInfo(song->filename, DB_FIELDNAME_genre, song->genre);
  244. wchar_t buf[32] = {0};
  245. if (song->track > 0)
  246. {
  247. if (song->tracks > 0)
  248. wsprintfW(buf, L"%d/%d", song->track, song->tracks);
  249. else
  250. wsprintfW(buf, L"%d", song->track);
  251. }
  252. else buf[0] = 0;
  253. updateFileInfo(song->filename, DB_FIELDNAME_track, buf);
  254. if (song->year > 0) wsprintfW(buf, L"%d", song->year);
  255. else buf[0] = 0;
  256. updateFileInfo(song->filename, DB_FIELDNAME_year, buf);
  257. if (song->disc > 0)
  258. {
  259. if (song->discs > 0)
  260. wsprintfW(buf, L"%d/%d", song->disc, song->discs);
  261. else
  262. wsprintfW(buf, L"%d", song->disc);
  263. }
  264. else buf[0] = 0;
  265. updateFileInfo(song->filename, DB_FIELDNAME_disc, buf);
  266. if (song->rating > 0) wsprintfW(buf, L"%d", song->rating);
  267. else buf[0] = 0;
  268. updateFileInfo(song->filename, DB_FIELDNAME_rating, buf);
  269. if (song->bpm > 0) wsprintfW(buf, L"%d", song->bpm);
  270. else buf[0] = 0;
  271. updateFileInfo(song->filename, DB_FIELDNAME_bpm, buf);
  272. updateFileInfo(song->filename, DB_FIELDNAME_albumartist, song->albumartist);
  273. updateFileInfo(song->filename, DB_FIELDNAME_publisher, song->publisher);
  274. updateFileInfo(song->filename, DB_FIELDNAME_composer, song->composer);
  275. updateFileInfo(song->filename, DB_FIELDNAME_category, song->category);
  276. updateFileInfo(song->filename, DB_FIELDNAME_director, getRecordExtendedItem_fast(song, extended_fields.director));
  277. updateFileInfo(song->filename, DB_FIELDNAME_producer, getRecordExtendedItem_fast(song, extended_fields.producer));
  278. if (!SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_WRITE_EXTENDED_FILE_INFO))
  279. {
  280. wchar_t tmp[1024] = {0};
  281. wsprintfW(tmp, WASABI_API_LNG->GetStringW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  282. GetModuleHandleW(L"winamp.exe"), IDS_ERROR_UPDATING_FILE), song->filename);
  283. int ret = MessageBoxW(hwndDlg, tmp, WASABI_API_LNG->GetStringW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  284. GetModuleHandleW(L"winamp.exe"), IDS_INFO_UPDATING_ERROR), MB_RETRYCANCEL);
  285. if (ret == IDRETRY) goto retry;
  286. if (ret == IDCANCEL)
  287. {
  288. EndDialog(hwndDlg, 0);
  289. break;
  290. }
  291. }
  292. }
  293. m_stopped = 0;
  294. }
  295. db_setFieldInt(m_scanner, MAINTABLE_ID_LASTUPDTIME, (int)time(NULL));
  296. db_setFieldInt(m_scanner, MAINTABLE_ID_FILETIME, (int)time(NULL));
  297. NDE_Scanner_Post(m_scanner);
  298. WASABI_API_SYSCB->syscb_issueCallback(api_mldb::SYSCALLBACK, api_mldb::MLDB_FILE_UPDATED, (size_t)song->filename, 0);
  299. }
  300. if (GetTickCount() - start_t < 30) goto again;
  301. }
  302. break;
  303. case WM_COMMAND:
  304. if (LOWORD(wParam) == IDCANCEL)
  305. {
  306. EndDialog(hwndDlg, 0);
  307. }
  308. break;
  309. case WM_DESTROY:
  310. {
  311. RECT scan_rect = {0};
  312. GetWindowRect(hwndDlg, &scan_rect);
  313. g_config->WriteInt(L"scan_x", scan_rect.left);
  314. g_config->WriteInt(L"scan_y", scan_rect.top);
  315. NDE_Table_DestroyScanner(g_table, m_scanner);
  316. NDE_Table_Sync(g_table);
  317. g_table_dirty = 0;
  318. LeaveCriticalSection(&g_db_cs);
  319. break;
  320. }
  321. }
  322. return FALSE;
  323. }
  324. static int checkEditInfoClick(HWND hwndDlg, POINT p, int item, int check)
  325. {
  326. RECT r = {0};
  327. GetWindowRect(GetDlgItem(hwndDlg, item), &r);
  328. ScreenToClient(hwndDlg, (LPPOINT)&r);
  329. ScreenToClient(hwndDlg, (LPPOINT)&r.right);
  330. if (PtInRect(&r, p) && !IsDlgButtonChecked(hwndDlg, check))
  331. {
  332. CheckDlgButton(hwndDlg, check, TRUE);
  333. if (item == IDC_COMBO_RATING) SendDlgItemMessage(hwndDlg, IDC_COMBO_RATING, CB_SHOWDROPDOWN, TRUE, 0);
  334. EnableWindow(GetDlgItem(hwndDlg, item), TRUE);
  335. EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
  336. PostMessageW(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, item), (LPARAM)TRUE);
  337. return 1;
  338. }
  339. return 0;
  340. }
  341. void getViewport(RECT *r, HWND wnd, int full, RECT *sr)
  342. {
  343. POINT *p = NULL;
  344. if (p || sr || wnd)
  345. {
  346. HMONITOR hm = NULL;
  347. if (sr)
  348. hm = MonitorFromRect(sr, MONITOR_DEFAULTTONEAREST);
  349. else if (wnd)
  350. hm = MonitorFromWindow(wnd, MONITOR_DEFAULTTONEAREST);
  351. else if (p)
  352. hm = MonitorFromPoint(*p, MONITOR_DEFAULTTONEAREST);
  353. if (hm)
  354. {
  355. MONITORINFOEXW mi;
  356. memset(&mi, 0, sizeof(mi));
  357. mi.cbSize = sizeof(mi);
  358. if (GetMonitorInfoW(hm, &mi))
  359. {
  360. if (!full)
  361. *r = mi.rcWork;
  362. else
  363. *r = mi.rcMonitor;
  364. return ;
  365. }
  366. }
  367. }
  368. if (full)
  369. { // this might be borked =)
  370. r->top = r->left = 0;
  371. r->right = GetSystemMetrics(SM_CXSCREEN);
  372. r->bottom = GetSystemMetrics(SM_CYSCREEN);
  373. }
  374. else
  375. {
  376. SystemParametersInfoW(SPI_GETWORKAREA, 0, r, 0);
  377. }
  378. }
  379. BOOL windowOffScreen(HWND hwnd, POINT pt)
  380. {
  381. RECT r = {0}, wnd = {0}, sr = {0};
  382. GetWindowRect(hwnd, &wnd);
  383. sr.left = pt.x;
  384. sr.top = pt.y;
  385. sr.right = sr.left + (wnd.right - wnd.left);
  386. sr.bottom = sr.top + (wnd.bottom - wnd.top);
  387. getViewport(&r, hwnd, 0, &sr);
  388. return !PtInRect(&r, pt);
  389. }
  390. // TODO: benski> can this copy-and-paste code be factored?
  391. static INT_PTR CALLBACK editInfo_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  392. {
  393. switch (uMsg)
  394. {
  395. case WM_INITDIALOG:
  396. {
  397. wchar_t *last_artist = NULL, *last_title = NULL, *last_album = NULL, *last_genre = NULL,
  398. *last_comment = NULL, *last_albumartist = NULL, *last_composer = NULL,
  399. *last_publisher = NULL, *last_ispodcast = NULL, *last_podcastchannel = NULL;
  400. const wchar_t *last_category = NULL, *last_director = NULL, *last_producer = NULL;
  401. int last_year = -1, last_track = -1, last_disc = -1, last_discs = -1, last_tracks = -1,
  402. last_bpm = -1, last_rating = -1, disable_artist = 0, disable_title = 0,
  403. disable_album = 0, disable_genre = 0, disable_year = 0, disable_track = 0,
  404. disable_comment = 0, disable_disc = 0, disable_albumartist = 0, disable_composer = 0,
  405. disable_publisher = 0, disable_discs = 0, disable_tracks = 0, disable_category = 0,
  406. disable_director = 0, disable_producer = 0, disable_bpm = 0, disable_rating = 0,
  407. disable_ispodcast = 0, disable_podcastchannel = 0, nb = 0;
  408. if (g_config->ReadInt(L"upd_tagz", 1)) CheckDlgButton(hwndDlg, IDC_CHECK1, BST_CHECKED);
  409. EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
  410. SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605\u2605\u2605\u2605");
  411. SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605\u2605\u2605");
  412. SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605\u2605");
  413. SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605\u2605");
  414. SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0, (LPARAM)L"\u2605");
  415. SendDlgItemMessageW(hwndDlg, IDC_COMBO_RATING, CB_ADDSTRING, 0,
  416. (LPARAM)WASABI_API_LNG->GetStringW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  417. GetModuleHandleW(L"winamp.exe"), IDS_NO_RATING));
  418. for (int i = 0; i < resultlist.GetCount(); i++)
  419. {
  420. if (!resultlist.GetSelected(i)) continue;
  421. itemRecordW *song = itemCache.Items + i;
  422. #define SAVE_LAST_STR(last, check, disable) if (!disable && check && check[0]) { if (!last) last = check; else if (wcscmp(check, last)) disable = 1; }
  423. #define SAVE_LAST_INT(last, check, disable) if (!disable && check > 0) { if (last == -1) last = check; else if (last != check) disable = 1; }
  424. SAVE_LAST_STR(last_artist, song->artist, disable_artist);
  425. SAVE_LAST_STR(last_title, song->title, disable_title);
  426. SAVE_LAST_STR(last_album, song->album, disable_album);
  427. SAVE_LAST_STR(last_comment, song->comment, disable_comment);
  428. SAVE_LAST_STR(last_genre, song->genre, disable_genre);
  429. SAVE_LAST_INT(last_year, song->year, disable_year);
  430. SAVE_LAST_INT(last_track, song->track, disable_track);
  431. SAVE_LAST_INT(last_tracks, song->tracks, disable_tracks);
  432. SAVE_LAST_INT(last_disc, song->disc, disable_disc);
  433. SAVE_LAST_INT(last_discs, song->discs, disable_discs);
  434. SAVE_LAST_INT(last_rating, song->rating, disable_rating);
  435. SAVE_LAST_INT(last_bpm, song->bpm, disable_bpm);
  436. SAVE_LAST_STR(last_albumartist, song->albumartist, disable_albumartist);
  437. SAVE_LAST_STR(last_composer, song->composer, disable_composer);
  438. SAVE_LAST_STR(last_publisher, song->publisher, disable_publisher);
  439. SAVE_LAST_STR(last_category, song->category, disable_category);
  440. #define SAVE_LAST_STR_EXTENDED(last, name, disable) if (!disable) { wchar_t *check = getRecordExtendedItem_fast(song, name); if (check && check[0]) { if (!last) last = check; else if (wcscmp(check, last)) disable = 1; }};
  441. SAVE_LAST_STR_EXTENDED(last_director, extended_fields.director, disable_director);
  442. SAVE_LAST_STR_EXTENDED(last_producer, extended_fields.producer, disable_producer);
  443. SAVE_LAST_STR_EXTENDED(last_podcastchannel, extended_fields.podcastchannel, disable_podcastchannel);
  444. SAVE_LAST_STR_EXTENDED(last_ispodcast, extended_fields.ispodcast, disable_ispodcast);
  445. nb++;
  446. }
  447. if (!disable_artist && last_artist) SetDlgItemTextW(hwndDlg, IDC_EDIT_ARTIST, last_artist);
  448. if (!disable_title && last_title) SetDlgItemTextW(hwndDlg, IDC_EDIT_TITLE, last_title);
  449. if (!disable_album && last_album) SetDlgItemTextW(hwndDlg, IDC_EDIT_ALBUM, last_album);
  450. if (!disable_comment && last_comment) SetDlgItemTextW(hwndDlg, IDC_EDIT_COMMENT, last_comment);
  451. if (!disable_albumartist && last_albumartist) SetDlgItemTextW(hwndDlg, IDC_EDIT_ALBUMARTIST, last_albumartist);
  452. if (!disable_composer && last_composer) SetDlgItemTextW(hwndDlg, IDC_EDIT_COMPOSER, last_composer);
  453. if (!disable_publisher && last_publisher) SetDlgItemTextW(hwndDlg, IDC_EDIT_PUBLISHER, last_publisher);
  454. if (!disable_genre && last_genre) SetDlgItemTextW(hwndDlg, IDC_EDIT_GENRE, last_genre);
  455. if (!disable_category && last_category) SetDlgItemTextW(hwndDlg, IDC_EDIT_CATEGORY, last_category);
  456. if (!disable_director && last_director) SetDlgItemTextW(hwndDlg, IDC_EDIT_DIRECTOR, last_director);
  457. if (!disable_producer && last_producer) SetDlgItemTextW(hwndDlg, IDC_EDIT_PRODUCER, last_producer);
  458. if (!disable_podcastchannel && last_podcastchannel) SetDlgItemTextW(hwndDlg, IDC_EDIT_PODCAST_CHANNEL, last_podcastchannel);
  459. if (!disable_ispodcast && last_ispodcast)
  460. {
  461. CheckDlgButton(hwndDlg, IDC_CHECK_PODCAST, (_wtoi(last_ispodcast) == 1 ? BST_CHECKED : BST_UNCHECKED));
  462. }
  463. if (!disable_year && last_year > 0)
  464. {
  465. wchar_t tmp[64] = {0};
  466. wsprintfW(tmp, L"%d", last_year);
  467. SetDlgItemTextW(hwndDlg, IDC_EDIT_YEAR, tmp);
  468. }
  469. if (!disable_bpm && last_bpm > 0)
  470. {
  471. wchar_t tmp[64] = {0};
  472. wsprintfW(tmp, L"%d", last_bpm);
  473. SetDlgItemTextW(hwndDlg, IDC_EDIT_BPM, tmp);
  474. }
  475. if (!disable_rating)
  476. {
  477. if (last_rating > 0 && last_rating <= 5)
  478. {
  479. SendDlgItemMessage(hwndDlg, IDC_COMBO_RATING, CB_SETCURSEL, 5 - last_rating, 0);
  480. }
  481. else SendDlgItemMessage(hwndDlg, IDC_COMBO_RATING, CB_SETCURSEL, 5, 0);
  482. }
  483. if (!disable_track && last_track > 0 && !disable_tracks)
  484. {
  485. wchar_t tmp[64] = {0};
  486. if (!disable_tracks && last_tracks > 0)
  487. wsprintfW(tmp, L"%d/%d", last_track, last_tracks);
  488. else
  489. wsprintfW(tmp, L"%d", last_track);
  490. SetDlgItemTextW(hwndDlg, IDC_EDIT_TRACK, tmp);
  491. }
  492. if (!disable_disc && last_disc > 0
  493. && !disable_discs)
  494. {
  495. wchar_t tmp[64] = {0};
  496. if (!disable_discs && last_discs > 0)
  497. wsprintfW(tmp, L"%d/%d", last_disc, last_discs);
  498. else
  499. wsprintfW(tmp, L"%d", last_disc);
  500. SetDlgItemTextW(hwndDlg, IDC_EDIT_DISC, tmp);
  501. }
  502. wchar_t tmp[512] = {0};
  503. wsprintfW(tmp, WASABI_API_LNG->GetStringW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  504. GetModuleHandleW(L"winamp.exe"), (nb==1?IDS_X_ITEM_SELECTED:IDS_X_ITEMS_SELECTED)), nb);
  505. SetDlgItemTextW(hwndDlg, IDC_HEADER, tmp);
  506. m_upd_nb_all = nb;
  507. // show edit info window and restore last position as applicable
  508. POINT pt = {g_config->ReadInt(L"edit_x", -1), g_config->ReadInt(L"edit_y", -1)};
  509. if (!windowOffScreen(hwndDlg, pt))
  510. SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
  511. }
  512. return 1;
  513. case WM_COMMAND:
  514. #define HANDLE_CONTROL(item, check) { int enabled = IsDlgButtonChecked(hwndDlg, check); EnableWindow(GetDlgItem(hwndDlg, item), enabled); EnableWindow(GetDlgItem(hwndDlg, IDOK), enabled); }
  515. switch (LOWORD(wParam))
  516. {
  517. case IDC_CHECK_ARTIST: HANDLE_CONTROL(IDC_EDIT_ARTIST, IDC_CHECK_ARTIST); break;
  518. case IDC_CHECK_TITLE: HANDLE_CONTROL(IDC_EDIT_TITLE, IDC_CHECK_TITLE); break;
  519. case IDC_CHECK_ALBUM: HANDLE_CONTROL(IDC_EDIT_ALBUM, IDC_CHECK_ALBUM); break;
  520. case IDC_CHECK_COMMENT: HANDLE_CONTROL(IDC_EDIT_COMMENT, IDC_CHECK_COMMENT); break;
  521. case IDC_CHECK_ALBUMARTIST: HANDLE_CONTROL(IDC_EDIT_ALBUMARTIST, IDC_CHECK_ALBUMARTIST); break;
  522. case IDC_CHECK_COMPOSER: HANDLE_CONTROL(IDC_EDIT_COMPOSER, IDC_CHECK_COMPOSER); break;
  523. case IDC_CHECK_PUBLISHER: HANDLE_CONTROL(IDC_EDIT_PUBLISHER, IDC_CHECK_PUBLISHER); break;
  524. case IDC_CHECK_TRACK: HANDLE_CONTROL(IDC_EDIT_TRACK, IDC_CHECK_TRACK); break;
  525. case IDC_CHECK_DISC: HANDLE_CONTROL(IDC_EDIT_DISC, IDC_CHECK_DISC); break;
  526. case IDC_CHECK_GENRE: HANDLE_CONTROL(IDC_EDIT_GENRE, IDC_CHECK_GENRE); break;
  527. case IDC_CHECK_YEAR: HANDLE_CONTROL(IDC_EDIT_YEAR, IDC_CHECK_YEAR); break;
  528. case IDC_CHECK_CATEGORY: HANDLE_CONTROL(IDC_EDIT_CATEGORY, IDC_CHECK_CATEGORY); break;
  529. case IDC_CHECK_DIRECTOR: HANDLE_CONTROL(IDC_EDIT_DIRECTOR, IDC_CHECK_DIRECTOR); break;
  530. case IDC_CHECK_PRODUCER: HANDLE_CONTROL(IDC_EDIT_PRODUCER, IDC_CHECK_PRODUCER); break;
  531. case IDC_CHECK_PODCAST_CHANNEL: HANDLE_CONTROL(IDC_EDIT_PODCAST_CHANNEL, IDC_CHECK_PODCAST_CHANNEL); break;
  532. case IDC_CHECK_BPM: HANDLE_CONTROL(IDC_EDIT_BPM, IDC_CHECK_BPM); break;
  533. case IDC_CHECK_RATING: HANDLE_CONTROL(IDC_COMBO_RATING, IDC_CHECK_RATING); break;
  534. case IDOK:
  535. {
  536. int updtagz = !!IsDlgButtonChecked(hwndDlg, IDC_CHECK1);
  537. g_config->WriteInt(L"upd_tagz", updtagz);
  538. int ret = WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  539. GetModuleHandleW(L"winamp.exe"), IDD_ADDSTUFF,
  540. hwndDlg, (DLGPROC)updateFiles_dialogProc, 0);
  541. ListView_RedrawItems(resultlist.getwnd(), 0, resultlist.GetCount() - 1);
  542. if (!ret) break;
  543. }
  544. case IDCANCEL:
  545. RECT edit_rect = {0};
  546. GetWindowRect(hwndDlg, &edit_rect);
  547. g_config->WriteInt(L"edit_x", edit_rect.left);
  548. g_config->WriteInt(L"edit_y", edit_rect.top);
  549. EndDialog(hwndDlg, 0);
  550. break;
  551. }
  552. break;
  553. case WM_LBUTTONDOWN:
  554. {
  555. POINTS p = MAKEPOINTS(lParam);
  556. POINT p2 = {p.x, p.y};
  557. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_ARTIST, IDC_CHECK_ARTIST)) break;
  558. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_TITLE, IDC_CHECK_TITLE)) break;
  559. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_ALBUM, IDC_CHECK_ALBUM)) break;
  560. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_COMMENT, IDC_CHECK_COMMENT)) break;
  561. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_ALBUMARTIST, IDC_CHECK_ALBUMARTIST)) break;
  562. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_COMPOSER, IDC_CHECK_COMPOSER)) break;
  563. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_PUBLISHER, IDC_CHECK_PUBLISHER)) break;
  564. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_TRACK, IDC_CHECK_TRACK)) break;
  565. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_GENRE, IDC_CHECK_GENRE)) break;
  566. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_YEAR, IDC_CHECK_YEAR)) break;
  567. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_DISC, IDC_CHECK_DISC)) break;
  568. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_CATEGORY, IDC_CHECK_CATEGORY)) break;
  569. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_DIRECTOR, IDC_CHECK_DIRECTOR)) break;
  570. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_PRODUCER, IDC_CHECK_PRODUCER)) break;
  571. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_PODCAST_CHANNEL, IDC_CHECK_PODCAST_CHANNEL)) break;
  572. if (checkEditInfoClick(hwndDlg, p2, IDC_EDIT_BPM, IDC_CHECK_BPM)) break;
  573. if (checkEditInfoClick(hwndDlg, p2, IDC_COMBO_RATING, IDC_CHECK_RATING)) break;
  574. }
  575. break;
  576. }
  577. return FALSE;
  578. }
  579. void editInfo(HWND hwndParent)
  580. {
  581. no_lv_update++;
  582. bgQuery_Stop();
  583. WASABI_API_LNG->LDialogBoxParamW(WASABI_API_LNG->FindDllHandleByGUID(WinampLangGUID),
  584. GetModuleHandleW(L"winamp.exe"), IDD_EDIT_INFO,
  585. hwndParent, (DLGPROC)editInfo_dialogProc, 0);
  586. EatKeyboard();
  587. no_lv_update--;
  588. }
  589. #define REFRESHCB_NUMITEMS (WM_USER)
  590. #define REFRESHCB_ITERATE (WM_USER+1)
  591. #define REFRESHCB_FINISH (WM_USER+2)
  592. static bool refreshKill;
  593. static int RefreshMetadataThread(HANDLE handle, void *_callback, intptr_t id)
  594. {
  595. HWND callback = (HWND)_callback;
  596. int l = resultlist.GetCount();
  597. PostMessage(callback, REFRESHCB_NUMITEMS, 0, resultlist.GetSelectedCount());
  598. for (int i = 0; i < l; i++)
  599. {
  600. if (refreshKill)
  601. break;
  602. if (!resultlist.GetSelected(i)) continue;
  603. itemRecordW *song = itemCache.Items + i;
  604. if (!song->filename || !song->filename[0]) continue;
  605. EnterCriticalSection(&g_db_cs);
  606. int guess = -1, meta = -1, rec = 1;
  607. autoscan_add_directory(song->filename, &guess, &meta, &rec, 1); // use this folder's guess/meta options
  608. if (guess == -1) guess = g_config->ReadInt(L"guessmode", 0);
  609. if (meta == -1) meta = g_config->ReadInt(L"usemetadata", 1);
  610. addFileToDb(song->filename, TRUE, meta, guess, 0, 0, true);
  611. UpdateItemRecordFromDB(song);
  612. PostMessage(callback, REFRESHCB_ITERATE, 0, 0);
  613. LeaveCriticalSection(&g_db_cs);
  614. }
  615. PostMessage(callback, REFRESHCB_FINISH, 0, 0);
  616. return 0;
  617. }
  618. static void WriteStatus(HWND hwndDlg, int dlgId, int numFiles, int totalFiles)
  619. {
  620. wchar_t temp[1024] = {0};
  621. if (numFiles + 1 > totalFiles)
  622. WASABI_API_LNGSTRINGW_BUF(IDS_FINISHED, temp, 1024);
  623. else
  624. StringCchPrintfW(temp, 1024, WASABI_API_LNGSTRINGW(IDS_REFRESH_MESSAGE), numFiles + 1, totalFiles);
  625. SetDlgItemTextW(hwndDlg, dlgId, temp);
  626. }
  627. static int numFiles, totalFiles;
  628. static INT_PTR WINAPI RefreshDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  629. {
  630. switch (msg)
  631. {
  632. case WM_INITDIALOG:
  633. {
  634. numFiles = 0;
  635. totalFiles = 0;
  636. refreshKill = false;
  637. WASABI_API_THREADPOOL->RunFunction(0, RefreshMetadataThread, (void *)hwndDlg, 0, api_threadpool::FLAG_LONG_EXECUTION);
  638. // show refresh info window and restore last position as applicable
  639. POINT pt = {g_config->ReadInt(L"refresh_x", -1), g_config->ReadInt(L"refresh_y", -1)};
  640. if (!windowOffScreen(hwndDlg, pt))
  641. SetWindowPos(hwndDlg, HWND_TOP, pt.x, pt.y, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
  642. }
  643. break;
  644. case WM_DESTROY:
  645. {
  646. RECT refresh_rect = {0};
  647. GetWindowRect(hwndDlg, &refresh_rect);
  648. g_config->WriteInt(L"refresh_x", refresh_rect.left);
  649. g_config->WriteInt(L"refresh_y", refresh_rect.top);
  650. }
  651. break;
  652. case REFRESHCB_NUMITEMS:
  653. totalFiles = lParam;
  654. WriteStatus(hwndDlg, IDC_REFRESHMETADATA_STATUS, numFiles, totalFiles);
  655. break;
  656. case REFRESHCB_ITERATE:
  657. numFiles++;
  658. WriteStatus(hwndDlg, IDC_REFRESHMETADATA_STATUS, numFiles, totalFiles);
  659. break;
  660. case REFRESHCB_FINISH:
  661. EndDialog(hwndDlg, 0);
  662. break;
  663. case WM_COMMAND:
  664. switch (LOWORD(wParam))
  665. {
  666. case IDCANCEL:
  667. refreshKill = true;
  668. break;
  669. }
  670. break;
  671. }
  672. return 0;
  673. }
  674. void RefreshMetadata(HWND parent)
  675. {
  676. bgQuery_Stop();
  677. WASABI_API_DIALOGBOXW(IDD_REFRESH_METADATA, parent, RefreshDlgProc);
  678. ListView_RedrawItems(resultlist.getwnd(), 0, resultlist.GetCount() - 1);
  679. }
  680. // when the rowcache is enabled, the filename pointers should be identical
  681. void UpdateRating_RowCache(const wchar_t *filename, int new_rating)
  682. {
  683. int itemcount = itemCache.Size;
  684. for (int i = 0; i < itemcount; i++)
  685. {
  686. itemRecordW *song = itemCache.Items + i;
  687. if (!song->filename || !song->filename[0]) continue;
  688. if (song->filename == filename || !nde_wcsicmp_fn(song->filename,filename))
  689. {
  690. song->rating = new_rating;
  691. ListView_RedrawItems(resultlist.getwnd(), i, i);
  692. break;
  693. }
  694. }
  695. }
  696. void UpdateLocalResultsCache(const wchar_t *filename) // perhaps just a itemRecordW parm
  697. {
  698. // Search thru the itemCache looking for the file that was changed
  699. int itemcount = itemCache.Size;
  700. for (int i = 0; i < itemcount; i++)
  701. {
  702. // TODO: linear search, yuck, look at this later
  703. itemRecordW *song = itemCache.Items + i;
  704. if (!song->filename || !song->filename[0]) continue;
  705. if (nde_wcsicmp_fn(song->filename,filename) == 0)
  706. {
  707. UpdateItemRecordFromDB(song);
  708. SetTimer(GetParent(resultlist.getwnd()), UPDATE_RESULT_LIST_TIMER_ID, 500, 0);
  709. break;
  710. }
  711. }
  712. }
  713. void fileInfoDialogs(HWND hwndParent)
  714. {
  715. no_lv_update++; // this might block other attempts from going thru but that's OK
  716. bgQuery_Stop();
  717. int l = resultlist.GetCount(), i;
  718. int needref = 0;
  719. for (i = 0;i < l;i++)
  720. {
  721. if (!resultlist.GetSelected(i)) continue;
  722. itemRecordW *song = itemCache.Items + i;
  723. if (!song->filename || !song->filename[0]) continue;
  724. infoBoxParamW p;
  725. p.filename = song->filename;
  726. p.parent = hwndParent;
  727. if (SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&p, IPC_INFOBOXW)) break;
  728. needref = 1;
  729. UpdateItemRecordFromDB(song);
  730. }
  731. EatKeyboard();
  732. if (needref) ListView_RedrawItems(resultlist.getwnd(), 0, resultlist.GetCount() - 1);
  733. no_lv_update--;
  734. }