PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/libusbK/src/inf-wizard2/src/CGridListCtrlGroups.cpp

http://usb-travis.googlecode.com/
C++ | 1239 lines | 850 code | 138 blank | 251 comment | 140 complexity | 6fc7a910bbade8fd9efc4cf67eb7f5b8 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, LGPL-2.0
  1. //------------------------------------------------------------------------
  2. // Author: Rolf Kristensen
  3. // Source: http://www.codeproject.com/KB/list/CGridListCtrlEx.aspx
  4. // License: Free to use for all (New BSD License)
  5. //------------------------------------------------------------------------
  6. #include "stdafx.h"
  7. #include "CGridListCtrlGroups.h"
  8. #include <shlwapi.h> // IsCommonControlsEnabled
  9. #pragma warning(disable:4100) // unreferenced formal parameter
  10. #include "CGridColumnTrait.h"
  11. // WIN32 defines for group-support is only available from 2003 PSDK
  12. #if _WIN32_WINNT >= 0x0501
  13. // If using VS2008 then the MFC libary will complain that group mode is only available for Unicode builds
  14. // - By default CGridListCtrlGroups will be disabled in non-Unicode mode
  15. // - Define CGRIDLISTCTRLEX_GROUPMODE in stdafx.h to force group mode in non-unicode builds
  16. #if _MSC_VER < 1500 || defined UNICODE || defined CGRIDLISTCTRLEX_GROUPMODE
  17. #if _MSC_VER >= 1500 && defined CGRIDLISTCTRLEX_GROUPMODE
  18. #pragma warning(disable:4996)
  19. #endif
  20. BEGIN_MESSAGE_MAP(CGridListCtrlGroups, CGridListCtrlEx)
  21. #if _WIN32_WINNT >= 0x0600
  22. ON_NOTIFY_REFLECT_EX(LVN_LINKCLICK, OnGroupTaskClick) // Task-Link Click
  23. ON_NOTIFY_REFLECT_EX(LVN_GETEMPTYMARKUP, OnGetEmptyMarkup) // Request text to display when empty
  24. #endif
  25. ON_WM_LBUTTONDBLCLK()
  26. END_MESSAGE_MAP()
  27. //------------------------------------------------------------------------
  28. //! Constructor
  29. //------------------------------------------------------------------------
  30. CGridListCtrlGroups::CGridListCtrlGroups()
  31. : m_GroupHeight(-1)
  32. {}
  33. //------------------------------------------------------------------------
  34. //! Inserts a group into the list view control.
  35. //!
  36. //! @param nIndex The insert position of the group
  37. //! @param nGroupId ID of the new group
  38. //! @param strHeader The group header title
  39. //! @param dwState Specifies the state of the group when inserted
  40. //! @param dwAlign Indicates the alignment of the header or footer text for the group
  41. //! @return Returns the index of the item that the group was added to, or -1 if the operation failed.
  42. //------------------------------------------------------------------------
  43. LRESULT CGridListCtrlGroups::InsertGroupHeader(int nIndex, int nGroupId, const CString& strHeader, DWORD dwState /* = LVGS_NORMAL */, DWORD dwAlign /*= LVGA_HEADER_LEFT*/)
  44. {
  45. LVGROUP lg = {0};
  46. lg.cbSize = sizeof(lg);
  47. lg.iGroupId = nGroupId;
  48. lg.state = dwState;
  49. lg.mask = LVGF_GROUPID | LVGF_HEADER | LVGF_STATE | LVGF_ALIGN;
  50. lg.uAlign = dwAlign;
  51. // Header-title must be unicode (Convert if necessary)
  52. #ifdef UNICODE
  53. lg.pszHeader = (LPWSTR)(LPCTSTR)strHeader;
  54. lg.cchHeader = strHeader.GetLength();
  55. #else
  56. CComBSTR header = strHeader;
  57. lg.pszHeader = header;
  58. lg.cchHeader = header.Length();
  59. #endif
  60. return InsertGroup(nIndex, (PLVGROUP)&lg );
  61. }
  62. //------------------------------------------------------------------------
  63. //! Moves a row into a group
  64. //!
  65. //! @param nRow The index of the row
  66. //! @param nGroupId ID of the group
  67. //! @return Nonzero if successful; otherwise zero
  68. //------------------------------------------------------------------------
  69. BOOL CGridListCtrlGroups::SetRowGroupId(int nRow, int nGroupId)
  70. {
  71. //OBS! Rows not assigned to a group will not show in group-view
  72. LVITEM lvItem = {0};
  73. lvItem.mask = LVIF_GROUPID;
  74. lvItem.iItem = nRow;
  75. lvItem.iSubItem = 0;
  76. lvItem.iGroupId = nGroupId;
  77. return SetItem( &lvItem );
  78. }
  79. //------------------------------------------------------------------------
  80. //! Retrieves the group id of a row
  81. //!
  82. //! @param nRow The index of the row
  83. //! @return ID of the group
  84. //------------------------------------------------------------------------
  85. int CGridListCtrlGroups::GetRowGroupId(int nRow)
  86. {
  87. LVITEM lvi = {0};
  88. lvi.mask = LVIF_GROUPID;
  89. lvi.iItem = nRow;
  90. VERIFY( GetItem(&lvi) );
  91. return lvi.iGroupId;
  92. }
  93. //------------------------------------------------------------------------
  94. //! Retrieves the group header title of a group
  95. //!
  96. //! @param nGroupId ID of the group
  97. //! @return Group header title
  98. //------------------------------------------------------------------------
  99. CString CGridListCtrlGroups::GetGroupHeader(int nGroupId)
  100. {
  101. LVGROUP lg = {0};
  102. lg.cbSize = sizeof(lg);
  103. lg.iGroupId = nGroupId;
  104. lg.mask = LVGF_HEADER | LVGF_GROUPID;
  105. VERIFY( GetGroupInfo(nGroupId, (PLVGROUP)&lg) != -1 );
  106. #ifdef UNICODE
  107. return lg.pszHeader;
  108. #elif _MSC_VER >= 1300
  109. CComBSTR header( lg.pszHeader );
  110. return (LPCTSTR)COLE2T(header);
  111. #else
  112. USES_CONVERSION;
  113. return W2A(lg.pszHeader);
  114. #endif
  115. }
  116. //------------------------------------------------------------------------
  117. //! Checks if it is possible to modify the collapse state of a group.
  118. //! This is only possible in Windows Vista.
  119. //!
  120. //! @return Groups can be collapsed (true / false)
  121. //------------------------------------------------------------------------
  122. BOOL CGridListCtrlGroups::IsGroupStateEnabled()
  123. {
  124. if (!IsGroupViewEnabled())
  125. return FALSE;
  126. return CheckOSVersion(0x0600);
  127. }
  128. //------------------------------------------------------------------------
  129. //! Checks whether a group has a certain state
  130. //!
  131. //! @param nGroupId ID of the group
  132. //! @param dwState Specifies the state to check
  133. //! @return The group has the state (true / false)
  134. //------------------------------------------------------------------------
  135. BOOL CGridListCtrlGroups::HasGroupState(int nGroupId, DWORD dwState)
  136. {
  137. // Vista SDK - ListView_GetGroupState / LVM_GETGROUPSTATE
  138. LVGROUP lg = {0};
  139. lg.cbSize = sizeof(lg);
  140. lg.mask = LVGF_STATE;
  141. lg.stateMask = dwState;
  142. if ( GetGroupInfo(nGroupId, (PLVGROUP)&lg) == -1)
  143. return FALSE;
  144. return lg.state == dwState;
  145. }
  146. //------------------------------------------------------------------------
  147. //! Updates the state of a group
  148. //!
  149. //! @param nGroupId ID of the group
  150. //! @param dwState Specifies the new state of the group
  151. //! @return The group state was updated (true / false)
  152. //------------------------------------------------------------------------
  153. BOOL CGridListCtrlGroups::SetGroupState(int nGroupId, DWORD dwState)
  154. {
  155. // Vista SDK - ListView_SetGroupState / LVM_SETGROUPINFO
  156. if (!IsGroupStateEnabled())
  157. return FALSE;
  158. LVGROUP lg = {0};
  159. lg.cbSize = sizeof(lg);
  160. lg.mask = LVGF_STATE;
  161. lg.state = dwState;
  162. lg.stateMask = dwState;
  163. #ifdef LVGS_COLLAPSIBLE
  164. // Maintain LVGS_COLLAPSIBLE state
  165. if (HasGroupState(nGroupId, LVGS_COLLAPSIBLE))
  166. lg.state |= LVGS_COLLAPSIBLE;
  167. #endif
  168. if (SetGroupInfo(nGroupId, (PLVGROUP)&lg) == -1)
  169. return FALSE;
  170. return TRUE;
  171. }
  172. //------------------------------------------------------------------------
  173. //! Find the group-id below the given point
  174. //!
  175. //! @param point Mouse position
  176. //! @return ID of the group
  177. //------------------------------------------------------------------------
  178. int CGridListCtrlGroups::GroupHitTest(const CPoint& point)
  179. {
  180. if (!IsGroupViewEnabled())
  181. return -1;
  182. if (HitTest(point) != -1)
  183. return -1;
  184. if (IsGroupStateEnabled())
  185. {
  186. #if _WIN32_WINNT >= 0x0600
  187. #ifdef ListView_HitTestEx
  188. #ifdef LVHT_EX_GROUP
  189. #ifdef ListView_GetGroupInfoByIndex
  190. LVHITTESTINFO lvhitinfo = {0};
  191. lvhitinfo.pt = point;
  192. ListView_HitTestEx(m_hWnd, &lvhitinfo);
  193. if ((lvhitinfo.flags & LVHT_EX_GROUP) == 0)
  194. return -1;
  195. LVGROUP lg = {0};
  196. lg.cbSize = sizeof(lg);
  197. lg.mask = LVGF_GROUPID;
  198. VERIFY( ListView_GetGroupInfoByIndex(m_hWnd, lvhitinfo.iGroup, &lg) );
  199. return lg.iGroupId;
  200. #endif
  201. #endif
  202. #endif
  203. #endif
  204. }
  205. if (IsGroupStateEnabled())
  206. {
  207. // Running on Vista or newer, but compiled without _WIN32_WINNT >= 0x0600
  208. #ifndef LVM_GETGROUPINFOBYINDEX
  209. #define LVM_GETGROUPINFOBYINDEX (LVM_FIRST + 153)
  210. #endif
  211. #ifndef LVM_GETGROUPCOUNT
  212. #define LVM_GETGROUPCOUNT (LVM_FIRST + 152)
  213. #endif
  214. #ifndef LVM_GETGROUPRECT
  215. #define LVM_GETGROUPRECT (LVM_FIRST + 98)
  216. #endif
  217. #ifndef LVGGR_HEADER
  218. #define LVGGR_HEADER (1)
  219. #endif
  220. LRESULT groupCount = SNDMSG((m_hWnd), LVM_GETGROUPCOUNT, (WPARAM)0, (LPARAM)0);
  221. if (groupCount <= 0)
  222. return -1;
  223. for(int i = 0 ; i < groupCount; ++i)
  224. {
  225. LVGROUP lg = {0};
  226. lg.cbSize = sizeof(lg);
  227. lg.mask = LVGF_GROUPID;
  228. VERIFY( SNDMSG((m_hWnd), LVM_GETGROUPINFOBYINDEX, (WPARAM)(i), (LPARAM)(&lg)) );
  229. CRect rect(0, LVGGR_HEADER, 0, 0);
  230. VERIFY( SNDMSG((m_hWnd), LVM_GETGROUPRECT, (WPARAM)(lg.iGroupId), (LPARAM)(RECT*)(&rect)) );
  231. if (rect.PtInRect(point))
  232. return lg.iGroupId;
  233. }
  234. // Don't try other ways to find the group
  235. return -1;
  236. }
  237. // We require that each group contains atleast one item
  238. if (GetItemCount() == 0)
  239. return -1;
  240. // This logic doesn't support collapsible groups
  241. int nFirstRow = -1;
  242. CRect gridRect;
  243. GetWindowRect(&gridRect);
  244. for(CPoint pt = point ; pt.y < gridRect.bottom ; pt.y += 2)
  245. {
  246. nFirstRow = HitTest(pt);
  247. if (nFirstRow != -1)
  248. break;
  249. }
  250. if (nFirstRow == -1)
  251. return -1;
  252. int nGroupId = GetRowGroupId(nFirstRow);
  253. // Extra validation that the above row belongs to a different group
  254. int nAboveRow = GetNextItem(nFirstRow, LVNI_ABOVE);
  255. if (nAboveRow != -1 && nGroupId == GetRowGroupId(nAboveRow))
  256. return -1;
  257. return nGroupId;
  258. }
  259. //------------------------------------------------------------------------
  260. //! Update the checkbox of the label column (first column)
  261. //!
  262. //! @param nGroupId ID of the group
  263. //! @param bChecked The new check box state
  264. //------------------------------------------------------------------------
  265. void CGridListCtrlGroups::CheckEntireGroup(int nGroupId, BOOL bChecked)
  266. {
  267. if (!(GetExtendedStyle() & LVS_EX_CHECKBOXES))
  268. return;
  269. for (int nRow = 0; nRow < GetItemCount(); ++nRow)
  270. {
  271. if (GetRowGroupId(nRow) == nGroupId)
  272. {
  273. SetCheck(nRow, bChecked ? TRUE : FALSE);
  274. }
  275. }
  276. }
  277. //------------------------------------------------------------------------
  278. //! Removes the group and all the rows part of the group
  279. //!
  280. //! @param nGroupId ID of the group
  281. //------------------------------------------------------------------------
  282. void CGridListCtrlGroups::DeleteEntireGroup(int nGroupId)
  283. {
  284. for (int nRow = 0; nRow < GetItemCount(); ++nRow)
  285. {
  286. if (GetRowGroupId(nRow) == nGroupId)
  287. {
  288. DeleteItem(nRow);
  289. nRow--;
  290. }
  291. }
  292. RemoveGroup(nGroupId);
  293. }
  294. //------------------------------------------------------------------------
  295. //! Create a group for each unique values within a column
  296. //!
  297. //! @param nCol The index of the column
  298. //! @return Succeeded in creating the group
  299. //------------------------------------------------------------------------
  300. BOOL CGridListCtrlGroups::GroupByColumn(int nCol)
  301. {
  302. CWaitCursor waitCursor;
  303. SetSortArrow(-1, false);
  304. SetRedraw(FALSE);
  305. RemoveAllGroups();
  306. EnableGroupView( GetItemCount() > 0 );
  307. if (IsGroupViewEnabled())
  308. {
  309. CSimpleMap<CString, CSimpleArray<int> > groups;
  310. // Loop through all rows and find possible groups
  311. for(int nRow = 0; nRow < GetItemCount(); ++nRow)
  312. {
  313. CString cellText = GetItemText(nRow, nCol);
  314. int nGroupId = groups.FindKey(cellText);
  315. if (nGroupId == -1)
  316. {
  317. CSimpleArray<int> rows;
  318. groups.Add(cellText, rows);
  319. nGroupId = groups.FindKey(cellText);
  320. }
  321. groups.GetValueAt(nGroupId).Add(nRow);
  322. }
  323. // Look through all groups and assign rows to group
  324. for(int nGroupId = 0; nGroupId < groups.GetSize(); ++nGroupId)
  325. {
  326. const CSimpleArray<int>& groupRows = groups.GetValueAt(nGroupId);
  327. DWORD dwState = LVGS_NORMAL;
  328. #ifdef LVGS_COLLAPSIBLE
  329. if (IsGroupStateEnabled())
  330. dwState = LVGS_COLLAPSIBLE;
  331. #endif
  332. VERIFY( InsertGroupHeader(nGroupId, nGroupId, groups.GetKeyAt(nGroupId), dwState) != -1);
  333. for(int groupRow = 0; groupRow < groupRows.GetSize(); ++groupRow)
  334. {
  335. VERIFY( SetRowGroupId(groupRows[groupRow], nGroupId) );
  336. }
  337. }
  338. SetRedraw(TRUE);
  339. Invalidate(FALSE);
  340. return TRUE;
  341. }
  342. SetRedraw(TRUE);
  343. Invalidate(FALSE);
  344. return FALSE;
  345. }
  346. //------------------------------------------------------------------------
  347. //! Collapse all groups
  348. //------------------------------------------------------------------------
  349. void CGridListCtrlGroups::CollapseAllGroups()
  350. {
  351. if (!IsGroupStateEnabled())
  352. return;
  353. // Loop through all rows and find possible groups
  354. for(int nRow = 0; nRow < GetItemCount(); ++nRow)
  355. {
  356. int nGroupId = GetRowGroupId(nRow);
  357. if (nGroupId != -1)
  358. {
  359. if (!HasGroupState(nGroupId, LVGS_COLLAPSED))
  360. {
  361. SetGroupState(nGroupId, LVGS_COLLAPSED);
  362. }
  363. }
  364. }
  365. }
  366. //------------------------------------------------------------------------
  367. //! Expand all groups
  368. //------------------------------------------------------------------------
  369. void CGridListCtrlGroups::ExpandAllGroups()
  370. {
  371. if (!IsGroupStateEnabled())
  372. return;
  373. // Loop through all rows and find possible groups
  374. for(int nRow = 0; nRow < GetItemCount(); ++nRow)
  375. {
  376. int nGroupId = GetRowGroupId(nRow);
  377. if (nGroupId != -1)
  378. {
  379. if (HasGroupState(nGroupId, LVGS_COLLAPSED))
  380. {
  381. SetGroupState(nGroupId, LVGS_NORMAL);
  382. }
  383. }
  384. }
  385. }
  386. //------------------------------------------------------------------------
  387. //! Called by the framework when a drop operation is to occur, where the
  388. //! origin is the CGridListCtrlEx itself
  389. //!
  390. //! @param pDataObject Points to the data object containing the data that can be dropped
  391. //! @param dropEffect The effect that the user chose for the drop operation (DROPEFFECT_COPY, DROPEFFECT_MOVE, DROPEFFECT_LINK)
  392. //! @param point Contains the current location of the cursor in client coordinates.
  393. //! @return Nonzero if the drop is successful; otherwise 0
  394. //------------------------------------------------------------------------
  395. BOOL CGridListCtrlGroups::OnDropSelf(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
  396. {
  397. // Internal drag (Move rows to other group)
  398. int nRow, nCol;
  399. CellHitTest(point, nRow, nCol);
  400. if (!IsGroupViewEnabled())
  401. return CGridListCtrlEx::MoveSelectedRows(nRow);
  402. if (GetStyle() & LVS_OWNERDATA)
  403. return false;
  404. int nGroupId = nRow != -1 ? GetRowGroupId(nRow) : GroupHitTest(point);
  405. if (nGroupId == -1)
  406. return FALSE;
  407. if (MoveSelectedRows(nGroupId))
  408. {
  409. if (nRow != -1)
  410. {
  411. EnsureVisible(nRow, FALSE);
  412. SetFocusRow(nRow);
  413. }
  414. }
  415. return TRUE;
  416. }
  417. //------------------------------------------------------------------------
  418. //! Moves the selected rows to the specified group
  419. //!
  420. //! @param nDropGroupId Moved the selected rows to this group
  421. //! @return Was rows rearranged ? (true / false)
  422. //------------------------------------------------------------------------
  423. BOOL CGridListCtrlGroups::MoveSelectedRows(int nDropGroupId)
  424. {
  425. if (GetStyle() & LVS_OWNERDATA)
  426. return false;
  427. if (nDropGroupId == -1)
  428. return false;
  429. POSITION pos = GetFirstSelectedItemPosition();
  430. if (pos == NULL)
  431. return false;
  432. while(pos != NULL)
  433. {
  434. int nRow = GetNextSelectedItem(pos);
  435. int nGroupId = GetRowGroupId(nRow);
  436. if (nGroupId != nDropGroupId)
  437. SetRowGroupId(nRow, nDropGroupId);
  438. }
  439. return true;
  440. }
  441. //------------------------------------------------------------------------
  442. //! WM_CONTEXTMENU message handler to show popup menu when mouse right
  443. //! click is used (or SHIFT+F10 on the keyboard)
  444. //!
  445. //! @param pWnd Handle to the window in which the user right clicked the mouse
  446. //! @param point Position of the cursor, in screen coordinates, at the time of the mouse click.
  447. //------------------------------------------------------------------------
  448. void CGridListCtrlGroups::OnContextMenu(CWnd* pWnd, CPoint point)
  449. {
  450. if ( IsGroupViewEnabled() )
  451. {
  452. if (pWnd != GetHeaderCtrl())
  453. {
  454. if (point.x != -1 && point.y != -1)
  455. {
  456. CPoint pt = point;
  457. ScreenToClient(&pt);
  458. int nGroupId = GroupHitTest(pt);
  459. if (nGroupId != -1)
  460. {
  461. OnContextMenuGroup(pWnd, point, nGroupId);
  462. return;
  463. }
  464. }
  465. }
  466. }
  467. CGridListCtrlEx::OnContextMenu(pWnd, point);
  468. }
  469. namespace
  470. {
  471. BOOL IsCommonControlsEnabled()
  472. {
  473. // Test if application has access to common controls (Required for grouping)
  474. HMODULE hinstDll = ::LoadLibrary(_T("comctl32.dll"));
  475. if (hinstDll)
  476. {
  477. DLLGETVERSIONPROC pDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(hinstDll, "DllGetVersion");
  478. ::FreeLibrary(hinstDll);
  479. if (pDllGetVersion != NULL)
  480. {
  481. DLLVERSIONINFO dvi = {0};
  482. dvi.cbSize = sizeof(dvi);
  483. HRESULT hRes = pDllGetVersion ((DLLVERSIONINFO*) &dvi);
  484. if (SUCCEEDED(hRes))
  485. return dvi.dwMajorVersion >= 6;
  486. }
  487. }
  488. return false;
  489. }
  490. }
  491. //------------------------------------------------------------------------
  492. //! Override this method to change the context menu when activating context
  493. //! menu for the column headers
  494. //!
  495. //! @param pWnd Handle to the window in which the user right clicked the mouse
  496. //! @param point Position of the cursor, in screen coordinates, at the time of the mouse click.
  497. //! @param nCol The index of the column
  498. //------------------------------------------------------------------------
  499. void CGridListCtrlGroups::OnContextMenuHeader(CWnd* pWnd, CPoint point, int nCol)
  500. {
  501. // Only Windows XP and above supports groups
  502. if (!IsCommonControlsEnabled())
  503. {
  504. CGridListCtrlEx::OnContextMenuHeader(pWnd, point, nCol);
  505. return;
  506. }
  507. // Show context-menu with the option to show hide columns
  508. CMenu menu;
  509. VERIFY( menu.CreatePopupMenu() );
  510. if (nCol != -1)
  511. {
  512. // Retrieve column-title
  513. const CString& columnTitle = GetColumnHeading(nCol);
  514. menu.AppendMenu(MF_STRING, 3, CString(_T("Group by: ")) + columnTitle);
  515. }
  516. if (IsGroupViewEnabled())
  517. {
  518. menu.AppendMenu(MF_STRING, 4, _T("Disable grouping"));
  519. }
  520. CString title_editor;
  521. if (HasColumnEditor(nCol, title_editor))
  522. {
  523. menu.AppendMenu(MF_STRING, 1, static_cast<LPCTSTR>(title_editor));
  524. }
  525. CString title_picker;
  526. if (HasColumnPicker(title_picker))
  527. {
  528. menu.AppendMenu(MF_STRING, 2, static_cast<LPCTSTR>(title_picker));
  529. }
  530. else
  531. {
  532. if (menu.GetMenuItemCount() > 0)
  533. menu.AppendMenu(MF_SEPARATOR, 0, _T(""));
  534. InternalColumnPicker(menu, 6);
  535. }
  536. CSimpleArray<CString> profiles;
  537. InternalColumnProfileSwitcher(menu, GetColumnCount() + 7, profiles);
  538. CString title_resetdefault;
  539. if (HasColumnDefaultState(title_resetdefault))
  540. {
  541. if (profiles.GetSize() == 0)
  542. menu.AppendMenu(MF_SEPARATOR, 0, _T(""));
  543. menu.AppendMenu(MF_STRING, 5, title_resetdefault);
  544. }
  545. // Will return zero if no selection was made (TPM_RETURNCMD)
  546. int nResult = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD, point.x, point.y, this, 0);
  547. switch(nResult)
  548. {
  549. case 0:
  550. break;
  551. case 1:
  552. OpenColumnEditor(nCol);
  553. break;
  554. case 2:
  555. OpenColumnPicker();
  556. break;
  557. case 3:
  558. GroupByColumn(nCol);
  559. break;
  560. case 4:
  561. {
  562. // Very strange problem when disabling group mode, then scrollbars are not updated
  563. // If placed in the bottom and disables group mode, then suddenly there is a strange offset
  564. // - Quick fix scroll to top, and then fix scroll bars afterwards
  565. int pos = GetScrollPos(SB_VERT);
  566. EnsureVisible(0, FALSE);
  567. RemoveAllGroups();
  568. EnableGroupView(FALSE);
  569. Scroll(CSize(0, pos));
  570. }
  571. break;
  572. case 5:
  573. ResetColumnDefaultState();
  574. break;
  575. default:
  576. {
  577. int nCol = nResult - 6;
  578. if (nCol < GetColumnCount())
  579. {
  580. ShowColumn(nCol, !IsColumnVisible(nCol));
  581. }
  582. else
  583. {
  584. int nProfile = nResult - GetColumnCount() - 7;
  585. SwichColumnProfile(profiles[nProfile]);
  586. }
  587. }
  588. break;
  589. }
  590. }
  591. //------------------------------------------------------------------------
  592. //! Override this method to change the context menu when activating context
  593. //! menu for the group headers
  594. //!
  595. //! @param pWnd Handle to the window in which the user right clicked the mouse
  596. //! @param point Position of the cursor, in screen coordinates, at the time of the mouse click.
  597. //! @param nGroupId ID of the group
  598. //------------------------------------------------------------------------
  599. void CGridListCtrlGroups::OnContextMenuGroup(CWnd* pWnd, CPoint point, int nGroupId)
  600. {
  601. CMenu menu;
  602. UINT uFlags = MF_BYPOSITION | MF_STRING;
  603. VERIFY( menu.CreatePopupMenu() );
  604. const CString& groupHeader = GetGroupHeader(nGroupId);
  605. // Provide menu-options for collapsing groups, if the collapsible state is not available
  606. #ifndef LVGS_COLLAPSIBLE
  607. if (IsGroupStateEnabled())
  608. {
  609. if (HasGroupState(nGroupId, LVGS_COLLAPSED))
  610. {
  611. CString menuText = CString(_T("Expand group: ")) + groupHeader;
  612. menu.InsertMenu(0, uFlags, 1, menuText);
  613. }
  614. else
  615. {
  616. CString menuText = CString(_T("Collapse group: ")) + groupHeader;
  617. menu.InsertMenu(0, uFlags, 2, menuText);
  618. }
  619. }
  620. #endif
  621. if (GetExtendedStyle() & LVS_EX_CHECKBOXES)
  622. {
  623. CString menuText = CString(_T("Check group: ")) + groupHeader;
  624. menu.InsertMenu(1, uFlags, 3, menuText);
  625. menuText = CString(_T("Uncheck group: ")) + groupHeader;
  626. menu.InsertMenu(2, uFlags, 4, menuText);
  627. }
  628. int nResult = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD, point.x, point.y, this, 0);
  629. switch(nResult)
  630. {
  631. case 1:
  632. SetGroupState(nGroupId, LVGS_NORMAL);
  633. break;
  634. case 2:
  635. SetGroupState(nGroupId, LVGS_COLLAPSED);
  636. break;
  637. case 3:
  638. CheckEntireGroup(nGroupId, true);
  639. break;
  640. case 4:
  641. CheckEntireGroup(nGroupId, false);
  642. break;
  643. }
  644. }
  645. //------------------------------------------------------------------------
  646. //! Override this method to change the context menu when activating context
  647. //! menu in client area with no rows
  648. //!
  649. //! @param pWnd Handle to the window in which the user right clicked the mouse
  650. //! @param point Position of the cursor, in screen coordinates, at the time of the mouse click.
  651. //------------------------------------------------------------------------
  652. void CGridListCtrlGroups::OnContextMenuGrid(CWnd* pWnd, CPoint point)
  653. {
  654. if (IsGroupStateEnabled())
  655. {
  656. CMenu menu;
  657. UINT uFlags = MF_BYPOSITION | MF_STRING;
  658. VERIFY( menu.CreatePopupMenu() );
  659. menu.InsertMenu(0, uFlags, 1, _T("Expand all groups"));
  660. menu.InsertMenu(1, uFlags, 2, _T("Collapse all groups"));
  661. menu.InsertMenu(2, uFlags, 3, _T("Disable grouping"));
  662. int nResult = menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD, point.x, point.y, this, 0);
  663. switch(nResult)
  664. {
  665. case 1:
  666. ExpandAllGroups();
  667. break;
  668. case 2:
  669. CollapseAllGroups();
  670. break;
  671. case 3:
  672. RemoveAllGroups();
  673. EnableGroupView(FALSE);
  674. break;
  675. }
  676. }
  677. }
  678. //------------------------------------------------------------------------
  679. //! Update the description text of the group footer
  680. //!
  681. //! @param nGroupId ID of the group
  682. //! @param strFooter The footer description text
  683. //! @param dwAlign Indicates the alignment of the footer text for the group
  684. //! @return Succeeded in updating the group footer
  685. //------------------------------------------------------------------------
  686. BOOL CGridListCtrlGroups::SetGroupFooter(int nGroupId, const CString& strFooter, DWORD dwAlign)
  687. {
  688. if (!IsGroupStateEnabled())
  689. return FALSE;
  690. #if _WIN32_WINNT >= 0x0600
  691. LVGROUP lg = {0};
  692. lg.cbSize = sizeof(lg);
  693. lg.mask = LVGF_FOOTER | LVGF_ALIGN;
  694. lg.uAlign = dwAlign;
  695. #ifdef UNICODE
  696. lg.pszFooter = (LPWSTR)(LPCTSTR)strFooter;
  697. lg.cchFooter = strFooter.GetLength();
  698. #else
  699. CComBSTR bstrFooter = strFooter;
  700. lg.pszFooter = bstrFooter;
  701. lg.cchFooter = bstrFooter.Length();
  702. #endif
  703. if (SetGroupInfo(nGroupId, (PLVGROUP)&lg) == -1)
  704. return FALSE;
  705. return TRUE;
  706. #else
  707. return FALSE;
  708. #endif
  709. }
  710. //------------------------------------------------------------------------
  711. //! Update the task link of the group header
  712. //!
  713. //! @param nGroupId ID of the group
  714. //! @param strTask The task description text
  715. //! @return Succeeded in updating the group task
  716. //------------------------------------------------------------------------
  717. BOOL CGridListCtrlGroups::SetGroupTask(int nGroupId, const CString& strTask)
  718. {
  719. if (!IsGroupStateEnabled())
  720. return FALSE;
  721. #if _WIN32_WINNT >= 0x0600
  722. LVGROUP lg = {0};
  723. lg.cbSize = sizeof(lg);
  724. lg.mask = LVGF_TASK;
  725. #ifdef UNICODE
  726. lg.pszTask = (LPWSTR)(LPCTSTR)strTask;
  727. lg.cchTask = strTask.GetLength();
  728. #else
  729. CComBSTR bstrTask = strTask;
  730. lg.pszTask = bstrTask;
  731. lg.cchTask = bstrTask.Length();
  732. #endif
  733. if (SetGroupInfo(nGroupId, (PLVGROUP)&lg) == -1)
  734. return FALSE;
  735. return TRUE;
  736. #else
  737. return FALSE;
  738. #endif
  739. }
  740. //------------------------------------------------------------------------
  741. //! Update the subtitle in the group header
  742. //!
  743. //! @param nGroupId ID of the group
  744. //! @param strSubtitle The subtitle description text
  745. //! @return Succeeded in updating the group subtitle
  746. //------------------------------------------------------------------------
  747. BOOL CGridListCtrlGroups::SetGroupSubtitle(int nGroupId, const CString& strSubtitle)
  748. {
  749. if (!IsGroupStateEnabled())
  750. return FALSE;
  751. #if _WIN32_WINNT >= 0x0600
  752. LVGROUP lg = {0};
  753. lg.cbSize = sizeof(lg);
  754. lg.mask = LVGF_SUBTITLE;
  755. #ifdef UNICODE
  756. lg.pszSubtitle = (LPWSTR)(LPCTSTR)strSubtitle;
  757. lg.cchSubtitle = strSubtitle.GetLength();
  758. #else
  759. CComBSTR bstrSubtitle = strSubtitle;
  760. lg.pszSubtitle = bstrSubtitle;
  761. lg.cchSubtitle = bstrSubtitle.Length();
  762. #endif
  763. if (SetGroupInfo(nGroupId, (PLVGROUP)&lg) == -1)
  764. return FALSE;
  765. return TRUE;
  766. #else
  767. return FALSE;
  768. #endif
  769. }
  770. //------------------------------------------------------------------------
  771. //! Update the image icon in the group header together with top and bottom
  772. //! description. Microsoft encourage people not to use this functionality.
  773. //!
  774. //! @param nGroupId ID of the group
  775. //! @param nImage Index of the title image in the control imagelist.
  776. //! @param strTopDesc Description text placed oppposite of the image
  777. //! @param strBottomDesc Description text placed below the top description
  778. //! @return Succeeded in updating the group image
  779. //------------------------------------------------------------------------
  780. BOOL CGridListCtrlGroups::SetGroupTitleImage(int nGroupId, int nImage, const CString& strTopDesc, const CString& strBottomDesc)
  781. {
  782. if (!IsGroupStateEnabled())
  783. return FALSE;
  784. #if _WIN32_WINNT >= 0x0600
  785. LVGROUP lg = {0};
  786. lg.cbSize = sizeof(lg);
  787. lg.mask = LVGF_TITLEIMAGE;
  788. lg.iTitleImage = nImage; // Index of the title image in the control imagelist.
  789. #ifdef UNICODE
  790. if (!strTopDesc.IsEmpty())
  791. {
  792. // Top description is drawn opposite the title image when there is
  793. // a title image, no extended image, and uAlign==LVGA_HEADER_CENTER.
  794. lg.mask |= LVGF_DESCRIPTIONTOP;
  795. lg.pszDescriptionTop = (LPWSTR)(LPCTSTR)strTopDesc;
  796. lg.cchDescriptionTop = strTopDesc.GetLength();
  797. }
  798. if (!strBottomDesc.IsEmpty())
  799. {
  800. // Bottom description is drawn under the top description text when there is
  801. // a title image, no extended image, and uAlign==LVGA_HEADER_CENTER.
  802. lg.mask |= LVGF_DESCRIPTIONBOTTOM;
  803. lg.pszDescriptionBottom = (LPWSTR)(LPCTSTR)strBottomDesc;
  804. lg.cchDescriptionBottom = strBottomDesc.GetLength();
  805. }
  806. #else
  807. CComBSTR bstrTopDesc = strTopDesc;
  808. CComBSTR bstrBottomDesc = strBottomDesc;
  809. if (!strTopDesc.IsEmpty())
  810. {
  811. lg.mask |= LVGF_DESCRIPTIONTOP;
  812. lg.pszDescriptionTop = bstrTopDesc;
  813. lg.cchDescriptionTop = bstrTopDesc.Length();
  814. }
  815. if (!strBottomDesc.IsEmpty())
  816. {
  817. lg.mask |= LVGF_DESCRIPTIONBOTTOM;
  818. lg.pszDescriptionBottom = bstrBottomDesc;
  819. lg.cchDescriptionBottom = bstrBottomDesc.Length();
  820. }
  821. #endif
  822. if (SetGroupInfo(nGroupId, (PLVGROUP)&lg) == -1)
  823. return FALSE;
  824. return TRUE;
  825. #else
  826. return FALSE;
  827. #endif
  828. }
  829. //------------------------------------------------------------------------
  830. //! LVN_GETEMPTYMARKUP message handler to show markup text when the list
  831. //! control is empty.
  832. //!
  833. //! @param pNMHDR Pointer to NMLVEMPTYMARKUP structure
  834. //! @param pResult Not used
  835. //! @return Is final message handler (Return FALSE to continue routing the message)
  836. //------------------------------------------------------------------------
  837. BOOL CGridListCtrlGroups::OnGetEmptyMarkup(NMHDR* pNMHDR, LRESULT* pResult)
  838. {
  839. if (m_EmptyMarkupText.IsEmpty())
  840. return FALSE;
  841. #if _WIN32_WINNT >= 0x0600
  842. NMLVEMPTYMARKUP* pEmptyMarkup = reinterpret_cast<NMLVEMPTYMARKUP*>(pNMHDR);
  843. pEmptyMarkup->dwFlags = EMF_CENTERED;
  844. #ifdef UNICODE
  845. lstrcpyn(pEmptyMarkup->szMarkup, (LPCTSTR)m_EmptyMarkupText, sizeof(pEmptyMarkup->szMarkup) / sizeof(WCHAR));
  846. #else
  847. #if __STDC_WANT_SECURE_LIB__
  848. mbstowcs_s(NULL, pEmptyMarkup->szMarkup, static_cast<LPCTSTR>(m_EmptyMarkupText), sizeof(pEmptyMarkup->szMarkup) / sizeof(WCHAR));
  849. #else
  850. mbstowcs(pEmptyMarkup->szMarkup, static_cast<LPCTSTR>(m_EmptyMarkupText), sizeof(pEmptyMarkup->szMarkup) / sizeof(WCHAR));
  851. #endif
  852. #endif
  853. *pResult = TRUE;
  854. #endif
  855. return TRUE;
  856. }
  857. //------------------------------------------------------------------------
  858. //! LVN_LINKCLICK message handler called when a group task link is clicked
  859. //!
  860. //! @param pNMHDR Pointer to NMLVLINK structure
  861. //! @param pResult Not used
  862. //! @return Is final message handler (Return FALSE to continue routing the message)
  863. //------------------------------------------------------------------------
  864. BOOL CGridListCtrlGroups::OnGroupTaskClick(NMHDR* pNMHDR, LRESULT* pResult)
  865. {
  866. #if _WIN32_WINNT >= 0x0600
  867. NMLVLINK* pLinkInfo = reinterpret_cast<NMLVLINK*>(pNMHDR);
  868. int nGroupId = pLinkInfo->iSubItem;
  869. nGroupId; // Avoid unreferenced variable warning
  870. #endif
  871. return FALSE;
  872. }
  873. //------------------------------------------------------------------------
  874. //! The framework calls this member function when the user double-clicks
  875. //! the left mouse button. Used to expand and collapse groups when group
  876. //! header is clicked.
  877. //!
  878. //! @param nFlags Indicates whether various virtual keys are down (MK_CONTROL, MK_SHIFT, etc.)
  879. //! @param point Specifies the x- and y-coordinate of the cursor relative to the upper-left corner of the window.
  880. //------------------------------------------------------------------------
  881. void CGridListCtrlGroups::OnLButtonDblClk(UINT nFlags, CPoint point)
  882. {
  883. CGridListCtrlEx::OnLButtonDblClk(nFlags, point);
  884. if (!IsGroupStateEnabled())
  885. return;
  886. int nGroupId = GroupHitTest(point);
  887. if (nGroupId != -1)
  888. {
  889. if (HasGroupState(nGroupId, LVGS_COLLAPSED))
  890. SetGroupState(nGroupId, LVGS_NORMAL);
  891. else
  892. SetGroupState(nGroupId, LVGS_COLLAPSED);
  893. }
  894. }
  895. //------------------------------------------------------------------------
  896. //! Override this method to provide the group a cell belongs to.
  897. //!
  898. //! @param nRow The index of the row
  899. //! @param nCol The index of the column
  900. //! @param nGroupId Text string to display in the cell
  901. //! @return True if the cell belongs to a group
  902. //------------------------------------------------------------------------
  903. BOOL CGridListCtrlGroups::OnDisplayCellGroup(int nRow, int nCol, int& nGroupId)
  904. {
  905. return false;
  906. }
  907. //------------------------------------------------------------------------
  908. //! LVN_GETDISPINFO message handler, which is called when details are
  909. //! needed for an item that specifies callback.
  910. //! - Cell-Group, when item is using I_GROUPIDCALLBACK
  911. //!
  912. //! @param pNMHDR Pointer to an NMLVDISPINFO structure
  913. //! @param pResult Not used
  914. //! @return Is final message handler (Return FALSE to continue routing the message)
  915. //------------------------------------------------------------------------
  916. BOOL CGridListCtrlGroups::OnGetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
  917. {
  918. NMLVDISPINFO* pNMW = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
  919. int nRow = pNMW->item.iItem;
  920. int nCol = pNMW->item.iSubItem;
  921. if (nRow < 0 || nRow >= GetItemCount())
  922. return FALSE; // requesting invalid item
  923. if (nCol < 0 || nCol >= GetHeaderCtrl()->GetItemCount())
  924. return FALSE; // requesting invalid item
  925. if (pNMW->item.mask & LVIF_GROUPID)
  926. {
  927. // Request group-id of the column (Virtual-list/LVS_OWNERDATA)
  928. int result = -1;
  929. if (OnDisplayCellGroup(nRow, nCol, result))
  930. pNMW->item.iGroupId = result;
  931. else
  932. pNMW->item.iGroupId = I_GROUPIDNONE;
  933. }
  934. return CGridListCtrlEx::OnGetDispInfo(pNMHDR, pResult);
  935. }
  936. namespace
  937. {
  938. struct PARAMSORT
  939. {
  940. PARAMSORT(HWND hWnd, int nCol, BOOL bAscending, CGridColumnTrait* pTrait)
  941. : m_hWnd(hWnd)
  942. , m_pTrait(pTrait)
  943. , m_ColumnIndex(nCol)
  944. , m_Ascending(bAscending)
  945. {}
  946. HWND m_hWnd;
  947. int m_ColumnIndex;
  948. BOOL m_Ascending;
  949. CGridColumnTrait* m_pTrait;
  950. CSimpleMap<int, CString> m_GroupNames;
  951. const CString& LookupGroupName(int nGroupId)
  952. {
  953. int groupIdx = m_GroupNames.FindKey(nGroupId);
  954. if (groupIdx == -1)
  955. {
  956. static const CString emptyStr;
  957. return emptyStr;
  958. }
  959. return m_GroupNames.GetValueAt(groupIdx);
  960. }
  961. };
  962. int CALLBACK SortFuncGroup(int leftId, int rightId, void* lParamSort)
  963. {
  964. PARAMSORT& ps = *(PARAMSORT*)lParamSort;
  965. const CString& leftText = ps.LookupGroupName(leftId);
  966. const CString& rightText = ps.LookupGroupName(rightId);
  967. LVITEM leftItem = {0};
  968. leftItem.pszText = const_cast<LPTSTR>((LPCTSTR)leftText);
  969. leftItem.cchTextMax = leftText.GetLength();
  970. LVITEM rightItem = {0};
  971. rightItem.pszText = const_cast<LPTSTR>((LPCTSTR)rightText);
  972. rightItem.cchTextMax = leftText.GetLength();
  973. return ps.m_pTrait->OnSortRows(leftItem, rightItem, ps.m_Ascending);
  974. }
  975. }
  976. //------------------------------------------------------------------------
  977. //! Changes the row sorting in regard to the specified column
  978. //!
  979. //! @param nCol The index of the column
  980. //! @param bAscending Should the arrow be up or down
  981. //! @return True / false depending on whether sort is possible
  982. //------------------------------------------------------------------------
  983. BOOL CGridListCtrlGroups::SortColumn(int nCol, BOOL bAscending)
  984. {
  985. CWaitCursor waitCursor;
  986. if (IsGroupViewEnabled())
  987. {
  988. SetRedraw(FALSE);
  989. GroupByColumn(nCol);
  990. // Cannot use GetGroupInfo during sort
  991. PARAMSORT paramsort(m_hWnd, nCol, bAscending, GetColumnTrait(nCol));
  992. for(int nRow = 0 ; nRow < GetItemCount() ; ++nRow)
  993. {
  994. int nGroupId = GetRowGroupId(nRow);
  995. if (nGroupId != -1 && paramsort.m_GroupNames.FindKey(nGroupId) == -1)
  996. paramsort.m_GroupNames.Add(nGroupId, GetGroupHeader(nGroupId));
  997. }
  998. SetRedraw(TRUE);
  999. Invalidate(FALSE);
  1000. // Avoid bug in CListCtrl::SortGroups() which differs from ListView_SortGroups
  1001. if (!ListView_SortGroups(m_hWnd, SortFuncGroup, &paramsort))
  1002. return false;
  1003. }
  1004. else
  1005. {
  1006. if (!CGridListCtrlEx::SortColumn(nCol, bAscending))
  1007. return false;
  1008. }
  1009. return true;
  1010. }
  1011. //------------------------------------------------------------------------
  1012. //! WM_PAINT message handler called when needing to redraw list control.
  1013. //! Used to display text when the list control is empty
  1014. //------------------------------------------------------------------------
  1015. void CGridListCtrlGroups::OnPaint()
  1016. {
  1017. #if _WIN32_WINNT >= 0x0600
  1018. if (UsingVisualStyle())
  1019. {
  1020. // Use LVN_GETEMPTYMARKUP if available
  1021. CListCtrl::OnPaint(); // default
  1022. return;
  1023. }
  1024. #endif
  1025. CGridListCtrlEx::OnPaint();
  1026. }
  1027. // MFC headers with group-support is only availabe from VS.NET
  1028. #if _MSC_VER < 1300
  1029. AFX_INLINE LRESULT CGridListCtrlGroups::InsertGroup(int index, PLVGROUP pgrp)
  1030. {
  1031. ASSERT(::IsWindow(m_hWnd));
  1032. return ListView_InsertGroup(m_hWnd, index, pgrp);
  1033. }
  1034. AFX_INLINE int CGridListCtrlGroups::SetGroupInfo(int iGroupId, PLVGROUP pgrp)
  1035. {
  1036. ASSERT(::IsWindow(m_hWnd));
  1037. return (int)ListView_SetGroupInfo(m_hWnd, iGroupId, pgrp);
  1038. }
  1039. AFX_INLINE int CGridListCtrlGroups::GetGroupInfo(int iGroupId, PLVGROUP pgrp) const
  1040. {
  1041. ASSERT(::IsWindow(m_hWnd));
  1042. return (int)ListView_GetGroupInfo(m_hWnd, iGroupId, pgrp);
  1043. }
  1044. AFX_INLINE LRESULT CGridListCtrlGroups::RemoveGroup(int iGroupId)
  1045. {
  1046. ASSERT(::IsWindow(m_hWnd));
  1047. return ListView_RemoveGroup(m_hWnd, iGroupId);
  1048. }
  1049. AFX_INLINE LRESULT CGridListCtrlGroups::MoveGroup(int iGroupId, int toIndex)
  1050. {
  1051. ASSERT(::IsWindow(m_hWnd));
  1052. return ListView_MoveGroup(m_hWnd, iGroupId, toIndex);
  1053. }
  1054. AFX_INLINE LRESULT CGridListCtrlGroups::MoveItemToGroup(int idItemFrom, int idGroupTo)
  1055. {
  1056. ASSERT(::IsWindow(m_hWnd));
  1057. return ListView_MoveItemToGroup(m_hWnd, idItemFrom, idGroupTo);
  1058. }
  1059. AFX_INLINE void CGridListCtrlGroups::SetGroupMetrics(PLVGROUPMETRICS pGroupMetrics)
  1060. {
  1061. ASSERT(::IsWindow(m_hWnd));
  1062. ListView_SetGroupMetrics(m_hWnd, pGroupMetrics);
  1063. }
  1064. AFX_INLINE void CGridListCtrlGroups::GetGroupMetrics(PLVGROUPMETRICS pGroupMetrics) const
  1065. {
  1066. ASSERT(::IsWindow(m_hWnd));
  1067. ListView_GetGroupMetrics(m_hWnd, pGroupMetrics);
  1068. }
  1069. AFX_INLINE LRESULT CGridListCtrlGroups::EnableGroupView(BOOL fEnable)
  1070. {
  1071. ASSERT(::IsWindow(m_hWnd));
  1072. return ListView_EnableGroupView(m_hWnd, fEnable);
  1073. }
  1074. AFX_INLINE BOOL CGridListCtrlGroups::SortGroups(PFNLVGROUPCOMPARE _pfnGroupCompare, LPVOID _plv)
  1075. {
  1076. ASSERT(::IsWindow(m_hWnd));
  1077. return (BOOL)::SendMessage(m_hWnd, LVM_SORTGROUPS, (WPARAM)(LPARAM)_plv, (LPARAM)_pfnGroupCompare );
  1078. }
  1079. AFX_INLINE LRESULT CGridListCtrlGroups::InsertGroupSorted(PLVINSERTGROUPSORTED pStructInsert)
  1080. {
  1081. ASSERT(::IsWindow(m_hWnd));
  1082. return ListView_InsertGroupSorted(m_hWnd, pStructInsert);
  1083. }
  1084. AFX_INLINE void CGridListCtrlGroups::RemoveAllGroups()
  1085. {
  1086. ASSERT(::IsWindow(m_hWnd));
  1087. ListView_RemoveAllGroups(m_hWnd);
  1088. }
  1089. AFX_INLINE BOOL CGridListCtrlGroups::HasGroup(int iGroupId) const
  1090. {
  1091. ASSERT(::IsWindow(m_hWnd));
  1092. return (BOOL)ListView_HasGroup(m_hWnd, iGroupId);
  1093. }
  1094. AFX_INLINE BOOL CGridListCtrlGroups::IsGroupViewEnabled() const
  1095. {
  1096. ASSERT(::IsWindow(m_hWnd));
  1097. return ListView_IsGroupViewEnabled(m_hWnd);
  1098. }
  1099. #endif // _MSC_VER < 1300
  1100. #endif // _MSC_VER < 1500 || defined UNICODE || defined CGRIDLISTCTRLEX_GROUPMODE
  1101. #endif // _WIN32_WINNT >= 0x0501