id3dlg.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. #include "main.h"
  2. #include "Metadata.h"
  3. #include "../Winamp/wa_ipc.h"
  4. // ID3v2 stuff
  5. #include "../id3v2/id3_tag.h"
  6. #include "FactoryHelper.h"
  7. #include "id3.h"
  8. #include "../nu/AutoWide.h"
  9. #include "../nu/AutoChar.h"
  10. #include "AACFrame.h"
  11. #include "LAMEinfo.h"
  12. #include <shlwapi.h>
  13. #include "../nu/ns_wc.h"
  14. #include "../nu/ListView.h"
  15. #include "resource.h"
  16. #include "Stopper.h"
  17. #include "config.h"
  18. #include <strsafe.h>
  19. // TODO: benski> CUT!!!
  20. char g_stream_title[256] = {0};
  21. int fixAACCBRbitrate(int br)
  22. {
  23. static short brs[] =
  24. {
  25. 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0
  26. };
  27. int x;
  28. for (x = 0; x < sizeof(brs) / sizeof(brs[0]); x ++)
  29. {
  30. int delta = (brs[x] * 8) / 128;
  31. if (delta < 2) delta = 2;
  32. if (br < brs[x] - delta) break;
  33. if (br < brs[x] + delta) return brs[x];
  34. }
  35. return br;
  36. }
  37. void ConvertTryUTF8(const char *in, wchar_t *out, size_t outlen)
  38. {
  39. out[0]=0;
  40. int x = MultiByteToWideCharSZ(CP_UTF8, MB_ERR_INVALID_CHARS, in, -1, out, (int)outlen);
  41. if (!x)
  42. {
  43. if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION)
  44. MultiByteToWideCharSZ(CP_ACP, 0, in, -1, out, (int)outlen);
  45. else
  46. MultiByteToWideCharSZ(CP_UTF8, 0, in, -1, out, (int)outlen);
  47. }
  48. }
  49. void getfileinfo(const wchar_t *filename, wchar_t *title, int *length_in_ms)
  50. {
  51. const wchar_t *fn;
  52. if (length_in_ms) *length_in_ms = -1000;
  53. if (filename && filename[0])
  54. fn = filename;
  55. else
  56. fn = lastfn;
  57. if (!_wcsnicmp(fn, L"file://", 7)) fn += 7;
  58. if (PathIsURL(fn))
  59. {
  60. if (title)
  61. {
  62. if (fn != filename || !_wcsicmp(fn, lastfn))
  63. {
  64. EnterCriticalSection(&g_lfnscs);
  65. if (lastfn_status[0])
  66. {
  67. char buf[4096] = {0};
  68. StringCchPrintfA(buf, 4096, "[%s] %s", lastfn_status, lastfn_data_ready ? g_stream_title : (char *)AutoChar(fn));
  69. ConvertTryUTF8(buf, title, 256);
  70. }
  71. else
  72. {
  73. if (!lastfn_data_ready)
  74. lstrcpynW(title, fn, 256);
  75. else
  76. {
  77. ConvertTryUTF8(g_stream_title, title, 256);
  78. }
  79. }
  80. LeaveCriticalSection(&g_lfnscs);
  81. if (length_in_ms) *length_in_ms = getlength();
  82. }
  83. else
  84. {
  85. lstrcpynW(title, fn, 256);
  86. }
  87. }
  88. return ;
  89. }
  90. else
  91. {
  92. Metadata info;
  93. if (info.Open(fn) == METADATA_SUCCESS)
  94. {
  95. if (title)
  96. {
  97. wchar_t mp3artist[256] = L"", mp3title[256] = L"";
  98. info.GetExtendedData("artist", mp3artist, 256);
  99. info.GetExtendedData("title", mp3title, 256);
  100. if (mp3artist[0] && mp3title[0])
  101. StringCchPrintfW(title, 256, L"%s - %s", mp3artist, mp3title);
  102. else if (mp3title[0])
  103. lstrcpynW(title, mp3title, 256);
  104. else
  105. {
  106. lstrcpynW(title, fn, MAX_PATH);
  107. PathStripPathW(title);
  108. PathRemoveExtensionW(title);
  109. }
  110. }
  111. if (fn == filename)
  112. {
  113. wchar_t ln[128]=L"";
  114. info.GetExtendedData("length", ln, 128);
  115. *length_in_ms = _wtoi(ln);
  116. }
  117. else
  118. *length_in_ms = getlength();
  119. }
  120. else if (fn != filename)
  121. *length_in_ms = getlength();
  122. }
  123. }
  124. int id3Dlg(const wchar_t *fn, HWND hwnd)
  125. {
  126. return 1;
  127. }
  128. extern const wchar_t *id3v1_genres[];
  129. extern size_t numGenres;
  130. static int our_change=0;
  131. #define GetMeta(hwnd) (Metadata *)GetPropW(GetParent(hwnd),L"mp3info")
  132. #define SetMeta(hwnd,meta) SetPropW(GetParent(hwnd),L"mp3info",(HANDLE)meta)
  133. static INT_PTR CALLBACK id3v1_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  134. {
  135. static int my_change_v1=0;
  136. static const int ctrls[] =
  137. {
  138. IDC_ID3V11_TRACK,
  139. IDC_ID3_TITLE,
  140. IDC_ID3_ARTIST,
  141. IDC_ID3_ALBUM,
  142. IDC_ID3_YEAR,
  143. IDC_ID3_COMMENT,
  144. IDC_ID3_GENRE,
  145. };
  146. static const int strs_lim[] =
  147. {
  148. 3,
  149. 30,
  150. 30,
  151. 30,
  152. 4,
  153. 28,
  154. 1,
  155. };
  156. static const char * strs[] =
  157. {
  158. "track",
  159. "title",
  160. "artist",
  161. "album",
  162. "year",
  163. "comment",
  164. "genre",
  165. };
  166. switch (uMsg)
  167. {
  168. case WM_INITDIALOG:
  169. {
  170. Metadata *meta = GetMeta(hwndDlg);
  171. if (meta)
  172. meta->AddRef();
  173. else
  174. {
  175. meta = new Metadata();
  176. meta->Open((wchar_t*)lParam);
  177. SetMeta(hwndDlg,meta);
  178. }
  179. wchar_t genre_buf[32] = {0};
  180. meta->id3v1.GetString("genre",genre_buf,32);
  181. our_change=1;
  182. SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_RESETCONTENT, 0, 0);
  183. SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, -1, 0);
  184. for (size_t x = 0; x != numGenres; x ++)
  185. {
  186. int y = (int)SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_ADDSTRING, 0, (LPARAM)id3v1_genres[x]);
  187. SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETITEMDATA, y, x);
  188. if (_wcsicmp(genre_buf,id3v1_genres[x])==0)
  189. SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, y, 0);
  190. }
  191. for (int i=0; i<sizeof(strs)/sizeof(char*); i++)
  192. {
  193. // make sure the edit boxes are limited to id3v1 spec sizes (trickier on number type fields)
  194. wchar_t buf[32] = {0};
  195. SendDlgItemMessage(hwndDlg,ctrls[i],EM_SETLIMITTEXT,strs_lim[i],0);
  196. meta->id3v1.GetString(strs[i],buf,32);
  197. SetDlgItemTextW(hwndDlg,ctrls[i],buf);
  198. }
  199. if (meta->id3v1.HasData())
  200. {
  201. CheckDlgButton(hwndDlg,IDC_ID3V1,TRUE);
  202. if (!config_write_id3v1)
  203. { // if we have id3v1 writing turned off, disable controls
  204. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  205. EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),FALSE);
  206. }
  207. }
  208. else
  209. { // no id3v1 tag present
  210. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  211. EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),FALSE);
  212. if (!config_create_id3v1)
  213. { // don't allow one to be created if the settings disallow
  214. EnableWindow(GetDlgItem(hwndDlg,IDC_ID3V1),FALSE);
  215. }
  216. }
  217. our_change=0;
  218. }
  219. break;
  220. case WM_USER:
  221. if (wParam && lParam && !our_change && !my_change_v1)
  222. {
  223. Metadata *meta = GetMeta(hwndDlg);
  224. if (!meta) break;
  225. if (!config_write_id3v1)
  226. break;
  227. if (!config_create_id3v1 && !meta->id3v1.HasData())
  228. break;
  229. wchar_t *key = (wchar_t*)wParam;
  230. wchar_t *value = (wchar_t*)lParam;
  231. AutoChar keyA(key);
  232. for (int i=0; i<sizeof(strs)/sizeof(char*); i++)
  233. {
  234. if (_stricmp(keyA,strs[i])==0)
  235. {
  236. // benski> i don't think this is what we want? meta->SetExtendedData(strs[i],value);
  237. meta->id3v1.SetString(strs[i], value);
  238. wchar_t buf[2048]=L"";
  239. meta->id3v1.GetString(strs[i],buf,2048);
  240. if (!IsDlgButtonChecked(hwndDlg,IDC_ID3V1))
  241. {
  242. // re-enable stuff
  243. CheckDlgButton(hwndDlg,IDC_ID3V1,TRUE);
  244. for (int j=0; j<sizeof(ctrls)/sizeof(int); j++)
  245. {
  246. EnableWindow(GetDlgItem(hwndDlg,ctrls[j]),TRUE);
  247. my_change_v1++;
  248. SetDlgItemTextW(hwndDlg,ctrls[j],L"");
  249. my_change_v1--;
  250. }
  251. }
  252. my_change_v1++;
  253. if (ctrls[i] == IDC_ID3_GENRE)
  254. {
  255. int n = (int)SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_FINDSTRINGEXACT, -1, (LPARAM)buf);
  256. SendDlgItemMessage(hwndDlg, IDC_ID3_GENRE, CB_SETCURSEL, n, 0);
  257. }
  258. else
  259. SetDlgItemTextW(hwndDlg,ctrls[i],buf);
  260. my_change_v1--;
  261. break;
  262. }
  263. }
  264. }
  265. break;
  266. case WM_COMMAND:
  267. switch (LOWORD(wParam))
  268. {
  269. case IDOK:
  270. {
  271. // this should be done by one pane ONLY. Doesn't matter which one.
  272. Metadata *meta = GetMeta(hwndDlg);
  273. if (!meta) break;
  274. Stopper stopper;
  275. if (!_wcsicmp(lastfn, meta->filename))
  276. stopper.Stop();
  277. int ret = meta->Save();
  278. stopper.Play();
  279. wchar_t boxtitle[256] = {0};
  280. switch(ret)
  281. {
  282. case SAVE_SUCCESS:
  283. {
  284. // cheap way to trigger a metadata reset in a thread-safe manner
  285. wchar_t d[10] = {0};
  286. extendedFileInfoStructW reset_info = {0};
  287. reset_info.filename=L".mp3";
  288. reset_info.metadata=L"artist";
  289. reset_info.ret = d;
  290. reset_info.retlen=10;
  291. SendMessage(mod.hMainWindow, WM_WA_IPC, (WPARAM)&reset_info, IPC_GET_EXTENDED_FILE_INFOW);
  292. }
  293. break;
  294. case SAVE_ERROR_READONLY:
  295. MessageBox(hwndDlg,
  296. WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_READONLY),
  297. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256),
  298. MB_OK);
  299. break;
  300. case SAVE_ERROR_OPENING_FILE:
  301. MessageBox(hwndDlg,
  302. WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_OPENING_FILE),
  303. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256),
  304. MB_OK);
  305. break;
  306. case SAVE_APEV2_WRITE_ERROR:
  307. MessageBox(hwndDlg,
  308. WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_APEV2),
  309. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256),
  310. MB_OK);
  311. break;
  312. case SAVE_LYRICS3_WRITE_ERROR:
  313. MessageBox(hwndDlg,
  314. WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_LYRICS3),
  315. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256),
  316. MB_OK);
  317. break;
  318. case SAVE_ID3V1_WRITE_ERROR:
  319. MessageBox(hwndDlg,
  320. WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_ID3V1),
  321. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256),
  322. MB_OK);
  323. break;
  324. case SAVE_ID3V2_WRITE_ERROR:
  325. MessageBox(hwndDlg,
  326. WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_ID3V2),
  327. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256),
  328. MB_OK);
  329. break;
  330. default:
  331. MessageBox(hwndDlg,
  332. WASABI_API_LNGSTRINGW(IDS_METADATA_ERROR_UNSPECIFIED),
  333. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA, boxtitle, 256),
  334. MB_OK);
  335. break;
  336. }
  337. }
  338. break;
  339. case IDC_ID3V1_TO_V2:
  340. {
  341. my_change_v1=1;
  342. Metadata *meta = GetMeta(hwndDlg);
  343. if (!meta) break;
  344. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  345. {
  346. wchar_t buf[2048]=L"";
  347. GetDlgItemTextW(hwndDlg,ctrls[i],buf,2048);
  348. meta->id3v2.SetString(strs[i],buf);
  349. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);
  350. }
  351. my_change_v1=0;
  352. }
  353. break;
  354. case IDC_ID3V1:
  355. {
  356. our_change=1;
  357. BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_ID3V1);
  358. Metadata *meta = GetMeta(hwndDlg);
  359. if (!meta) break;
  360. if (!checked)
  361. meta->id3v1.Clear();
  362. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  363. {
  364. EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),checked);
  365. wchar_t buf[2048]=L"";
  366. if (checked)
  367. {
  368. GetDlgItemText(hwndDlg,ctrls[i],buf,2048);
  369. if (buf[0])
  370. meta->id3v1.SetString(strs[i],buf);
  371. }
  372. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);
  373. }
  374. our_change=0;
  375. }
  376. break;
  377. default:
  378. if (!our_change && !my_change_v1 && (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CBN_SELCHANGE))
  379. {
  380. our_change=1;
  381. for (int i=0; i<sizeof(strs)/sizeof(char*); i++)
  382. {
  383. if (LOWORD(wParam) == ctrls[i])
  384. {
  385. wchar_t buf[2048]=L"";
  386. if (HIWORD(wParam) == EN_CHANGE)
  387. GetDlgItemTextW(hwndDlg,ctrls[i],buf,2048);
  388. else
  389. {
  390. LRESULT n = SendDlgItemMessage(hwndDlg, ctrls[i], CB_GETCURSEL, 0, 0);
  391. n = SendDlgItemMessage(hwndDlg, ctrls[i], CB_GETITEMDATA, n, 0);
  392. if (n>=0 && n<(LRESULT)numGenres)
  393. lstrcpyn(buf,id3v1_genres[n],2048);
  394. }
  395. Metadata *meta = GetMeta(hwndDlg);
  396. if (!meta) break;
  397. meta->id3v1.SetString(strs[i],buf);
  398. if (!meta->id3v2.HasData())
  399. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);
  400. }
  401. }
  402. our_change=0;
  403. }
  404. }
  405. break;
  406. case WM_DESTROY:
  407. {
  408. Metadata *meta = GetMeta(hwndDlg);
  409. if (meta) meta->Release();
  410. }
  411. break;
  412. }
  413. return 0;
  414. }
  415. static INT_PTR CALLBACK id3v2_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  416. {
  417. static int my_change_v2=0;
  418. static const int ctrls[] =
  419. {
  420. IDC_ID3V2_TRACK,
  421. IDC_ID3V2_TITLE,
  422. IDC_ID3V2_ARTIST,
  423. IDC_ID3V2_ALBUM,
  424. IDC_ID3V2_YEAR,
  425. IDC_ID3V2_COMMENT,
  426. IDC_ID3V2_GENRE,
  427. IDC_ID3V2_COMPOSER,
  428. IDC_ID3V2_PUBLISHER,
  429. IDC_ID3V2_MARTIST,
  430. IDC_ID3V2_RECORD,
  431. IDC_ID3V2_URL,
  432. IDC_ID3V2_ENCODER,
  433. IDC_ID3V2_ALBUM_ARTIST,
  434. IDC_ID3V2_DISC,
  435. IDC_TRACK_GAIN,
  436. IDC_ALBUM_GAIN,
  437. IDC_ID3V2_BPM,
  438. };
  439. static const char * strs[] =
  440. {
  441. "track",
  442. "title",
  443. "artist",
  444. "album",
  445. "year",
  446. "comment",
  447. "genre",
  448. "composer",
  449. "publisher",
  450. "originalartist",
  451. "copyright",
  452. "url",
  453. "tool",
  454. "albumartist",
  455. "disc",
  456. "replaygain_track_gain", // 15
  457. "replaygain_album_gain", // 16
  458. "bpm",
  459. };
  460. switch (uMsg)
  461. {
  462. case WM_INITDIALOG:
  463. {
  464. our_change=1;
  465. SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_RESETCONTENT, 0, 0);
  466. for (size_t x = 0; x != numGenres; x ++)
  467. {
  468. int y = (int)SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_ADDSTRING, 0, (LPARAM)id3v1_genres[x]);
  469. SendDlgItemMessage(hwndDlg, IDC_ID3V2_GENRE, CB_SETITEMDATA, y, x);
  470. }
  471. Metadata *meta = GetMeta(hwndDlg);
  472. if (meta)
  473. meta->AddRef();
  474. else
  475. {
  476. meta = new Metadata();
  477. meta->Open((wchar_t*)lParam);
  478. SetMeta(hwndDlg,meta);
  479. }
  480. for (int i=0; i<sizeof(strs)/sizeof(char*); i++)
  481. {
  482. wchar_t buf[32768] = {0};
  483. meta->id3v2.GetString(strs[i],buf,32768);
  484. if((i == 15 || i == 16) && buf[0])
  485. {
  486. SetDlgItemTextW(hwndDlg,ctrls[i],L"buf");
  487. // this isn't nice but it localises the RG values
  488. // for display as they're saved in the "C" locale
  489. double value = _wtof_l(buf,WASABI_API_LNG->Get_C_NumericLocale());
  490. StringCchPrintfW(buf,64,L"%-+.2f dB", value);
  491. }
  492. SetDlgItemTextW(hwndDlg,ctrls[i],buf);
  493. }
  494. if (meta->id3v2.HasData())
  495. CheckDlgButton(hwndDlg,IDC_ID3V2,TRUE);
  496. else
  497. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  498. EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),FALSE);
  499. our_change=0;
  500. }
  501. break;
  502. case WM_USER:
  503. if (wParam && lParam && !our_change && !my_change_v2)
  504. {
  505. Metadata *meta = GetMeta(hwndDlg);
  506. if (!meta) break;
  507. wchar_t *key = (wchar_t*)wParam;
  508. wchar_t *value = (wchar_t*)lParam;
  509. AutoChar keyA(key);
  510. for (int i=0; i<sizeof(strs)/sizeof(char*); i++)
  511. {
  512. if (_stricmp(keyA,strs[i])==0)
  513. {
  514. // benski> cut? i don't think this is what we want: meta->SetExtendedData(strs[i],value);
  515. meta->id3v2.SetString(strs[i], value);
  516. wchar_t buf[32768]=L"";
  517. meta->id3v2.GetString(strs[i],buf,32768);
  518. if (!IsDlgButtonChecked(hwndDlg,IDC_ID3V2))
  519. {
  520. // re-enable items
  521. CheckDlgButton(hwndDlg,IDC_ID3V2,TRUE);
  522. for (int j=0; j<sizeof(ctrls)/sizeof(int); j++)
  523. {
  524. EnableWindow(GetDlgItem(hwndDlg,ctrls[j]),TRUE);
  525. my_change_v2++;
  526. SetDlgItemTextW(hwndDlg,ctrls[j],L"");
  527. my_change_v2--;
  528. }
  529. }
  530. my_change_v2++;
  531. SetDlgItemTextW(hwndDlg,ctrls[i],buf);
  532. my_change_v2--;
  533. break;
  534. }
  535. }
  536. }
  537. break;
  538. case WM_COMMAND:
  539. switch (LOWORD(wParam))
  540. {
  541. case IDC_ID3V2_TO_V1:
  542. {
  543. Metadata *meta = GetMeta(hwndDlg);
  544. if (!meta) break;
  545. my_change_v2=1;
  546. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  547. {
  548. wchar_t buf[32768]=L"";
  549. GetDlgItemTextW(hwndDlg,ctrls[i],buf,32768);
  550. meta->id3v1.SetString(strs[i],buf);
  551. meta->GetExtendedData(strs[i], buf, 32768);
  552. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);
  553. }
  554. my_change_v2=0;
  555. }
  556. break;
  557. case IDC_ID3V2:
  558. {
  559. our_change=1;
  560. BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_ID3V2);
  561. Metadata *meta = GetMeta(hwndDlg);
  562. if (!meta) break;
  563. if (!checked)
  564. meta->id3v2.Clear();
  565. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  566. {
  567. EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),checked);
  568. wchar_t buf[32768]=L"";
  569. if (checked)
  570. {
  571. GetDlgItemText(hwndDlg,ctrls[i],buf,32768);
  572. if (buf[0])
  573. meta->id3v2.SetString(strs[i],buf);
  574. }
  575. meta->GetExtendedData(strs[i],buf,32768);
  576. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);
  577. }
  578. our_change=0;
  579. }
  580. break;
  581. case IDOK:
  582. {
  583. extern Metadata *m_ext_get_mp3info;
  584. if (m_ext_get_mp3info)
  585. m_ext_get_mp3info->Release();
  586. m_ext_get_mp3info=0;
  587. }
  588. break;
  589. default:
  590. if (!our_change && !my_change_v2 && (HIWORD(wParam) == EN_CHANGE || HIWORD(wParam) == CBN_SELCHANGE || HIWORD(wParam) == CBN_EDITCHANGE || HIWORD(wParam) == CBN_EDITUPDATE))
  591. {
  592. our_change=1;
  593. for (int i=0; i<sizeof(strs)/sizeof(char*); i++)
  594. {
  595. if (LOWORD(wParam) == ctrls[i])
  596. {
  597. wchar_t buf[32768] = {0};
  598. if (HIWORD(wParam) == EN_CHANGE)
  599. GetDlgItemTextW(hwndDlg,ctrls[i],buf,32768);
  600. else
  601. {
  602. LRESULT n = SendMessage(GetDlgItem(hwndDlg, ctrls[i]), CB_GETCURSEL, 0, 0);
  603. n = SendMessage(GetDlgItem(hwndDlg, ctrls[i]), CB_GETITEMDATA, n, 0);
  604. if (n>=0 && n<(LRESULT)numGenres)
  605. lstrcpyn(buf,id3v1_genres[n],32768);
  606. else{
  607. GetDlgItemTextW(hwndDlg,ctrls[i],buf,32768);
  608. }
  609. }
  610. Metadata *meta = GetMeta(hwndDlg);
  611. if (!meta) break;
  612. meta->id3v2.SetString(strs[i],buf);
  613. meta->GetExtendedData(strs[i],buf,32768);
  614. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);
  615. }
  616. }
  617. our_change=0;
  618. }
  619. }
  620. break;
  621. case WM_DESTROY:
  622. {
  623. Metadata *meta = GetMeta(hwndDlg);
  624. if (meta)
  625. meta->Release();
  626. }
  627. break;
  628. }
  629. return 0;
  630. }
  631. static INT_PTR CALLBACK lyrics3_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  632. {
  633. static const int ctrls[] =
  634. {
  635. IDC_LYRICS3_TITLE,
  636. IDC_LYRICS3_ARTIST,
  637. IDC_LYRICS3_ALBUM,
  638. };
  639. static const char * strs[] =
  640. {
  641. "title",
  642. "artist",
  643. "album",
  644. };
  645. switch (uMsg)
  646. {
  647. case WM_INITDIALOG:
  648. {
  649. Metadata *meta = GetMeta(hwndDlg);
  650. if (meta)
  651. meta->AddRef();
  652. else
  653. {
  654. meta = new Metadata();
  655. meta->Open((wchar_t*)lParam);
  656. SetMeta(hwndDlg,meta);
  657. }
  658. for (int i=0; i<sizeof(strs)/sizeof(char*); i++)
  659. {
  660. wchar_t buf[2048] = {0};
  661. SendDlgItemMessage(hwndDlg,ctrls[i],EM_SETLIMITTEXT,250,0);
  662. meta->lyrics3.GetString(strs[i],buf,250);
  663. SetDlgItemTextW(hwndDlg,ctrls[i],buf);
  664. }
  665. if (meta->lyrics3.HasData())
  666. CheckDlgButton(hwndDlg,IDC_LYRICS3,TRUE);
  667. }
  668. break;
  669. case WM_COMMAND:
  670. switch (LOWORD(wParam))
  671. {
  672. case IDC_LYRICS3:
  673. {
  674. BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_LYRICS3);
  675. Metadata *meta = GetMeta(hwndDlg);
  676. if (!meta) break;
  677. if (!checked)
  678. meta->lyrics3.Clear();
  679. else // clear the dirty state if re-enabled so we don't lose the lyrics3 tag
  680. meta->lyrics3.ResetDirty();
  681. for (int i=0; i<sizeof(ctrls)/sizeof(int); i++)
  682. {
  683. EnableWindow(GetDlgItem(hwndDlg,ctrls[i]),checked);
  684. wchar_t buf[2048]=L"";
  685. if (checked)
  686. {
  687. GetDlgItemText(hwndDlg,ctrls[i],buf,2048);
  688. if (buf[0])
  689. meta->lyrics3.SetString(strs[i],buf);
  690. }
  691. meta->GetExtendedData(strs[i],buf,2048);
  692. // if we don't flag this then we can lose info in the id3v1 and v2 tags which is definitely bad
  693. our_change++;
  694. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)(wchar_t*)AutoWide(strs[i]),(LPARAM)buf);
  695. our_change--;
  696. }
  697. }
  698. break;
  699. }
  700. break;
  701. case WM_DESTROY:
  702. {
  703. Metadata *meta = GetMeta(hwndDlg);
  704. if (meta) meta->Release();
  705. }
  706. break;
  707. }
  708. return 0;
  709. }
  710. /* ================
  711. APEv2 Editor Tab
  712. ================ */
  713. static INT_PTR CALLBACK apev2_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  714. {
  715. static W_ListView listview;
  716. static int my_change_ape=0;
  717. switch (uMsg)
  718. {
  719. case WM_NOTIFYFORMAT:
  720. return NFR_UNICODE;
  721. case WM_INITDIALOG:
  722. {
  723. our_change++;
  724. Metadata *meta = GetMeta(hwndDlg);
  725. if (meta)
  726. {
  727. meta->AddRef();
  728. }
  729. else
  730. {
  731. meta = new Metadata();
  732. meta->Open((wchar_t*)lParam);
  733. SetMeta(hwndDlg,meta);
  734. }
  735. if (meta->apev2.HasData())
  736. CheckDlgButton(hwndDlg,IDC_APEV2,TRUE);
  737. listview.setwnd(GetDlgItem(hwndDlg, IDC_APE_LIST));
  738. listview.SetDoubleBuffered(true);
  739. listview.AddCol(WASABI_API_LNGSTRINGW(IDS_NAME), 82);
  740. listview.AddCol(WASABI_API_LNGSTRINGW(IDS_VALUE), 160);
  741. listview.SetVirtualCount((int)meta->apev2.GetNumItems());
  742. listview.AutoSizeColumn(0);
  743. listview.AutoSizeColumn(1);
  744. SetDlgItemTextW(hwndDlg,IDC_APE_KEY,L"");
  745. SetDlgItemTextW(hwndDlg,IDC_APE_VALUE,L"");
  746. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),FALSE);
  747. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),FALSE);
  748. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),FALSE);
  749. our_change--;
  750. return 1;
  751. }
  752. break;
  753. case WM_COMMAND:
  754. switch(LOWORD(wParam))
  755. {
  756. case IDC_APE_KEY:
  757. case IDC_APE_VALUE:
  758. if(HIWORD(wParam) == EN_CHANGE)
  759. {
  760. int selected = listview.GetNextSelected();
  761. if (selected != LB_ERR)
  762. {
  763. listview.RefreshItem(selected);
  764. }
  765. }
  766. else if(HIWORD(wParam) == EN_KILLFOCUS)
  767. {
  768. Metadata *meta = GetMeta(hwndDlg);
  769. if (!meta) break;
  770. my_change_ape++;
  771. char key[1024] = {0};
  772. wchar_t value[32768] = {0};
  773. GetDlgItemTextA(hwndDlg, IDC_APE_KEY, key, 1024);
  774. GetDlgItemText(hwndDlg, IDC_APE_VALUE, value, 32768);
  775. int selected = listview.GetNextSelected();
  776. if (selected != LB_ERR)
  777. {
  778. meta->apev2.SetKeyValueByIndex(selected, key, value);
  779. }
  780. const wchar_t *winamp_key = APE::MapApeKeyToWinampKeyW(key);
  781. if (winamp_key)
  782. {
  783. our_change++;
  784. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)winamp_key,(WPARAM)value);
  785. our_change--;
  786. }
  787. my_change_ape--;
  788. }
  789. break;
  790. case IDC_APEV2:
  791. {
  792. BOOL checked = IsDlgButtonChecked(hwndDlg,IDC_APEV2);
  793. Metadata *meta = GetMeta(hwndDlg);
  794. if (!meta) break;
  795. if (!checked)
  796. meta->apev2.MarkClear();
  797. else // clear the dirty state if re-enabled so we don't lose the apev2 tag
  798. meta->apev2.ResetDirty();
  799. }
  800. break;
  801. case IDC_DELETE_ALL:
  802. {
  803. Metadata *meta = GetMeta(hwndDlg);
  804. if (!meta) break;
  805. my_change_ape++;
  806. listview.UnselectAll();
  807. meta->apev2.Clear();
  808. listview.SetVirtualCount((int)meta->apev2.GetNumItems());
  809. my_change_ape--;
  810. }
  811. break;
  812. case IDC_APE_ADD:
  813. {
  814. Metadata *meta = GetMeta(hwndDlg);
  815. if (!meta) break;
  816. int index = listview.GetCount();
  817. if (meta->apev2.AddItem() == APEv2::APEV2_SUCCESS)
  818. {
  819. listview.SetVirtualCount((int)meta->apev2.GetNumItems());
  820. listview.SetSelected(index);
  821. listview.ScrollTo(index);
  822. SetFocus(GetDlgItem(hwndDlg, IDC_APE_KEY));
  823. }
  824. }
  825. break;
  826. case IDC_APE_DELETE:
  827. {
  828. Metadata *meta = GetMeta(hwndDlg);
  829. if (!meta) break;
  830. int selected = listview.GetNextSelected();
  831. if (selected != LB_ERR)
  832. {
  833. listview.UnselectAll();
  834. meta->apev2.RemoveItem(selected);
  835. listview.SetVirtualCount((int)meta->apev2.GetNumItems());
  836. }
  837. }
  838. break;
  839. }
  840. break;
  841. case WM_NOTIFY:
  842. {
  843. LPNMHDR l=(LPNMHDR)lParam;
  844. if (l->idFrom==IDC_APE_LIST) switch (l->code)
  845. {
  846. case LVN_GETDISPINFO:
  847. {
  848. Metadata *meta = GetMeta(hwndDlg);
  849. if (meta)
  850. {
  851. NMLVDISPINFO *lpdi = (NMLVDISPINFO*) l;
  852. if (lpdi->item.mask & LVIF_TEXT)
  853. {
  854. int selected = listview.GetNextSelected();
  855. switch (lpdi->item.iSubItem)
  856. {
  857. case 0:
  858. {
  859. if (lpdi->item.iItem == selected)
  860. {
  861. GetDlgItemText(hwndDlg, IDC_APE_KEY, lpdi->item.pszText, lpdi->item.cchTextMax);
  862. }
  863. else
  864. {
  865. const char *key=0;
  866. meta->apev2.EnumValue(lpdi->item.iItem, &key, 0, 0);
  867. MultiByteToWideCharSZ(CP_ACP, 0, key?key:"", -1, lpdi->item.pszText, lpdi->item.cchTextMax);
  868. }
  869. }
  870. return 0;
  871. case 1:
  872. {
  873. if (lpdi->item.iItem == selected)
  874. {
  875. GetDlgItemText(hwndDlg, IDC_APE_VALUE, lpdi->item.pszText, lpdi->item.cchTextMax);
  876. }
  877. else
  878. {
  879. const char *key=0;
  880. meta->apev2.EnumValue(lpdi->item.iItem, &key, lpdi->item.pszText, lpdi->item.cchTextMax);
  881. }
  882. }
  883. return 0;
  884. }
  885. }
  886. }
  887. }
  888. break;
  889. case LVN_KEYDOWN:
  890. break;
  891. case LVN_ITEMCHANGED:
  892. {
  893. my_change_ape++;
  894. LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;
  895. if (lv->uNewState & LVIS_SELECTED)
  896. {
  897. Metadata *meta = GetMeta(hwndDlg);
  898. if (meta)
  899. {
  900. const char *key=0;
  901. wchar_t value[32768] = {0};
  902. meta->apev2.EnumValue(lv->iItem, &key, value, 32768);
  903. SetDlgItemTextA(hwndDlg,IDC_APE_KEY,key);
  904. SetDlgItemText(hwndDlg,IDC_APE_VALUE,value);
  905. BOOL editable = meta->apev2.IsItemReadOnly(lv->iItem)?FALSE:TRUE;
  906. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),editable);
  907. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),editable);
  908. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),TRUE);
  909. listview.RefreshItem(lv->iItem);
  910. }
  911. }
  912. if (lv->uOldState & LVIS_SELECTED)
  913. {
  914. SetDlgItemTextW(hwndDlg,IDC_APE_KEY,L"");
  915. SetDlgItemTextW(hwndDlg,IDC_APE_VALUE,L"");
  916. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_KEY),FALSE);
  917. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_VALUE),FALSE);
  918. EnableWindow(GetDlgItem(hwndDlg,IDC_APE_DELETE),FALSE);
  919. }
  920. my_change_ape--;
  921. }
  922. }
  923. }
  924. break;
  925. case WM_USER:
  926. if (wParam && lParam && !our_change && !my_change_ape)
  927. {
  928. Metadata *meta = GetMeta(hwndDlg);
  929. if (meta)
  930. {
  931. const wchar_t *keyW = (const wchar_t *)wParam;
  932. const wchar_t *value = (const wchar_t *)lParam;
  933. AutoChar key(keyW);
  934. my_change_ape++;
  935. meta->apev2.SetString(key, value);
  936. listview.UnselectAll();
  937. listview.SetVirtualCount((int)meta->apev2.GetNumItems());
  938. listview.RefreshAll();
  939. my_change_ape--;
  940. }
  941. }
  942. break;
  943. case WM_DESTROY:
  944. {
  945. Metadata *meta = GetMeta(hwndDlg);
  946. if (meta) meta->Release();
  947. }
  948. break;
  949. }
  950. return 0;
  951. }
  952. extern "C"
  953. {
  954. // return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox)
  955. // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
  956. __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
  957. {
  958. if (!_wcsnicmp(fn, L"file://", 7)) fn += 7;
  959. if (PathIsURLW(fn)) return 2;
  960. return 1;
  961. }
  962. // should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab.
  963. // Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced").
  964. // filename will be valid for the life of your window. n is the tab number. This function will first be
  965. // called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like).
  966. // The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel.
  967. // when the user edits a field which is duplicated in another pane, do a SendMessage(GetParent(hwnd),WM_USER,(WPARAM)L"fieldname",(LPARAM)L"newvalue");
  968. // this will be broadcast to all panes (including yours) as a WM_USER.
  969. __declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen)
  970. {
  971. if (n == 0)
  972. {
  973. SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1);
  974. lstrcpyn(name,L"ID3v1", (int)namelen);
  975. return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_ID3V1, parent, id3v1_dlgproc, (LPARAM)filename);
  976. }
  977. if (n == 1)
  978. {
  979. lstrcpyn(name,L"ID3v2", (int)namelen);
  980. return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_ID3V2, parent, id3v2_dlgproc, (LPARAM)filename);
  981. }
  982. if (n == 2)
  983. {
  984. Metadata *meta = (Metadata *)GetPropW(parent, L"mp3info");
  985. if (meta->lyrics3.HasData())
  986. {
  987. lstrcpyn(name,L"Lyrics3", (int)namelen);
  988. return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_LYRICS3, parent, lyrics3_dlgproc, (LPARAM)filename);
  989. }
  990. else if (meta->apev2.HasData())
  991. {
  992. lstrcpyn(name,L"APEv2", (int)namelen);
  993. return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_APEV2, parent, apev2_dlgproc, (LPARAM)filename);
  994. }
  995. }
  996. if (n == 3)
  997. {
  998. Metadata *meta = (Metadata *)GetPropW(parent, L"mp3info");
  999. if (meta->lyrics3.HasData() && meta->apev2.HasData())
  1000. {
  1001. lstrcpyn(name,L"APEv2", (int)namelen);
  1002. return WASABI_API_CREATEDIALOGPARAMW(IDD_INFO_APEV2, parent, apev2_dlgproc, (LPARAM)filename);
  1003. }
  1004. }
  1005. return NULL;
  1006. }
  1007. }