SkinnedListView.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. #include "main.h"
  2. #include "SkinnedListView.h"
  3. #include "resource1.h"
  4. #include "api__ml_pmp.h"
  5. #include "./local_menu.h"
  6. #include "../replicant/nx/nxstring.h"
  7. #include <strsafe.h>
  8. extern DeviceView * currentViewedDevice;
  9. extern winampMediaLibraryPlugin plugin;
  10. extern HMENU m_context_menus;
  11. extern HINSTANCE cloud_hinst;
  12. extern int IPC_GET_CLOUD_HINST;
  13. static bool doneFirstInit=false;
  14. int (*wad_getColor)(int idx);
  15. #define SKIP_THE_AND_WHITESPACE(x) { while (!iswalnum(*x) && *x) x++; if (!_wcsnicmp(x,L"the ",4)) x+=4; while (*x == L' ') x++; }
  16. extern int STRCMP_NULLOK(const wchar_t *pa, const wchar_t *pb);
  17. SkinnedListView::SkinnedListView(ListContents * lc, int dlgitem, HWND libraryParent, HWND parent, bool enableHeaderMenu)
  18. : enableHeaderMenu(enableHeaderMenu), skinlistview_handle(0), contents(0), headerWindow(0)
  19. {
  20. if(!doneFirstInit) {
  21. *(void **)&wad_getColor=(void*)SendMessage(libraryParent,WM_ML_IPC,1,ML_IPC_SKIN_WADLG_GETFUNC);
  22. doneFirstInit=true;
  23. }
  24. this->dlgitem = dlgitem;
  25. this->contents = lc;
  26. this->libraryParent = libraryParent;
  27. }
  28. void SkinnedListView::UpdateList(bool softUpdate) {
  29. if(!softUpdate) {
  30. ListView_SetItemCount(listview.getwnd(),0);
  31. ListView_SetItemCount(listview.getwnd(),(contents ? contents->GetNumRows() : 0));
  32. }
  33. ListView_RedrawItems(listview.getwnd(),0,(contents ? contents->GetNumRows() - 1 : 0));
  34. }
  35. int SkinnedListView::GetFindItemColumn() {
  36. return contents->GetSortColumn();
  37. }
  38. HMENU SkinnedListView::GetMenu(bool isFilter, int filterNum, C_Config *c, HMENU themenu) {
  39. HMENU menu;
  40. menu = GetSubMenu(themenu, (FALSE != isFilter) ? 9 : 8);
  41. if (NULL == menu)
  42. return NULL;
  43. if(isFilter)
  44. {
  45. MENUITEMINFO m={sizeof(m),MIIM_ID,0};
  46. int i, count;
  47. unsigned int filterMarker;
  48. filterMarker = (((unsigned char)(1+filterNum)) << 24);
  49. count = GetMenuItemCount(menu);
  50. for(i = 0; i < count; i++)
  51. {
  52. if (GetMenuItemInfo(menu,i,TRUE,&m))
  53. {
  54. m.wID = filterMarker | (m.wID & 0x00FFFFFF);
  55. SetMenuItemInfo(menu,i,TRUE,&m);
  56. }
  57. }
  58. wchar_t conf[100] = {0};
  59. StringCchPrintf(conf, ARRAYSIZE(conf), L"media_scroll_%d",filterNum);
  60. bool enablescroll = c->ReadInt(conf,0)!=0;
  61. CheckMenuItem(menu,
  62. filterMarker | (ID_FILTERHEADERWND_SHOWHORIZONTALSCROLLBAR & 0x00FFFFFF),
  63. MF_BYCOMMAND | (enablescroll ? MF_CHECKED : MF_UNCHECKED));
  64. }
  65. return menu;
  66. }
  67. void SkinnedListView::ProcessMenuResult(int r, bool isFilter, int filterNum, C_Config *c, HWND parent) {
  68. int mid = (r >> 24);
  69. if(!isFilter && mid) return;
  70. if(isFilter && mid-1 != filterNum) return;
  71. r &= 0xFFFF;
  72. switch(r) {
  73. case ID_HEADERWND_CUSTOMIZECOLUMNS:
  74. {
  75. contents->CustomizeColumns(listview.getwnd(),FALSE);
  76. while(ListView_DeleteColumn(listview.getwnd(),0));
  77. for(int i=0; i < contents->GetNumColumns(); i++)
  78. listview.AddCol(contents->GetColumnTitle(i),contents->GetColumnWidth(i));
  79. }
  80. break;
  81. case ID_FILTERHEADERWND_SHOWHORIZONTALSCROLLBAR:
  82. {
  83. wchar_t conf[100] = {0};
  84. StringCchPrintf(conf, ARRAYSIZE(conf), L"media_scroll_%d",filterNum);
  85. bool enablescroll = !c->ReadInt(conf,0);
  86. c->WriteInt(conf,enablescroll?1:0);
  87. if (FALSE != MLSkinnedScrollWnd_ShowHorzBar(listview.getwnd(), enablescroll))
  88. {
  89. RECT rect;
  90. if(FALSE != GetWindowRect(listview.getwnd(), &rect))
  91. {
  92. OffsetRect(&rect, -rect.left, -rect.top);
  93. SetWindowPos(listview.getwnd(), NULL, 0, 0, rect.right - 1, rect.bottom,
  94. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW);
  95. SetWindowPos(listview.getwnd(), NULL, 0, 0, rect.right, rect.bottom,
  96. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW);
  97. RedrawWindow(listview.getwnd(), NULL, NULL,
  98. RDW_INVALIDATE | RDW_ERASE | RDW_FRAME |
  99. RDW_ERASENOW | RDW_UPDATENOW);
  100. }
  101. }
  102. }
  103. break;
  104. }
  105. }
  106. void SkinnedListView::InitializeFilterData(int filterNum, C_Config *config)
  107. {
  108. wchar_t buffer[64] = {0};
  109. BOOL enableHorzScrollbar;
  110. if (NULL == config)
  111. return;
  112. if (filterNum < 0)
  113. return;
  114. if(FAILED(StringCchPrintf(buffer, ARRAYSIZE(buffer), L"media_scroll_%d",filterNum)))
  115. return;
  116. enableHorzScrollbar = (FALSE != config->ReadInt(buffer, FALSE));
  117. if (FALSE != MLSkinnedScrollWnd_ShowHorzBar(listview.getwnd(), enableHorzScrollbar))
  118. {
  119. SetWindowPos(listview.getwnd(), NULL, 0, 0, 0, 0,
  120. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
  121. }
  122. }
  123. LRESULT SkinnedListView::pmp_listview(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  124. if (uMsg == WM_NOTIFY)
  125. {
  126. LPNMHDR l=(LPNMHDR)lParam;
  127. switch (l->code)
  128. {
  129. case TTN_SHOW:
  130. {
  131. LVHITTESTINFO lvh = {0};
  132. GetCursorPos(&lvh.pt);
  133. ScreenToClient(hwnd, &lvh.pt);
  134. ListView_SubItemHitTest(hwnd, &lvh);
  135. ListContents * contents = (ListContents *)GetPropW(hwnd, L"pmp_list_info");
  136. if (lvh.iItem != -1 && lvh.iSubItem == contents->cloudcol)
  137. {
  138. LPTOOLTIPTEXTW tt = (LPTOOLTIPTEXTW)lParam;
  139. RECT r = {0};
  140. if (lvh.iSubItem)
  141. ListView_GetSubItemRect(hwnd, lvh.iItem, lvh.iSubItem, LVIR_BOUNDS, &r);
  142. else
  143. {
  144. ListView_GetItemRect(hwnd, lvh.iItem, &r, LVIR_BOUNDS);
  145. r.right = r.left + ListView_GetColumnWidth(hwnd, contents->cloudcol);
  146. }
  147. MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)&r, 2);
  148. SetWindowPos(tt->hdr.hwndFrom, HWND_TOPMOST, r.right, r.top + 2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
  149. return 1;
  150. }
  151. }
  152. break;
  153. case TTN_NEEDTEXTW:
  154. {
  155. LVHITTESTINFO lvh = {0};
  156. GetCursorPos(&lvh.pt);
  157. ScreenToClient(hwnd, &lvh.pt);
  158. ListView_SubItemHitTest(hwnd, &lvh);
  159. static wchar_t tt_buf[256] = {L""};
  160. ListContents * contents = (ListContents *)GetPropW(hwnd, L"pmp_list_info");
  161. if (lvh.iItem != -1 && lvh.iSubItem == contents->cloudcol)
  162. {
  163. LPNMTTDISPINFO lpnmtdi = (LPNMTTDISPINFO)lParam;
  164. static int last_item = -1;
  165. if (last_item == lvh.iItem)
  166. {
  167. lpnmtdi->lpszText = tt_buf;
  168. return 0;
  169. }
  170. if (contents->cloud_cache[lvh.iItem] == 4)
  171. {
  172. WASABI_API_LNGSTRINGW_BUF(IDS_UPLOAD_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
  173. }
  174. else
  175. {
  176. if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndWinampParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST);
  177. if (cloud_hinst && cloud_hinst != (HINSTANCE)1)
  178. {
  179. winampMediaLibraryPlugin *(*gp)();
  180. gp = (winampMediaLibraryPlugin * (__cdecl *)(void))GetProcAddress(cloud_hinst, "winampGetMediaLibraryPlugin");
  181. if (gp)
  182. {
  183. winampMediaLibraryPlugin *mlplugin = gp();
  184. if (mlplugin && (mlplugin->version >= MLHDR_VER_OLD && mlplugin->version <= MLHDR_VER))
  185. {
  186. WASABI_API_LNGSTRINGW_BUF(IDS_TRACK_AVAILABLE, tt_buf, ARRAYSIZE(tt_buf));
  187. int message = 0x405;
  188. wchar_t value[1024] = {0};
  189. songid_t s = contents->GetTrack(lvh.iItem);
  190. currentViewedDevice->dev->getTrackExtraInfo(s, L"filepath", value, ARRAYSIZE(value));
  191. if (!value[0])
  192. {
  193. message = 0x407;
  194. currentViewedDevice->dev->getTrackExtraInfo(s, L"metahash", value, ARRAYSIZE(value));
  195. }
  196. nx_string_t *out_devicenames = 0;
  197. size_t num_names = mlplugin->MessageProc(message, (INT_PTR)&value, (INT_PTR)&out_devicenames, 0);
  198. if (num_names > 0)
  199. {
  200. for (size_t i = 0; i < num_names; i++)
  201. {
  202. if (i > 0) StringCchCatW(tt_buf, ARRAYSIZE(tt_buf), L", ");
  203. StringCchCatW(tt_buf, ARRAYSIZE(tt_buf), out_devicenames[i]->string);
  204. }
  205. }
  206. else
  207. {
  208. WASABI_API_LNGSTRINGW_BUF(IDS_UPLOAD_TO_SOURCE, tt_buf, ARRAYSIZE(tt_buf));
  209. }
  210. if (out_devicenames)
  211. NXStringRelease(*out_devicenames);
  212. }
  213. }
  214. }
  215. }
  216. last_item = lvh.iItem;
  217. lpnmtdi->lpszText = tt_buf;
  218. // bit of a fiddle but it allows for multi-line tooltips
  219. //SendMessage(l->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 0);
  220. }
  221. else
  222. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
  223. }
  224. return 0;
  225. }
  226. }
  227. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
  228. }
  229. LRESULT SkinnedListView::pmp_listview_alt(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  230. if (uMsg == WM_NOTIFY)
  231. {
  232. LPNMHDR l=(LPNMHDR)lParam;
  233. switch (l->code)
  234. {
  235. case TTN_SHOW:
  236. {
  237. LVHITTESTINFO lvh = {0};
  238. GetCursorPos(&lvh.pt);
  239. ScreenToClient(hwnd, &lvh.pt);
  240. ListView_SubItemHitTest(hwnd, &lvh);
  241. ListContents * contents = (ListContents *)GetPropW(hwnd, L"pmp_list_info");
  242. if (lvh.iItem > 0 && lvh.iSubItem == contents->cloudcol)
  243. {
  244. LPTOOLTIPTEXTW tt = (LPTOOLTIPTEXTW)lParam;
  245. RECT r = {0};
  246. if (lvh.iSubItem)
  247. ListView_GetSubItemRect(hwnd, lvh.iItem, lvh.iSubItem, LVIR_BOUNDS, &r);
  248. else
  249. {
  250. ListView_GetItemRect(hwnd, lvh.iItem, &r, LVIR_BOUNDS);
  251. r.right = r.left + ListView_GetColumnWidth(hwnd, contents->cloudcol);
  252. }
  253. MapWindowPoints(hwnd, HWND_DESKTOP, (LPPOINT)&r, 2);
  254. SetWindowPos(tt->hdr.hwndFrom, HWND_TOPMOST, r.right, r.top + 2, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE);
  255. return 1;
  256. }
  257. }
  258. break;
  259. case TTN_NEEDTEXTW:
  260. {
  261. LVHITTESTINFO lvh = {0};
  262. GetCursorPos(&lvh.pt);
  263. ScreenToClient(hwnd, &lvh.pt);
  264. ListView_SubItemHitTest(hwnd, &lvh);
  265. static wchar_t tt_buf[256] = {L""};
  266. ListContents * contents = (ListContents *)GetPropW(hwnd, L"pmp_list_info");
  267. if (lvh.iItem > 0 && lvh.iSubItem == contents->cloudcol)
  268. {
  269. LPNMTTDISPINFO lpnmtdi = (LPNMTTDISPINFO)lParam;
  270. static int last_item = -1;
  271. if (last_item == lvh.iItem)
  272. {
  273. lpnmtdi->lpszText = tt_buf;
  274. return 0;
  275. }
  276. wchar_t temp[8] = {0};
  277. contents->GetCellText(lvh.iItem, lvh.iSubItem, temp, 8);
  278. int status = _wtoi(temp);
  279. if (status == 0 || status == 4)
  280. {
  281. WASABI_API_LNGSTRINGW_BUF(IDS_ALL_TRACKS_PLAYABLE, tt_buf, ARRAYSIZE(tt_buf));
  282. }
  283. else if (status == 1)
  284. {
  285. WASABI_API_LNGSTRINGW_BUF(IDS_ALL_TRACKS_PLAYABLE_HERE, tt_buf, ARRAYSIZE(tt_buf));
  286. }
  287. else if (status == 2)
  288. {
  289. WASABI_API_LNGSTRINGW_BUF(IDS_SOME_TRACKS_PLAYABLE, tt_buf, ARRAYSIZE(tt_buf));
  290. }
  291. else if (status == 3)
  292. {
  293. WASABI_API_LNGSTRINGW_BUF(IDS_NO_TRACKS_PLAYABLE, tt_buf, ARRAYSIZE(tt_buf));
  294. }
  295. last_item = lvh.iItem;
  296. lpnmtdi->lpszText = tt_buf;
  297. // bit of a fiddle but it allows for multi-line tooltips
  298. //SendMessage(l->hwndFrom, TTM_SETMAXTIPWIDTH, 0, 0);
  299. }
  300. else
  301. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
  302. }
  303. return 0;
  304. }
  305. }
  306. return CallWindowProcW((WNDPROC)GetPropW(hwnd, L"pmp_list_proc"), hwnd, uMsg, wParam, lParam);
  307. }
  308. BOOL SkinnedListView::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) {
  309. switch(uMsg) {
  310. case WM_INITDIALOG:
  311. {
  312. #if defined(_UNICODE) || defined(UNICODE)
  313. SendMessage(hwndDlg,CCM_SETUNICODEFORMAT,TRUE,0);
  314. #endif
  315. HWND list = GetDlgItem(hwndDlg, dlgitem);
  316. headerWindow = (HWND)SendMessageW(list, LVM_GETHEADER, 0, 0L);
  317. listview.setwnd(list);
  318. // setup tooltip handling as needed
  319. if (dlgitem == IDC_LIST_ARTIST || dlgitem == IDC_LIST_ALBUM || dlgitem == IDC_LIST_ALBUM2)
  320. {
  321. if (!GetPropW(list, L"pmp_list_proc")) {
  322. SetPropW(list, L"pmp_list_proc", (HANDLE)SetWindowLongPtrW(list, GWLP_WNDPROC, (LONG_PTR)this->pmp_listview_alt));
  323. SetPropW(list, L"pmp_list_info", (HANDLE)this->contents);
  324. }
  325. }
  326. else if (dlgitem != IDC_LIST_TRANSFERS)
  327. {
  328. if (!GetPropW(list, L"pmp_list_proc")) {
  329. SetPropW(list, L"pmp_list_proc", (HANDLE)SetWindowLongPtrW(list, GWLP_WNDPROC, (LONG_PTR)this->pmp_listview));
  330. SetPropW(list, L"pmp_list_info", (HANDLE)this->contents);
  331. }
  332. }
  333. if(!wParam)
  334. {
  335. MLSKINWINDOW m = {0};
  336. m.skinType = SKINNEDWND_TYPE_LISTVIEW;
  337. m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS;
  338. m.hwndToSkin = listview.getwnd();
  339. MLSkinWindow(libraryParent, &m);
  340. }
  341. if (contents)
  342. {
  343. for(int i=0; i < contents->GetNumColumns(); i++)
  344. {
  345. if (contents->cloud)
  346. {
  347. listview.AddCol((i == contents->cloudcol ? L"" : contents->GetColumnTitle(i)),contents->GetColumnWidth(i));
  348. }
  349. else
  350. {
  351. listview.AddCol(contents->GetColumnTitle(i),contents->GetColumnWidth(i));
  352. }
  353. }
  354. if(contents->GetSortColumn() != -1) // display sort arrow
  355. SendMessage(headerWindow,WM_ML_IPC,MAKEWPARAM(contents->GetSortColumn(),!contents->GetSortDirection()),ML_IPC_SKINNEDHEADER_DISPLAYSORT);
  356. UpdateList();
  357. }
  358. }
  359. break;
  360. case WM_DISPLAYCHANGE:
  361. ListView_SetTextColor(listview.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMFG):RGB(0xff,0xff,0xff));
  362. ListView_SetBkColor(listview.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00));
  363. ListView_SetTextBkColor(listview.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00));
  364. listview.SetFont((HFONT)SendMessage(libraryParent, WM_ML_IPC, 66, ML_IPC_SKIN_WADLG_GETFUNC));
  365. break;
  366. case WM_DESTROY:
  367. if (contents)
  368. {
  369. for(int i=0; i<contents->GetNumColumns(); i++)
  370. if(contents->GetColumnWidth(i) != listview.GetColumnWidth(i))
  371. contents->ColumnResize(i,listview.GetColumnWidth(i));
  372. }
  373. break;
  374. case WM_NOTIFYFORMAT:
  375. return NFR_UNICODE;
  376. case WM_CONTEXTMENU:
  377. {
  378. POINT pt={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
  379. HWND hwndFromChild = WindowFromPoint(pt);
  380. if (enableHeaderMenu && hwndFromChild == ListView_GetHeader(listview.getwnd())) {
  381. if(contents->CustomizeColumns(listview.getwnd(), TRUE)) {
  382. while (ListView_DeleteColumn(listview.getwnd(), 0));
  383. for(int i=0; i < contents->GetNumColumns(); i++)
  384. listview.AddCol(contents->GetColumnTitle(i),contents->GetColumnWidth(i));
  385. }
  386. }
  387. }
  388. break;
  389. case WM_NOTIFY:
  390. {
  391. LPNMHDR l=(LPNMHDR)lParam;
  392. if (l->idFrom==dlgitem) {
  393. switch(l->code) {
  394. case NM_DBLCLK:
  395. break;
  396. case LVN_KEYDOWN:
  397. switch(((LPNMLVKEYDOWN)lParam)->wVKey) {
  398. case 0x41: //A
  399. if(GetAsyncKeyState(VK_CONTROL) && !GetAsyncKeyState(VK_SHIFT)){
  400. int num=listview.GetCount();
  401. for(int x = 0; x < num; x ++) listview.SetSelected(x);
  402. }
  403. break;
  404. case 0x2E: //Delete
  405. break;
  406. }
  407. break;
  408. case LVN_ODFINDITEM:
  409. {
  410. NMLVFINDITEM *t = (NMLVFINDITEM *)lParam;
  411. int i=t->iStart;
  412. if (i >= contents->GetNumRows()) i=0;
  413. int cnt=contents->GetNumRows()-i;
  414. if (t->lvfi.flags & LVFI_WRAP) cnt+=i;
  415. while (cnt-->0) {
  416. wchar_t tmp[128]=L"";
  417. wchar_t *name=0;
  418. contents->GetCellText(i,GetFindItemColumn(),tmp,sizeof(tmp)/sizeof(wchar_t));
  419. name = tmp;
  420. if (!name) name=L"";
  421. else SKIP_THE_AND_WHITESPACE(name)
  422. if (t->lvfi.flags & (4|LVFI_PARTIAL)) {
  423. if (!_wcsnicmp(name,t->lvfi.psz,lstrlen(t->lvfi.psz))) {
  424. SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,i);
  425. return 1;
  426. }
  427. }
  428. else if (t->lvfi.flags & LVFI_STRING) {
  429. if (!STRCMP_NULLOK(name,t->lvfi.psz)) {
  430. SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,i);
  431. return 1;
  432. }
  433. }
  434. else {
  435. SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,-1);
  436. return 1;
  437. }
  438. if (++i == contents->GetNumRows()) i=0;
  439. }
  440. SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,-1);
  441. return 1;
  442. }
  443. break;
  444. case LVN_GETDISPINFOW:
  445. {
  446. NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam;
  447. int item=lpdi->item.iItem;
  448. if (item < 0 || contents && item >= contents->GetNumRows()) return 0;
  449. if (lpdi->item.mask & LVIF_TEXT) {
  450. lpdi->item.pszText[0]=0;
  451. contents->GetCellText(item,lpdi->item.iSubItem,lpdi->item.pszText,lpdi->item.cchTextMax);
  452. // will cache the cloud status for use in drawing later on (not ideal but it'll do for now)
  453. if (lpdi->item.iSubItem == contents->cloudcol) contents->cloud_cache[item] = _wtoi(lpdi->item.pszText);
  454. }
  455. }
  456. break;
  457. case LVN_COLUMNCLICK:
  458. {
  459. NMLISTVIEW *p=(NMLISTVIEW*)lParam;
  460. contents->ColumnClicked(p->iSubItem);
  461. if(contents->GetSortColumn() != -1) {
  462. SendMessage(headerWindow,WM_ML_IPC,MAKEWPARAM(contents->GetSortColumn(),!contents->GetSortDirection()),ML_IPC_SKINNEDHEADER_DISPLAYSORT);
  463. }
  464. UpdateList();
  465. }
  466. break;
  467. }
  468. }
  469. switch(l->code) {
  470. case HDN_ITEMCHANGING:
  471. {
  472. if (headerWindow == l->hwndFrom)
  473. {
  474. LPNMHEADERW phdr = (LPNMHEADERW)lParam;
  475. if (phdr->pitem && (HDI_WIDTH & phdr->pitem->mask) && phdr->iItem == contents->cloudcol)
  476. {
  477. INT width = phdr->pitem->cxy;
  478. if (MLCloudColumn_GetWidth(plugin.hwndLibraryParent, &width))
  479. {
  480. phdr->pitem->cxy = width;
  481. }
  482. }
  483. break;
  484. }
  485. }
  486. }
  487. }
  488. break;
  489. }
  490. return 0;
  491. }
  492. ListContents::~ListContents() {
  493. for(int i=0; i<fields.GetSize(); i++) delete ((ListField*)fields.Get(i));
  494. for(int i=0; i<hiddenfields.GetSize(); i++) delete ((ListField*)hiddenfields.Get(i));
  495. }
  496. static int sortFunc_cols(const void *elem1, const void *elem2) {
  497. ListField * a = *(ListField **)elem1;
  498. ListField * b = *(ListField **)elem2;
  499. return a->pos - b->pos;
  500. }
  501. void ListContents::SortColumns() {
  502. for(int i=0; i<fields.GetSize(); i++){
  503. ListField *l = (ListField*)fields.Get(i);
  504. if(l->hidden) {
  505. hiddenfields.Add(l);
  506. fields.Del(i--);
  507. }
  508. }
  509. qsort(fields.GetAll(),fields.GetSize(),sizeof(void*),sortFunc_cols);
  510. }
  511. typedef struct CustomizeColumnsCreateParam
  512. {
  513. ListContents *list;
  514. HWND ownerWindow;
  515. } CustomizeColumnsCreateParam;
  516. static INT_PTR CALLBACK custColumns_dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  517. {
  518. static HWND m_curlistbox_hwnd, m_availlistbox_hwnd;
  519. static ListContents *list;
  520. switch (uMsg) {
  521. case WM_INITDIALOG:
  522. {
  523. CustomizeColumnsCreateParam *param;
  524. HWND centerWindow;
  525. m_curlistbox_hwnd = GetDlgItem(hwndDlg, IDC_LIST1);
  526. m_availlistbox_hwnd = GetDlgItem(hwndDlg, IDC_LIST2);
  527. param = (CustomizeColumnsCreateParam*)lParam;
  528. if (NULL != param)
  529. {
  530. list = param->list;
  531. centerWindow = param->ownerWindow;
  532. if (NULL == centerWindow)
  533. centerWindow = CENTER_OVER_ML_VIEW;
  534. }
  535. else
  536. {
  537. list = NULL;
  538. centerWindow = CENTER_OVER_ML_VIEW;
  539. }
  540. if (FALSE != CenterWindow(hwndDlg, centerWindow))
  541. {
  542. if (FALSE == IS_INTRESOURCE(centerWindow))
  543. {
  544. wchar_t buffer[64] = {0};
  545. if (FALSE != GetClassName(centerWindow, buffer, ARRAYSIZE(buffer)) &&
  546. CSTR_EQUAL == CompareString(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT),
  547. NORM_IGNORECASE, buffer, -1, L"SysListView32", -1))
  548. {
  549. RECT rect;
  550. long top = 0;
  551. if (FALSE != GetClientRect(centerWindow, &rect))
  552. {
  553. MapWindowPoints(centerWindow, HWND_DESKTOP, (POINT*)&rect, 2);
  554. top = rect.top;
  555. HWND headerWindow = (HWND)SendMessage(centerWindow, LVM_GETHEADER, 0, 0L);
  556. if (NULL != headerWindow &&
  557. (0 != (WS_VISIBLE & GetWindowLongPtr(headerWindow, GWL_STYLE))) &&
  558. GetWindowRect(headerWindow, &rect))
  559. {
  560. if (rect.top == top)
  561. top = rect.bottom;
  562. }
  563. }
  564. top += 12;
  565. if (FALSE != GetWindowRect(hwndDlg, &rect) &&
  566. rect.top != top)
  567. {
  568. SetWindowPos(hwndDlg, NULL, rect.left, top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
  569. }
  570. }
  571. }
  572. SendMessage(hwndDlg, DM_REPOSITION, 0, 0L);
  573. }
  574. }
  575. case WM_USER + 32:
  576. {
  577. int i;
  578. for (i=0; i<list->fields.GetSize(); i++) {
  579. ListField * l = (ListField *)list->fields.Get(i);
  580. int r = SendMessage(m_curlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)l->name);
  581. SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, (LPARAM)l);
  582. }
  583. for (i=0; i<list->hiddenfields.GetSize(); i++) {
  584. ListField * l = (ListField *)list->hiddenfields.Get(i);
  585. int r = SendMessage(m_availlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)l->name);
  586. SendMessage(m_availlistbox_hwnd, LB_SETITEMDATA, r, (LPARAM)l);
  587. }
  588. }
  589. break;
  590. case WM_COMMAND:
  591. switch (LOWORD(wParam)) {
  592. case IDC_DEFS:
  593. SendMessage(m_curlistbox_hwnd, LB_RESETCONTENT, 0, 0);
  594. SendMessage(m_availlistbox_hwnd, LB_RESETCONTENT, 0, 0);
  595. list->ResetColumns();
  596. SendMessage(hwndDlg, WM_USER + 32, 0, 0);
  597. break;
  598. case IDC_LIST2:
  599. if (HIWORD(wParam) != LBN_DBLCLK) {
  600. if (HIWORD(wParam) == LBN_SELCHANGE) {
  601. int r = SendMessage(m_availlistbox_hwnd, LB_GETSELCOUNT, 0, 0) > 0;
  602. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), r);
  603. }
  604. return 0;
  605. }
  606. case IDC_BUTTON2:
  607. //add column
  608. {
  609. for (int i = 0;i < SendMessage(m_availlistbox_hwnd, LB_GETCOUNT, 0, 0);i++) {
  610. if (SendMessage(m_availlistbox_hwnd, LB_GETSEL, i, 0)) {
  611. ListField* c = (ListField*)SendMessage(m_availlistbox_hwnd, LB_GETITEMDATA, i, 0);
  612. if(!c) continue;
  613. SendMessage(m_availlistbox_hwnd, LB_DELETESTRING, i--, 0);
  614. int r = SendMessage(m_curlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)c->name);
  615. SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, (LPARAM)c);
  616. }
  617. }
  618. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), 0);
  619. }
  620. break;
  621. case IDC_LIST1:
  622. if (HIWORD(wParam) != LBN_DBLCLK) {
  623. if (HIWORD(wParam) == LBN_SELCHANGE) {
  624. int r = SendMessage(m_curlistbox_hwnd, LB_GETSELCOUNT, 0, 0) > 0;
  625. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON3), r);
  626. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON4), r);
  627. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON5), r);
  628. }
  629. return 0;
  630. }
  631. case IDC_BUTTON3:
  632. //remove column
  633. {
  634. for (int i = 0;i < SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);i++) {
  635. if (SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0)) {
  636. ListField* c = (ListField*)SendMessage(m_curlistbox_hwnd, LB_GETITEMDATA, i, 0);
  637. if(!c) continue;
  638. SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i, 0);
  639. i--;
  640. int r = SendMessage(m_availlistbox_hwnd, LB_ADDSTRING, 0, (LPARAM)c->name);
  641. SendMessage(m_availlistbox_hwnd, LB_SETITEMDATA, r, (LPARAM)c);
  642. }
  643. }
  644. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON3), 0);
  645. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON4), 0);
  646. EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON5), 0);
  647. }
  648. break;
  649. case IDC_BUTTON4:
  650. //move column up
  651. {
  652. for (int i = 0;i < (INT)SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);i++)
  653. {
  654. if (i != 0 && (INT)SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
  655. {
  656. ListField* c = (ListField*)SendMessage(m_curlistbox_hwnd, LB_GETITEMDATA, i - 1, 0);
  657. SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i - 1, 0);
  658. int r = (INT)SendMessage(m_curlistbox_hwnd, LB_INSERTSTRING, i, (LPARAM)c->name);
  659. SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, (LPARAM)c);
  660. }
  661. }
  662. }
  663. break;
  664. case IDC_BUTTON5:
  665. //move column down
  666. {
  667. int l = SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);
  668. for (int i = l - 2;i >= 0;i--)
  669. {
  670. if (SendMessage(m_curlistbox_hwnd, LB_GETSEL, i, 0))
  671. {
  672. ListField* c = (ListField*)SendMessage(m_curlistbox_hwnd, LB_GETITEMDATA, i + 1, 0);
  673. SendMessage(m_curlistbox_hwnd, LB_DELETESTRING, i + 1, 0);
  674. int r = (INT)SendMessage(m_curlistbox_hwnd, LB_INSERTSTRING, i, (LPARAM)c->name);
  675. SendMessage(m_curlistbox_hwnd, LB_SETITEMDATA, r, (LPARAM)c);
  676. }
  677. }
  678. }
  679. break;
  680. case IDOK:
  681. // read and apply changes...
  682. {
  683. while(list->fields.GetSize()) list->fields.Del(0);
  684. while(list->hiddenfields.GetSize()) list->hiddenfields.Del(0);
  685. wchar_t buf[100] = {0};
  686. int i;
  687. int l = (INT)SendMessage(m_curlistbox_hwnd, LB_GETCOUNT, 0, 0);
  688. for (i = 0;i < l;i++) {
  689. ListField* c = (ListField*)SendMessage(m_curlistbox_hwnd, LB_GETITEMDATA, i, 0);
  690. list->fields.Add(c);
  691. c->pos=i;
  692. c->hidden=false;
  693. StringCchPrintf(buf, ARRAYSIZE(buf), L"colPos_%d",c->field);
  694. list->config->WriteInt(buf,i);
  695. StringCchPrintf(buf, ARRAYSIZE(buf), L"colHidden_%d",c->field);
  696. list->config->WriteInt(buf,0);
  697. }
  698. l = (INT)SendMessage(m_availlistbox_hwnd, LB_GETCOUNT, 0, 0);
  699. for (i = 0;i < l;i++) {
  700. ListField* c = (ListField*)SendMessage(m_availlistbox_hwnd, LB_GETITEMDATA, i, 0);
  701. list->hiddenfields.Add(c);
  702. c->hidden=true;
  703. StringCchPrintf(buf, ARRAYSIZE(buf), L"colHidden_%d",c->field);
  704. list->config->WriteInt(buf,1);
  705. }
  706. list->SortColumns();
  707. }
  708. EndDialog(hwndDlg, 1);
  709. break;
  710. case IDCANCEL:
  711. EndDialog(hwndDlg, 0);
  712. break;
  713. }
  714. break;
  715. }
  716. return FALSE;
  717. }
  718. bool ListContents::CustomizeColumns(HWND parent, BOOL showmenu) {
  719. if(!fields.GetSize()) return false;
  720. if(showmenu) {
  721. HMENU menu = GetSubMenu(m_context_menus, 8);
  722. POINT p;
  723. GetCursorPos(&p);
  724. int r = Menu_TrackSkinnedPopup(menu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTBUTTON, p.x, p.y, parent, NULL);
  725. if(r != ID_HEADERWND_CUSTOMIZECOLUMNS) return false;
  726. }
  727. CustomizeColumnsCreateParam param;
  728. param.list = this;
  729. param.ownerWindow = parent;
  730. bool r = !!WASABI_API_DIALOGBOXPARAMW(IDD_CUSTCOLUMNS, parent, custColumns_dialogProc,(LPARAM)&param);
  731. MSG msg;
  732. while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); //eat return
  733. MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(parent), (this->cloudcol = -1)); // reset the cloud status column so it'll be correctly removed
  734. if (cloud)
  735. {
  736. // not pretty but it'll allow us to know the current
  737. // position of the cloud column for drawing purposes
  738. for(int i = 0; i < fields.GetSize(); i++)
  739. {
  740. if (!lstrcmpi(((ListField *)fields.Get(i))->name, L"cloud"))
  741. {
  742. this->cloudcol = ((ListField *)fields.Get(i))->pos;
  743. // update the cloud column
  744. MLSkinnedHeader_SetCloudColumn(ListView_GetHeader(parent), this->cloudcol);
  745. break;
  746. }
  747. }
  748. }
  749. return r;
  750. }
  751. ListField::ListField(int field, int defwidth, wchar_t * name, C_Config * config, bool hidden):field(field),name(name),hiddenDefault(hidden) {
  752. wchar_t buf[100] = {0};
  753. StringCchPrintf(buf, ARRAYSIZE(buf), L"colWidth_%d",field);
  754. width = config->ReadInt(buf,defwidth);
  755. StringCchPrintf(buf, ARRAYSIZE(buf), L"colPos_%d",field);
  756. pos = config->ReadInt(buf,field==-1?-1:(field%100));
  757. StringCchPrintf(buf, ARRAYSIZE(buf), L"colHidden_%d",field);
  758. this->hidden = config->ReadInt(buf,hidden?1:0)!=0;
  759. this->name = _wcsdup(name);
  760. }
  761. void ListField::ResetPos() { pos = ((field==-1)?-1:(pos%100)); hidden = hiddenDefault;}
  762. void ListContents::ResetColumns() {
  763. while(hiddenfields.GetSize()) { fields.Add(hiddenfields.Get(0)); hiddenfields.Del(0); }
  764. for (int i=0; i<fields.GetSize(); i++) ((ListField*)fields.Get(i))->ResetPos();
  765. SortColumns();
  766. }
  767. int ListContents::GetSortDirection() { return TRUE; }
  768. int ListContents::GetSortColumn() { return -1; }
  769. void ListContents::ColumnClicked(int col) {}
  770. void ListContents::ColumnResize(int col, int newWidth) {}
  771. void ListContents::GetInfoString(wchar_t * buf) { buf[0]=0; }