FileInfo.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. ** Copyright (C) 2007-2011 Nullsoft, Inc.
  3. **
  4. ** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
  5. ** liable for any damages arising from the use of this software.
  6. **
  7. ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
  8. ** alter it and redistribute it freely, subject to the following restrictions:
  9. **
  10. ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
  11. ** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
  12. **
  13. ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  14. **
  15. ** 3. This notice may not be removed or altered from any source distribution.
  16. **
  17. ** Author: Ben Allison [email protected]
  18. ** Created: March 1, 2007
  19. **
  20. */
  21. #include <FLAC/all.h>
  22. #include "main.h"
  23. #include "../nu/ns_wc.h"
  24. #include <windows.h>
  25. #include "resource.h"
  26. #include "Metadata.h"
  27. #include "../nu/AutoWide.h"
  28. #include "../nu/AutoChar.h"
  29. #include "Stopper.h"
  30. #include <strsafe.h>
  31. #include <commctrl.h>
  32. #include "../Agave/Language/api_language.h"
  33. bool FlacTagToWinampTag(wchar_t * tag, int len)
  34. {
  35. #define TAG_ALIAS(b,a) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
  36. TAG_ALIAS("title", "TITLE");
  37. TAG_ALIAS("artist", "ARTIST");
  38. TAG_ALIAS("album", "ALBUM");
  39. TAG_ALIAS("genre", "GENRE");
  40. TAG_ALIAS("comment", "COMMENT");
  41. TAG_ALIAS("year", "DATE");
  42. TAG_ALIAS("track", "TRACKNUMBER");
  43. TAG_ALIAS("albumartist", "ALBUM ARTIST");
  44. TAG_ALIAS("composer", "COMPOSER");
  45. TAG_ALIAS("disc", "DISCNUMBER");
  46. TAG_ALIAS("publisher", "PUBLISHER");
  47. TAG_ALIAS("conductor", "CONDUCTOR");
  48. TAG_ALIAS("bpm", "BPM");
  49. return false;
  50. #undef TAG_ALIAS
  51. }
  52. bool WinampTagToFlacTag(wchar_t * tag, int len)
  53. {
  54. #define TAG_ALIAS(a,b) if(!_wcsicmp(L ## a, tag)) { lstrcpynW(tag, L ## b, len); return true; }
  55. TAG_ALIAS("title", "TITLE");
  56. TAG_ALIAS("artist", "ARTIST");
  57. TAG_ALIAS("album", "ALBUM");
  58. TAG_ALIAS("genre", "GENRE");
  59. TAG_ALIAS("comment", "COMMENT");
  60. TAG_ALIAS("year", "DATE");
  61. TAG_ALIAS("track", "TRACKNUMBER");
  62. TAG_ALIAS("albumartist", "ALBUM ARTIST");
  63. TAG_ALIAS("composer", "COMPOSER");
  64. TAG_ALIAS("disc", "DISCNUMBER");
  65. TAG_ALIAS("publisher", "PUBLISHER");
  66. TAG_ALIAS("conductor", "CONDUCTOR");
  67. TAG_ALIAS("bpm", "BPM");
  68. return false;
  69. #undef TAG_ALIAS
  70. }
  71. static INT_PTR CALLBACK ChildProc_Advanced(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
  72. static int sel=-1;
  73. static int ismychange=0;
  74. wchar_t key[512]={0};
  75. wchar_t value[32768]={0};
  76. switch(msg)
  77. {
  78. case WM_NOTIFYFORMAT:
  79. return NFR_UNICODE;
  80. case WM_INITDIALOG:
  81. {
  82. #define ListView_InsertColumnW(hwnd, iCol, pcol) \
  83. (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol))
  84. SetWindowLongPtr(hwndDlg,GWLP_USERDATA,lParam);
  85. sel=-1;
  86. HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
  87. ListView_SetExtendedListViewStyle(hwndlist, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
  88. LVCOLUMNW lvc = {0, };
  89. lvc.mask = LVCF_TEXT|LVCF_WIDTH;
  90. lvc.pszText = WASABI_API_LNGSTRINGW(IDS_NAME);
  91. lvc.cx = 82;
  92. ListView_InsertColumnW(hwndlist, 0, &lvc);
  93. lvc.pszText = WASABI_API_LNGSTRINGW(IDS_VALUE);
  94. lvc.cx = 160;
  95. ListView_InsertColumnW(hwndlist, 1, &lvc);
  96. Info *info = (Info *)lParam;
  97. int n = info->metadata.GetNumMetadataItems();
  98. for(int i=0; i<n; i++) {
  99. char key_[512] = {0};
  100. const char* value_ = info->metadata.EnumMetadata(i,key_,512);
  101. if(value_ && key_[0]) {
  102. AutoWide k(key_, CP_UTF8);
  103. AutoWide v(value_, CP_UTF8);
  104. LVITEMW lvi={LVIF_TEXT,i,0};
  105. lvi.pszText = k;
  106. SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
  107. lvi.iSubItem=1;
  108. lvi.pszText = v;
  109. SendMessage(hwndlist,LVM_SETITEMW,0,(LPARAM)&lvi);
  110. }
  111. }
  112. ListView_SetColumnWidth(hwndlist,0,LVSCW_AUTOSIZE);
  113. ListView_SetColumnWidth(hwndlist,1,LVSCW_AUTOSIZE);
  114. SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
  115. SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
  116. EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
  117. EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
  118. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
  119. }
  120. break;
  121. case WM_DESTROY:
  122. {
  123. HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
  124. ListView_DeleteAllItems(hwndlist);
  125. while(ListView_DeleteColumn(hwndlist,0));
  126. Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
  127. delete info;
  128. info = 0;
  129. }
  130. break;
  131. case WM_USER:
  132. if(wParam && lParam && !ismychange)
  133. {
  134. wchar_t * value = (wchar_t*)lParam;
  135. wchar_t tag[100] = {0};
  136. lstrcpynW(tag,(wchar_t*)wParam,100);
  137. WinampTagToFlacTag(tag,100);
  138. Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  139. if(!*value)
  140. {
  141. info->metadata.RemoveMetadata(AutoChar(tag,CP_UTF8));
  142. if(!_wcsicmp(L"ALBUM ARTIST",tag))
  143. {
  144. // need to remove these two, also, or else it's gonna look like delete doesn't work
  145. // if the file was tagged using these alternate fields
  146. info->metadata.RemoveMetadata("ALBUMARTIST");
  147. info->metadata.RemoveMetadata("ENSEMBLE");
  148. }
  149. if(!_wcsicmp(L"PUBLISHER",tag))
  150. {
  151. // need to remove this also, or else it's gonna look like delete doesn't work
  152. // if the file was tagged using this alternate field
  153. info->metadata.RemoveMetadata("ORGANIZATION");
  154. }
  155. if(!_wcsicmp(L"DATE",tag))
  156. {
  157. // need to remove this also, or else it's gonna look like delete doesn't work
  158. // if the file was tagged using this alternate field
  159. info->metadata.RemoveMetadata("YEAR");
  160. }
  161. if(!_wcsicmp(L"TRACKNUMBER",tag))
  162. {
  163. // need to remove this also, or else it's gonna look like delete doesn't work
  164. // if the file was tagged using this alternate field
  165. info->metadata.RemoveMetadata("TRACK");
  166. }
  167. }
  168. else
  169. {
  170. info->metadata.SetMetadata(AutoChar(tag,CP_UTF8),AutoChar(value,CP_UTF8));
  171. }
  172. HWND hlist = GetDlgItem(hwndDlg,IDC_LIST);
  173. int n = ListView_GetItemCount(hlist);
  174. for(int i=0; i<n; i++)
  175. {
  176. key[0]=0;
  177. LVITEMW lvi={LVIF_TEXT,i,0};
  178. lvi.pszText=key;
  179. lvi.cchTextMax=sizeof(key)/sizeof(*key);
  180. SendMessage(hlist,LVM_GETITEMW,0,(LPARAM)&lvi);
  181. if(!_wcsicmp(key,tag))
  182. {
  183. lvi.iSubItem = 1;
  184. lvi.pszText = value;
  185. SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
  186. if(!*value)
  187. ListView_DeleteItem(hlist,i);
  188. else if(ListView_GetItemState(hlist,i,LVIS_SELECTED))
  189. SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
  190. return 0;
  191. }
  192. }
  193. // bew hew, not found
  194. LVITEMW lvi={0,0x7FFFFFF0,0};
  195. n = SendMessage(hlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
  196. lvi.mask = LVIF_TEXT;
  197. lvi.iItem = n;
  198. lvi.iSubItem = 0;
  199. lvi.pszText = tag;
  200. SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
  201. lvi.iSubItem = 1;
  202. lvi.pszText = value;
  203. SendMessage(hlist,LVM_SETITEMW,0,(LPARAM)&lvi);
  204. }
  205. break;
  206. case WM_NOTIFY:
  207. {
  208. LPNMHDR l=(LPNMHDR)lParam;
  209. if(l->idFrom==IDC_LIST && l->code == LVN_KEYDOWN) {
  210. if((((LPNMLVKEYDOWN)l)->wVKey) == VK_DELETE){
  211. int selitem = ListView_GetNextItem(l->hwndFrom,-1,LVNI_SELECTED|LVNI_FOCUSED);
  212. if(selitem != -1)
  213. SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_BUTTON_DEL,BN_CLICKED),(LPARAM)GetDlgItem(hwndDlg,IDC_BUTTON_DEL));
  214. }
  215. }
  216. else if(l->idFrom==IDC_LIST && l->code == LVN_ITEMCHANGED) {
  217. LPNMLISTVIEW lv=(LPNMLISTVIEW)lParam;
  218. if(lv->uNewState & LVIS_SELECTED) {
  219. int n = lv->iItem;
  220. LVITEMW lvi={LVIF_TEXT,lv->iItem,0};
  221. lvi.pszText=key;
  222. lvi.cchTextMax=sizeof(key)/sizeof(*key);
  223. SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
  224. lvi.pszText=value;
  225. lvi.cchTextMax=sizeof(value)/sizeof(*value);
  226. lvi.iSubItem=1;
  227. SendMessage(l->hwndFrom,LVM_GETITEMW,0,(LPARAM)&lvi);
  228. SetDlgItemTextW(hwndDlg,IDC_NAME,key);
  229. SetDlgItemTextW(hwndDlg,IDC_VALUE,value);
  230. sel = n;
  231. EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),TRUE);
  232. EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),TRUE);
  233. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),TRUE);
  234. }
  235. if(lv->uOldState & LVIS_SELECTED) {
  236. sel = -1;
  237. SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
  238. SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
  239. EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
  240. EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
  241. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
  242. }
  243. }
  244. }
  245. break;
  246. case WM_COMMAND:
  247. switch(LOWORD(wParam)) {
  248. case IDOK:
  249. {
  250. Info * info = (Info*)GetWindowLongPtr(hwndDlg,GWLP_USERDATA);
  251. Stopper stopper;
  252. if (lastfn && !_wcsicmp(lastfn, info->filename))
  253. stopper.Stop();
  254. bool success = info->metadata.Save(info->filename);
  255. stopper.Play();
  256. if (success)
  257. {
  258. ResetMetadataCache();
  259. }
  260. else
  261. {
  262. wchar_t title[128] = {0};
  263. MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_CANNOT_SAVE_METADATA),
  264. WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SAVING_METADATA,title,128),
  265. MB_OK | MB_ICONWARNING);
  266. }
  267. }
  268. break;
  269. case IDC_NAME:
  270. case IDC_VALUE:
  271. if(HIWORD(wParam) == EN_CHANGE && sel>=0) {
  272. LVITEMW lvi={LVIF_TEXT,sel,0};
  273. GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
  274. GetDlgItemTextW(hwndDlg,IDC_VALUE,value,sizeof(value)/sizeof(*value));
  275. lvi.pszText=key;
  276. lvi.cchTextMax=sizeof(key)/sizeof(*key);
  277. SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
  278. lvi.pszText=value;
  279. lvi.cchTextMax=sizeof(value)/sizeof(*value);
  280. lvi.iSubItem=1;
  281. SendMessage(GetDlgItem(hwndDlg,IDC_LIST),LVM_SETITEMW,0,(LPARAM)&lvi);
  282. FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
  283. ismychange=1;
  284. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
  285. ismychange=0;
  286. }
  287. else if(HIWORD(wParam) == EN_KILLFOCUS && sel>=0) {
  288. GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
  289. GetDlgItemTextW(hwndDlg,IDC_VALUE,value,sizeof(value)/sizeof(*value));
  290. Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  291. char oldkeyA[100]="";
  292. bool newitem=true;
  293. if(sel < info->metadata.GetNumMetadataItems()) {
  294. info->metadata.EnumMetadata(sel,oldkeyA,100);
  295. newitem=false;
  296. }
  297. AutoWide oldkey(oldkeyA);
  298. if(!newitem && _wcsicmp(oldkey,key)) { // key changed
  299. info->metadata.SetTag(sel,AutoChar(key,CP_UTF8));
  300. } else {
  301. info->metadata.SetMetadata(AutoChar(key,CP_UTF8),AutoChar(value,CP_UTF8));
  302. }
  303. FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
  304. ismychange=1;
  305. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)value);
  306. ismychange=0;
  307. }
  308. break;
  309. case IDC_BUTTON_DEL:
  310. if(sel >= 0){
  311. GetDlgItemTextW(hwndDlg,IDC_NAME,key,sizeof(key)/sizeof(*key));
  312. SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
  313. SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
  314. EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
  315. EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
  316. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
  317. Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  318. if(sel < info->metadata.GetNumMetadataItems())
  319. info->metadata.RemoveMetadata(sel);
  320. ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_LIST),sel);
  321. sel=-1;
  322. FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
  323. ismychange=1;
  324. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)L"");
  325. ismychange=0;
  326. }
  327. break;
  328. case IDC_BUTTON_DELALL:
  329. ListView_DeleteAllItems(GetDlgItem(hwndDlg,IDC_LIST));
  330. SetDlgItemTextW(hwndDlg,IDC_NAME,L"");
  331. SetDlgItemTextW(hwndDlg,IDC_VALUE,L"");
  332. EnableWindow(GetDlgItem(hwndDlg,IDC_NAME),FALSE);
  333. EnableWindow(GetDlgItem(hwndDlg,IDC_VALUE),FALSE);
  334. EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON_DEL),FALSE);
  335. sel=-1;
  336. {
  337. Info *info = (Info *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
  338. int n = info->metadata.GetNumMetadataItems();
  339. while(n>0) {
  340. --n;
  341. char tag[100] = {0};
  342. info->metadata.EnumMetadata(n,tag,100);
  343. MultiByteToWideCharSZ(CP_UTF8, 0, tag, -1, key, sizeof(key)/sizeof(*key));
  344. FlacTagToWinampTag(key,sizeof(key)/sizeof(*key));
  345. ismychange=1;
  346. SendMessage(GetParent(hwndDlg),WM_USER,(WPARAM)key,(WPARAM)L"");
  347. ismychange=0;
  348. info->metadata.RemoveMetadata(n);
  349. }
  350. }
  351. break;
  352. case IDC_BUTTON_ADD:
  353. {
  354. HWND hwndlist = GetDlgItem(hwndDlg,IDC_LIST);
  355. LVITEMW lvi={0,0x7FFFFFF0,0};
  356. int n = SendMessage(hwndlist,LVM_INSERTITEMW,0,(LPARAM)&lvi);
  357. ListView_SetItemState(hwndlist,n,LVIS_SELECTED,LVIS_SELECTED);
  358. }
  359. break;
  360. }
  361. break;
  362. }
  363. return 0;
  364. }
  365. extern "C"
  366. {
  367. // 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)
  368. // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")!
  369. __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn)
  370. {
  371. return 1;
  372. }
  373. // should return a child window of 513x271 pixels (341x164 in msvc dlg units), or return NULL for no tab.
  374. // Fill in name (a buffer of namelen characters), this is the title of the tab (defaults to "Advanced").
  375. // filename will be valid for the life of your window. n is the tab number. This function will first be
  376. // called with n == 0, then n == 1 and so on until you return NULL (so you can add as many tabs as you like).
  377. // The window you return will recieve WM_COMMAND, IDOK/IDCANCEL messages when the user clicks OK or Cancel.
  378. // 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");
  379. // this will be broadcast to all panes (including yours) as a WM_USER.
  380. __declspec(dllexport) HWND winampAddUnifiedFileInfoPane(int n, const wchar_t * filename, HWND parent, wchar_t *name, size_t namelen)
  381. {
  382. if(n == 0) { // add first pane
  383. SetPropW(parent,L"INBUILT_NOWRITEINFO", (HANDLE)1);
  384. info = new Info;
  385. info->filename = filename;
  386. info->metadata.Open(filename, true);
  387. return WASABI_API_CREATEDIALOGPARAMW(IDD_INFOCHILD_ADVANCED,parent,ChildProc_Advanced,(LPARAM)info);
  388. }
  389. return NULL;
  390. }
  391. };