PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

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

http://usb-travis.googlecode.com/
C++ | 2001 lines | 1294 code | 212 blank | 495 comment | 307 complexity | 92c3679da46e50cbf16eea7677c9cb79 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-3.0, LGPL-2.0

Large files files are truncated, but you can click here to view the full file

  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 "CGridListCtrlEx.h"
  8. #include <shlwapi.h> // IsThemeEnabled
  9. #include <afxole.h> // COleDataSource
  10. #pragma warning(disable:4100) // unreferenced formal parameter
  11. #include "CGridColumnTraitText.h"
  12. #include "CGridRowTraitText.h"
  13. #include "ViewConfigSection.h"
  14. //------------------------------------------------------------------------
  15. //! @cond INTERNAL
  16. //------------------------------------------------------------------------
  17. template<class T>
  18. class COleDropTargetWnd : public COleDropTarget
  19. {
  20. T* m_pWnd;
  21. BOOL m_DragSource;
  22. BOOL m_DragDestination;
  23. public:
  24. COleDropTargetWnd()
  25. : m_pWnd(NULL)
  26. , m_DragSource(false)
  27. , m_DragDestination(false)
  28. {}
  29. BOOL Register(T* pWnd)
  30. {
  31. if (m_pWnd != NULL)
  32. {
  33. ASSERT(m_pWnd == pWnd);
  34. return TRUE; // Already registered
  35. }
  36. if (COleDropTarget::Register(pWnd) == FALSE)
  37. return FALSE;
  38. m_pWnd = pWnd;
  39. return TRUE;
  40. }
  41. BOOL IsDragSource() const
  42. {
  43. return m_DragSource;
  44. }
  45. BOOL IsDragDestination() const
  46. {
  47. return m_DragDestination;
  48. }
  49. void SetDragSource(BOOL value)
  50. {
  51. m_DragSource = value;
  52. m_DragDestination = false;
  53. }
  54. virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
  55. {
  56. ASSERT(m_pWnd == pWnd && m_pWnd != NULL);
  57. m_DragDestination = true;
  58. return m_pWnd->OnDragEnter(pDataObject, dwKeyState, point);
  59. }
  60. virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
  61. {
  62. ASSERT(m_pWnd == pWnd && m_pWnd != NULL);
  63. return m_pWnd->OnDragOver(pDataObject, dwKeyState, point);
  64. }
  65. virtual void OnDragLeave(CWnd* pWnd)
  66. {
  67. ASSERT(m_pWnd == pWnd && m_pWnd != NULL);
  68. m_DragDestination = false;
  69. m_pWnd->OnDragLeave();
  70. }
  71. virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
  72. {
  73. ASSERT(m_pWnd == pWnd && m_pWnd != NULL);
  74. m_DragDestination = false;
  75. return m_pWnd->OnDrop(pDataObject, dropEffect, point);
  76. }
  77. };
  78. template<class T>
  79. class COleDataSourceWnd : public COleDataSource
  80. {
  81. COleDropTargetWnd<T>* m_pTarget;
  82. public:
  83. COleDataSourceWnd(COleDropTargetWnd<T>* pTarget)
  84. : m_pTarget(pTarget)
  85. {
  86. if (m_pTarget != NULL)
  87. m_pTarget->SetDragSource(true);
  88. }
  89. ~COleDataSourceWnd()
  90. {
  91. if (m_pTarget != NULL)
  92. m_pTarget->SetDragSource(false);
  93. }
  94. };
  95. //------------------------------------------------------------------------
  96. //! @endcond INTERNAL
  97. //------------------------------------------------------------------------
  98. BEGIN_MESSAGE_MAP(CGridListCtrlEx, CListCtrl)
  99. //{{AFX_MSG_MAP(CGridListCtrlEx)
  100. ON_NOTIFY_REFLECT_EX(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
  101. ON_NOTIFY_REFLECT_EX(LVN_ENDLABELEDIT, OnEndLabelEdit)
  102. ON_NOTIFY_REFLECT_EX(LVN_GETDISPINFO, OnGetDispInfo) // Text Callback
  103. ON_MESSAGE(LVM_DELETECOLUMN, OnDeleteColumn)
  104. ON_MESSAGE(LVM_INSERTCOLUMN, OnInsertColumn)
  105. ON_MESSAGE(LVM_SETCOLUMNWIDTH, OnSetColumnWidth)
  106. ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
  107. ON_NOTIFY_EX(HDN_BEGINTRACKA, 0, OnHeaderBeginResize)
  108. ON_NOTIFY_EX(HDN_BEGINTRACKW, 0, OnHeaderBeginResize)
  109. ON_NOTIFY_EX(HDN_ENDTRACKA, 0, OnHeaderEndResize)
  110. ON_NOTIFY_EX(HDN_ENDTRACKW, 0, OnHeaderEndResize)
  111. ON_NOTIFY_EX(HDN_ITEMCHANGINGA, 0, OnHeaderItemChanging)
  112. ON_NOTIFY_EX(HDN_ITEMCHANGINGW, 0, OnHeaderItemChanging)
  113. ON_NOTIFY_EX(HDN_BEGINDRAG, 0, OnHeaderBeginDrag)
  114. ON_NOTIFY_EX(HDN_ENDDRAG, 0, OnHeaderEndDrag)
  115. ON_NOTIFY_EX(HDN_DIVIDERDBLCLICKA, 0, OnHeaderDividerDblClick)
  116. ON_NOTIFY_EX(HDN_DIVIDERDBLCLICKW, 0, OnHeaderDividerDblClick)
  117. ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnToolNeedText)
  118. ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnToolNeedText)
  119. ON_NOTIFY_REFLECT_EX(LVN_COLUMNCLICK, OnHeaderClick) // Column Click
  120. ON_NOTIFY_REFLECT_EX(NM_CLICK, OnItemClick) // Cell Click
  121. ON_NOTIFY_REFLECT_EX(NM_DBLCLK, OnItemDblClick) // Cell Double Click
  122. ON_NOTIFY_REFLECT_EX(LVN_ODFINDITEM, OnOwnerDataFindItem) // Owner Data Find Item
  123. ON_NOTIFY_REFLECT_EX(LVN_BEGINDRAG, OnBeginDrag) // Begin Drag Dropb
  124. ON_WM_CONTEXTMENU() // OnContextMenu
  125. ON_WM_KEYDOWN() // OnKeyDown
  126. ON_WM_LBUTTONDOWN() // OnLButtonDown(UINT nFlags, CPoint point)
  127. ON_WM_RBUTTONDOWN() // OnRButtonDown(UINT nFlags, CPoint point)
  128. ON_WM_LBUTTONDBLCLK() // OnLButtonDblClk
  129. ON_WM_HSCROLL() // OnHScroll
  130. ON_WM_VSCROLL() // OnVScroll
  131. ON_WM_CHAR() // OnChar (Keyboard search)
  132. ON_WM_PAINT() // OnPaint
  133. ON_WM_CREATE() // OnCreate
  134. ON_WM_KILLFOCUS() // OnKillFocus
  135. ON_WM_DESTROY() // OnDestroy
  136. ON_MESSAGE(WM_COPY, OnCopy) // Clipboard
  137. ON_MESSAGE(WM_SETFONT, OnSetFont) // SetFont notification
  138. //}}AFX_MSG_MAP
  139. END_MESSAGE_MAP()
  140. //------------------------------------------------------------------------
  141. //! Constructor
  142. //------------------------------------------------------------------------
  143. CGridListCtrlEx::CGridListCtrlEx()
  144. : m_FocusCell(-1)
  145. , m_SortCol(-1)
  146. , m_Ascending(false)
  147. , m_UsingVisualStyle(false)
  148. , m_pEditor(NULL)
  149. , m_LastSearchCell(-1)
  150. , m_LastSearchRow(-1)
  151. , m_RepeatSearchCount(0)
  152. , m_EmptyMarkupText(_T("There are no items to show in this view."))
  153. , m_pOleDropTarget(NULL)
  154. , m_Margin(1.0) // Higher row-height (more room for edit-ctrl border)
  155. , m_pDefaultRowTrait(new CGridRowTraitText)
  156. , m_pColumnConfig(NULL)
  157. , m_InvalidateMarkupText(true)
  158. , m_bUseVisualStyles(TRUE)
  159. {}
  160. //------------------------------------------------------------------------
  161. //! Destructor
  162. //------------------------------------------------------------------------
  163. CGridListCtrlEx::~CGridListCtrlEx()
  164. {
  165. for(int nCol = GetColumnTraitSize() - 1; nCol >= 0 ; --nCol)
  166. DeleteColumnTrait(nCol);
  167. delete m_pDefaultRowTrait;
  168. m_pDefaultRowTrait = NULL;
  169. delete m_pColumnConfig;
  170. m_pColumnConfig = NULL;
  171. if (m_pOleDropTarget) delete m_pOleDropTarget;
  172. m_pOleDropTarget = NULL;
  173. }
  174. //------------------------------------------------------------------------
  175. //! Sets the interface for handling state persistence for the list control
  176. //!
  177. //! @param pColumnConfig The new column state interface handler
  178. //------------------------------------------------------------------------
  179. void CGridListCtrlEx::SetupColumnConfig(CViewConfigSectionProfiles* pColumnConfig)
  180. {
  181. delete m_pColumnConfig;
  182. m_pColumnConfig = pColumnConfig;
  183. if (!m_pColumnConfig->HasDefaultConfig())
  184. {
  185. // Validate that column data is setup correctly
  186. CSimpleMap<int, int> uniqueChecker;
  187. for(int nCol = 0; nCol < GetColumnCount(); ++nCol)
  188. {
  189. if (IsColumnAlwaysHidden(nCol))
  190. continue;
  191. int nColData = GetColumnData(nCol);
  192. ASSERT(uniqueChecker.FindKey(nColData) == -1);
  193. uniqueChecker.Add(nColData, nCol);
  194. }
  195. SaveState(m_pColumnConfig->GetDefaultConfig());
  196. }
  197. LoadState(*m_pColumnConfig);
  198. }
  199. //------------------------------------------------------------------------
  200. //! Loads and applies the column configuration for the list control
  201. //!
  202. //! @param config The interface for persisting the configuration
  203. //------------------------------------------------------------------------
  204. void CGridListCtrlEx::LoadState(CViewConfigSection& config)
  205. {
  206. // Prevent updating the internal column-state-container while loading
  207. CViewConfigSectionProfiles* pColumnConfig = m_pColumnConfig;
  208. m_pColumnConfig = NULL;
  209. int nVisibleCols = config.GetIntSetting(_T("ColumnCount"));
  210. int nColCount = GetColumnCount();
  211. int* pOrderArray = new int[nColCount];
  212. GetColumnOrderArray(pOrderArray, nColCount);
  213. SetRedraw(FALSE);
  214. // All invisible columns must be place in the begining of the order-array
  215. int nColOrder = nColCount;
  216. for(int i = nVisibleCols - 1; i >= 0; --i)
  217. {
  218. CString colSetting;
  219. colSetting.Format(_T("ColumnData_%d"), i);
  220. int nColData = config.GetIntSetting(colSetting);
  221. for(int nCol = 0; nCol < nColCount; ++nCol)
  222. {
  223. // Check if already in array
  224. BOOL alreadyIncluded = false;
  225. for(int j = nColOrder; j < nColCount; ++j)
  226. if (pOrderArray[j] == nCol)
  227. {
  228. alreadyIncluded = true;
  229. break;
  230. }
  231. if (alreadyIncluded)
  232. continue;
  233. if (nColData == GetColumnData(nCol))
  234. {
  235. // Column still exists
  236. if (IsColumnAlwaysHidden(nCol))
  237. continue;
  238. CGridColumnTrait::ColumnState& columnState = GetColumnTrait(nCol)->GetColumnState();
  239. columnState.m_Visible = true;
  240. LoadColumnState(i, nCol, config);
  241. pOrderArray[--nColOrder] = nCol;
  242. break;
  243. }
  244. }
  245. }
  246. // Are there any always visible columns, that we must ensure are visible ?
  247. for(int nCol = nColCount - 1; nCol >= 0; --nCol)
  248. {
  249. if (!IsColumnAlwaysVisible(nCol))
  250. continue;
  251. BOOL visible = false;
  252. for(int i = nColOrder; i < nColCount; ++i)
  253. {
  254. if (pOrderArray[i] == nCol)
  255. {
  256. visible = true;
  257. break;
  258. }
  259. }
  260. if (!visible)
  261. {
  262. CGridColumnTrait::ColumnState& columnState = GetColumnTrait(nCol)->GetColumnState();
  263. columnState.m_Visible = true;
  264. pOrderArray[--nColOrder] = nCol;
  265. }
  266. }
  267. // Did we find any visible columns in the saved configuration ?
  268. if (nColOrder < nColCount)
  269. {
  270. // All remaining columns are marked as invisible
  271. while(nColOrder > 0)
  272. {
  273. // Find nCol som ikke er i array
  274. int nCol = -1;
  275. for(nCol = nColCount - 1; nCol >= 0; --nCol)
  276. {
  277. BOOL visible = false;
  278. for(int j = nColOrder; j < nColCount; ++j)
  279. {
  280. if (pOrderArray[j] == nCol)
  281. {
  282. visible = true;
  283. break;
  284. }
  285. }
  286. if (!visible)
  287. break;
  288. }
  289. ASSERT(nCol != -1);
  290. CGridColumnTrait::ColumnState& columnState = GetColumnTrait(nCol)->GetColumnState();
  291. columnState.m_OrgPosition = -1;
  292. columnState.m_OrgWidth = GetColumnWidth(nCol);
  293. SetColumnWidth(nCol, 0);
  294. columnState.m_Visible = false;
  295. ASSERT(nColOrder > 0);
  296. pOrderArray[--nColOrder] = nCol;
  297. }
  298. // Only update the column configuration if there are visible columns
  299. ASSERT(nColOrder == 0); // All entries in the order-array must be set
  300. SetColumnOrderArray(nColCount, pOrderArray);
  301. }
  302. delete [] pOrderArray;
  303. m_pColumnConfig = pColumnConfig;
  304. SetRedraw(TRUE);
  305. Invalidate(TRUE);
  306. UpdateWindow();
  307. }
  308. //------------------------------------------------------------------------
  309. //! Loads the column state of a single column
  310. //!
  311. //! @param nConfigCol The column index in the persisting interface
  312. //! @param nOwnerCol The column index in the owner list control
  313. //! @param config The interface for persisting the configuration
  314. //------------------------------------------------------------------------
  315. void CGridListCtrlEx::LoadColumnState(int nConfigCol, int nOwnerCol, CViewConfigSection& config)
  316. {
  317. CString colSetting;
  318. if (IsColumnResizable(nOwnerCol))
  319. {
  320. colSetting.Format(_T("ColumnWidth_%d"), nConfigCol);
  321. int width = config.GetIntSetting(colSetting);
  322. SetColumnWidth(nOwnerCol, width);
  323. }
  324. }
  325. //------------------------------------------------------------------------
  326. //! Saves the column configuration of the list control
  327. //!
  328. //! @param config The interface for persisting the configuration
  329. //------------------------------------------------------------------------
  330. void CGridListCtrlEx::SaveState(CViewConfigSection& config)
  331. {
  332. config.RemoveCurrentConfig(); // Reset the existing config
  333. int nColCount = GetColumnCount();
  334. int* pOrderArray = new int[nColCount];
  335. GetColumnOrderArray(pOrderArray, nColCount);
  336. int nVisibleCols = 0;
  337. for(int i = 0 ; i < nColCount; ++i)
  338. {
  339. int nCol = pOrderArray[i];
  340. int nColData = GetColumnData(nCol);
  341. if (IsColumnVisible(nCol))
  342. {
  343. CString colSetting;
  344. colSetting.Format(_T("ColumnData_%d"), nVisibleCols);
  345. config.SetIntSetting(colSetting, nColData);
  346. SaveColumnState(nVisibleCols, nCol, config);
  347. nVisibleCols++;
  348. }
  349. }
  350. config.SetIntSetting(_T("ColumnCount"), nVisibleCols);
  351. delete [] pOrderArray;
  352. }
  353. //------------------------------------------------------------------------
  354. //! Saves the column state of a single column
  355. //!
  356. //! @param nConfigCol The column index in the persisting interface
  357. //! @param nOwnerCol The column index in the owner list control
  358. //! @param config The interface for persisting the configuration
  359. //------------------------------------------------------------------------
  360. void CGridListCtrlEx::SaveColumnState(int nConfigCol, int nOwnerCol, CViewConfigSection& config)
  361. {
  362. CString colSetting;
  363. colSetting.Format(_T("ColumnWidth_%d"), nConfigCol);
  364. config.SetIntSetting(colSetting, GetColumnWidth(nOwnerCol));
  365. }
  366. //------------------------------------------------------------------------
  367. //! Is there a column configuration editor available for this column ?
  368. //!
  369. //! @param nCol The index of the column
  370. //! @param strTitle Title to show in the context menu when right-clicking the column
  371. //! @return Column editor available (true / false)
  372. //------------------------------------------------------------------------
  373. BOOL CGridListCtrlEx::HasColumnEditor(int nCol, CString& strTitle)
  374. {
  375. return false;
  376. }
  377. //------------------------------------------------------------------------
  378. //! Open the column configuration editor for the column (If one available)
  379. //!
  380. //! @param nCol The index of the column
  381. //------------------------------------------------------------------------
  382. void CGridListCtrlEx::OpenColumnEditor(int nCol)
  383. {
  384. }
  385. //------------------------------------------------------------------------
  386. //! Is there a column picker available that can add / remove columns
  387. //!
  388. //! @param strTitle Title to show in the context menu when right-clicking the column
  389. //! @return Column picker available (true / false)
  390. //------------------------------------------------------------------------
  391. BOOL CGridListCtrlEx::HasColumnPicker(CString& strTitle)
  392. {
  393. return false;
  394. }
  395. //------------------------------------------------------------------------
  396. //! Open the column picker for the list control
  397. //------------------------------------------------------------------------
  398. void CGridListCtrlEx::OpenColumnPicker()
  399. {
  400. }
  401. //------------------------------------------------------------------------
  402. //! Has the ability to reset the column configuration to its default configuration
  403. //!
  404. //! @param strTitle Title to show in the context menu when right-clicking the column
  405. //! @return Default column configuration is available (true / false)
  406. //------------------------------------------------------------------------
  407. BOOL CGridListCtrlEx::HasColumnDefaultState(CString& strTitle)
  408. {
  409. if (m_pColumnConfig == NULL)
  410. return false;
  411. strTitle = _T("Reset columns");
  412. return m_pColumnConfig->HasDefaultConfig();
  413. }
  414. //------------------------------------------------------------------------
  415. //! Reset the column configuration to its default configuration
  416. //------------------------------------------------------------------------
  417. void CGridListCtrlEx::ResetColumnDefaultState()
  418. {
  419. if (m_pColumnConfig == NULL)
  420. return;
  421. m_pColumnConfig->ResetConfigDefault();
  422. LoadState(*m_pColumnConfig);
  423. }
  424. //------------------------------------------------------------------------
  425. //! Can switch between multiple column configurations
  426. //!
  427. //! @param profiles List of available column profiles
  428. //! @param strTitle Title to show in the context menu when right-clicking the column
  429. //! @return Name of the current column profile
  430. //------------------------------------------------------------------------
  431. CString CGridListCtrlEx::HasColumnProfiles(CSimpleArray<CString>& profiles, CString& strTitle)
  432. {
  433. if (m_pColumnConfig == NULL)
  434. return _T("");
  435. strTitle = _T("Column Profiles");
  436. m_pColumnConfig->GetProfiles(profiles);
  437. return m_pColumnConfig->GetActiveProfile();
  438. }
  439. //------------------------------------------------------------------------
  440. //! Switch to different column configurations profile
  441. //!
  442. //! @param strProfile List of available column profiles
  443. //------------------------------------------------------------------------
  444. void CGridListCtrlEx::SwichColumnProfile(const CString& strProfile)
  445. {
  446. if (m_pColumnConfig == NULL)
  447. return;
  448. // Save the current configuration before switching to the new one
  449. SaveState(*m_pColumnConfig);
  450. m_pColumnConfig->SetActiveProfile(strProfile);
  451. LoadState(*m_pColumnConfig);
  452. }
  453. //------------------------------------------------------------------------
  454. //! Called after a column has been added / removed
  455. //------------------------------------------------------------------------
  456. void CGridListCtrlEx::OnSaveStateColumnPick()
  457. {
  458. if (m_pColumnConfig != NULL)
  459. SaveState(*m_pColumnConfig);
  460. }
  461. //------------------------------------------------------------------------
  462. //! Called after a column has been resized
  463. //------------------------------------------------------------------------
  464. void CGridListCtrlEx::OnSaveStateColumnResize()
  465. {
  466. if (m_pColumnConfig != NULL)
  467. SaveState(*m_pColumnConfig);
  468. }
  469. //------------------------------------------------------------------------
  470. //! Called when the list control looses focus to another control
  471. //------------------------------------------------------------------------
  472. void CGridListCtrlEx::OnSaveStateKillFocus()
  473. {
  474. if (m_pColumnConfig != NULL)
  475. SaveState(*m_pColumnConfig);
  476. }
  477. //------------------------------------------------------------------------
  478. //! Checks if the current OS version against the requested OS version
  479. //!
  480. //! @param requestOS The full version number of the OS required (Ex 0x0600)
  481. //------------------------------------------------------------------------
  482. BOOL CGridListCtrlEx::CheckOSVersion(WORD requestOS)
  483. {
  484. static WORD fullver = 0;
  485. if (fullver == 0)
  486. {
  487. OSVERSIONINFO osver = {0};
  488. osver.dwOSVersionInfoSize = sizeof(osver);
  489. VERIFY( GetVersionEx(&osver) );
  490. fullver = MAKEWORD(osver.dwMinorVersion, osver.dwMajorVersion);
  491. }
  492. return requestOS <= fullver;
  493. }
  494. namespace
  495. {
  496. BOOL IsCommonControlsEnabled()
  497. {
  498. BOOL commoncontrols = false;
  499. // Test if application has access to common controls
  500. HMODULE hinstDll = ::LoadLibrary(_T("comctl32.dll"));
  501. if (hinstDll)
  502. {
  503. DLLGETVERSIONPROC pDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(hinstDll, "DllGetVersion");
  504. if (pDllGetVersion != NULL)
  505. {
  506. DLLVERSIONINFO dvi = {0};
  507. dvi.cbSize = sizeof(dvi);
  508. HRESULT hRes = pDllGetVersion ((DLLVERSIONINFO*) &dvi);
  509. if (SUCCEEDED(hRes))
  510. commoncontrols = dvi.dwMajorVersion >= 6;
  511. }
  512. ::FreeLibrary(hinstDll);
  513. }
  514. return commoncontrols;
  515. }
  516. BOOL IsThemeEnabled()
  517. {
  518. BOOL XPStyle = false;
  519. BOOL (__stdcall * pIsAppThemed)();
  520. BOOL (__stdcall * pIsThemeActive)();
  521. // Test if operating system has themes enabled
  522. HMODULE hinstDll = ::LoadLibrary(_T("UxTheme.dll"));
  523. if (hinstDll)
  524. {
  525. (FARPROC&)pIsAppThemed = ::GetProcAddress(hinstDll, "IsAppThemed");
  526. (FARPROC&)pIsThemeActive = ::GetProcAddress(hinstDll, "IsThemeActive");
  527. if (pIsAppThemed != NULL && pIsThemeActive != NULL)
  528. {
  529. if (pIsAppThemed() && pIsThemeActive())
  530. {
  531. // Test if application has themes enabled by loading the proper DLL
  532. XPStyle = IsCommonControlsEnabled();
  533. }
  534. }
  535. ::FreeLibrary(hinstDll);
  536. }
  537. return XPStyle;
  538. }
  539. LRESULT EnableWindowTheme(HWND hwnd, LPCWSTR classList, LPCWSTR subApp, LPCWSTR idlist)
  540. {
  541. LRESULT lResult = S_FALSE;
  542. HRESULT (__stdcall * pSetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
  543. HANDLE (__stdcall * pOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
  544. HRESULT (__stdcall * pCloseThemeData)(HANDLE hTheme);
  545. HMODULE hinstDll = ::LoadLibrary(_T("UxTheme.dll"));
  546. if (hinstDll)
  547. {
  548. (FARPROC&)pOpenThemeData = ::GetProcAddress(hinstDll, "OpenThemeData");
  549. (FARPROC&)pCloseThemeData = ::GetProcAddress(hinstDll, "CloseThemeData");
  550. (FARPROC&)pSetWindowTheme = ::GetProcAddress(hinstDll, "SetWindowTheme");
  551. if (pSetWindowTheme && pOpenThemeData && pCloseThemeData)
  552. {
  553. HANDLE theme = pOpenThemeData(hwnd, classList);
  554. if (theme != NULL)
  555. {
  556. VERIFY(pCloseThemeData(theme) == S_OK);
  557. lResult = pSetWindowTheme(hwnd, subApp, idlist);
  558. }
  559. }
  560. ::FreeLibrary(hinstDll);
  561. }
  562. return lResult;
  563. }
  564. }
  565. //------------------------------------------------------------------------
  566. //! Activate visual style for the list control (Vista Theme)
  567. //!
  568. //! @param bValue Specifies whether the visual styles should be enabled or not
  569. //! @return S_FALSE if visual styles could not be enabled
  570. //------------------------------------------------------------------------
  571. LRESULT CGridListCtrlEx::EnableVisualStyles(BOOL bValue)
  572. {
  573. if (!IsThemeEnabled())
  574. {
  575. m_UsingVisualStyle = false;
  576. return S_FALSE;
  577. }
  578. if (!CheckOSVersion(0x0600))
  579. {
  580. m_UsingVisualStyle = false;
  581. return S_FALSE;
  582. }
  583. LRESULT rc = S_FALSE;
  584. if (bValue)
  585. rc = EnableWindowTheme(GetSafeHwnd(), L"ListView", L"Explorer", NULL);
  586. else
  587. rc = EnableWindowTheme(GetSafeHwnd(), L"", L"", NULL);
  588. if (bValue && rc == S_OK)
  589. {
  590. // OBS! Focus retangle is not painted properly without double-buffering
  591. m_UsingVisualStyle = true;
  592. #if (_WIN32_WINNT >= 0x501)
  593. if (CheckOSVersion(0x501))
  594. SetExtendedStyle(LVS_EX_DOUBLEBUFFER | GetExtendedStyle());
  595. #endif
  596. }
  597. else
  598. {
  599. m_UsingVisualStyle = false;
  600. }
  601. return rc;
  602. }
  603. //------------------------------------------------------------------------
  604. //! Configures the initial style of the list control when the it is created
  605. //------------------------------------------------------------------------
  606. void CGridListCtrlEx::OnCreateStyle()
  607. {
  608. // Will be called twice when placed inside a CView
  609. // Not using VERIFY / ASSERT as MessageBox cannot be opened during subclassing/creating
  610. if (!(GetStyle() & LVS_REPORT))
  611. DebugBreak(); // CListCtrl must be created with style LVS_REPORT
  612. if (GetStyle() & LVS_OWNERDRAWFIXED)
  613. DebugBreak(); // CListCtrl must be created without style LVS_OWNERDRAWFIXED
  614. ModifyStyle(0, LVS_SHOWSELALWAYS);
  615. SetExtendedStyle(GetExtendedStyle() | LVS_EX_FULLROWSELECT);
  616. SetExtendedStyle(GetExtendedStyle() | LVS_EX_HEADERDRAGDROP);
  617. SetExtendedStyle(GetExtendedStyle() | LVS_EX_GRIDLINES);
  618. SetExtendedStyle(GetExtendedStyle() | LVS_EX_SUBITEMIMAGES);
  619. #if (_WIN32_WINNT >= 0x501)
  620. if (CheckOSVersion(0x501))
  621. SetExtendedStyle(GetExtendedStyle() | LVS_EX_DOUBLEBUFFER);
  622. #endif
  623. // Enable Vista-look if possible
  624. EnableVisualStyles(m_bUseVisualStyles);
  625. // Enable the standard tooltip
  626. EnableToolTips(TRUE);
  627. // Disable the builtin tooltip (if available)
  628. CToolTipCtrl* pToolTipCtrl = (CToolTipCtrl*)CWnd::FromHandle((HWND)::SendMessage(m_hWnd, LVM_GETTOOLTIPS, 0, 0L));
  629. if (pToolTipCtrl != NULL && pToolTipCtrl->m_hWnd != NULL)
  630. pToolTipCtrl->Activate(FALSE);
  631. RegisterDropTarget();
  632. }
  633. //------------------------------------------------------------------------
  634. //! WM_CREATE message handler. Called when inside a CView.
  635. //!
  636. //! @param lpCreateStruct Pointer to a CREATESTRUCT structure that contains information about the list control object being created.
  637. //------------------------------------------------------------------------
  638. int CGridListCtrlEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
  639. {
  640. // Will not be called when placed inside a CDialog
  641. int rc = CListCtrl::OnCreate(lpCreateStruct);
  642. if (rc == 0)
  643. OnCreateStyle();
  644. return rc;
  645. }
  646. //------------------------------------------------------------------------
  647. //! Normally used for subclassing controls, but here used to configure
  648. //! initial style when list control is created.
  649. //------------------------------------------------------------------------
  650. void CGridListCtrlEx::PreSubclassWindow()
  651. {
  652. // Changes made to style will not having any effect when placed in a CView
  653. CListCtrl::PreSubclassWindow();
  654. OnCreateStyle();
  655. }
  656. //------------------------------------------------------------------------
  657. //! Inserts a new column in the list control, and gives the option to customize the column using a trait
  658. //!
  659. //! @param nCol Index of the new column
  660. //! @param strColumnHeading Title of the new column
  661. //! @param nFormat Text alignment of the new column
  662. //! @param nWidth Width of the new column
  663. //! @param nSubItem Unique identifier used to recognize the column independent of index
  664. //! @param pTrait Column trait interface for the new column
  665. //! @return The index of the new column if successful or -1 otherwise.
  666. //------------------------------------------------------------------------
  667. int CGridListCtrlEx::InsertColumnTrait(int nCol, const CString& strColumnHeading, int nFormat, int nWidth, int nSubItem, CGridColumnTrait* pTrait)
  668. {
  669. VERIFY(m_ColumnTraits.GetSize() == GetColumnCount());
  670. if (pTrait != NULL)
  671. {
  672. if (pTrait->GetColumnState().m_AlwaysHidden)
  673. {
  674. nWidth = 0;
  675. pTrait->GetColumnState().m_Visible = false;
  676. }
  677. // nCol specifies the position, if nCol is greater than count,
  678. // then insert as last
  679. if (nCol >= GetHeaderCtrl()->GetItemCount())
  680. {
  681. nCol = GetHeaderCtrl()->GetItemCount();
  682. InsertColumnTrait(nCol, pTrait);
  683. }
  684. else
  685. {
  686. InsertColumnTrait(nCol, pTrait);
  687. }
  688. }
  689. int index = InsertColumn(nCol, static_cast<LPCTSTR>(strColumnHeading), nFormat, nWidth, nSubItem);
  690. if (index != -1)
  691. {
  692. VERIFY( index == nCol );
  693. GetColumnTrait(nCol)->OnInsertColumn(*this, nCol);
  694. }
  695. else
  696. DeleteColumnTrait(nCol);
  697. return index;
  698. }
  699. //------------------------------------------------------------------------
  700. //! Inserts the label column (first column) with invisible state
  701. //! The label column behaves differently from the rest of the columns,
  702. //! and to get the uniform look, then it should be hidden away.
  703. //! - It has a special margin, which no other column has
  704. //! - When dragged to another position than the first, then it looses it special margin
  705. //!
  706. //! @return The index of the new column if successful or -1 otherwise.
  707. //------------------------------------------------------------------------
  708. int CGridListCtrlEx::InsertHiddenLabelColumn()
  709. {
  710. // Must be the label column
  711. VERIFY(GetHeaderCtrl()->GetItemCount() == 0);
  712. VERIFY(m_ColumnTraits.GetSize() == 0);
  713. CGridColumnTrait* pColumTrait = new CGridColumnTrait;
  714. pColumTrait->GetColumnState().m_AlwaysHidden = true;
  715. return InsertColumnTrait(0, _T(""), LVCFMT_LEFT, -1, -1, pColumTrait);
  716. }
  717. //------------------------------------------------------------------------
  718. //! Retrieves the header control of a list control.
  719. //!
  720. //! @return A pointer to the header control, used by the list control.
  721. //------------------------------------------------------------------------
  722. const CHeaderCtrl* CGridListCtrlEx::GetHeaderCtrl() const
  723. {
  724. ASSERT(::IsWindow(m_hWnd));
  725. HWND hWnd = (HWND) ::SendMessage(m_hWnd, LVM_GETHEADER, 0, 0);
  726. if (hWnd == NULL)
  727. return NULL;
  728. else
  729. return (const CHeaderCtrl*) CHeaderCtrl::FromHandle(hWnd);
  730. }
  731. //------------------------------------------------------------------------
  732. //! Retrieves the number of columns from the header control.
  733. //!
  734. //! @return Number of header control items if successful; otherwise &#x2013; 1.
  735. //------------------------------------------------------------------------
  736. int CGridListCtrlEx::GetColumnCount() const
  737. {
  738. return GetHeaderCtrl()->GetItemCount();
  739. }
  740. //------------------------------------------------------------------------
  741. //! Retrieves the font used to draw cells in the list control
  742. //!
  743. //! @return A pointer to the current font used by the list control.
  744. //------------------------------------------------------------------------
  745. CFont* CGridListCtrlEx::GetCellFont()
  746. {
  747. if (m_CellFont.GetSafeHandle() == NULL)
  748. return GetFont();
  749. return &m_CellFont;
  750. }
  751. //------------------------------------------------------------------------
  752. //! Takes the current font and increases the font with the given margin
  753. //! multiplier. Increases the row-height but keeps the cell font intact.
  754. //! Gives more room for the grid-cell editors and their border.
  755. //!
  756. //! @param margin Multiplier for how much to increase the font size
  757. //------------------------------------------------------------------------
  758. void CGridListCtrlEx::SetCellMargin(double margin)
  759. {
  760. LOGFONT lf = {0};
  761. VERIFY(GetFont()->GetLogFont(&lf) != 0);
  762. if (static_cast<HFONT>(m_CellFont))
  763. VERIFY( m_CellFont.DeleteObject() );
  764. VERIFY( m_CellFont.CreateFontIndirect(&lf) );
  765. lf.lfHeight = (int)( lf.lfHeight * margin );
  766. lf.lfWidth = (int)( lf.lfWidth * margin );
  767. if (static_cast<HFONT>(m_GridFont))
  768. VERIFY( m_GridFont.DeleteObject() );
  769. VERIFY( m_GridFont.CreateFontIndirect(&lf) );
  770. m_Margin = -1; // Avoid loop in WM_SETFONT message handler
  771. CListCtrl::SetFont(&m_GridFont);
  772. m_Margin = margin;
  773. GetHeaderCtrl()->SetFont(&m_CellFont);
  774. CToolTipCtrl* pToolTipCtrl = (CToolTipCtrl*)CWnd::FromHandle((HWND)::SendMessage(m_hWnd, LVM_GETTOOLTIPS, 0, 0L));
  775. if (pToolTipCtrl != NULL && pToolTipCtrl->m_hWnd != NULL)
  776. pToolTipCtrl->SetFont(&m_CellFont);
  777. }
  778. //------------------------------------------------------------------------
  779. //! WM_SETFONT message handler. For re-applying margin if font changes
  780. //!
  781. //! @param wParam Handle to the font (HFONT), where NULL means default font
  782. //! @param lParam The low-order word of lParam specifies whether to redraw
  783. //! @return Not used
  784. //------------------------------------------------------------------------
  785. LRESULT CGridListCtrlEx::OnSetFont(WPARAM wParam, LPARAM lParam)
  786. {
  787. LRESULT result = DefWindowProc(WM_SETFONT, wParam, lParam);
  788. if (m_Margin >= 0)
  789. SetCellMargin(m_Margin);
  790. return result;
  791. }
  792. //------------------------------------------------------------------------
  793. //! The column version of GetItemData(), one can specify an unique
  794. //! identifier when using InsertColumn()
  795. //!
  796. //! @param nCol Index of the column
  797. //! @return Unique identifier of the column specified
  798. //------------------------------------------------------------------------
  799. int CGridListCtrlEx::GetColumnData(int nCol) const
  800. {
  801. LVCOLUMN lvc = {0};
  802. lvc.mask = LVCF_SUBITEM;
  803. VERIFY( GetColumn(nCol, &lvc) );
  804. return lvc.iSubItem;
  805. }
  806. //------------------------------------------------------------------------
  807. //! Get column position in the CHeaderCtrl's display order array
  808. //!
  809. //! @param nCol Index of the column
  810. //! @return Column offset is in left-to-right order. For example, zero indicates the leftmost column.
  811. //------------------------------------------------------------------------
  812. int CGridListCtrlEx::GetColumnOrder(int nCol) const
  813. {
  814. LVCOLUMN lvc = {0};
  815. lvc.mask = LVCF_ORDER;
  816. VERIFY( GetColumn(nCol, &lvc) );
  817. return lvc.iOrder;
  818. }
  819. //------------------------------------------------------------------------
  820. //! Retrieve column title of a column in the list control
  821. //!
  822. //! @param nCol Index of the column
  823. //! @return Column header text of the specified column
  824. //------------------------------------------------------------------------
  825. CString CGridListCtrlEx::GetColumnHeading(int nCol) const
  826. {
  827. // Retrieve column-title
  828. LVCOLUMN lvc = {0};
  829. lvc.mask = LVCF_TEXT;
  830. TCHAR sColText[256];
  831. lvc.pszText = sColText;
  832. lvc.cchTextMax = sizeof(sColText) - 1;
  833. VERIFY( GetColumn(nCol, &lvc) );
  834. return lvc.pszText;
  835. }
  836. //------------------------------------------------------------------------
  837. //! Retrieve row with the LVIS_FOCUSED state flag set
  838. //!
  839. //! @return The index of the row if successful, or -1 otherwise.
  840. //------------------------------------------------------------------------
  841. int CGridListCtrlEx::GetFocusRow() const
  842. {
  843. return GetNextItem(-1, LVNI_FOCUSED);
  844. }
  845. //------------------------------------------------------------------------
  846. //! Sets LVIS_FOCUSED state flag for the specified row
  847. //!
  848. //! @param nRow The index of the row
  849. //------------------------------------------------------------------------
  850. void CGridListCtrlEx::SetFocusRow(int nRow)
  851. {
  852. SetItemState(nRow, LVIS_FOCUSED, LVIS_FOCUSED);
  853. }
  854. //------------------------------------------------------------------------
  855. //! Checks if the LVIS_SELECTED state flag set for the specified row
  856. //!
  857. //! @param nRow The index of the row
  858. //! @return True if the row is selected
  859. //------------------------------------------------------------------------
  860. BOOL CGridListCtrlEx::IsRowSelected(int nRow) const
  861. {
  862. return (GetItemState(nRow, LVIS_SELECTED) & LVIS_SELECTED) == LVIS_SELECTED;
  863. }
  864. //------------------------------------------------------------------------
  865. //! Sets the LVIS_SELECTED state flag for the specified row
  866. //!
  867. //! @param nRow The index of the row. -1 means all rows
  868. //! @param bSelect Whether row should be selected or not
  869. //! @return Nonzero if successful; otherwise zero.
  870. //------------------------------------------------------------------------
  871. BOOL CGridListCtrlEx::SelectRow(int nRow, BOOL bSelect)
  872. {
  873. return SetItemState(nRow, bSelect ? LVIS_SELECTED : 0, LVIS_SELECTED);
  874. }
  875. //------------------------------------------------------------------------
  876. //! Improved version of GetSubItemRect().
  877. //! - It doesn't return entire row-rect when using LVIR_BOUNDS for label-column (nCol==0)
  878. //! - It supports LVIR_LABEL for sub-items (nCol>0)
  879. //! - It supports LVIR_BOUNDS when column width is less than subitem image width
  880. //!
  881. //! @param nRow The index of the row
  882. //! @param nCol The index of the column
  883. //! @param nCode Determines the portion of the bounding rectangle (of the list view subitem) to be retrieved.
  884. //! @param rect Reference to a CRect object that contains the coordinates of the cell's bounding rectangle.
  885. //! @return Nonzero if successful; otherwise zero.
  886. //------------------------------------------------------------------------
  887. BOOL CGridListCtrlEx::GetCellRect(int nRow, int nCol, UINT nCode, CRect& rect)
  888. {
  889. if (GetSubItemRect(nRow, nCol, nCode, rect) == FALSE)
  890. return FALSE;
  891. if (nCode == LVIR_BOUNDS)
  892. {
  893. // Find the left and right of the cell-rectangle using the CHeaderCtrl
  894. CRect colRect;
  895. if (GetHeaderCtrl()->GetItemRect(nCol, colRect) == FALSE)
  896. return FALSE;
  897. if (nCol == 0)
  898. {
  899. // Fix bug where LVIR_BOUNDS gives the entire row for nCol==0
  900. CRect labelRect;
  901. if (GetSubItemRect(nRow, nCol, LVIR_LABEL, labelRect) == FALSE)
  902. return FALSE;
  903. rect.right = labelRect.right;
  904. rect.left = labelRect.right - colRect.Width();
  905. }
  906. else
  907. {
  908. // Fix bug when width is smaller than subitem image width
  909. rect.right = rect.left + colRect.Width();
  910. }
  911. }
  912. if (nCode == LVIR_ICON)
  913. {
  914. if (nCol > 0 && !(GetExtendedStyle() & LVS_EX_SUBITEMIMAGES))
  915. return FALSE; // no image in subitem
  916. int nImage = GetCellImage(nRow, nCol);
  917. if (nImage == I_IMAGECALLBACK)
  918. return FALSE; // no image
  919. return TRUE;
  920. }
  921. if (nCode == LVIR_LABEL && nCol > 0)
  922. {
  923. if (!(GetExtendedStyle() & LVS_EX_SUBITEMIMAGES))
  924. return TRUE; // no image in subitem
  925. int nImage = GetCellImage(nRow, nCol);
  926. if (nImage == I_IMAGECALLBACK)
  927. return TRUE; // No image in subitem
  928. CRect iconRect;
  929. if (GetSubItemRect(nRow, nCol, LVIR_ICON, iconRect) == FALSE)
  930. return FALSE;
  931. rect.left += iconRect.Width();
  932. }
  933. return TRUE;
  934. }
  935. //------------------------------------------------------------------------
  936. //! Replicates the SubItemHitTest() but in a const version. Finds the cell
  937. //! below the given mouse cursor position.
  938. //!
  939. //! @param pt The position to hit test, in client coordinates.
  940. //! @param nRow The index of the row (Returns -1 if no row)
  941. //! @param nCol The index of the column (Returns -1 if no column)
  942. //------------------------------------------------------------------------
  943. void CGridListCtrlEx::CellHitTest(const CPoint& pt, int& nRow, int& nCol) const
  944. {
  945. nRow = -1;
  946. nCol = -1;
  947. LVHITTESTINFO lvhti = {0};
  948. lvhti.pt = pt;
  949. nRow = ListView_SubItemHitTest(m_hWnd, &lvhti); // SubItemHitTest is non-const
  950. nCol = lvhti.iSubItem;
  951. if (!(lvhti.flags & LVHT_ONITEM))
  952. nRow = -1;
  953. }
  954. //------------------------------------------------------------------------
  955. //! Checks if the current cell is using callback to retrieve its text value
  956. //!
  957. //! @param nRow The index of the row
  958. //! @param nCol The index of the column
  959. //! @return Returns true if the cell is using call back to retrieve its text value
  960. //------------------------------------------------------------------------
  961. BOOL CGridListCtrlEx::IsCellCallback(int nRow, int nCol) const
  962. {
  963. if (GetStyle() & LVS_OWNERDATA)
  964. return true;
  965. LVITEM lvi = {0};
  966. lvi.iItem = nRow;
  967. lvi.iSubItem = nCol;
  968. lvi.mask = LVIF_TEXT | LVIF_NORECOMPUTE;
  969. VERIFY( GetItem( &lvi ) );
  970. return lvi.pszText == LPSTR_TEXTCALLBACK;
  971. }
  972. //------------------------------------------------------------------------
  973. //! Retrieves the icon index of the specified cell
  974. //!
  975. //! @param nRow The index of the row
  976. //! @param nCol The index of the column
  977. //! @return Index of the cell's icon in the control's image list (I_IMAGECALLBACK means no image)
  978. //------------------------------------------------------------------------
  979. int CGridListCtrlEx::GetCellImage(int nRow, int nCol) const
  980. {
  981. LVITEM lvi = {0};
  982. lvi.iItem = nRow;
  983. lvi.iSubItem = nCol;
  984. lvi.mask = LVIF_IMAGE;
  985. VERIFY( GetItem( &lvi ) );
  986. return lvi.iImage;
  987. }
  988. //------------------------------------------------------------------------
  989. //! Sets the icon of the specified cell
  990. //!
  991. //! @param nRow The index of the row
  992. //! @param nCol The index of the column
  993. //! @param nImageId The icon index in the list control image list
  994. //! @return Nonzero if successful; otherwise zero.
  995. //------------------------------------------------------------------------
  996. BOOL CGridListCtrlEx::SetCellImage(int nRow, int nCol, int nImageId)
  997. {
  998. LVITEM lvitem = {0};
  999. lvitem.mask = LVIF_IMAGE;
  1000. lvitem.iItem = nRow;
  1001. lvitem.iSubItem = nCol;
  1002. lvitem.iImage = nImageId; // I_IMAGENONE (Indent but no image), I_IMAGECALLBACK
  1003. return SetItem(&lvitem);
  1004. }
  1005. //------------------------------------------------------------------------
  1006. //! Changes the focus cell.
  1007. //! Override this method and set m_FocusCell = -1 if wanting to disable subitem focus
  1008. //!
  1009. //! @param nCol The index of the column
  1010. //! @param bRedraw Should the focus row be redrawn ? (true / false)
  1011. //------------------------------------------------------------------------
  1012. void CGridListCtrlEx::SetFocusCell(int nCol, BOOL bRedraw)
  1013. {
  1014. m_FocusCell = nCol;
  1015. if (bRedraw)
  1016. {
  1017. int nFocusRow = GetFocusRow();
  1018. if (nFocusRow >= 0)
  1019. {
  1020. CRect itemRect;
  1021. VERIFY( GetItemRect(nFocusRow, itemRect, LVIR_BOUNDS) );
  1022. InvalidateRect(itemRect);
  1023. UpdateWindow();
  1024. }
  1025. }
  1026. }
  1027. //------------------------------------------------------------------------
  1028. //! Shifts the cell focus left or right in the same row
  1029. //!
  1030. //! @param bMoveRight Specifies whether the cell focus should be left or right
  1031. //------------------------------------------------------------------------
  1032. void CGridListCtrlEx::MoveFocusCell(BOOL bMoveRight)
  1033. {
  1034. if (GetItemCount() <= 0)
  1035. {
  1036. SetFocusCell(-1); // Entire row selected
  1037. return;
  1038. }
  1039. if (GetFocusCell() == -1)
  1040. {
  1041. // Entire row already selected
  1042. if (bMoveRight)
  1043. {
  1044. // Change to the first column in the current order
  1045. SetFocusCell( GetFirstVisibleColumn() );
  1046. }
  1047. }
  1048. else
  1049. {
  1050. // Convert focus-cell to order index
  1051. int nOrderIndex = -1;
  1052. for(int i = 0; i < GetHeaderCtrl()->GetItemCount(); ++i)
  1053. {
  1054. int nCol = GetHeaderCtrl()->OrderToIndex(i);
  1055. if (nCol == GetFocusCell())
  1056. {
  1057. nOrderIndex = i;
  1058. break;
  1059. }
  1060. }
  1061. // Move to the following column
  1062. if (bMoveRight)
  1063. nOrderIndex++;
  1064. else
  1065. nOrderIndex--;
  1066. // Convert order-index to focus cell
  1067. if (nOrderIndex >= 0 && nOrderIndex < GetHeaderCtrl()->GetItemCount())
  1068. {
  1069. int nCol = GetHeaderCtrl()->OrderToIndex(nOrderIndex);
  1070. if (IsColumnVisible(nCol))
  1071. SetFocusCell(nCol);
  1072. else if (!bMoveRight)
  1073. SetFocusCell(-1); // Entire row selection
  1074. }
  1075. else if (!bMoveRight)
  1076. SetFocusCell(-1); // Entire row selection
  1077. }
  1078. // Ensure the column is visible
  1079. if (GetFocusCell() >= 0)
  1080. {
  1081. VERIFY( EnsureColumnVisible(GetFocusCell(), false) );
  1082. }
  1083. SetFocusCell(GetFocusCell(), true);
  1084. }
  1085. //------------------------------------------------------------------------
  1086. //! Scrolls the view, so the column becomes visible
  1087. //!
  1088. //! http://www.codeguru.com/cpp/controls/listview/columns/article.php/c931/
  1089. //!
  1090. //! @param nCol The index of the column
  1091. //! @param bPartialOK Is partially visible good enough ?
  1092. //! @return Nonzero if successful; otherwise zero.
  1093. //------------------------------------------------------------------------
  1094. BOOL CGridListCtrlEx::EnsureColumnVisible(int nCol, BOOL bPartialOK)
  1095. {
  1096. if (nCol < 0 || nCol >= GetHeaderCtrl()->GetItemCount())
  1097. return FALSE;
  1098. CRect rcHeader;
  1099. if (GetHeaderCtrl()->GetItemRect(nCol, rcHeader) == FALSE)
  1100. return FALSE;
  1101. CRect rcClient;
  1102. GetClientRect(&rcClient);
  1103. int nOffset = GetScrollPos(SB_HORZ);
  1104. if(bPartialOK)
  1105. {
  1106. if((rcHeader.left - nOffset < rcClient.right) && (rcHeader.right - nOffset > 0))
  1107. {
  1108. return TRUE;
  1109. }
  1110. }
  1111. int nScrollX = 0;
  1112. if((rcHeader.Width() > rcClient.Width()) || (rcHeader.left - nOffset < 0))
  1113. {
  1114. nScrollX = rcHeader.left - nOffset;
  1115. }
  1116. else if(rcHeader.right - nOffset > rcClient.right)
  1117. {
  1118. nScrollX = rcHeader.right - nOffset - rcClient.right;
  1119. }
  1120. if(nScrollX != 0)
  1121. {
  1122. CSize size(nScrollX, 0);
  1123. if (Scroll(size) == FALSE)
  1124. return FALSE;
  1125. }
  1126. return TRUE;
  1127. }
  1128. //------------------------------------------------------------------------
  1129. //! Retrieves the column index of the first visible column
  1130. //!
  1131. //! @return Column index of the first visible column (-1 if no visible columns)
  1132. //------------------------------------------------------------------------
  1133. int CGridListCtrlEx::GetFirstVisibleColumn()
  1134. {
  1135. int nColCount = GetHeaderCtrl()->GetItemCount();
  1136. for(int i = 0; i < nColCount; ++i)
  1137. {
  1138. int nCol = GetHeaderCtrl()->OrderToIndex(i);
  1139. if (IsColumnVisible(nCol))
  1140. {
  1141. return nCol;
  1142. }
  1143. }
  1144. return -1;
  1145. }
  1146. //------------------------------------------------------------------------
  1147. //! Changes the visible state of column.
  1148. //! Hides a column by resizing the column width to zero and moving it to
  1149. //! the outer left in the column order. Shows a column by returning it to
  1150. //! its original position.
  1151. //!
  1152. //! @param nCol The index of the column
  1153. //! @param bShow Specifies whether the column should be shown or hidden
  1154. //! @return Nonzero if successful; otherwise zero.
  1155. //------------------------------------------------------------------------
  1156. BOOL CGridListCtrlEx::ShowColumn(int nCol, BOOL bShow)
  1157. {
  1158. if (!bShow && IsColumnAlwaysVisible(nCol))
  1159. return FALSE;
  1160. if (bShow && IsColumnAlwaysHidden(nCol))
  1161. return FALSE;
  1162. CGridColumnTrait* pTrait = GetColumnTrait(nCol);
  1163. CGridColumnTrait::ColumnState& columnState = pTrait->GetColumnState();
  1164. SetRedraw(FALSE);
  1165. int nColCount = GetColumnCount();
  1166. int* pOrderArray = new int[nColCount];
  1167. VERIFY( GetColumnOrderArray(pOrderArray, nColCount) );
  1168. if (bShow)
  1169. {
  1170. if (columnState.m_OrgPosition == -1)
  1171. {
  1172. // Restore the default position of the column (No column drag drop)
  1173. columnState.m_OrgPosition = nCol;
  1174. int nCurIndex = -1;
  1175. for(int i = 0; i < nColCount ; ++i)
  1176. {
  1177. if (pOrderArray[i] == nCol)
  1178. {
  1179. nCurIndex = i;
  1180. }
  1181. else if (nCurIndex != -1)
  1182. {
  1183. if (!IsColumnVisible(pOrderArray[i]) && pOrderArray[i] > nCol)
  1184. columnState.m_OrgPosition++;
  1185. pOrderArray[nCurIndex] = pOrderArray[i];
  1186. pOrderArray[i] = nCol;
  1187. nCurIndex = i;
  1188. // We want to move it to the original visible position
  1189. if (i >= columnState.m_OrgPosition)
  1190. {
  1191. if ( (i + 1 == nColCount) || IsColumnVisible(pOrderArray[i + 1]) )
  1192. break;
  1193. }
  1194. }
  1195. else
  1196. {
  1197. if (pOrderArray[i] > nCol)
  1198. columnState.m_OrgPosition++;
  1199. }
  1200. }
  1201. }
  1202. else
  1203. {
  1204. // Restore the last position of the column (Support column drag-drop)
  1205. int nColOffSet = 0;
  1206. int nCurIndex = -1;
  1207. for(int i = 0; i < nColCount ; ++i)
  1208. {
  1209. if (pOrderArray[i] == nCol)
  1210. {
  1211. nCurIndex = i;
  1212. columnState.m_OrgPosition += nColOffSet;
  1213. }
  1214. else if (nCurIndex != -1)
  1215. {
  1216. pOrderArray[nCurIndex] = pOrderArray[i];
  1217. pOrderArray[i] = nCol;
  1218. nCurIndex = i;
  1219. // We want to move it to the original visible position
  1220. if (i >= columnState.m_OrgPosition)
  1221. {
  1222. if ( (i + 1 == nColCount) || IsColumnVisible(pOrderArray[i + 1]) )
  1223. break;
  1224. }
  1225. }
  1226. else
  1227. {
  1228. if (GetColumnTrait(pOrderArray[i])->GetColumnState().m_OrgPosition != -1)
  1229. {
  1230. // Other columns have been hidden after, this column was hidden
  1231. // - The other column was originally placed before this column (Showing this column changes the original position of the other column)
  1232. if (GetColumnTrait(pOrderArray[i])->GetColumnState().m_OrgPosition <= columnState.m_OrgPosition)
  1233. GetColumnTrait(pOrderArray[i])->GetColumnState().m_OrgPosition--;
  1234. // - The other column was originally placed after this column (This column needs to adjust original position)
  1235. if (GetColumnTrait(pOrderArray[i])->GetColumnState().m_OrgPosition >= columnState.m_OrgPosition)
  1236. nColOffSet++;
  1237. }
  1238. }
  1239. }
  1240. }
  1241. }
  1242. else
  1243. {
  1244. // Move the column to the front of the display order list
  1245. int nCurIndex(-1);
  1246. for(int i = nColCount - 1; i >= 0 ; --i)
  1247. {
  1248. if (pOrderArray[i] == nCol)
  1249. {
  1250. // Backup the current position of the column
  1251. columnState.m_OrgPosition = i;
  1252. nCurIndex = i;
  1253. }
  1254. else if (nCurIndex != -1)
  1255. {
  1256. pOrderArray[nCurIndex] = pOrderArray[i];
  1257. pOrderArray[i] = nCol;
  1258. nCurIndex = i;
  1259. }
  1260. }
  1261. }
  1262. // Validate that all column-ids are unique and are between 0 og nCount
  1263. for(int i = 0; i < nColCount ; ++i)
  1264. {
  1265. ASSERT(pOrderArray[i] >= 0);
  1266. ASSERT(pOrderArray[i] < nColCount);
  1267. for(int j = 0; j < nColCount ; ++j)
  1268. {
  1269. if (j == i)
  1270. continue;
  1271. ASSERT(pOrderArray[i] != pOrderArray[j]);
  1272. }
  1273. }
  1274. VERIFY( SetColumnOrderArray(nColCount, pOrderArray) );
  1275. delete [] pOrderArray;
  1276. if (bShow)
  1277. {
  1278. // Restore the column width
  1279. columnState.m_Visible = true;
  1280. VERIFY( SetColumnWidth(nCol, columnState.m_OrgWidth) );
  1281. }
  1282. else
  1283. {
  1284. // Backup the column width
  1285. int orgWidth = GetColumnWidth(nCol);
  1286. VERIFY( SetColumnWidth(nCol, 0) );
  1287. columnState.m_Visible = false;
  1288. columnState.m_OrgWidth = orgWidth;
  1289. }
  1290. OnSaveStateColumnPick();
  1291. SetRedraw(TRUE);
  1292. Invalidate(FALSE);
  1293. return TRUE;
  1294. }
  1295. //------------------------------------------------------------------------
  1296. //! Resizes the width of a column according the contents of the cells below
  1297. //!
  1298. //! @param nCol The index of the column
  1299. //! @param bIncludeHeader Include the column header text the column width calculation
  1300. //! @return Nonzero if successful; otherwise zero.
  1301. //------------------------------------------------------------------------
  1302. BOOL CGridListCtrlEx::SetColumnWidthAuto(int nCol, BOOL bIncludeHeader)
  1303. {
  1304. if (nCol == -1)
  1305. {
  1306. for(int i = 0; i < GetHeaderCtrl()->GetItemCount() ; ++i)
  1307. {
  1308. SetColumnWidthAuto(i, bIncludeHeader);
  1309. }
  1310. return TRUE;
  1311. }
  1312. else
  1313. {
  1314. if (bIncludeHeader)
  1315. return SetColumnWidth(nCol, LVSCW_AUTOSIZE_USEHEADER);
  1316. else
  1317. return SetColumnWidth(nCol, LVSCW_AUTOSIZE);
  1318. }
  1319. }
  1320. namespace
  1321. {
  1322. HBITMAP CreateSortBitmap(BOOL bAscending)
  1323. {
  1324. // Aquire the Display DC
  1325. CDC* pDC = CDC::FromHandle(::GetDC(::GetDesktopWindow()));
  1326. //create a memory dc
  1327. CDC memDC;
  1328. memDC.CreateCompatibleDC(pDC);
  1329. //Create a memory bitmap
  1330. CBitmap newbmp;
  1331. CRect rcIcon(0, 0, 16, 16);
  1332. newbmp.CreateCompatibleBitmap(pDC, rcIcon.Height(), rcIcon.Width());
  1333. //select the bitmap in the memory dc
  1334. CBitmap* pOldBitmap = memDC.SelectObject(&newbmp);
  1335. //make the bitmap white to begin with
  1336. memDC.FillSolidRect(rcIcon.top, rcIcon.left, rcIcon.bottom, rcIcon.right, ::GetSysColor(COLOR_3DFACE));
  1337. // Set up pens to use for drawing the triangle
  1338. CPen penLight(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
  1339. CPen penShadow(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
  1340. CPen* pOldPen = memDC.SelectObject( &penLight );
  1341. int iOffset = (rcIcon.bottom - rcIcon.top) / 4;
  1342. if( bAscending )
  1343. {
  1344. // draw the arrow pointing upwards.
  1345. memDC.MoveTo( rcIcon.right - 2 * iOffset, iOffset);
  1346. memDC.LineTo( rcIcon.right - iOffset, rcIcon.bottom - iOffset - 1 );
  1347. memDC.LineTo( rcIcon.right - 3 * iOffset - 2, rcIcon.bottom - iOffset - 1 );
  1348. (void)memDC.SelectObject( &penShadow );
  1349. memDC.MoveTo( rcIcon.right - 3 * iOffset - 1, rcIcon.bottom - iOffset - 1 );
  1350. memDC.LineTo( rcIcon.right - 2 * iOffset, iOffset - 1);
  1351. }
  1352. else
  1353. {
  1354. // draw the arrow pointing downwards.
  1355. memDC.MoveTo( rcIcon.right - iOffset - 1, iOffset );
  1356. memDC.LineTo( rcIcon.right - 2 * iOffset - 1, rcIcon.bottom - iOffset );
  1357. (void)memDC.SelectObject( &penShadow );
  1358. memDC.MoveTo( rcIcon.right - 2 * iOffset - 2, rcIcon.bottom - iOffset );
  1359. memDC.LineTo( rcIcon.right - 3 * iOffset - 1, iOffset );
  1360. memDC.LineTo( rcIcon.right - iOffset - 1, iOffset );
  1361. }
  1362. // Restore the pen
  1363. memDC.SelectObject(pOldPen);
  1364. //select old bitmap back into the memory dc
  1365. memDC.SelectObject(pOldBitmap);
  1366. return (HBITMAP)newbmp.Detach();
  1367. }
  1368. }
  1369. //------------------------------------------------------------------------
  1370. //! Puts a sort-icon in the column header of the specified column
  1371. //!
  1372. //! @param nCol The index of the column
  1373. //! @param bAscending Should the arrow be up or down…

Large files files are truncated, but you can click here to view the full file