Leaked source code of windows server 2003
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.

1444 lines
48 KiB

  1. #include "pch.hxx"
  2. #include <shlwapi.h>
  3. #include "resource.h"
  4. #include "msoeobj.h"
  5. #include "strconst.h"
  6. #include "columns.h"
  7. #include "error.h"
  8. #include "imagelst.h"
  9. #include "msgview.h"
  10. #include "shlwapip.h"
  11. #include "goptions.h"
  12. #include "demand.h"
  13. #include "menures.h"
  14. const COLUMN_DATA c_rgColumnData[COLUMN_MAX] =
  15. {
  16. /* COLUMN_TO */ { idsTo, 155, LVCFMT_LEFT, 0 },
  17. /* COLUMN_FROM */ { idsFrom, 155, LVCFMT_LEFT, 0 },
  18. /* COLUMN_SUBJECT */ { idsSubject, 280, LVCFMT_LEFT, 0 },
  19. /* COLUMN_RECEIVED */ { idsReceived, 110, LVCFMT_LEFT, 0 },
  20. /* COLUMN_SENT */ { idsSent, 110, LVCFMT_LEFT, 0 },
  21. /* COLUMN_SIZE */ { idsSize, 75, LVCFMT_RIGHT, 0 },
  22. /* COLUMN_FOLDER */ { idsFolder, 155, LVCFMT_LEFT, 0 },
  23. /* COLUMN_TOTAL */ { idsTotal, 75, LVCFMT_RIGHT, 0 },
  24. /* COLUMN_UNREAD */ { idsUnread, 75, LVCFMT_RIGHT, 0 },
  25. /* COLUMN_NEW */ { idsNew, 75, LVCFMT_RIGHT, 0 },
  26. /* COLUMN_DESCRIPTION */ { idsDescription, 250, LVCFMT_LEFT, 0 },
  27. /* COLUMN_LAST_UPDATED */ { idsLastUpdated, 155, LVCFMT_LEFT, 0 },
  28. /* COLUMN_WASTED_SPACE */ { idsWastedSpace, 75, LVCFMT_RIGHT, 0 },
  29. /* COLUMN_ACCOUNT */ { idsAccount, 155, LVCFMT_LEFT, 0 },
  30. /* COLUMN_LINES */ { idsColLines, 75, LVCFMT_RIGHT, 0 },
  31. /* COLUMN_PRIORITY */ { idsColPriority, 19, LVCFMT_LEFT, iiconHeaderPri },
  32. /* COLUMN_ATTACHMENT */ { idsColAttach, 22, LVCFMT_LEFT, iiconHeaderAttach },
  33. /* COLUMN_SHOW */ { idsShow, 39, LVCFMT_LEFT, IICON_TEXTHDR },
  34. /* COLUMN_DOWNLOAD */ { idsColDownload, 155, LVCFMT_LEFT, 0 },
  35. /* COLUMN_NEWSGROUP */ { idsNewsgroup, 155, LVCFMT_LEFT, 0 },
  36. /* COLUMN_FLAG */ { idsFlag, 25, LVCFMT_LEFT, iiconHeaderFlag },
  37. /* COLUMN_SUBSCRIBE */ { idsSubscribe, 59, LVCFMT_LEFT, IICON_TEXTHDR },
  38. /* COLUMN_DOWNLOADMSG */ { idsColDownloadMsg, 23, LVCFMT_LEFT, iiconHeaderDownload },
  39. /* COLUMN_THREADSTATE */ { idsColThreadState, 29, LVCFMT_LEFT, iiconHeaderThreadState }
  40. };
  41. const COLUMN_SET c_rgColDefaultMail[] =
  42. {
  43. { COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  44. { COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  45. { COLUMN_FLAG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  46. { COLUMN_FROM, COLFLAG_VISIBLE, -1 },
  47. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  48. { COLUMN_RECEIVED, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  49. { COLUMN_ACCOUNT, 0, -1 },
  50. { COLUMN_SIZE, 0, -1 },
  51. { COLUMN_SENT, 0, -1 },
  52. { COLUMN_TO, 0, -1 },
  53. { COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
  54. };
  55. const COLUMN_SET c_rgColDefaultOutbox[] =
  56. {
  57. { COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  58. { COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  59. { COLUMN_TO, COLFLAG_VISIBLE, -1 },
  60. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  61. { COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  62. { COLUMN_ACCOUNT, COLFLAG_VISIBLE, -1 },
  63. { COLUMN_FROM, 0, -1 },
  64. { COLUMN_SIZE, 0, -1 },
  65. { COLUMN_RECEIVED, 0, -1 },
  66. { COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
  67. { COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
  68. };
  69. const COLUMN_SET c_rgColDefaultNews[] =
  70. {
  71. { COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  72. { COLUMN_DOWNLOADMSG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  73. { COLUMN_THREADSTATE, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  74. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  75. { COLUMN_FROM, COLFLAG_VISIBLE, -1 },
  76. { COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  77. { COLUMN_SIZE, COLFLAG_VISIBLE, -1 },
  78. { COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
  79. { COLUMN_LINES, 0, -1 }
  80. };
  81. const COLUMN_SET c_rgColDefaultIMAP[] =
  82. {
  83. { COLUMN_PRIORITY, COLFLAG_VISIBLE, -1 },
  84. { COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  85. { COLUMN_FLAG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  86. { COLUMN_DOWNLOADMSG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  87. { COLUMN_FROM, COLFLAG_VISIBLE, -1 },
  88. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  89. { COLUMN_RECEIVED, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  90. { COLUMN_SENT, 0, -1 },
  91. { COLUMN_SIZE, 0, -1 },
  92. { COLUMN_TO, 0, -1 },
  93. { COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
  94. };
  95. const COLUMN_SET c_rgColDefaultIMAPOutbox[] =
  96. {
  97. { COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  98. { COLUMN_TO, COLFLAG_VISIBLE, -1 },
  99. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  100. { COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  101. { COLUMN_ACCOUNT, COLFLAG_VISIBLE, -1 },
  102. { COLUMN_FROM, 0, -1 },
  103. { COLUMN_SIZE, 0, -1 },
  104. { COLUMN_RECEIVED, 0, -1 },
  105. { COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
  106. { COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 },
  107. { COLUMN_DOWNLOADMSG, COLFLAG_FIXED_WIDTH, -1 }
  108. };
  109. const COLUMN_SET c_rgColDefaultFind[] =
  110. {
  111. { COLUMN_PRIORITY, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  112. { COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  113. { COLUMN_FLAG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  114. { COLUMN_FROM, COLFLAG_VISIBLE, -1 },
  115. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  116. { COLUMN_RECEIVED, COLFLAG_VISIBLE, -1 },
  117. { COLUMN_FOLDER, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  118. { COLUMN_ACCOUNT, 0, -1 },
  119. { COLUMN_SENT, 0, -1 },
  120. { COLUMN_SIZE, 0, -1 },
  121. { COLUMN_TO, 0, -1 },
  122. { COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 },
  123. { COLUMN_LINES, 0, -1 }
  124. };
  125. const COLUMN_SET c_rgColDefaultNewsAccount[] =
  126. {
  127. { COLUMN_NEWSGROUP, COLFLAG_VISIBLE, -1 },
  128. { COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
  129. { COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
  130. { COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
  131. };
  132. const COLUMN_SET c_rgColDefaultIMAPAccount[] =
  133. {
  134. { COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
  135. { COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
  136. { COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
  137. { COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
  138. };
  139. const COLUMN_SET c_rgColDefaultLocalStore[] =
  140. {
  141. { COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
  142. { COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
  143. { COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
  144. };
  145. const COLUMN_SET c_rgColDefaultNewsSub[] =
  146. {
  147. { COLUMN_NEWSGROUP, COLFLAG_VISIBLE, -1 },
  148. { COLUMN_DESCRIPTION, COLFLAG_VISIBLE, -1 },
  149. };
  150. const COLUMN_SET c_rgColDefaultImapSub[] =
  151. {
  152. { COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
  153. };
  154. const COLUMN_SET c_rgColDefaultOffline[] =
  155. {
  156. { COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
  157. { COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
  158. };
  159. const COLUMN_SET c_rgColDefaultPickGrp[] =
  160. {
  161. { COLUMN_NEWSGROUP, COLFLAG_VISIBLE, -1 },
  162. };
  163. const COLUMN_SET c_rgColDefaultHTTPMail[] =
  164. {
  165. { COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  166. { COLUMN_DOWNLOADMSG, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  167. { COLUMN_FROM, COLFLAG_VISIBLE, -1 },
  168. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  169. { COLUMN_RECEIVED, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  170. { COLUMN_SIZE, 0, -1 },
  171. { COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 }
  172. };
  173. const COLUMN_SET c_rgColDefaultHTTPMailAccount[] =
  174. {
  175. { COLUMN_FOLDER, COLFLAG_VISIBLE, -1 },
  176. { COLUMN_UNREAD, COLFLAG_VISIBLE, -1 },
  177. { COLUMN_TOTAL, COLFLAG_VISIBLE, -1 },
  178. { COLUMN_DOWNLOAD, COLFLAG_VISIBLE, -1 },
  179. };
  180. const COLUMN_SET c_rgColDefaultHTTPMailOutbox[] =
  181. {
  182. { COLUMN_ATTACHMENT, COLFLAG_VISIBLE | COLFLAG_FIXED_WIDTH, -1 },
  183. { COLUMN_TO, COLFLAG_VISIBLE, -1 },
  184. { COLUMN_SUBJECT, COLFLAG_VISIBLE, -1 },
  185. { COLUMN_SENT, COLFLAG_VISIBLE | COLFLAG_SORT_ASCENDING, -1 },
  186. { COLUMN_ACCOUNT, COLFLAG_VISIBLE, -1 },
  187. { COLUMN_FROM, 0, -1 },
  188. { COLUMN_SIZE, 0, -1 },
  189. { COLUMN_RECEIVED, 0, -1 },
  190. { COLUMN_FLAG, COLFLAG_FIXED_WIDTH, -1 },
  191. { COLUMN_THREADSTATE, COLFLAG_FIXED_WIDTH, -1 },
  192. { COLUMN_DOWNLOADMSG, COLFLAG_FIXED_WIDTH, -1 }
  193. };
  194. // NOTE - Keep this in the same order as COLUMN_SET_TYPE enumeration.
  195. const COLUMN_SET_INFO c_rgColumnSetInfo[COLUMN_SET_MAX] =
  196. {
  197. { COLUMN_SET_MAIL, ARRAYSIZE(c_rgColDefaultMail), c_rgColDefaultMail, c_szRegMailColsIn, TRUE },
  198. { COLUMN_SET_OUTBOX, ARRAYSIZE(c_rgColDefaultOutbox), c_rgColDefaultOutbox, c_szRegMailColsOut, TRUE },
  199. { COLUMN_SET_NEWS, ARRAYSIZE(c_rgColDefaultNews), c_rgColDefaultNews, c_szRegNewsCols, TRUE },
  200. { COLUMN_SET_IMAP, ARRAYSIZE(c_rgColDefaultIMAP), c_rgColDefaultIMAP, c_szRegIMAPCols, TRUE },
  201. { COLUMN_SET_IMAP_OUTBOX, ARRAYSIZE(c_rgColDefaultIMAPOutbox), c_rgColDefaultIMAPOutbox, c_szRegIMAPColsOut, TRUE },
  202. { COLUMN_SET_FIND, ARRAYSIZE(c_rgColDefaultFind), c_rgColDefaultFind, c_szRegFindPopCols, TRUE },
  203. { COLUMN_SET_NEWS_ACCOUNT, ARRAYSIZE(c_rgColDefaultNewsAccount), c_rgColDefaultNewsAccount, c_szRegAccountNewsCols, FALSE },
  204. { COLUMN_SET_IMAP_ACCOUNT, ARRAYSIZE(c_rgColDefaultIMAPAccount), c_rgColDefaultIMAPAccount, c_szRegAccountIMAPCols, FALSE },
  205. { COLUMN_SET_LOCAL_STORE, ARRAYSIZE(c_rgColDefaultLocalStore), c_rgColDefaultLocalStore, c_szRegLocalStoreCols, FALSE },
  206. { COLUMN_SET_NEWS_SUB, ARRAYSIZE(c_rgColDefaultNewsSub), c_rgColDefaultNewsSub, c_szRegNewsSubCols, FALSE },
  207. { COLUMN_SET_IMAP_SUB, ARRAYSIZE(c_rgColDefaultImapSub), c_rgColDefaultImapSub, c_szRegImapSubCols, FALSE },
  208. { COLUMN_SET_OFFLINE, ARRAYSIZE(c_rgColDefaultOffline), c_rgColDefaultOffline, c_szRegOfflineCols, FALSE },
  209. { COLUMN_SET_PICKGRP, ARRAYSIZE(c_rgColDefaultPickGrp), c_rgColDefaultPickGrp, NULL, FALSE },
  210. { COLUMN_SET_HTTPMAIL, ARRAYSIZE(c_rgColDefaultHTTPMail), c_rgColDefaultHTTPMail, c_szRegHTTPMailCols, TRUE },
  211. { COLUMN_SET_HTTPMAIL_ACCOUNT, ARRAYSIZE(c_rgColDefaultHTTPMailAccount), c_rgColDefaultHTTPMailAccount, c_szRegHTTPMailAccountCols, FALSE },
  212. { COLUMN_SET_HTTPMAIL_OUTBOX, ARRAYSIZE(c_rgColDefaultHTTPMailOutbox), c_rgColDefaultHTTPMailOutbox, c_szRegHTTPMailColsOut, TRUE },
  213. };
  214. /////////////////////////////////////////////////////////////////////////////
  215. // CColumns
  216. //
  217. CColumns::CColumns()
  218. {
  219. m_cRef = 1;
  220. m_fInitialized = FALSE;
  221. m_pColumnSet = NULL;
  222. m_cColumns = 0;
  223. m_idColumnSort = COLUMN_SUBJECT;
  224. m_fAscending = TRUE;
  225. }
  226. CColumns::~CColumns()
  227. {
  228. SafeMemFree(m_pColumnSet);
  229. }
  230. //
  231. // FUNCTION: CColumns::Init()
  232. //
  233. // PURPOSE: Initializes the class with the listview and column set type
  234. // that will be used later.
  235. //
  236. // PARAMETERS:
  237. // [in] hwndList - Handle of the ListView window that we will manage
  238. // columns for.
  239. // [in] type - Type of column set to apply to this window.
  240. //
  241. // RETURN VALUE:
  242. // S_OK - The data was groovy
  243. // E_INVALIDARG - The data was heinous
  244. //
  245. HRESULT CColumns::Initialize(HWND hwndList, COLUMN_SET_TYPE type)
  246. {
  247. // Verify what was given to us
  248. if (!IsWindow(hwndList))
  249. {
  250. AssertSz(!IsWindow(hwndList), "CColumns::Init() - Called with an invalid window handle.");
  251. return (E_INVALIDARG);
  252. }
  253. if (type >= COLUMN_SET_MAX)
  254. {
  255. AssertSz(type >= COLUMN_SET_MAX, "CColumns::Init() - Called with an invalid column set type.");
  256. return (E_INVALIDARG);
  257. }
  258. // Save the information for later
  259. m_wndList.Attach(hwndList);
  260. m_type = type;
  261. m_hwndHdr = ListView_GetHeader(m_wndList);
  262. m_fInitialized = TRUE;
  263. return (S_OK);
  264. }
  265. //
  266. // FUNCTION: CColumns::ApplyColumns()
  267. //
  268. // PURPOSE: Takes the current column set and applies it to the ListView
  269. // that was provided in the call to Init().
  270. //
  271. // RETURN VALUE:
  272. // HRESULT
  273. //
  274. HRESULT CColumns::ApplyColumns(COLUMN_LOAD_TYPE type, LPBYTE pb, DWORD cb)
  275. {
  276. HKEY hkey;
  277. DWORD cbSize, dwType;
  278. LPBYTE pbT = NULL;
  279. COLUMN_PERSIST_INFO *pInfo = (COLUMN_PERSIST_INFO *) pb;
  280. const COLUMN_SET *rgColumns = NULL;
  281. DWORD cColumns = 0;
  282. // Verify that we have been initialized first
  283. if (!m_fInitialized)
  284. {
  285. AssertSz(m_fInitialized, "CColumns::ApplyColumns() - Class has not yet been initialized.");
  286. return (E_UNEXPECTED);
  287. }
  288. // Double check the listview didn't go away
  289. Assert(IsWindow(m_wndList));
  290. // Check to see what we're supposed to do
  291. if (type == COLUMN_LOAD_REGISTRY)
  292. {
  293. Assert(pInfo == NULL);
  294. if (ERROR_SUCCESS == AthUserOpenKey(c_szRegPathColumns, KEY_READ, &hkey))
  295. {
  296. cbSize = 0;
  297. if (c_rgColumnSetInfo[m_type].pszRegValue != NULL &&
  298. ERROR_SUCCESS == RegQueryValueEx(hkey, c_rgColumnSetInfo[m_type].pszRegValue, NULL, &dwType, NULL, &cbSize) &&
  299. dwType == REG_BINARY &&
  300. cbSize > 0)
  301. {
  302. if (MemAlloc((void **)&pbT, cbSize))
  303. {
  304. if (ERROR_SUCCESS == RegQueryValueEx(hkey, c_rgColumnSetInfo[m_type].pszRegValue, NULL, &dwType, pbT, &cbSize))
  305. pInfo = (COLUMN_PERSIST_INFO *) pbT;
  306. }
  307. }
  308. RegCloseKey(hkey);
  309. }
  310. if (pInfo != NULL)
  311. type = COLUMN_LOAD_BUFFER;
  312. else
  313. type = COLUMN_LOAD_DEFAULT;
  314. }
  315. if (type == COLUMN_LOAD_BUFFER)
  316. {
  317. Assert(pInfo);
  318. if (pInfo->dwVersion == COLUMN_PERSIST_VERSION)
  319. {
  320. rgColumns = pInfo->rgColumns;
  321. cColumns = pInfo->cColumns;
  322. }
  323. else
  324. {
  325. // Do the default
  326. type = COLUMN_LOAD_DEFAULT;
  327. }
  328. }
  329. if (type == COLUMN_LOAD_DEFAULT)
  330. {
  331. // Verify some person didn't mess up the c_rgColumnSetInfo array.
  332. Assert(c_rgColumnSetInfo[m_type].type == m_type);
  333. // We couldn't load from the registry, so instead use the defaults.
  334. rgColumns = c_rgColumnSetInfo[m_type].rgColumns;
  335. cColumns = c_rgColumnSetInfo[m_type].cColumns;
  336. }
  337. // Update the listview to use these new columns
  338. _SetListViewColumns(rgColumns, cColumns);
  339. if (pbT != NULL)
  340. MemFree(pbT);
  341. return (S_OK);
  342. }
  343. HRESULT CColumns::Save(LPBYTE pBuffer, DWORD *pcb)
  344. {
  345. HRESULT hr = S_OK;
  346. COLUMN_PERSIST_INFO *pInfo;
  347. DWORD dwSize;
  348. // Collect the information needed to get a COLUMN_PERSIST_INFO struct put
  349. // together. First allocate a struct big enough.
  350. dwSize = sizeof(COLUMN_PERSIST_INFO) + (sizeof(COLUMN_SET) * (m_cColumns - 1));
  351. IF_NULLEXIT(pInfo = (COLUMN_PERSIST_INFO *) new BYTE[dwSize]);
  352. ZeroMemory( pInfo, dwSize);
  353. // Set the basic information
  354. pInfo->dwVersion = COLUMN_PERSIST_VERSION;
  355. pInfo->cColumns = m_cColumns;
  356. // We want to save the _ordered_ version of the columns
  357. DWORD rgOrder[COLUMN_MAX] = {0};
  358. // Get the count of columns in the header. Make sure that matches
  359. // what we think we have.
  360. #ifdef DEBUG
  361. DWORD cOrder;
  362. cOrder = Header_GetItemCount(m_hwndHdr);
  363. Assert(m_cColumns == cOrder);
  364. #endif
  365. // The columns might have been reordered by the user, so get the order
  366. // arrray from the ListView
  367. if (0 == (Header_GetOrderArray(m_hwndHdr, m_cColumns, rgOrder)))
  368. return (E_FAIL);
  369. // Now loop through out current column set and copy it to the structure
  370. COLUMN_SET *pColumnDst;
  371. DWORD iColumn;
  372. for (iColumn = 0, pColumnDst = pInfo->rgColumns; iColumn < m_cColumns; iColumn++, pColumnDst++)
  373. {
  374. Assert(rgOrder[iColumn] < m_cColumns);
  375. *pColumnDst = m_pColumnSet[rgOrder[iColumn]];
  376. if (pColumnDst->id == m_idColumnSort)
  377. {
  378. // Clear out any old flags
  379. pColumnDst->flags &= ~(COLFLAG_SORT_ASCENDING | COLFLAG_SORT_DESCENDING);
  380. // Add the new one
  381. pColumnDst->flags |= (m_fAscending ? COLFLAG_SORT_ASCENDING : COLFLAG_SORT_DESCENDING);
  382. }
  383. else
  384. {
  385. pColumnDst->flags &= ~(COLFLAG_SORT_ASCENDING | COLFLAG_SORT_DESCENDING);
  386. }
  387. }
  388. if (pBuffer == NULL)
  389. {
  390. Assert(pcb == NULL);
  391. LRESULT lRes = AthUserSetValue(c_szRegPathColumns, c_rgColumnSetInfo[m_type].pszRegValue, REG_BINARY, (LPBYTE) pInfo, dwSize);
  392. Assert( lRes == ERROR_SUCCESS);
  393. if (lRes != ERROR_SUCCESS)
  394. {
  395. hr = E_FAIL;
  396. }
  397. }
  398. else if (dwSize <= *pcb)
  399. {
  400. CopyMemory(pBuffer, (LPBYTE) pInfo, dwSize);
  401. *pcb = dwSize;
  402. }
  403. else
  404. {
  405. hr = E_INVALIDARG;
  406. }
  407. exit:
  408. SafeDelete(pInfo);
  409. return hr;
  410. }
  411. //
  412. // FUNCTION: CColumns::_SetListViewColumns()
  413. //
  414. // PURPOSE: Takes the column set provided and inserts those columns into
  415. // the ListView.
  416. //
  417. // PARAMETERS:
  418. // [in] rgColumns - Array of columns to insert into the ListView
  419. // [in] cColumns - Number of columns in rgColumns
  420. //
  421. // RETURN VALUE:
  422. // HRESULT
  423. //
  424. HRESULT CColumns::_SetListViewColumns(const COLUMN_SET *rgColumns, DWORD cColumns)
  425. {
  426. LV_COLUMN lvc;
  427. TCHAR sz[CCHMAX_STRINGRES];
  428. // Set up the LV_COLUMN structure
  429. lvc.pszText = sz;
  430. // Remove any existing columns
  431. while (ListView_DeleteColumn(m_wndList, 0))
  432. ;
  433. // Reset this
  434. m_idColumnSort = COLUMN_MAX;
  435. // Loop through all of the columns in the provided rgColumns looking for
  436. // any that have an icon and is visible.
  437. //
  438. // We have to do this because the listview requires that column zero have
  439. // text. If the user doesn't want column zero to have text, ie attachment
  440. // column, then we insert that column as column 1, and use
  441. // ListView_SetColumnOrderArray later to make it appear as if column zero
  442. // was the image-only column. -- steveser
  443. DWORD iColumn;
  444. DWORD iColumnSkip = cColumns;
  445. DWORD iInsertPos = 0;
  446. const COLUMN_SET *pColumn;
  447. for (iColumn = 0, pColumn = rgColumns; iColumn < cColumns; iColumn++, pColumn++)
  448. {
  449. if ((0 == c_rgColumnData[pColumn->id].iIcon) && (pColumn->flags & COLFLAG_VISIBLE))
  450. {
  451. iColumnSkip = iColumn;
  452. // Insert this column into the ListView as column zero
  453. lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  454. lvc.iSubItem = 0;
  455. lvc.fmt = c_rgColumnData[pColumn->id].format;
  456. LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
  457. sz, ARRAYSIZE(sz));
  458. // If the column width provided is -1, then it hasn't been
  459. // customized yet so use the default.
  460. if (pColumn->cxWidth == -1)
  461. lvc.cx = c_rgColumnData[pColumn->id].cxWidth;
  462. else
  463. lvc.cx = pColumn->cxWidth;
  464. // Insert the column
  465. ListView_InsertColumn(m_wndList, 0, &lvc);
  466. // Up the count for the next column position
  467. iInsertPos++;
  468. // Check to see if this is the sort column
  469. if ((pColumn->flags & COLFLAG_SORT_ASCENDING) ||
  470. (pColumn->flags & COLFLAG_SORT_DESCENDING))
  471. {
  472. m_idColumnSort = pColumn->id;
  473. m_fAscending = COLFLAG_SORT_ASCENDING == (pColumn->flags & COLFLAG_SORT_ASCENDING);
  474. }
  475. // Bail out of this loop
  476. break;
  477. }
  478. }
  479. // Now insert the rest of the columns, skipping over the column we inserted
  480. // previously (stored in iColumnSkip).
  481. for (iColumn = 0, pColumn = rgColumns; iColumn < cColumns; iColumn++, pColumn++)
  482. {
  483. // If this column is visible and it's not the one we skipped over
  484. if ((pColumn->flags & COLFLAG_VISIBLE) && (iColumn != iColumnSkip))
  485. {
  486. // Figure out what the mask is and load the icon or string
  487. if (c_rgColumnData[pColumn->id].iIcon <= 0)
  488. {
  489. lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
  490. LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
  491. sz, ARRAYSIZE(sz));
  492. }
  493. else
  494. {
  495. lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_IMAGE | LVCF_SUBITEM;
  496. lvc.iImage = c_rgColumnData[pColumn->id].iIcon;
  497. }
  498. lvc.iSubItem = iInsertPos;
  499. lvc.fmt = c_rgColumnData[pColumn->id].format;
  500. // If the column width provided is -1, then it hasn't been
  501. // customized yet so use the default.
  502. if (pColumn->cxWidth == -1)
  503. lvc.cx = c_rgColumnData[pColumn->id].cxWidth;
  504. else
  505. lvc.cx = pColumn->cxWidth;
  506. // Check to see if this is the sort column
  507. if ((pColumn->flags & COLFLAG_SORT_ASCENDING) ||
  508. (pColumn->flags & COLFLAG_SORT_DESCENDING))
  509. {
  510. // Save the info
  511. m_idColumnSort = pColumn->id;
  512. m_fAscending = COLFLAG_SORT_ASCENDING == (pColumn->flags & COLFLAG_SORT_ASCENDING);
  513. }
  514. // Insert this column
  515. ListView_InsertColumn(m_wndList, iInsertPos, &lvc);
  516. iInsertPos++;
  517. }
  518. }
  519. // If we had to skip over a column, the we need to set the column order
  520. // array so it appears correctly to the user.
  521. if (iColumnSkip > 0 && iColumnSkip < cColumns)
  522. {
  523. DWORD cColumnOrder = 0;
  524. int rgOrder[COLUMN_MAX];
  525. // Add all of the columns to the order array in order up to iColumnSkip
  526. for (iColumn = 1; iColumn <= iColumnSkip; iColumn++)
  527. {
  528. if (rgColumns[iColumn].flags & COLFLAG_VISIBLE)
  529. rgOrder[cColumnOrder++] = iColumn;
  530. }
  531. // Add the skipped column
  532. rgOrder[cColumnOrder++] = 0;
  533. // Add the rest of the columns
  534. for (iColumn = iColumnSkip + 1; iColumn < cColumns; iColumn++)
  535. {
  536. if (rgColumns[iColumn].flags & COLFLAG_VISIBLE)
  537. rgOrder[cColumnOrder++] = iColumn;
  538. }
  539. // Update the ListView
  540. ListView_SetColumnOrderArray(m_wndList, cColumnOrder, rgOrder);
  541. // Reorder the rgColumns passed in to match the order in the ListView
  542. // and keep a copy of it.
  543. if (m_pColumnSet)
  544. g_pMalloc->Free(m_pColumnSet);
  545. m_pColumnSet = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cColumns);
  546. for (iColumn = 0; iColumn < cColumnOrder; iColumn++)
  547. m_pColumnSet[rgOrder[iColumn]] = rgColumns[iColumn];
  548. m_cColumns = cColumnOrder;
  549. }
  550. else
  551. {
  552. // We still need to keep a copy of the column array ordering for
  553. // filling in the virtual ListView later.
  554. if (m_pColumnSet)
  555. g_pMalloc->Free(m_pColumnSet);
  556. m_pColumnSet = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cColumns);
  557. CopyMemory(m_pColumnSet, rgColumns, sizeof(COLUMN_SET) * cColumns);
  558. m_cColumns = iInsertPos;
  559. }
  560. // If we _still_ don't have sort information, then we pick the first sortable
  561. // column.
  562. if (m_idColumnSort == COLUMN_MAX)
  563. {
  564. m_idColumnSort = m_pColumnSet[0].id;
  565. m_fAscending = TRUE;
  566. }
  567. // Make sure the arrow is drawn correctly
  568. SetSortInfo(m_idColumnSort, m_fAscending);
  569. return (S_OK);
  570. }
  571. HRESULT CColumns::GetColumnInfo(COLUMN_SET_TYPE* pType, COLUMN_SET** prgColumns, DWORD *pcColumns)
  572. {
  573. COLUMN_SET rgColumns[COLUMN_MAX];
  574. DWORD cColumns = COLUMN_MAX;
  575. HRESULT hr;
  576. // This one is easy
  577. if (pType)
  578. *pType = m_type;
  579. // Update our list of columns from the ListView
  580. if (FAILED(hr = _GetListViewColumns(rgColumns, &cColumns)))
  581. {
  582. // If we failed, we should return the default information
  583. cColumns = c_rgColumnSetInfo[m_type].cColumns;
  584. CopyMemory(rgColumns, c_rgColumnSetInfo[m_type].rgColumns, sizeof(COLUMN_SET) * cColumns);
  585. }
  586. if (prgColumns)
  587. {
  588. // Need to allocate an array for this
  589. *prgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cColumns);
  590. CopyMemory(*prgColumns, rgColumns, sizeof(COLUMN_SET) * cColumns);
  591. }
  592. if (pcColumns)
  593. *pcColumns = cColumns;
  594. return (S_OK);
  595. }
  596. HRESULT CColumns::_GetListViewColumns(COLUMN_SET* rgColumns, DWORD* pcColumns)
  597. {
  598. DWORD rgOrder[COLUMN_MAX];
  599. DWORD iColumn;
  600. *pcColumns = m_cColumns;
  601. // The columns might have been reordered by the user, so get the order
  602. // arrray from the ListView
  603. if (!Header_GetOrderArray(m_hwndHdr, m_cColumns, rgOrder))
  604. {
  605. // If this fails, we're pretty much out of luck.
  606. return (E_UNEXPECTED);
  607. }
  608. // Duplicate the stored column set
  609. COLUMN_SET rgColumnsTemp[COLUMN_MAX];
  610. CopyMemory(rgColumnsTemp, m_pColumnSet, sizeof(COLUMN_SET) * m_cColumns);
  611. // Reorder the array
  612. for (iColumn = 0; iColumn < m_cColumns; iColumn++)
  613. {
  614. rgColumns[iColumn] = rgColumnsTemp[rgOrder[iColumn]];
  615. rgColumns[iColumn].flags &= ~(COLFLAG_SORT_ASCENDING | COLFLAG_SORT_DESCENDING);
  616. if (m_idColumnSort == rgColumns[iColumn].id)
  617. rgColumns[iColumn].flags |= (m_fAscending ? COLFLAG_SORT_ASCENDING : COLFLAG_SORT_DESCENDING);
  618. }
  619. #ifdef DEBUG
  620. // Dump the array to make sure it's in the right order
  621. COLUMN_SET* pColumn;
  622. for (iColumn = 0, pColumn = rgColumns; iColumn < m_cColumns; iColumn++, pColumn++)
  623. {
  624. TCHAR sz[CCHMAX_STRINGRES];
  625. LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
  626. sz, ARRAYSIZE(sz));
  627. TRACE("Column %d: %s", iColumn, sz);
  628. }
  629. #endif
  630. // Return 'em
  631. return (S_OK);
  632. }
  633. HRESULT CColumns::SetColumnInfo(COLUMN_SET* rgColumns, DWORD cColumns)
  634. {
  635. Assert(rgColumns != NULL);
  636. Assert(cColumns > 0);
  637. // Update the ListView
  638. _SetListViewColumns(rgColumns, cColumns);
  639. return (S_OK);
  640. }
  641. //
  642. // FUNCTION: CColumns::FillSortMenu()
  643. //
  644. // PURPOSE: Fills the provided menu with the list of columns in the ListView
  645. // and checks the item that is already sorted on.
  646. //
  647. // PARAMETERS:
  648. // [in] hMenu - Handle of the menu to insert items into
  649. // [in] idBase - Base ID for the command IDs
  650. // [out] pcItems - Number of items that were inserted by this function
  651. //
  652. // RETURN VALUE:
  653. // S_OK - Everything succeeded
  654. //
  655. HRESULT CColumns::FillSortMenu(HMENU hMenu, DWORD idBase, DWORD *pcItems, DWORD *pidCurrent)
  656. {
  657. TCHAR sz[CCHMAX_STRINGRES];
  658. int ids;
  659. DWORD iItemChecked = -1;
  660. BOOL fAscending = TRUE;
  661. COLUMN_SET rgColumns[COLUMN_MAX];
  662. DWORD cColumns;
  663. // Update our snapshot of the columns in the ListView
  664. _GetListViewColumns(rgColumns, &cColumns);
  665. // If there aren't any columns yet, bail
  666. if (cColumns == 0)
  667. return (E_UNEXPECTED);
  668. // Clear any items that were already on the menu
  669. while ((WORD) -1 != (WORD) GetMenuItemID(hMenu, 0))
  670. DeleteMenu(hMenu, 0, MF_BYPOSITION);
  671. // Loop through and insert a menu item for each column
  672. COLUMN_SET *pColumn = rgColumns;
  673. DWORD iColumn;
  674. for (iColumn = 0; iColumn < cColumns; iColumn++, pColumn++)
  675. {
  676. // Load the string resource for this column
  677. LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
  678. sz, ARRAYSIZE(sz));
  679. // Insert the menu
  680. InsertMenu(hMenu, iColumn, MF_BYPOSITION | MF_STRING | MF_ENABLED,
  681. idBase + iColumn, sz);
  682. // Check to see if this is the column we're currently sorted on
  683. if (pColumn->id == m_idColumnSort)
  684. {
  685. if (pidCurrent)
  686. *pidCurrent = idBase + iColumn;
  687. iItemChecked = iColumn;
  688. fAscending = m_fAscending;
  689. }
  690. }
  691. // Check the item that is sorted on
  692. CheckMenuRadioItem(hMenu, 0, iColumn - 1, iItemChecked, MF_BYPOSITION);
  693. // Check ascending or descending
  694. CheckMenuRadioItem(hMenu, ID_SORT_ASCENDING, ID_SORT_DESCENDING,
  695. fAscending ? ID_SORT_ASCENDING : ID_SORT_DESCENDING, MF_BYCOMMAND);
  696. // If the caller cares, return the number of items we've added
  697. if (pcItems)
  698. *pcItems = iColumn;
  699. return (S_OK);
  700. }
  701. HRESULT CColumns::ColumnsDialog(HWND hwndParent)
  702. {
  703. CColumnsDlg cDialog;
  704. cDialog.Init(this);
  705. cDialog.DoModal(hwndParent);
  706. return (S_OK);
  707. }
  708. DWORD CColumns::GetCount(void)
  709. {
  710. return (m_cColumns);
  711. }
  712. HRESULT CColumns::GetSortInfo(COLUMN_ID *pidColumn, BOOL *pfAscending)
  713. {
  714. if (pidColumn)
  715. *pidColumn = m_idColumnSort;
  716. if (pfAscending)
  717. *pfAscending = m_fAscending;
  718. return (S_OK);
  719. }
  720. HRESULT CColumns::SetSortInfo(COLUMN_ID idColumn, BOOL fAscending)
  721. {
  722. LV_COLUMN lvc;
  723. COLUMN_SET *pColumn;
  724. DWORD iColumn;
  725. // Loop through the column array and verify this column is visible
  726. for (iColumn = 0, pColumn = m_pColumnSet; iColumn < m_cColumns; iColumn++, pColumn++)
  727. {
  728. if (pColumn->id == idColumn)
  729. {
  730. // Remove the sort arrow from the previously sorted column
  731. if (c_rgColumnSetInfo[m_type].fSort && c_rgColumnData[m_idColumnSort].iIcon == 0)
  732. {
  733. lvc.mask = LVCF_FMT;
  734. lvc.fmt = c_rgColumnData[m_idColumnSort].format;
  735. lvc.fmt &= ~(LVCFMT_IMAGE | LVCFMT_BITMAP_ON_RIGHT);
  736. ListView_SetColumn(m_wndList, GetColumn(m_idColumnSort), &lvc);
  737. }
  738. // Update our cached information
  739. m_idColumnSort = idColumn;
  740. m_fAscending = fAscending;
  741. // Update the ListView with a new sort column unless the sort column
  742. // already has an image
  743. if (c_rgColumnSetInfo[m_type].fSort && c_rgColumnData[idColumn].iIcon <= 0)
  744. {
  745. lvc.fmt = LVCFMT_IMAGE | LVCFMT_BITMAP_ON_RIGHT | c_rgColumnData[idColumn].format;
  746. lvc.mask = LVCF_IMAGE | LVCF_FMT;
  747. lvc.iImage = fAscending ? iiconSortAsc : iiconSortDesc;
  748. ListView_SetColumn(m_wndList, iColumn, &lvc);
  749. }
  750. return (S_OK);
  751. }
  752. }
  753. return (E_INVALIDARG);
  754. }
  755. COLUMN_ID CColumns::GetId(DWORD iColumn)
  756. {
  757. DWORD rgOrder[COLUMN_MAX];
  758. if (iColumn > m_cColumns)
  759. return COLUMN_MAX;
  760. // The columns might have been reordered by the user, so get the order
  761. // arrray from the ListView
  762. if (0 == Header_GetOrderArray(m_hwndHdr, m_cColumns, rgOrder))
  763. return (COLUMN_MAX);
  764. return (m_pColumnSet[iColumn].id);
  765. }
  766. DWORD CColumns::GetColumn(COLUMN_ID id)
  767. {
  768. COLUMN_SET *pColumn;
  769. DWORD iColumn;
  770. for (iColumn = 0, pColumn = m_pColumnSet; iColumn < m_cColumns; iColumn++, pColumn++)
  771. {
  772. if (pColumn->id == id)
  773. return (iColumn);
  774. }
  775. return (-1);
  776. }
  777. HRESULT CColumns::SetColumnWidth(DWORD iColumn, DWORD cxWidth)
  778. {
  779. if (iColumn > m_cColumns)
  780. return (E_INVALIDARG);
  781. m_pColumnSet[iColumn].cxWidth = cxWidth;
  782. return (S_OK);
  783. }
  784. HRESULT CColumns::InsertColumn(COLUMN_ID id, DWORD iInsertBefore)
  785. {
  786. COLUMN_SET rgOld[COLUMN_MAX];
  787. DWORD cColumns = COLUMN_MAX;
  788. // Update our list of columns from the ListView
  789. _GetListViewColumns(rgOld, &cColumns);
  790. // Allocate an array big enough for all of the possible columns
  791. COLUMN_SET *rgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * (cColumns + 1));
  792. if (!rgColumns)
  793. return (E_OUTOFMEMORY);
  794. // Insert the requested flag first
  795. rgColumns->id = id;
  796. rgColumns->flags = COLFLAG_VISIBLE;
  797. rgColumns->cxWidth = -1;
  798. // Now copy the rest
  799. CopyMemory(&(rgColumns[1]), rgOld, sizeof(COLUMN_SET) * cColumns);
  800. // Set the updated column structure into the ListView
  801. SetColumnInfo(rgColumns, cColumns + 1);
  802. g_pMalloc->Free(rgColumns);
  803. return (S_OK);
  804. }
  805. HRESULT CColumns::IsColumnVisible(COLUMN_ID id, BOOL *pfVisible)
  806. {
  807. if (0 == pfVisible)
  808. return E_INVALIDARG;
  809. // Just do a quick run through the column array to see if the requested
  810. // column is visible
  811. COLUMN_SET *pColumn = m_pColumnSet;
  812. for (DWORD i = 0; i < m_cColumns; i++, pColumn++)
  813. {
  814. if (pColumn->id == id)
  815. {
  816. *pfVisible = !!(pColumn->flags & COLFLAG_VISIBLE);
  817. return (S_OK);
  818. }
  819. }
  820. *pfVisible = FALSE;
  821. return (E_UNEXPECTED);
  822. }
  823. /////////////////////////////////////////////////////////////////////////////
  824. // CColumnsDlg
  825. //
  826. CColumnsDlg::CColumnsDlg() : m_ctlEdit(NULL, this, 1)
  827. {
  828. /*
  829. m_dwTitleID = idsColumnDlgTitle;
  830. m_dwHelpFileID = 0;
  831. m_dwDocStringID = idsColumnDlgTitle;
  832. */
  833. m_type = COLUMN_SET_MAIL;
  834. m_iItemWidth = -1;
  835. m_pColumnInfo = 0;
  836. m_rgColumns = 0;
  837. }
  838. CColumnsDlg::~CColumnsDlg()
  839. {
  840. SafeRelease(m_pColumnInfo);
  841. if (m_rgColumns)
  842. g_pMalloc->Free(m_rgColumns);
  843. }
  844. #undef SubclassWindow
  845. LRESULT CColumnsDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  846. {
  847. m_hwndList = GetDlgItem(IDC_COLUMN_LIST);
  848. m_ctlEdit.SubclassWindow(GetDlgItem(IDC_WIDTH));
  849. // Set the extended styles on the ListView
  850. ListView_SetExtendedListViewStyle(m_hwndList, LVS_EX_FULLROWSELECT);
  851. // Retrieve some information about the column set we're supposed to be
  852. // displaying.
  853. COLUMN_SET* pColumns;
  854. DWORD cColumns;
  855. m_pColumnInfo->GetColumnInfo(&m_type, &pColumns, &cColumns);
  856. // Allocate an array to hold our column info
  857. DWORD foo = c_rgColumnSetInfo[m_type].cColumns;
  858. Assert(m_rgColumns == NULL);
  859. m_rgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * c_rgColumnSetInfo[m_type].cColumns);
  860. CopyMemory(m_rgColumns, pColumns, sizeof(COLUMN_SET) * cColumns);
  861. g_pMalloc->Free(pColumns);
  862. m_cColumns = cColumns;
  863. // Add a single column to the ListView
  864. RECT rcClient;
  865. ::GetClientRect(m_hwndList, &rcClient);
  866. LV_COLUMN lvc;
  867. lvc.mask = LVCF_SUBITEM | LVCF_WIDTH;
  868. lvc.cx = rcClient.right - GetSystemMetrics(SM_CXVSCROLL);
  869. lvc.iSubItem = 0;
  870. ListView_InsertColumn(m_hwndList, 0, &lvc);
  871. // Load the state image bitmap
  872. HIMAGELIST himlState = ImageList_LoadBitmap(g_hLocRes, MAKEINTRESOURCE(idb16x16st),
  873. 16, 0, RGB(255, 0, 255));
  874. ListView_SetImageList(m_hwndList, himlState, LVSIL_STATE);
  875. // Fill the ListView
  876. _FillList(m_rgColumns, m_cColumns);
  877. // Set the first item to be focused
  878. ListView_SetItemState(m_hwndList, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
  879. // Everything is clean
  880. SetDirty(FALSE);
  881. return 1; // Let the system set the focus
  882. }
  883. static const HELPMAP g_rgCtxMapColumns[] = {
  884. {IDC_COLUMN_LIST, 50400},
  885. {IDC_MOVEUP, 50405},
  886. {IDC_MOVEDOWN, 50410},
  887. {IDC_SHOW, 50415},
  888. {IDC_HIDE, 50420},
  889. {IDC_WIDTH, 50425},
  890. {IDC_RESET_COLUMNS, 353507},
  891. {0, 0}
  892. };
  893. LRESULT CColumnsDlg::OnHelp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  894. {
  895. return(OnContextHelp(m_hWnd, uMsg, wParam, lParam, g_rgCtxMapColumns));
  896. }
  897. HRESULT CColumnsDlg::Apply(void)
  898. {
  899. HRESULT hr = S_OK;
  900. TRACE(_T("CColumnsDlg::Apply\n"));
  901. // Build a column set array from the data in the ListView. Only include
  902. // visible columns.
  903. int cItems = ListView_GetItemCount(m_hwndList);
  904. // Allocate an array big enough for all of the possible columns
  905. COLUMN_SET *rgColumns = (COLUMN_SET *) g_pMalloc->Alloc(sizeof(COLUMN_SET) * cItems);
  906. DWORD cColumns = 0;
  907. if (!rgColumns)
  908. return (E_OUTOFMEMORY);
  909. LV_ITEM lvi;
  910. lvi.mask = LVIF_PARAM | LVIF_STATE;
  911. lvi.stateMask = LVIS_SELECTED;
  912. lvi.iSubItem = 0;
  913. // Loop through the listview
  914. for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++)
  915. {
  916. // Check to see if this one is visible
  917. if (_IsChecked(lvi.iItem))
  918. {
  919. // If so, then retrieve the cached column info pointer
  920. ListView_GetItem(m_hwndList, &lvi);
  921. // And copy the structure into our new array
  922. rgColumns[cColumns] = *((COLUMN_SET *) lvi.lParam);
  923. // If this item was selected, then we should grab the column width
  924. // from the edit box.
  925. if (lvi.state & LVIS_SELECTED)
  926. rgColumns[cColumns].cxWidth = GetDlgItemInt(IDC_WIDTH, NULL, FALSE);
  927. // Make sure the flag sayz visible
  928. rgColumns[cColumns++].flags |= COLFLAG_VISIBLE;
  929. }
  930. }
  931. // Make sure there's at least one column
  932. if (!cColumns)
  933. {
  934. AthMessageBoxW(m_hWnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrSelectOneColumn),
  935. 0, MB_ICONEXCLAMATION | MB_OK);
  936. hr = E_UNEXPECTED;
  937. }
  938. else
  939. {
  940. // Set the updated column structure into the ListView
  941. if (SUCCEEDED(m_pColumnInfo->SetColumnInfo(rgColumns, cColumns)))
  942. {
  943. SetDirty(FALSE);
  944. hr = S_OK;
  945. }
  946. else
  947. hr = E_UNEXPECTED;
  948. }
  949. g_pMalloc->Free(rgColumns);
  950. return (hr);
  951. }
  952. LRESULT CColumnsDlg::OnClick(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
  953. {
  954. DWORD dwPos;
  955. LV_HITTESTINFO lvhti;
  956. // Double check this
  957. Assert(idCtrl == IDC_COLUMN_LIST);
  958. // Figure out where the cursor was
  959. dwPos = GetMessagePos();
  960. lvhti.pt.x = (int)(short) LOWORD(dwPos);
  961. lvhti.pt.y = (int)(short) HIWORD(dwPos);
  962. ::ScreenToClient(m_hwndList, &(lvhti.pt));
  963. // Ask the ListView where this is
  964. if (-1 == ListView_HitTest(m_hwndList, &lvhti))
  965. return 0;
  966. // If this was on a state image area, toggle the check
  967. if (lvhti.flags == LVHT_ONITEMSTATEICON || pnmh->code == NM_DBLCLK)
  968. {
  969. _SetCheck(lvhti.iItem, !_IsChecked(lvhti.iItem));
  970. }
  971. return (0);
  972. }
  973. LRESULT CColumnsDlg::OnItemChanged(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
  974. {
  975. Assert(idCtrl == IDC_COLUMN_LIST);
  976. // The only change we're looking for is when a new item is selected.
  977. NMLISTVIEW* pnmlv = (NMLISTVIEW *) pnmh;
  978. COLUMN_SET* pColumn = ((COLUMN_SET *) pnmlv->lParam);
  979. DWORD cxWidth = pColumn->cxWidth == -1 ? c_rgColumnData[pColumn->id].cxWidth : pColumn->cxWidth;
  980. // Narrow it down to state changes
  981. if (pnmlv->uChanged & LVIF_STATE)
  982. {
  983. _UpdateButtonState(pnmlv->iItem);
  984. // If the new state contains selected, and the old state does not, then
  985. // we have a new selected item.
  986. if ((pnmlv->uNewState & LVIS_SELECTED) && (pnmlv->uNewState & LVIS_FOCUSED)
  987. && (0 == (pnmlv->uOldState & LVIS_SELECTED)))
  988. {
  989. LV_ITEM lvi;
  990. lvi.iSubItem = 0;
  991. lvi.mask = LVIF_PARAM;
  992. // If there was a previously selected item
  993. if (m_iItemWidth != -1)
  994. {
  995. lvi.iItem = m_iItemWidth;
  996. ListView_GetItem(m_hwndList, &lvi);
  997. // Save the width
  998. ((COLUMN_SET *) lvi.lParam)->cxWidth = GetDlgItemInt(IDC_WIDTH, NULL, FALSE);
  999. }
  1000. // Set the column width edit box
  1001. SetDlgItemInt(IDC_WIDTH, cxWidth, FALSE);
  1002. m_iItemWidth = pnmlv->iItem;
  1003. }
  1004. }
  1005. return (0);
  1006. }
  1007. BOOL CColumnsDlg::_IsChecked(DWORD iItem)
  1008. {
  1009. DWORD state;
  1010. // Get the state from the selected item
  1011. state = ListView_GetItemState(m_hwndList, iItem, LVIS_STATEIMAGEMASK);
  1012. return (state & INDEXTOSTATEIMAGEMASK(iiconStateChecked + 1));
  1013. }
  1014. void CColumnsDlg::_SetCheck(DWORD iItem, BOOL fChecked)
  1015. {
  1016. ListView_SetItemState(m_hwndList, iItem,
  1017. INDEXTOSTATEIMAGEMASK(1 + iiconStateUnchecked + fChecked),
  1018. LVIS_STATEIMAGEMASK);
  1019. SetDirty(TRUE);
  1020. }
  1021. void CColumnsDlg::_FillList(const COLUMN_SET *rgColumns, DWORD cColumns)
  1022. {
  1023. LV_ITEM lvi;
  1024. TCHAR sz[CCHMAX_STRINGRES];
  1025. COLUMN_SET *pColumn;
  1026. BOOL fChecked;
  1027. // Set the basic fields in the item struct
  1028. lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
  1029. lvi.iSubItem = 0;
  1030. lvi.pszText = sz;
  1031. lvi.stateMask = LVIS_STATEIMAGEMASK;
  1032. // Loop through the columns in rgColumns, adding each in order to the
  1033. // ListView.
  1034. for (lvi.iItem = 0, pColumn = (COLUMN_SET *) rgColumns; lvi.iItem < (int) cColumns; lvi.iItem++, pColumn++)
  1035. {
  1036. // Load the string for the column
  1037. LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
  1038. sz, ARRAYSIZE(sz));
  1039. // Set the checkbox state
  1040. fChecked = !!(pColumn->flags & COLFLAG_VISIBLE);
  1041. lvi.state = INDEXTOSTATEIMAGEMASK(1 + iiconStateUnchecked + fChecked);
  1042. // Save the width in the lParam
  1043. if (pColumn->cxWidth == -1)
  1044. pColumn->cxWidth = c_rgColumnData[pColumn->id].cxWidth;
  1045. lvi.lParam = (LPARAM) pColumn;
  1046. // Insert this item into the list
  1047. ListView_InsertItem(m_hwndList, &lvi);
  1048. }
  1049. // Check to see if the columns we just added were the default columns
  1050. if (lvi.iItem != (int) c_rgColumnSetInfo[m_type].cColumns)
  1051. {
  1052. // Now we need to go through and add the columns that are not currently in
  1053. // the column set, but could be.
  1054. DWORD i, j;
  1055. BOOL fInsert;
  1056. for (i = 0, pColumn = (COLUMN_SET *) c_rgColumnSetInfo[m_type].rgColumns;
  1057. i < c_rgColumnSetInfo[m_type].cColumns;
  1058. i++, pColumn++)
  1059. {
  1060. fInsert = TRUE;
  1061. for (j = 0; j < cColumns; j++)
  1062. {
  1063. if (pColumn->id == m_rgColumns[j].id)
  1064. {
  1065. fInsert = FALSE;
  1066. break;
  1067. }
  1068. }
  1069. // If it wasn't found in m_rgColumns, then insert it
  1070. if (fInsert)
  1071. {
  1072. // Copy the struct
  1073. m_rgColumns[lvi.iItem] = *pColumn;
  1074. m_rgColumns[lvi.iItem].cxWidth = c_rgColumnData[pColumn->id].cxWidth;
  1075. m_rgColumns[lvi.iItem].flags &= ~COLFLAG_VISIBLE;
  1076. // Load the string for the column
  1077. LoadString(g_hLocRes, c_rgColumnData[pColumn->id].idsColumnName,
  1078. sz, ARRAYSIZE(sz));
  1079. // Set the checkbox state. These are _always_ unchecked.
  1080. lvi.state = INDEXTOSTATEIMAGEMASK(1 + iiconStateUnchecked);
  1081. // Save the width in the lParam
  1082. lvi.lParam = (LPARAM) &m_rgColumns[lvi.iItem];
  1083. // Insert this item into the list
  1084. ListView_InsertItem(m_hwndList, &lvi);
  1085. // Increment the position
  1086. lvi.iItem++;
  1087. }
  1088. }
  1089. }
  1090. m_cColumns = ListView_GetItemCount(m_hwndList);
  1091. }
  1092. LRESULT CColumnsDlg::OnShowHide(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  1093. {
  1094. int iItem = -1;
  1095. // Loop through the selected items and make them checked
  1096. while (-1 != (iItem = ListView_GetNextItem(m_hwndList, iItem, LVNI_SELECTED)))
  1097. {
  1098. _SetCheck(iItem, wID == IDC_SHOW);
  1099. }
  1100. return (0);
  1101. }
  1102. LRESULT CColumnsDlg::OnReset(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  1103. {
  1104. // Remove all of the columns from the ListView
  1105. ListView_DeleteAllItems(m_hwndList);
  1106. // Fill the array of columns with the default column information
  1107. CopyMemory(m_rgColumns, c_rgColumnSetInfo[m_type].rgColumns, sizeof(COLUMN_SET) * c_rgColumnSetInfo[m_type].cColumns);
  1108. // Reset the list to contain the default column information
  1109. _FillList(m_rgColumns, m_cColumns);
  1110. // Set the first item to be focused
  1111. ListView_SetItemState(m_hwndList, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
  1112. SetDirty(TRUE);
  1113. return (0);
  1114. }
  1115. void CColumnsDlg::_UpdateButtonState(DWORD iItemSel)
  1116. {
  1117. HWND hwnd;
  1118. BOOL fChecked = _IsChecked(iItemSel);
  1119. DWORD dwItems = ListView_GetItemCount(m_hwndList);
  1120. DWORD dwSel = ListView_GetSelectedCount(m_hwndList);
  1121. hwnd = GetFocus();
  1122. ::EnableWindow(GetDlgItem(IDC_MOVEUP), (iItemSel != 0) && dwSel);
  1123. ::EnableWindow(GetDlgItem(IDC_MOVEDOWN), (iItemSel != (dwItems - 1)) && dwSel);
  1124. ::EnableWindow(GetDlgItem(IDC_SHOW), (!fChecked && dwSel));
  1125. ::EnableWindow(GetDlgItem(IDC_HIDE), fChecked && dwSel);
  1126. // don't disable button that has the focus
  1127. if (!::IsWindowEnabled(hwnd))
  1128. {
  1129. hwnd = GetNextDlgTabItem(hwnd, FALSE);
  1130. ::SetFocus(hwnd);
  1131. }
  1132. }
  1133. LRESULT CColumnsDlg::OnMove(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  1134. {
  1135. COLUMN_SET *pColumn = 0;
  1136. // Make sure this is reset
  1137. m_iItemWidth = -1;
  1138. // Figure out which one is selected
  1139. DWORD iItem = ListView_GetNextItem(m_hwndList, -1, LVNI_SELECTED);
  1140. // Get the item from the ListView
  1141. LV_ITEM lvi;
  1142. TCHAR sz[CCHMAX_STRINGRES];
  1143. lvi.mask = LVIF_TEXT | LVIF_STATE | LVIF_PARAM;
  1144. lvi.iItem = iItem;
  1145. lvi.iSubItem = 0;
  1146. lvi.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
  1147. lvi.pszText = sz;
  1148. lvi.cchTextMax = ARRAYSIZE(sz);
  1149. ListView_GetItem(m_hwndList, &lvi);
  1150. // Insert this item to the position one up or down from where it is
  1151. lvi.iItem += (wID == IDC_MOVEUP) ? -1 : 2;
  1152. // Update the column width
  1153. pColumn = (COLUMN_SET *) lvi.lParam;
  1154. pColumn->cxWidth = GetDlgItemInt(IDC_WIDTH, NULL, FALSE);
  1155. ListView_InsertItem(m_hwndList, &lvi);
  1156. // Force a redraw of the new item and make sure it's visible
  1157. ListView_EnsureVisible(m_hwndList, lvi.iItem, FALSE);
  1158. ListView_RedrawItems(m_hwndList, lvi.iItem, lvi.iItem);
  1159. // Delete the old item
  1160. m_iItemWidth = -1;
  1161. ListView_DeleteItem(m_hwndList, iItem + (wID == IDC_MOVEUP));
  1162. SetDirty(TRUE);
  1163. return (0);
  1164. }
  1165. LRESULT CColumnsDlg::OnApply(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
  1166. {
  1167. if (SUCCEEDED(Apply()))
  1168. SetWindowLong(DWLP_MSGRESULT, PSNRET_NOERROR);
  1169. else
  1170. SetWindowLong(DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  1171. return (TRUE);
  1172. }