Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

584 lines
16 KiB

  1. /*
  2. * lv - common dialog proc handler for listview pages
  3. */
  4. #include "tweakui.h"
  5. #pragma BEGIN_CONST_DATA
  6. #pragma END_CONST_DATA
  7. /*****************************************************************************
  8. *
  9. * LV_AddItem
  10. *
  11. * Add an entry to the listview.
  12. *
  13. * Returns the resulting item number.
  14. *
  15. *****************************************************************************/
  16. int PASCAL
  17. LV_AddItem(HWND hwnd, int ix, LPCTSTR ptszDesc, int iImage, BOOL fState)
  18. {
  19. LV_ITEM lvi;
  20. lvi.mask = LVIF_TEXT | LVIF_PARAM;
  21. lvi.iItem = MAXLONG;
  22. lvi.iSubItem = 0; /* Must be zero */
  23. lvi.pszText = (LPTSTR)ptszDesc;
  24. lvi.lParam = ix; /* Take it */
  25. if (iImage >= 0) {
  26. lvi.iImage = iImage;
  27. lvi.mask |= LVIF_IMAGE;
  28. }
  29. if (fState >= 0) {
  30. lvi.state = INDEXTOSTATEIMAGEMASK(fState + 1);
  31. lvi.mask |= LVIF_STATE;
  32. }
  33. return ListView_InsertItem(hwnd, &lvi);
  34. }
  35. /*****************************************************************************
  36. *
  37. * LV_Toggle
  38. *
  39. * Toggle the state icon of the current selection.
  40. *
  41. *****************************************************************************/
  42. void PASCAL
  43. LV_Toggle(HWND hwnd, int iItem)
  44. {
  45. LV_ITEM lvi;
  46. lvi.stateMask = LVIS_STATEIMAGEMASK;
  47. Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_STATE);
  48. if (lvi.state & LVIS_STATEIMAGEMASK) {
  49. lvi.state ^= INDEXTOSTATEIMAGEMASK(1) ^
  50. INDEXTOSTATEIMAGEMASK(2); /* toggle checkmark */
  51. ListView_SetItem(hwnd, &lvi); /* Set the state */
  52. Common_SetDirty(GetParent(hwnd));
  53. }
  54. }
  55. /*****************************************************************************
  56. *
  57. * LV_Rename
  58. *
  59. * Rename an item in the list view.
  60. *
  61. *****************************************************************************/
  62. void PASCAL
  63. LV_Rename(HWND hwnd, int iItem)
  64. {
  65. ListView_EditLabel(hwnd, iItem);
  66. }
  67. /*****************************************************************************
  68. *
  69. * LV_ResizeReportColumn
  70. *
  71. * Resize the column in the report to be as large as possible without
  72. * colliding with the vertical scrollbar (if any).
  73. *
  74. *****************************************************************************/
  75. void PASCAL
  76. LV_ResizeReportColumn(HWND hwnd)
  77. {
  78. ListView_SetColumnWidth(hwnd, 0, LVSCW_AUTOSIZE);
  79. }
  80. /*****************************************************************************
  81. *
  82. * LV_OnInitDialog
  83. *
  84. * The callback will initialize the listview.
  85. *
  86. * Once the callback is happy, we initialize the columns.
  87. *
  88. * All of our listviews are simple reports with but one column.
  89. *
  90. *****************************************************************************/
  91. /* New IE5 feature we will use if available */
  92. #define LVS_EX_LABELTIP 0x00004000
  93. #define LVM_SETEXTENDEDLISTVIEWSTYLE (LVM_FIRST + 54)
  94. #define ListView_SetExtendedListViewStyle(hwndLV, dw)\
  95. (DWORD)SNDMSG((hwndLV), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dw)
  96. BOOL PASCAL
  97. LV_OnInitDialog(PLVV plvv, HWND hdlg)
  98. {
  99. HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
  100. ListView_SetExtendedListViewStyle(hwnd, LVS_EX_LABELTIP);
  101. if (plvv->lvvfl & lvvflCanCheck) {
  102. ListView_SetImageList(hwnd, pcdii->himlState, LVSIL_STATE);
  103. }
  104. if (plvv->lvvfl & lvvflIcons) {
  105. ListView_SetImageList(hwnd, GetSystemImageList(SHGFI_SMALLICON),
  106. LVSIL_SMALL);
  107. }
  108. if (plvv->OnInitDialog(hwnd)) { /* Add the column to the report */
  109. LV_COLUMN col;
  110. col.mask = 0;
  111. ListView_InsertColumn(hwnd, 0, &col);
  112. LV_ResizeReportColumn(hwnd);
  113. Misc_LV_SetCurSel(hwnd, 0);
  114. }
  115. return 1;
  116. }
  117. /*****************************************************************************
  118. *
  119. * LV_OnLvContextMenu
  120. *
  121. * The context menu shall appear in the listview.
  122. *
  123. * Allow the callback to modify the menu before we pop it up.
  124. * Then let the window procedure's WM_COMMAND do the rest.
  125. *
  126. * If lvvflCanCheck is set, we will automatically adjust IDC_LVTOGGLE
  127. * to match.
  128. *
  129. * If lvvflCanDelete is set, we will adjust IDC_LVDELETE to match.
  130. *
  131. *****************************************************************************/
  132. void PASCAL
  133. LV_OnLvContextMenu(PLVV plvv, HWND hdlg, HWND hwnd, POINT pt)
  134. {
  135. int iItem = Misc_LV_GetCurSel(hwnd);
  136. if (iItem != -1) {
  137. HMENU hmenu;
  138. ClientToScreen(hwnd, &pt); /* Make it screen coordinates */
  139. hmenu = GetSubMenu(pcdii->hmenu, plvv->iMenu);
  140. if (plvv->lvvfl & lvvflCanCheck) {
  141. MENUITEMINFO mii;
  142. LV_ITEM lvi;
  143. lvi.stateMask = LVIS_STATEIMAGEMASK;
  144. Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_STATE);
  145. mii.cbSize = cbX(mii);
  146. mii.fMask = MIIM_STATE;
  147. switch (isiPlvi(&lvi)) {
  148. case isiUnchecked: mii.fState = MFS_ENABLED | MFS_UNCHECKED; break;
  149. case isiChecked: mii.fState = MFS_ENABLED | MFS_CHECKED; break;
  150. default: mii.fState = MFS_DISABLED; break;
  151. }
  152. SetMenuItemInfo(hmenu, IDC_LVTOGGLE, 0, &mii);
  153. }
  154. if (plvv->lvvfl & lvvflCanDelete) {
  155. Misc_EnableMenuFromHdlgId(hmenu, hdlg, IDC_LVDELETE);
  156. }
  157. if (plvv->OnInitContextMenu) {
  158. plvv->OnInitContextMenu(hwnd, iItem, hmenu);
  159. }
  160. TrackPopupMenuEx(hmenu, TPM_RIGHTBUTTON | TPM_VERTICAL |
  161. TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, hdlg, 0);
  162. }
  163. }
  164. /*****************************************************************************
  165. *
  166. * LV_OnContextMenu
  167. *
  168. * If the context menu came from the listview, figure out which
  169. * item got clicked on. If we find an item, pop up its context
  170. * menu. Otherwise, just do the standard help thing.
  171. *
  172. * NOTE! We don't use LVHT_ONITEM because ListView is broken!
  173. * Watch:
  174. *
  175. * #define LVHT_ONITEMSTATEICON 0x0008
  176. * #define LVHT_ABOVE 0x0008
  177. *
  178. * Oops. This means that clicks above the item are treated as
  179. * clicks on the state icon.
  180. *
  181. * Fortunately, we reside completely in report view, so you can't
  182. * legally click above the item. The only way it can happen is
  183. * if the coordinates to OnContextMenu are out of range, so we
  184. * catch that up front and munge it accordingly.
  185. *
  186. *****************************************************************************/
  187. void PASCAL
  188. LV_OnContextMenu(PLVV plvv, HWND hdlg, HWND hwnd, LPARAM lp)
  189. {
  190. if (GetDlgCtrlID(hwnd) == IDC_LISTVIEW) {
  191. LV_HITTESTINFO hti;
  192. if ((DWORD)lp == 0xFFFFFFFF) {
  193. /* Pretend it was on the center of the small icon */
  194. ListView_GetItemPosition(hwnd, Misc_LV_GetCurSel(hwnd),
  195. &hti.pt);
  196. hti.pt.x += GetSystemMetrics(SM_CXSMICON) / 2;
  197. hti.pt.y += GetSystemMetrics(SM_CYSMICON) / 2;
  198. LV_OnLvContextMenu(plvv, hdlg, hwnd, hti.pt);
  199. } else {
  200. Misc_LV_HitTest(hwnd, &hti, lp);
  201. if ((hti.flags & LVHT_ONITEM)) {
  202. /* Because LV sometimes forgets to move the focus... */
  203. Misc_LV_SetCurSel(hwnd, hti.iItem);
  204. LV_OnLvContextMenu(plvv, hdlg, hwnd, hti.pt);
  205. } else {
  206. Common_OnContextMenu((WPARAM)hwnd, plvv->pdwHelp);
  207. }
  208. }
  209. } else {
  210. Common_OnContextMenu((WPARAM)hwnd, plvv->pdwHelp);
  211. }
  212. }
  213. /*****************************************************************************
  214. *
  215. * LV_OnCommand_Dispatch
  216. *
  217. * Dispatch a recognized command to the handler.
  218. *
  219. *****************************************************************************/
  220. void PASCAL
  221. LV_OnCommand_Dispatch(void (PASCAL *pfn)(HWND hwnd, int iItem), HWND hdlg)
  222. {
  223. HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
  224. int iItem = Misc_LV_GetCurSel(hwnd);
  225. if (iItem != -1) {
  226. pfn(hwnd, iItem);
  227. }
  228. }
  229. /*****************************************************************************
  230. *
  231. * LV_OnCommand
  232. *
  233. * Ooh, we got a command.
  234. *
  235. * See if it's one of ours. If not, pass it through to the handler.
  236. *
  237. *****************************************************************************/
  238. BOOL PASCAL
  239. LV_OnCommand(PLVV plvv, HWND hdlg, int id, UINT codeNotify)
  240. {
  241. PLVCI plvci;
  242. switch (id) {
  243. case IDC_LVTOGGLE:
  244. LV_OnCommand_Dispatch(LV_Toggle, hdlg);
  245. break;
  246. case IDC_LVRENAME:
  247. if (plvv->Dirtify) LV_OnCommand_Dispatch(LV_Rename, hdlg);
  248. break;
  249. default:
  250. if (plvv->rglvci) {
  251. for (plvci = plvv->rglvci; plvci[0].id; plvci++) {
  252. if (id == plvci->id) {
  253. LV_OnCommand_Dispatch(plvci->pfn, hdlg);
  254. goto dispatched;
  255. }
  256. }
  257. }
  258. if (plvv->OnCommand) {
  259. plvv->OnCommand(hdlg, id, codeNotify);
  260. }
  261. dispatched:;
  262. break;
  263. }
  264. return 0;
  265. }
  266. /*****************************************************************************
  267. *
  268. * LV_OnNotify_OnClick
  269. *
  270. * Somebody clicked or double-clicked on the listview.
  271. *
  272. *****************************************************************************/
  273. void PASCAL
  274. LV_OnNotify_OnClick(PLVV plvv, HWND hwnd, NMHDR FAR *pnm)
  275. {
  276. LV_HITTESTINFO hti;
  277. Misc_LV_HitTest(hwnd, &hti, (LPARAM)GetMessagePos());
  278. /*
  279. * A click/dblclick on the item state icon toggles.
  280. *
  281. * A click/dblclick anywhere toggles *if* you can't rename.
  282. */
  283. if (((hti.flags & LVHT_ONITEMSTATEICON) ||
  284. ((hti.flags & LVHT_ONITEM) && !(plvv->lvvfl & lvvflCanRename)))) {
  285. Misc_LV_SetCurSel(hwnd, hti.iItem); /* LV doesn't do this, oddly */
  286. LV_Toggle(hwnd, hti.iItem);
  287. } else if (pnm->code == NM_DBLCLK && plvv->idDblClk) {
  288. LV_OnCommand(plvv, GetParent(hwnd), plvv->idDblClk, 0);
  289. } else if (pnm->code == NM_DBLCLK && (hti.flags & LVHT_ONITEMLABEL)) {
  290. if (plvv->lvvfl & lvvflCanRename) {
  291. LV_Rename(hwnd, hti.iItem);
  292. }
  293. }
  294. }
  295. /*****************************************************************************
  296. *
  297. * LV_OnNotify_OnKeyDown
  298. *
  299. * Somebody pressed a key while focus is on a listview.
  300. *
  301. * F2 = Rename
  302. * Space = Toggle
  303. * Del = Delete
  304. *
  305. *****************************************************************************/
  306. void PASCAL
  307. LV_OnNotify_OnKeyDown(PLVV plvv, HWND hwnd, LV_KEYDOWN FAR *lvkd)
  308. {
  309. int iItem = Misc_LV_GetCurSel(hwnd);
  310. if (iItem != -1) {
  311. switch (lvkd->wVKey) {
  312. case VK_SPACE:
  313. /*
  314. * But not if the ALT key is down!
  315. */
  316. if (GetKeyState(VK_MENU) >= 0) {
  317. LV_Toggle(hwnd, iItem);
  318. }
  319. break;
  320. case VK_F2:
  321. if (plvv->lvvfl & lvvflCanRename) {
  322. LV_Rename(hwnd, iItem);
  323. }
  324. break;
  325. case VK_DELETE:
  326. LV_OnCommand(plvv, GetParent(hwnd), IDC_LVDELETE, 0); break;
  327. break;
  328. }
  329. }
  330. }
  331. /*****************************************************************************
  332. *
  333. * LV_OnNotify_OnBeginLabelEdit
  334. *
  335. * Allow it to go through if label editing is permitted.
  336. *
  337. *****************************************************************************/
  338. BOOL INLINE
  339. LV_OnNotify_OnBeginLabelEdit(PLVV plvv, HWND hdlg, HWND hwnd)
  340. {
  341. if (plvv->lvvfl & lvvflCanRename) {
  342. return 0;
  343. } else {
  344. SetWindowLongPtr(hdlg, DWLP_MSGRESULT, TRUE);
  345. return 1;
  346. }
  347. }
  348. /*****************************************************************************
  349. *
  350. * LV_OnNotify_OnEndLabelEdit
  351. *
  352. * Trim leading and trailing whitespace. If there's anything left,
  353. * then we'll accept it.
  354. *
  355. *****************************************************************************/
  356. void PASCAL
  357. LV_OnNotify_OnEndLabelEdit(PLVV plvv, HWND hwnd, LV_DISPINFO FAR *lpdi)
  358. {
  359. if (lpdi->item.iItem != -1 && lpdi->item.pszText) {
  360. LV_ITEM lvi;
  361. lvi.pszText = Misc_Trim(lpdi->item.pszText);
  362. if (lvi.pszText[0]) {
  363. Misc_LV_GetItemInfo(hwnd, &lvi, lpdi->item.iItem, LVIF_PARAM);
  364. lvi.mask ^= LVIF_TEXT ^ LVIF_PARAM;
  365. ListView_SetItem(hwnd, &lvi);
  366. Common_SetDirty(GetParent(hwnd));
  367. plvv->Dirtify(lvi.lParam);
  368. }
  369. }
  370. }
  371. /*****************************************************************************
  372. *
  373. * LV_OnNotify_OnItemChanged
  374. *
  375. * If we are being told about a new selection, call the callback.
  376. *
  377. *****************************************************************************/
  378. void PASCAL
  379. LV_OnNotify_OnItemChanged(PLVV plvv, HWND hwnd, NM_LISTVIEW *pnmlv)
  380. {
  381. if ((pnmlv->uChanged & LVIF_STATE) && (pnmlv->uNewState & LVIS_SELECTED)) {
  382. if (plvv->OnSelChange) {
  383. plvv->OnSelChange(hwnd, pnmlv->iItem);
  384. }
  385. }
  386. }
  387. /*****************************************************************************
  388. *
  389. * LV_OnNotify
  390. *
  391. * Ooh, we got a notification. See if it's something we recognize.
  392. *
  393. * NOTE! We don't support private notifications.
  394. *
  395. *****************************************************************************/
  396. BOOL PASCAL
  397. LV_OnNotify(PLVV plvv, HWND hdlg, NMHDR FAR *pnm)
  398. {
  399. switch (pnm->idFrom) {
  400. case 0: /* Property sheet */
  401. switch (pnm->code) {
  402. case PSN_APPLY:
  403. plvv->OnApply(hdlg);
  404. break;
  405. }
  406. break;
  407. case IDC_LISTVIEW: /* List view */
  408. {
  409. HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
  410. switch (pnm->code) {
  411. case NM_CLICK:
  412. case NM_DBLCLK:
  413. LV_OnNotify_OnClick(plvv, hwnd, pnm);
  414. break;
  415. case LVN_KEYDOWN:
  416. LV_OnNotify_OnKeyDown(plvv, hwnd, (LV_KEYDOWN *)pnm);
  417. break;
  418. case LVN_BEGINLABELEDIT:
  419. return LV_OnNotify_OnBeginLabelEdit(plvv, hdlg, hwnd);
  420. case LVN_ENDLABELEDIT:
  421. LV_OnNotify_OnEndLabelEdit(plvv, hwnd, (LV_DISPINFO *)pnm);
  422. break;
  423. case LVN_ITEMCHANGED:
  424. LV_OnNotify_OnItemChanged(plvv, hwnd, (NM_LISTVIEW *)pnm);
  425. break;
  426. }
  427. }
  428. break;
  429. }
  430. return 0;
  431. }
  432. /*****************************************************************************
  433. *
  434. * LV_OnSettingChange
  435. *
  436. *****************************************************************************/
  437. void PASCAL
  438. LV_OnSettingChange(PLVV plvv, HWND hdlg, WPARAM wp, LPARAM lp)
  439. {
  440. HWND hwnd = GetDlgItem(hdlg, IDC_LISTVIEW);
  441. SendMessage(hwnd, WM_SETTINGCHANGE, wp, lp);
  442. if (wp == SPI_SETNONCLIENTMETRICS) {
  443. /* If we have icons, then go rebuild them. */
  444. if (plvv->GetIcon) {
  445. int iItem;
  446. int cItem = ListView_GetItemCount(hwnd);
  447. for (iItem = 0; iItem < cItem; iItem++) {
  448. LV_ITEM lvi;
  449. Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_PARAM);
  450. lvi.iImage = plvv->GetIcon(lvi.lParam);
  451. lvi.mask |= LVIF_IMAGE;
  452. ListView_SetItem(hwnd, &lvi);
  453. }
  454. }
  455. /* In case the scrollbars changed size, resize to accomodate */
  456. LV_ResizeReportColumn(hwnd);
  457. /*
  458. * HACK AROUND BUG IN COMCTL32.DLL - Explicitly hide and show
  459. * the window. This tickles report view into recalculating
  460. * its scrollbars.
  461. */
  462. ShowWindow(hwnd, SW_HIDE);
  463. ShowWindow(hwnd, SW_SHOW);
  464. }
  465. /*
  466. * Note: Do not need to handle WM_SETICONTITLELOGFONT because we
  467. * are in a dialog and therefore will use the dialog font, not the
  468. * icon title LOGFONT.
  469. */
  470. }
  471. /*****************************************************************************
  472. *
  473. * The common listview window procedure.
  474. *
  475. *****************************************************************************/
  476. BOOL EXPORT
  477. LV_DlgProc(PLVV plvv, HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
  478. {
  479. switch (wm) {
  480. case WM_INITDIALOG:
  481. return LV_OnInitDialog(plvv, hdlg);
  482. case WM_COMMAND:
  483. return LV_OnCommand(plvv, hdlg,
  484. (int)GET_WM_COMMAND_ID(wParam, lParam),
  485. (UINT)GET_WM_COMMAND_CMD(wParam, lParam));
  486. case WM_HELP: Common_OnHelp(lParam, plvv->pdwHelp); break;
  487. case WM_NOTIFY:
  488. return LV_OnNotify(plvv, hdlg, (NMHDR FAR *)lParam);
  489. case WM_SYSCOLORCHANGE:
  490. FORWARD_WM_SYSCOLORCHANGE(GetDlgItem(hdlg, IDC_LISTVIEW), SendMessage);
  491. break;
  492. case WM_DESTROY:
  493. if (plvv->OnDestroy) plvv->OnDestroy(hdlg);
  494. break;
  495. case WM_CONTEXTMENU:
  496. LV_OnContextMenu(plvv, hdlg, (HWND)wParam, lParam);
  497. break;
  498. case WM_SETTINGCHANGE:
  499. LV_OnSettingChange(plvv, hdlg, wParam, lParam);
  500. break;
  501. default: return 0; /* Unhandled */
  502. }
  503. return 1; /* Handled */
  504. }