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.

492 lines
16 KiB

  1. #include "shellprv.h"
  2. #include "defviewp.h"
  3. #include "ids.h"
  4. CColumnDlg::CColumnDlg(CDefView *pdsv) :
  5. _pdsv(pdsv), _bChanged(FALSE), _pdwOrder(NULL), _pWidths(NULL), _bLoaded(FALSE), _bUpdating(FALSE), _ppui(NULL)
  6. {
  7. _cColumns = _pdsv->_vs.GetColumnCount();
  8. }
  9. CColumnDlg::~CColumnDlg()
  10. {
  11. if (_pdwOrder)
  12. LocalFree(_pdwOrder);
  13. if (_pWidths)
  14. LocalFree(_pWidths);
  15. if (_ppui)
  16. _ppui->Release();
  17. }
  18. HRESULT CColumnDlg::ShowDialog(HWND hwnd)
  19. {
  20. _bChanged = FALSE; // We are on the stack, so no zero allocator
  21. _pdwOrder = (UINT *) LocalAlloc(LPTR, sizeof(*_pdwOrder) * _cColumns); // total columns
  22. _pWidths = (int *) LocalAlloc(LPTR, sizeof(*_pWidths) * _cColumns); // total columns
  23. if (_pdwOrder && _pWidths)
  24. {
  25. DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_COLUMN_SETTINGS), hwnd, s_DlgProc, (LPARAM)this);
  26. return S_OK;
  27. }
  28. return E_OUTOFMEMORY;
  29. }
  30. // Remember, each column is identified in 3 ways...
  31. // 1. A 'real' column number, the ordinal out of all possible columns
  32. // 2. A 'visible' column number, the index to this column in the listview
  33. // 3. A 'column order #', the position in the header's columnorderarray
  34. void CColumnDlg::_OnInitDlg()
  35. {
  36. // Fill in order array with visible columns, and set up inverse table
  37. UINT cVisible = _pdsv->_RealToVisibleCol(-1) + 1; // count
  38. ListView_GetColumnOrderArray(_pdsv->_hwndListview, cVisible, _pdwOrder);
  39. UINT *pOrderInverse = (UINT *)LocalAlloc(LPTR, sizeof(*pOrderInverse) * cVisible);
  40. if (pOrderInverse)
  41. {
  42. for (UINT i = 0; i < cVisible; i++)
  43. pOrderInverse[_pdwOrder[i]] = i;
  44. _hwndLVAll = GetDlgItem(_hdlg, IDC_COL_LVALL);
  45. ListView_SetExtendedListViewStyle(_hwndLVAll, LVS_EX_CHECKBOXES);
  46. LV_COLUMN lvc = {0};
  47. lvc.mask = (LVCF_FMT | LVCF_SUBITEM);
  48. lvc.fmt = LVCFMT_LEFT;
  49. ListView_InsertColumn(_hwndLVAll, 0, &lvc);
  50. LV_ITEM lvi = {0};
  51. lvi.mask = LVIF_TEXT;
  52. // Add entry for each column (except non-UI columns)
  53. for (i = 0; i < (int)_cColumns; i++)
  54. {
  55. if (!_pdsv->_IsColumnHidden(i)) // Don't put in entries for hidden columns
  56. {
  57. lvi.iItem = i;
  58. lvi.pszText = LPSTR_TEXTCALLBACK;
  59. ListView_InsertItem(_hwndLVAll, &lvi);
  60. }
  61. }
  62. lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
  63. // set the visible columns
  64. for (i = 0; i < (int) cVisible; i++)
  65. {
  66. UINT iReal = _pdsv->_VisibleToRealCol(i);
  67. lvi.pszText = _pdsv->_vs.GetColumnName(iReal);
  68. lvi.state = INDEXTOSTATEIMAGEMASK(_pdsv->_IsDetailsColumn(iReal) ? 2 : 1); // on check mark (or off for tileview columns)
  69. lvi.stateMask = LVIS_STATEIMAGEMASK;
  70. lvi.lParam = iReal; // store the real col index in the lParam
  71. lvi.iItem = pOrderInverse[i];
  72. ListView_SetItem(_hwndLVAll, &lvi);
  73. // Get the column width from the view's listview
  74. _pWidths[iReal] = ListView_GetColumnWidth(_pdsv->_hwndListview, i);
  75. }
  76. UINT iItem = cVisible;
  77. for (i = 0; i < (int)_cColumns; i++)
  78. {
  79. if (!_pdsv->_IsColumnInListView(i) && !_pdsv->_IsColumnHidden(i))
  80. {
  81. lvi.pszText = _pdsv->_vs.GetColumnName(i);
  82. lvi.state = INDEXTOSTATEIMAGEMASK(1); // off check mark
  83. lvi.stateMask = LVIS_STATEIMAGEMASK;
  84. lvi.lParam = i;
  85. lvi.iItem = iItem;
  86. ListView_SetItem(_hwndLVAll, &lvi);
  87. iItem++;
  88. // get the default width we've got saved away
  89. _pWidths[i] = _pdsv->_vs.GetColumnCharCount(i) * _pdsv->_cxChar;
  90. }
  91. }
  92. // set the size properly
  93. ListView_SetColumnWidth(_hwndLVAll, 0, LVSCW_AUTOSIZE);
  94. ListView_SetItemState(_hwndLVAll, 0, LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED);
  95. LocalFree(pOrderInverse);
  96. _bLoaded = TRUE;
  97. }
  98. SendDlgItemMessage(_hdlg, IDC_COL_WIDTH, EM_LIMITTEXT, 3, 0); // 3 digits
  99. }
  100. #define SWAP(x,y) {(x) ^= (y); (y) ^= (x); (x) ^= (y);}
  101. void CColumnDlg::_MoveItem(int iDelta)
  102. {
  103. int i = ListView_GetSelectionMark(_hwndLVAll);
  104. if (i != -1)
  105. {
  106. int iNew = i + iDelta;
  107. if (iNew >= 0 && iNew <= (ListView_GetItemCount(_hwndLVAll) - 1))
  108. {
  109. LV_ITEM lvi = {0}, lvi2 = {0};
  110. TCHAR szTmp1[MAX_COLUMN_NAME_LEN], szTmp2[MAX_COLUMN_NAME_LEN];
  111. _bChanged = TRUE;
  112. _bUpdating = TRUE;
  113. lvi.iItem = i;
  114. lvi.pszText = szTmp1;
  115. lvi.cchTextMax = ARRAYSIZE(szTmp1);
  116. lvi.stateMask = LVIS_STATEIMAGEMASK;
  117. lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
  118. lvi2.iItem = iNew;
  119. lvi2.pszText = szTmp2;
  120. lvi2.cchTextMax = ARRAYSIZE(szTmp2);
  121. lvi2.stateMask = LVIS_STATEIMAGEMASK;
  122. lvi2.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
  123. ListView_GetItem(_hwndLVAll, &lvi);
  124. ListView_GetItem(_hwndLVAll, &lvi2);
  125. SWAP(lvi.iItem, lvi2.iItem);
  126. ListView_SetItem(_hwndLVAll, &lvi);
  127. ListView_SetItem(_hwndLVAll, &lvi2);
  128. _bUpdating = FALSE;
  129. // update selection
  130. ListView_SetSelectionMark(_hwndLVAll, iNew);
  131. ListView_SetItemState(_hwndLVAll, iNew , LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED);
  132. // HACK: SetItemState sends notifications for i, iNew, then i again.
  133. // we need to call it twice in a row, so _UpdateDlgButtons will get the right item
  134. ListView_SetItemState(_hwndLVAll, iNew , LVIS_FOCUSED|LVIS_SELECTED, LVIS_FOCUSED|LVIS_SELECTED);
  135. return;
  136. }
  137. }
  138. TraceMsg(TF_WARNING, "ccd.mi couldn't move %d to %d",i, i+iDelta);
  139. MessageBeep(MB_ICONEXCLAMATION);
  140. }
  141. BOOL CColumnDlg::_SaveState()
  142. {
  143. // Check order
  144. if (_bChanged)
  145. {
  146. int iOrderIndex = 0;
  147. LV_ITEM lvi = {0};
  148. lvi.stateMask = LVIS_STATEIMAGEMASK;
  149. lvi.mask = LVIF_PARAM | LVIF_STATE;
  150. int cItems = ListView_GetItemCount(_hwndLVAll);
  151. for (int i = 0; i < cItems; i++)
  152. {
  153. lvi.iItem = i;
  154. ListView_GetItem(_hwndLVAll, &lvi);
  155. // toggle it, if the state in the dialog doesn't match the listview state
  156. if (BOOLIFY(ListView_GetCheckState(_hwndLVAll, i)) != BOOLIFY(_pdsv->_IsDetailsColumn((UINT)lvi.lParam)))
  157. {
  158. _pdsv->_HandleColumnToggle((UINT)lvi.lParam, FALSE);
  159. }
  160. if (_pdsv->_IsColumnInListView((UINT)lvi.lParam))
  161. _pdwOrder[iOrderIndex++] = (UINT)lvi.lParam; // incorrectly store real (not vis) col #, fix up below
  162. }
  163. // must be in a separate loop. (can't map real to visible, if we aren't done setting visible)
  164. for (i = 0; i < iOrderIndex; i++)
  165. {
  166. UINT iReal = _pdwOrder[i];
  167. _pdwOrder[i] = _pdsv->_RealToVisibleCol(iReal);
  168. if (_pWidths[iReal] < 0) // negative width means they edited it
  169. ListView_SetColumnWidth(_pdsv->_hwndListview, _pdwOrder[i], -_pWidths[iReal]);
  170. }
  171. ListView_SetColumnOrderArray(_pdsv->_hwndListview, iOrderIndex, _pdwOrder);
  172. // kick the listview into repainting everything
  173. InvalidateRect(_pdsv->_hwndListview, NULL, TRUE);
  174. _bChanged = FALSE;
  175. }
  176. return !_bChanged;
  177. }
  178. BOOL EnableDlgItem(HWND hdlg, UINT idc, BOOL f)
  179. {
  180. return EnableWindow(GetDlgItem(hdlg, idc), f);
  181. }
  182. void CColumnDlg::_UpdateDlgButtons(NMLISTVIEW *pnmlv)
  183. {
  184. BOOL bChecked, bOldUpdateState = _bUpdating;
  185. int iItem = ListView_GetSelectionMark(_hwndLVAll);
  186. // to disable checking
  187. _bUpdating = TRUE;
  188. if (pnmlv->uNewState & LVIS_STATEIMAGEMASK)
  189. bChecked = (pnmlv->uNewState & LVIS_STATEIMAGEMASK) == (UINT)INDEXTOSTATEIMAGEMASK(2);
  190. else
  191. bChecked = ListView_GetCheckState(_hwndLVAll, pnmlv->iItem);
  192. EnableDlgItem(_hdlg, IDC_COL_UP, pnmlv->iItem > 0);
  193. EnableDlgItem(_hdlg, IDC_COL_DOWN, pnmlv->iItem < (int)_cColumns - 1);
  194. EnableDlgItem(_hdlg, IDC_COL_SHOW, !bChecked && (pnmlv->lParam != 0));
  195. EnableDlgItem(_hdlg, IDC_COL_HIDE, bChecked && (pnmlv->lParam != 0));
  196. // update the width edit box
  197. int iWidth = _pWidths[pnmlv->lParam];
  198. if (iWidth < 0)
  199. iWidth = -iWidth; // we store negative values to track if it changed or not
  200. SetDlgItemInt(_hdlg, IDC_COL_WIDTH, iWidth, TRUE);
  201. _bUpdating = bOldUpdateState;
  202. }
  203. BOOL_PTR CALLBACK CColumnDlg::s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  204. {
  205. CColumnDlg *pcd = (CColumnDlg*) GetWindowLongPtr(hdlg, DWLP_USER);
  206. if (uMsg == WM_INITDIALOG)
  207. {
  208. pcd = (CColumnDlg *) lParam;
  209. pcd->_hdlg = hdlg;
  210. SetWindowLongPtr(hdlg, DWLP_USER, (LONG_PTR) pcd);
  211. }
  212. return pcd ? pcd->DlgProc(uMsg, wParam, lParam) : FALSE;
  213. }
  214. HRESULT CColumnDlg::_GetPropertyUI(IPropertyUI **pppui)
  215. {
  216. if (!_ppui)
  217. SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &_ppui));
  218. return _ppui ? _ppui->QueryInterface(IID_PPV_ARG(IPropertyUI, pppui)) : E_NOTIMPL;
  219. }
  220. UINT CColumnDlg::_HelpIDForItem(int iItem, LPTSTR pszHelpFile, UINT cch)
  221. {
  222. UINT uHelpID = 0;
  223. *pszHelpFile = 0;
  224. LV_ITEM lvi = {0};
  225. lvi.iItem = iItem;
  226. lvi.mask = LVIF_PARAM;
  227. if (ListView_GetItem(_hwndLVAll, &lvi))
  228. {
  229. IShellFolder2 *psf;
  230. if (SUCCEEDED(_pdsv->GetFolder(IID_PPV_ARG(IShellFolder2, &psf))))
  231. {
  232. SHCOLUMNID scid;
  233. if (SUCCEEDED(psf->MapColumnToSCID(lvi.lParam, &scid)))
  234. {
  235. IPropertyUI *ppui;
  236. if (SUCCEEDED(_GetPropertyUI(&ppui)))
  237. {
  238. ppui->GetHelpInfo(scid.fmtid, scid.pid, pszHelpFile, cch, &uHelpID);
  239. ppui->Release();
  240. }
  241. }
  242. psf->Release();
  243. }
  244. }
  245. return uHelpID; // IDH_ values
  246. }
  247. const static DWORD c_rgColumnDlgHelpIDs[] =
  248. {
  249. IDC_COL_UP, 1,
  250. IDC_COL_DOWN, 1,
  251. IDC_COL_SHOW, 1,
  252. IDC_COL_HIDE, 1,
  253. IDC_COL_WIDTH, 10055,
  254. IDC_COL_WIDTH_TEXT, 10055,
  255. 0, 0
  256. };
  257. BOOL_PTR CColumnDlg::DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
  258. {
  259. switch (uMsg)
  260. {
  261. case WM_INITDIALOG:
  262. _OnInitDlg();
  263. break;
  264. case WM_COMMAND:
  265. switch (LOWORD(wParam))
  266. {
  267. case IDC_COL_UP:
  268. _MoveItem(- 1);
  269. SetFocus(_hwndLVAll);
  270. break;
  271. case IDC_COL_DOWN:
  272. _MoveItem(+ 1);
  273. SetFocus(_hwndLVAll);
  274. break;
  275. case IDC_COL_SHOW:
  276. case IDC_COL_HIDE:
  277. {
  278. UINT iItem = ListView_GetSelectionMark(_hwndLVAll);
  279. ListView_SetCheckState(_hwndLVAll, iItem, LOWORD(wParam) == IDC_COL_SHOW);
  280. SetFocus(_hwndLVAll);
  281. break;
  282. }
  283. case IDC_COL_WIDTH:
  284. if (HIWORD(wParam) == EN_CHANGE && !_bUpdating)
  285. {
  286. LV_ITEM lvi = {0};
  287. lvi.iItem = ListView_GetSelectionMark(_hwndLVAll);
  288. lvi.mask = LVIF_PARAM;
  289. ListView_GetItem(_hwndLVAll, &lvi);
  290. _pWidths[lvi.lParam] = - (int)GetDlgItemInt(_hdlg, IDC_COL_WIDTH, NULL, FALSE);
  291. _bChanged = TRUE;
  292. }
  293. break;
  294. case IDOK:
  295. _SaveState();
  296. // fall through
  297. case IDCANCEL:
  298. return EndDialog(_hdlg, TRUE);
  299. }
  300. break;
  301. case WM_NOTIFY:
  302. if (_bLoaded && !_bUpdating)
  303. {
  304. NMLISTVIEW * pnmlv = (NMLISTVIEW *)lParam;
  305. switch (((NMHDR *)lParam)->code)
  306. {
  307. case LVN_ITEMCHANGING:
  308. // fix up the buttons & such here
  309. if (pnmlv->uChanged & LVIF_STATE)
  310. _UpdateDlgButtons(pnmlv);
  311. // We want to reject turning off the name column
  312. // it both doesn't make sense to have no name column, and defview assumes there will be one
  313. if (pnmlv->lParam == 0 &&
  314. (pnmlv->uNewState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1))
  315. {
  316. MessageBeep(MB_ICONEXCLAMATION);
  317. SetWindowLongPtr(_hdlg, DWLP_MSGRESULT, TRUE);
  318. return TRUE;
  319. }
  320. else
  321. {
  322. // if something besides focus changed
  323. if ((pnmlv->uChanged & ~LVIF_STATE) ||
  324. ((pnmlv->uNewState & LVIS_STATEIMAGEMASK) != (pnmlv->uOldState & LVIS_STATEIMAGEMASK)))
  325. _bChanged = TRUE;
  326. }
  327. break;
  328. case NM_DBLCLK:
  329. {
  330. BOOL bCheck = ListView_GetCheckState(_hwndLVAll, pnmlv->iItem);
  331. ListView_SetCheckState(_hwndLVAll, pnmlv->iItem, !bCheck);
  332. }
  333. break;
  334. }
  335. }
  336. break;
  337. case WM_SYSCOLORCHANGE:
  338. SendMessage(_hwndLVAll, uMsg, wParam, lParam);
  339. break;
  340. case WM_HELP: // F1
  341. {
  342. HELPINFO *phi = (HELPINFO *)lParam;
  343. //if the help is for one of the command buttons then call winhelp
  344. if (phi->iCtrlId == IDC_COL_LVALL)
  345. {
  346. //Help is for the tree item so we need to do some special processing
  347. int iItem;
  348. // Is this help invoked throught F1 key
  349. if (GetAsyncKeyState(VK_F1) < 0)
  350. {
  351. iItem = ListView_GetSelectionMark(_hwndLVAll);
  352. }
  353. else
  354. {
  355. LV_HITTESTINFO info;
  356. info.pt = phi->MousePos;
  357. ScreenToClient(_hwndLVAll, &info.pt);
  358. iItem = ListView_HitTest(_hwndLVAll, &info);
  359. }
  360. if (iItem >= 0)
  361. {
  362. DWORD mapIDCToIDH[4] = {0};
  363. TCHAR szFile[MAX_PATH];
  364. mapIDCToIDH[0] = phi->iCtrlId;
  365. mapIDCToIDH[1] = _HelpIDForItem(iItem, szFile, ARRAYSIZE(szFile));
  366. WinHelp((HWND)((HELPINFO *)lParam)->hItemHandle, szFile[0] ? szFile : NULL,
  367. HELP_WM_HELP, (DWORD_PTR)(LPSTR)mapIDCToIDH);
  368. }
  369. }
  370. else
  371. {
  372. WinHelp((HWND)((HELPINFO *)lParam)->hItemHandle, TEXT(SHELL_HLP),
  373. HELP_WM_HELP, (DWORD_PTR)(LPSTR)c_rgColumnDlgHelpIDs);
  374. }
  375. break;
  376. }
  377. case WM_CONTEXTMENU:
  378. {
  379. int iItem;
  380. if ((LPARAM)-1 == lParam)
  381. {
  382. iItem = ListView_GetSelectionMark(_hwndLVAll);
  383. }
  384. else
  385. {
  386. LV_HITTESTINFO info;
  387. info.pt.x = GET_X_LPARAM(lParam);
  388. info.pt.y = GET_Y_LPARAM(lParam);
  389. ScreenToClient(_hwndLVAll, &info.pt);
  390. iItem = ListView_HitTest(_hwndLVAll, &info);
  391. }
  392. if (iItem >= 0)
  393. {
  394. DWORD mapIDCToIDH[4] = {0};
  395. TCHAR szFile[MAX_PATH];
  396. mapIDCToIDH[0] = IDC_COL_LVALL;
  397. mapIDCToIDH[1] = _HelpIDForItem(iItem, szFile, ARRAYSIZE(szFile)); // IDH_ values
  398. WinHelp((HWND)wParam, szFile[0] ? szFile : NULL, HELP_CONTEXTMENU, (DWORD_PTR)(LPSTR)mapIDCToIDH);
  399. }
  400. break;
  401. }
  402. default:
  403. return FALSE;
  404. }
  405. return TRUE;
  406. }