PageRenderTime 42ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/thirdparty/wtl/atlctrlx.h

http://crashrpt.googlecode.com/
C Header | 4978 lines | 4061 code | 711 blank | 206 comment | 937 complexity | 6edefb128a55dda12e6890062fe322aa MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause
  1. // Windows Template Library - WTL version 8.1
  2. // Copyright (C) Microsoft Corporation. All rights reserved.
  3. //
  4. // This file is a part of the Windows Template Library.
  5. // The use and distribution terms for this software are covered by the
  6. // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php)
  7. // which can be found in the file CPL.TXT at the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by
  9. // the terms of this license. You must not remove this notice, or
  10. // any other, from this software.
  11. #ifndef __ATLCTRLX_H__
  12. #define __ATLCTRLX_H__
  13. #pragma once
  14. #ifndef __ATLAPP_H__
  15. #error atlctrlx.h requires atlapp.h to be included first
  16. #endif
  17. #ifndef __ATLCTRLS_H__
  18. #error atlctrlx.h requires atlctrls.h to be included first
  19. #endif
  20. #ifndef WM_UPDATEUISTATE
  21. #define WM_UPDATEUISTATE 0x0128
  22. #endif // !WM_UPDATEUISTATE
  23. ///////////////////////////////////////////////////////////////////////////////
  24. // Classes in this file:
  25. //
  26. // CBitmapButtonImpl<T, TBase, TWinTraits>
  27. // CBitmapButton
  28. // CCheckListViewCtrlImpl<T, TBase, TWinTraits>
  29. // CCheckListViewCtrl
  30. // CHyperLinkImpl<T, TBase, TWinTraits>
  31. // CHyperLink
  32. // CWaitCursor
  33. // CCustomWaitCursor
  34. // CMultiPaneStatusBarCtrlImpl<T, TBase>
  35. // CMultiPaneStatusBarCtrl
  36. // CPaneContainerImpl<T, TBase, TWinTraits>
  37. // CPaneContainer
  38. // CSortListViewImpl<T>
  39. // CSortListViewCtrlImpl<T, TBase, TWinTraits>
  40. // CSortListViewCtrl
  41. // CTabViewImpl<T, TBase, TWinTraits>
  42. // CTabView
  43. namespace WTL
  44. {
  45. ///////////////////////////////////////////////////////////////////////////////
  46. // CBitmapButton - bitmap button implementation
  47. #ifndef _WIN32_WCE
  48. // bitmap button extended styles
  49. #define BMPBTN_HOVER 0x00000001
  50. #define BMPBTN_AUTO3D_SINGLE 0x00000002
  51. #define BMPBTN_AUTO3D_DOUBLE 0x00000004
  52. #define BMPBTN_AUTOSIZE 0x00000008
  53. #define BMPBTN_SHAREIMAGELISTS 0x00000010
  54. #define BMPBTN_AUTOFIRE 0x00000020
  55. template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
  56. class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits>
  57. {
  58. public:
  59. DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
  60. enum
  61. {
  62. _nImageNormal = 0,
  63. _nImagePushed,
  64. _nImageFocusOrHover,
  65. _nImageDisabled,
  66. _nImageCount = 4,
  67. };
  68. enum
  69. {
  70. ID_TIMER_FIRST = 1000,
  71. ID_TIMER_REPEAT = 1001
  72. };
  73. // Bitmap button specific extended styles
  74. DWORD m_dwExtendedStyle;
  75. CImageList m_ImageList;
  76. int m_nImage[_nImageCount];
  77. CToolTipCtrl m_tip;
  78. LPTSTR m_lpstrToolTipText;
  79. // Internal states
  80. unsigned m_fMouseOver:1;
  81. unsigned m_fFocus:1;
  82. unsigned m_fPressed:1;
  83. // Constructor/Destructor
  84. CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
  85. m_ImageList(hImageList), m_dwExtendedStyle(dwExtendedStyle),
  86. m_lpstrToolTipText(NULL),
  87. m_fMouseOver(0), m_fFocus(0), m_fPressed(0)
  88. {
  89. m_nImage[_nImageNormal] = -1;
  90. m_nImage[_nImagePushed] = -1;
  91. m_nImage[_nImageFocusOrHover] = -1;
  92. m_nImage[_nImageDisabled] = -1;
  93. }
  94. ~CBitmapButtonImpl()
  95. {
  96. if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
  97. m_ImageList.Destroy();
  98. delete [] m_lpstrToolTipText;
  99. }
  100. // overridden to provide proper initialization
  101. BOOL SubclassWindow(HWND hWnd)
  102. {
  103. #if (_MSC_VER >= 1300)
  104. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
  105. #else // !(_MSC_VER >= 1300)
  106. typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
  107. BOOL bRet = _baseClass::SubclassWindow(hWnd);
  108. #endif // !(_MSC_VER >= 1300)
  109. if(bRet)
  110. Init();
  111. return bRet;
  112. }
  113. // Attributes
  114. DWORD GetBitmapButtonExtendedStyle() const
  115. {
  116. return m_dwExtendedStyle;
  117. }
  118. DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  119. {
  120. DWORD dwPrevStyle = m_dwExtendedStyle;
  121. if(dwMask == 0)
  122. m_dwExtendedStyle = dwExtendedStyle;
  123. else
  124. m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  125. return dwPrevStyle;
  126. }
  127. HIMAGELIST GetImageList() const
  128. {
  129. return m_ImageList;
  130. }
  131. HIMAGELIST SetImageList(HIMAGELIST hImageList)
  132. {
  133. HIMAGELIST hImageListPrev = m_ImageList;
  134. m_ImageList = hImageList;
  135. if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd))
  136. SizeToImage();
  137. return hImageListPrev;
  138. }
  139. int GetToolTipTextLength() const
  140. {
  141. return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
  142. }
  143. bool GetToolTipText(LPTSTR lpstrText, int nLength) const
  144. {
  145. ATLASSERT(lpstrText != NULL);
  146. if(m_lpstrToolTipText == NULL)
  147. return false;
  148. errno_t nRet = SecureHelper::strncpy_x(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
  149. return (nRet == 0 || nRet == STRUNCATE);
  150. }
  151. bool SetToolTipText(LPCTSTR lpstrText)
  152. {
  153. if(m_lpstrToolTipText != NULL)
  154. {
  155. delete [] m_lpstrToolTipText;
  156. m_lpstrToolTipText = NULL;
  157. }
  158. if(lpstrText == NULL)
  159. {
  160. if(m_tip.IsWindow())
  161. m_tip.Activate(FALSE);
  162. return true;
  163. }
  164. int cchLen = lstrlen(lpstrText) + 1;
  165. ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
  166. if(m_lpstrToolTipText == NULL)
  167. return false;
  168. SecureHelper::strcpy_x(m_lpstrToolTipText, cchLen, lpstrText);
  169. if(m_tip.IsWindow())
  170. {
  171. m_tip.Activate(TRUE);
  172. m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
  173. }
  174. return true;
  175. }
  176. // Operations
  177. void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
  178. {
  179. if(nNormal != -1)
  180. m_nImage[_nImageNormal] = nNormal;
  181. if(nPushed != -1)
  182. m_nImage[_nImagePushed] = nPushed;
  183. if(nFocusOrHover != -1)
  184. m_nImage[_nImageFocusOrHover] = nFocusOrHover;
  185. if(nDisabled != -1)
  186. m_nImage[_nImageDisabled] = nDisabled;
  187. }
  188. BOOL SizeToImage()
  189. {
  190. ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL);
  191. int cx = 0;
  192. int cy = 0;
  193. if(!m_ImageList.GetIconSize(cx, cy))
  194. return FALSE;
  195. return ResizeClient(cx, cy);
  196. }
  197. // Overrideables
  198. void DoPaint(CDCHandle dc)
  199. {
  200. ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set
  201. ATLASSERT(m_nImage[0] != -1); // main bitmap must be set
  202. // set bitmap according to the current button state
  203. int nImage = -1;
  204. bool bHover = IsHoverMode();
  205. if(!IsWindowEnabled())
  206. nImage = m_nImage[_nImageDisabled];
  207. else if(m_fPressed == 1)
  208. nImage = m_nImage[_nImagePushed];
  209. else if((!bHover && m_fFocus == 1) || (bHover && m_fMouseOver == 1))
  210. nImage = m_nImage[_nImageFocusOrHover];
  211. if(nImage == -1) // not there, use default one
  212. nImage = m_nImage[_nImageNormal];
  213. // draw the button image
  214. int xyPos = 0;
  215. if((m_fPressed == 1) && ((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0) && (m_nImage[_nImagePushed] == -1))
  216. xyPos = 1;
  217. m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
  218. // draw 3D border if required
  219. if((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0)
  220. {
  221. RECT rect;
  222. GetClientRect(&rect);
  223. if(m_fPressed == 1)
  224. dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
  225. else if(!bHover || m_fMouseOver == 1)
  226. dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
  227. if(!bHover && m_fFocus == 1)
  228. {
  229. ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
  230. dc.DrawFocusRect(&rect);
  231. }
  232. }
  233. }
  234. // Message map and handlers
  235. BEGIN_MSG_MAP(CBitmapButtonImpl)
  236. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  237. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  238. MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
  239. MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
  240. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  241. MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
  242. MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
  243. MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
  244. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  245. MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
  246. MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
  247. MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
  248. MESSAGE_HANDLER(WM_ENABLE, OnEnable)
  249. MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
  250. MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
  251. MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
  252. MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
  253. MESSAGE_HANDLER(WM_TIMER, OnTimer)
  254. MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
  255. END_MSG_MAP()
  256. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  257. {
  258. Init();
  259. bHandled = FALSE;
  260. return 1;
  261. }
  262. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  263. {
  264. if(m_tip.IsWindow())
  265. {
  266. m_tip.DestroyWindow();
  267. m_tip.m_hWnd = NULL;
  268. }
  269. bHandled = FALSE;
  270. return 1;
  271. }
  272. LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  273. {
  274. MSG msg = { m_hWnd, uMsg, wParam, lParam };
  275. if(m_tip.IsWindow())
  276. m_tip.RelayEvent(&msg);
  277. bHandled = FALSE;
  278. return 1;
  279. }
  280. LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  281. {
  282. return 1; // no background needed
  283. }
  284. LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  285. {
  286. T* pT = static_cast<T*>(this);
  287. if(wParam != NULL)
  288. {
  289. pT->DoPaint((HDC)wParam);
  290. }
  291. else
  292. {
  293. CPaintDC dc(m_hWnd);
  294. pT->DoPaint(dc.m_hDC);
  295. }
  296. return 0;
  297. }
  298. LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  299. {
  300. m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
  301. Invalidate();
  302. UpdateWindow();
  303. bHandled = FALSE;
  304. return 1;
  305. }
  306. LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  307. {
  308. LRESULT lRet = 0;
  309. if(IsHoverMode())
  310. SetCapture();
  311. else
  312. lRet = DefWindowProc(uMsg, wParam, lParam);
  313. if(::GetCapture() == m_hWnd)
  314. {
  315. m_fPressed = 1;
  316. Invalidate();
  317. UpdateWindow();
  318. }
  319. if((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0)
  320. {
  321. int nElapse = 250;
  322. int nDelay = 0;
  323. if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
  324. nElapse += nDelay * 250; // all milli-seconds
  325. SetTimer(ID_TIMER_FIRST, nElapse);
  326. }
  327. return lRet;
  328. }
  329. LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  330. {
  331. LRESULT lRet = 0;
  332. if(!IsHoverMode())
  333. lRet = DefWindowProc(uMsg, wParam, lParam);
  334. if(::GetCapture() != m_hWnd)
  335. SetCapture();
  336. if(m_fPressed == 0)
  337. {
  338. m_fPressed = 1;
  339. Invalidate();
  340. UpdateWindow();
  341. }
  342. return lRet;
  343. }
  344. LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  345. {
  346. LRESULT lRet = 0;
  347. bool bHover = IsHoverMode();
  348. if(!bHover)
  349. lRet = DefWindowProc(uMsg, wParam, lParam);
  350. if(::GetCapture() == m_hWnd)
  351. {
  352. if(bHover && m_fPressed == 1)
  353. ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
  354. ::ReleaseCapture();
  355. }
  356. return lRet;
  357. }
  358. LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  359. {
  360. if(m_fPressed == 1)
  361. {
  362. m_fPressed = 0;
  363. Invalidate();
  364. UpdateWindow();
  365. }
  366. bHandled = FALSE;
  367. return 1;
  368. }
  369. LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  370. {
  371. Invalidate();
  372. UpdateWindow();
  373. bHandled = FALSE;
  374. return 1;
  375. }
  376. LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  377. {
  378. if(::GetCapture() == m_hWnd)
  379. {
  380. POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  381. ClientToScreen(&ptCursor);
  382. RECT rect = { 0 };
  383. GetWindowRect(&rect);
  384. unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
  385. if(m_fPressed != uPressed)
  386. {
  387. m_fPressed = uPressed;
  388. Invalidate();
  389. UpdateWindow();
  390. }
  391. }
  392. else if(IsHoverMode() && m_fMouseOver == 0)
  393. {
  394. m_fMouseOver = 1;
  395. Invalidate();
  396. UpdateWindow();
  397. StartTrackMouseLeave();
  398. }
  399. bHandled = FALSE;
  400. return 1;
  401. }
  402. LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  403. {
  404. if(m_fMouseOver == 1)
  405. {
  406. m_fMouseOver = 0;
  407. Invalidate();
  408. UpdateWindow();
  409. }
  410. return 0;
  411. }
  412. LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  413. {
  414. if(wParam == VK_SPACE && IsHoverMode())
  415. return 0; // ignore if in hover mode
  416. if(wParam == VK_SPACE && m_fPressed == 0)
  417. {
  418. m_fPressed = 1;
  419. Invalidate();
  420. UpdateWindow();
  421. }
  422. bHandled = FALSE;
  423. return 1;
  424. }
  425. LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  426. {
  427. if(wParam == VK_SPACE && IsHoverMode())
  428. return 0; // ignore if in hover mode
  429. if(wParam == VK_SPACE && m_fPressed == 1)
  430. {
  431. m_fPressed = 0;
  432. Invalidate();
  433. UpdateWindow();
  434. }
  435. bHandled = FALSE;
  436. return 1;
  437. }
  438. LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  439. {
  440. ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
  441. switch(wParam) // timer ID
  442. {
  443. case ID_TIMER_FIRST:
  444. KillTimer(ID_TIMER_FIRST);
  445. if(m_fPressed == 1)
  446. {
  447. ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
  448. int nElapse = 250;
  449. int nRepeat = 40;
  450. if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
  451. nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated
  452. SetTimer(ID_TIMER_REPEAT, nElapse);
  453. }
  454. break;
  455. case ID_TIMER_REPEAT:
  456. if(m_fPressed == 1)
  457. ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
  458. else if(::GetCapture() != m_hWnd)
  459. KillTimer(ID_TIMER_REPEAT);
  460. break;
  461. default: // not our timer
  462. break;
  463. }
  464. return 0;
  465. }
  466. LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  467. {
  468. // If the control is subclassed or superclassed, this message can cause
  469. // repainting without WM_PAINT. We don't use this state, so just do nothing.
  470. return 0;
  471. }
  472. // Implementation
  473. void Init()
  474. {
  475. // We need this style to prevent Windows from painting the button
  476. ModifyStyle(0, BS_OWNERDRAW);
  477. // create a tool tip
  478. m_tip.Create(m_hWnd);
  479. ATLASSERT(m_tip.IsWindow());
  480. if(m_tip.IsWindow() && m_lpstrToolTipText != NULL)
  481. {
  482. m_tip.Activate(TRUE);
  483. m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
  484. }
  485. if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0)
  486. SizeToImage();
  487. }
  488. BOOL StartTrackMouseLeave()
  489. {
  490. TRACKMOUSEEVENT tme = { 0 };
  491. tme.cbSize = sizeof(tme);
  492. tme.dwFlags = TME_LEAVE;
  493. tme.hwndTrack = m_hWnd;
  494. return _TrackMouseEvent(&tme);
  495. }
  496. bool IsHoverMode() const
  497. {
  498. return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
  499. }
  500. };
  501. class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
  502. {
  503. public:
  504. DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
  505. CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
  506. CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
  507. { }
  508. };
  509. #endif // !_WIN32_WCE
  510. ///////////////////////////////////////////////////////////////////////////////
  511. // CCheckListCtrlView - list view control with check boxes
  512. template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
  513. class CCheckListViewCtrlImplTraits
  514. {
  515. public:
  516. static DWORD GetWndStyle(DWORD dwStyle)
  517. {
  518. return (dwStyle == 0) ? t_dwStyle : dwStyle;
  519. }
  520. static DWORD GetWndExStyle(DWORD dwExStyle)
  521. {
  522. return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
  523. }
  524. static DWORD GetExtendedLVStyle()
  525. {
  526. return t_dwExListViewStyle;
  527. }
  528. };
  529. typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT> CCheckListViewCtrlTraits;
  530. template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
  531. class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
  532. {
  533. public:
  534. DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
  535. // Attributes
  536. static DWORD GetExtendedLVStyle()
  537. {
  538. return TWinTraits::GetExtendedLVStyle();
  539. }
  540. // Operations
  541. BOOL SubclassWindow(HWND hWnd)
  542. {
  543. #if (_MSC_VER >= 1300)
  544. BOOL bRet = ATL::CWindowImplBaseT< TBase, TWinTraits>::SubclassWindow(hWnd);
  545. #else // !(_MSC_VER >= 1300)
  546. typedef ATL::CWindowImplBaseT< TBase, TWinTraits> _baseClass;
  547. BOOL bRet = _baseClass::SubclassWindow(hWnd);
  548. #endif // !(_MSC_VER >= 1300)
  549. if(bRet)
  550. {
  551. T* pT = static_cast<T*>(this);
  552. pT;
  553. ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
  554. SetExtendedListViewStyle(pT->GetExtendedLVStyle());
  555. }
  556. return bRet;
  557. }
  558. void CheckSelectedItems(int nCurrItem)
  559. {
  560. // first check if this item is selected
  561. LVITEM lvi = { 0 };
  562. lvi.iItem = nCurrItem;
  563. lvi.iSubItem = 0;
  564. lvi.mask = LVIF_STATE;
  565. lvi.stateMask = LVIS_SELECTED;
  566. GetItem(&lvi);
  567. // if item is not selected, don't do anything
  568. if(!(lvi.state & LVIS_SELECTED))
  569. return;
  570. // new check state will be reverse of the current state,
  571. BOOL bCheck = !GetCheckState(nCurrItem);
  572. int nItem = -1;
  573. int nOldItem = -1;
  574. while((nItem = GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
  575. {
  576. if(nItem != nCurrItem)
  577. SetCheckState(nItem, bCheck);
  578. nOldItem = nItem;
  579. }
  580. }
  581. // Implementation
  582. BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
  583. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  584. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  585. MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
  586. MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
  587. END_MSG_MAP()
  588. LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  589. {
  590. // first let list view control initialize everything
  591. LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
  592. T* pT = static_cast<T*>(this);
  593. pT;
  594. ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
  595. SetExtendedListViewStyle(pT->GetExtendedLVStyle());
  596. return lRet;
  597. }
  598. LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  599. {
  600. POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  601. LVHITTESTINFO lvh = { 0 };
  602. lvh.pt = ptMsg;
  603. if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0)
  604. {
  605. T* pT = static_cast<T*>(this);
  606. pT->CheckSelectedItems(lvh.iItem);
  607. }
  608. bHandled = FALSE;
  609. return 1;
  610. }
  611. LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  612. {
  613. if(wParam == VK_SPACE)
  614. {
  615. int nCurrItem = GetNextItem(-1, LVNI_FOCUSED);
  616. if(nCurrItem != -1 && ::GetKeyState(VK_CONTROL) >= 0)
  617. {
  618. T* pT = static_cast<T*>(this);
  619. pT->CheckSelectedItems(nCurrItem);
  620. }
  621. }
  622. bHandled = FALSE;
  623. return 1;
  624. }
  625. };
  626. class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
  627. {
  628. public:
  629. DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
  630. };
  631. ///////////////////////////////////////////////////////////////////////////////
  632. // CHyperLink - hyper link control implementation
  633. #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
  634. __declspec(selectany) struct
  635. {
  636. enum { cxWidth = 32, cyHeight = 32 };
  637. int xHotSpot;
  638. int yHotSpot;
  639. unsigned char arrANDPlane[cxWidth * cyHeight / 8];
  640. unsigned char arrXORPlane[cxWidth * cyHeight / 8];
  641. } _AtlHyperLink_CursorData =
  642. {
  643. 5, 0,
  644. {
  645. 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF,
  646. 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF,
  647. 0xF0, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF,
  648. 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF,
  649. 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF,
  650. 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  651. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  652. 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
  653. },
  654. {
  655. 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
  656. 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x06, 0xD8, 0x00, 0x00,
  657. 0x06, 0xDA, 0x00, 0x00, 0x06, 0xDB, 0x00, 0x00, 0x67, 0xFB, 0x00, 0x00, 0x77, 0xFF, 0x00, 0x00,
  658. 0x37, 0xFF, 0x00, 0x00, 0x17, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00,
  659. 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00,
  660. 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  661. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  662. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  663. }
  664. };
  665. #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
  666. #define HLINK_UNDERLINED 0x00000000
  667. #define HLINK_NOTUNDERLINED 0x00000001
  668. #define HLINK_UNDERLINEHOVER 0x00000002
  669. #define HLINK_COMMANDBUTTON 0x00000004
  670. #define HLINK_NOTIFYBUTTON 0x0000000C
  671. #define HLINK_USETAGS 0x00000010
  672. #define HLINK_USETAGSBOLD 0x00000030
  673. #define HLINK_NOTOOLTIP 0x00000040
  674. #define HLINK_AUTOCREATELINKFONT 0x00000080
  675. #define HLINK_SINGLELINE 0x00000100
  676. // Notes:
  677. // - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
  678. // - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
  679. template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
  680. class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
  681. {
  682. public:
  683. LPTSTR m_lpstrLabel;
  684. LPTSTR m_lpstrHyperLink;
  685. HCURSOR m_hCursor;
  686. HFONT m_hFontLink;
  687. HFONT m_hFontNormal;
  688. RECT m_rcLink;
  689. #ifndef _WIN32_WCE
  690. CToolTipCtrl m_tip;
  691. #endif // !_WIN32_WCE
  692. COLORREF m_clrLink;
  693. COLORREF m_clrVisited;
  694. DWORD m_dwExtendedStyle; // Hyper Link specific extended styles
  695. bool m_bPaintLabel:1;
  696. bool m_bVisited:1;
  697. bool m_bHover:1;
  698. bool m_bInternalLinkFont:1;
  699. bool m_bInternalNormalFont:1;
  700. // Constructor/Destructor
  701. CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) :
  702. m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
  703. m_hCursor(NULL), m_hFontLink(NULL), m_hFontNormal(NULL),
  704. m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
  705. m_dwExtendedStyle(dwExtendedStyle),
  706. m_bPaintLabel(true), m_bVisited(false),
  707. m_bHover(false), m_bInternalLinkFont(false), m_bInternalNormalFont(false)
  708. {
  709. ::SetRectEmpty(&m_rcLink);
  710. }
  711. ~CHyperLinkImpl()
  712. {
  713. delete [] m_lpstrLabel;
  714. delete [] m_lpstrHyperLink;
  715. #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
  716. // It was created, not loaded, so we have to destroy it
  717. if(m_hCursor != NULL)
  718. ::DestroyCursor(m_hCursor);
  719. #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
  720. }
  721. // Attributes
  722. DWORD GetHyperLinkExtendedStyle() const
  723. {
  724. return m_dwExtendedStyle;
  725. }
  726. DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  727. {
  728. DWORD dwPrevStyle = m_dwExtendedStyle;
  729. if(dwMask == 0)
  730. m_dwExtendedStyle = dwExtendedStyle;
  731. else
  732. m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  733. return dwPrevStyle;
  734. }
  735. bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
  736. {
  737. if(m_lpstrLabel == NULL)
  738. return false;
  739. ATLASSERT(lpstrBuffer != NULL);
  740. if(nLength <= lstrlen(m_lpstrLabel))
  741. return false;
  742. SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrLabel);
  743. return true;
  744. }
  745. bool SetLabel(LPCTSTR lpstrLabel)
  746. {
  747. delete [] m_lpstrLabel;
  748. m_lpstrLabel = NULL;
  749. int cchLen = lstrlen(lpstrLabel) + 1;
  750. ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
  751. if(m_lpstrLabel == NULL)
  752. return false;
  753. SecureHelper::strcpy_x(m_lpstrLabel, cchLen, lpstrLabel);
  754. T* pT = static_cast<T*>(this);
  755. pT->CalcLabelRect();
  756. if(m_hWnd != NULL)
  757. SetWindowText(lpstrLabel); // Set this for accessibility
  758. return true;
  759. }
  760. bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
  761. {
  762. if(m_lpstrHyperLink == NULL)
  763. return false;
  764. ATLASSERT(lpstrBuffer != NULL);
  765. if(nLength <= lstrlen(m_lpstrHyperLink))
  766. return false;
  767. SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrHyperLink);
  768. return true;
  769. }
  770. bool SetHyperLink(LPCTSTR lpstrLink)
  771. {
  772. delete [] m_lpstrHyperLink;
  773. m_lpstrHyperLink = NULL;
  774. int cchLen = lstrlen(lpstrLink) + 1;
  775. ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
  776. if(m_lpstrHyperLink == NULL)
  777. return false;
  778. SecureHelper::strcpy_x(m_lpstrHyperLink, cchLen, lpstrLink);
  779. if(m_lpstrLabel == NULL)
  780. {
  781. T* pT = static_cast<T*>(this);
  782. pT->CalcLabelRect();
  783. }
  784. #ifndef _WIN32_WCE
  785. if(m_tip.IsWindow())
  786. {
  787. m_tip.Activate(TRUE);
  788. m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
  789. }
  790. #endif // !_WIN32_WCE
  791. return true;
  792. }
  793. HFONT GetLinkFont() const
  794. {
  795. return m_hFontLink;
  796. }
  797. void SetLinkFont(HFONT hFont)
  798. {
  799. if(m_bInternalLinkFont)
  800. {
  801. ::DeleteObject(m_hFontLink);
  802. m_bInternalLinkFont = false;
  803. }
  804. m_hFontLink = hFont;
  805. T* pT = static_cast<T*>(this);
  806. pT->CalcLabelRect();
  807. }
  808. int GetIdealHeight() const
  809. {
  810. ATLASSERT(::IsWindow(m_hWnd));
  811. if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
  812. return -1;
  813. if(!m_bPaintLabel)
  814. return -1;
  815. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  816. CClientDC dc(m_hWnd);
  817. RECT rect = { 0 };
  818. GetClientRect(&rect);
  819. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  820. RECT rcText = rect;
  821. dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | uFormat | DT_CALCRECT);
  822. dc.SelectFont(m_hFontLink);
  823. RECT rcLink = rect;
  824. dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
  825. dc.SelectFont(hFontOld);
  826. return max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
  827. }
  828. bool GetIdealSize(SIZE& size) const
  829. {
  830. int cx = 0, cy = 0;
  831. bool bRet = GetIdealSize(cx, cy);
  832. if(bRet)
  833. {
  834. size.cx = cx;
  835. size.cy = cy;
  836. }
  837. return bRet;
  838. }
  839. bool GetIdealSize(int& cx, int& cy) const
  840. {
  841. ATLASSERT(::IsWindow(m_hWnd));
  842. if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
  843. return false;
  844. if(!m_bPaintLabel)
  845. return false;
  846. CClientDC dc(m_hWnd);
  847. RECT rcClient = { 0 };
  848. GetClientRect(&rcClient);
  849. RECT rcAll = rcClient;
  850. if(IsUsingTags())
  851. {
  852. // find tags and label parts
  853. LPTSTR lpstrLeft = NULL;
  854. int cchLeft = 0;
  855. LPTSTR lpstrLink = NULL;
  856. int cchLink = 0;
  857. LPTSTR lpstrRight = NULL;
  858. int cchRight = 0;
  859. const T* pT = static_cast<const T*>(this);
  860. pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
  861. // get label part rects
  862. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  863. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  864. RECT rcLeft = rcClient;
  865. dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
  866. dc.SelectFont(m_hFontLink);
  867. RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
  868. dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
  869. dc.SelectFont(m_hFontNormal);
  870. RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
  871. dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat | DT_CALCRECT);
  872. dc.SelectFont(hFontOld);
  873. int cyMax = max(rcLeft.bottom, max(rcLink.bottom, rcRight.bottom));
  874. ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
  875. }
  876. else
  877. {
  878. HFONT hOldFont = NULL;
  879. if(m_hFontLink != NULL)
  880. hOldFont = dc.SelectFont(m_hFontLink);
  881. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  882. DWORD dwStyle = GetStyle();
  883. UINT uFormat = DT_LEFT;
  884. if (dwStyle & SS_CENTER)
  885. uFormat = DT_CENTER;
  886. else if (dwStyle & SS_RIGHT)
  887. uFormat = DT_RIGHT;
  888. uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  889. dc.DrawText(lpstrText, -1, &rcAll, uFormat | DT_CALCRECT);
  890. if(m_hFontLink != NULL)
  891. dc.SelectFont(hOldFont);
  892. if (dwStyle & SS_CENTER)
  893. {
  894. int dx = (rcClient.right - rcAll.right) / 2;
  895. ::OffsetRect(&rcAll, dx, 0);
  896. }
  897. else if (dwStyle & SS_RIGHT)
  898. {
  899. int dx = rcClient.right - rcAll.right;
  900. ::OffsetRect(&rcAll, dx, 0);
  901. }
  902. }
  903. cx = rcAll.right - rcAll.left;
  904. cy = rcAll.bottom - rcAll.top;
  905. return true;
  906. }
  907. // for command buttons only
  908. bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
  909. {
  910. ATLASSERT(IsCommandButton());
  911. return GetHyperLink(lpstrBuffer, nLength);
  912. }
  913. bool SetToolTipText(LPCTSTR lpstrToolTipText)
  914. {
  915. ATLASSERT(IsCommandButton());
  916. return SetHyperLink(lpstrToolTipText);
  917. }
  918. // Operations
  919. BOOL SubclassWindow(HWND hWnd)
  920. {
  921. ATLASSERT(m_hWnd == NULL);
  922. ATLASSERT(::IsWindow(hWnd));
  923. if(m_hFontNormal == NULL)
  924. m_hFontNormal = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L);
  925. #if (_MSC_VER >= 1300)
  926. BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
  927. #else // !(_MSC_VER >= 1300)
  928. typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
  929. BOOL bRet = _baseClass::SubclassWindow(hWnd);
  930. #endif // !(_MSC_VER >= 1300)
  931. if(bRet)
  932. {
  933. T* pT = static_cast<T*>(this);
  934. pT->Init();
  935. }
  936. return bRet;
  937. }
  938. bool Navigate()
  939. {
  940. ATLASSERT(::IsWindow(m_hWnd));
  941. bool bRet = true;
  942. if(IsNotifyButton())
  943. {
  944. NMHDR nmhdr = { m_hWnd, GetDlgCtrlID(), NM_CLICK };
  945. ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
  946. }
  947. else if(IsCommandButton())
  948. {
  949. ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
  950. }
  951. else
  952. {
  953. ATLASSERT(m_lpstrHyperLink != NULL);
  954. #ifndef _WIN32_WCE
  955. DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
  956. bRet = (dwRet > 32);
  957. #else // CE specific
  958. SHELLEXECUTEINFO shExeInfo = { sizeof(SHELLEXECUTEINFO), 0, 0, L"open", m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL, 0, 0, 0, 0, 0, 0, 0 };
  959. ::ShellExecuteEx(&shExeInfo);
  960. DWORD_PTR dwRet = (DWORD_PTR)shExeInfo.hInstApp;
  961. bRet = (dwRet == 0) || (dwRet > 32);
  962. #endif // _WIN32_WCE
  963. ATLASSERT(bRet);
  964. if(bRet)
  965. {
  966. m_bVisited = true;
  967. Invalidate();
  968. }
  969. }
  970. return bRet;
  971. }
  972. void CreateLinkFontFromNormal()
  973. {
  974. if(m_bInternalLinkFont)
  975. {
  976. ::DeleteObject(m_hFontLink);
  977. m_bInternalLinkFont = false;
  978. }
  979. CFontHandle font = (m_hFontNormal != NULL) ? m_hFontNormal : (HFONT)::GetStockObject(SYSTEM_FONT);
  980. LOGFONT lf = { 0 };
  981. font.GetLogFont(&lf);
  982. if(IsUsingTagsBold())
  983. lf.lfWeight = FW_BOLD;
  984. else if(!IsNotUnderlined())
  985. lf.lfUnderline = TRUE;
  986. m_hFontLink = ::CreateFontIndirect(&lf);
  987. m_bInternalLinkFont = true;
  988. ATLASSERT(m_hFontLink != NULL);
  989. }
  990. // Message map and handlers
  991. BEGIN_MSG_MAP(CHyperLinkImpl)
  992. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  993. #ifndef _WIN32_WCE
  994. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  995. MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
  996. #endif // !_WIN32_WCE
  997. MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
  998. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  999. #ifndef _WIN32_WCE
  1000. MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
  1001. #endif // !_WIN32_WCE
  1002. MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
  1003. MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
  1004. MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
  1005. #ifndef _WIN32_WCE
  1006. MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
  1007. #endif // !_WIN32_WCE
  1008. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
  1009. MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
  1010. MESSAGE_HANDLER(WM_CHAR, OnChar)
  1011. MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
  1012. MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
  1013. MESSAGE_HANDLER(WM_ENABLE, OnEnable)
  1014. MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
  1015. MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
  1016. MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
  1017. MESSAGE_HANDLER(WM_SIZE, OnSize)
  1018. END_MSG_MAP()
  1019. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1020. {
  1021. T* pT = static_cast<T*>(this);
  1022. pT->Init();
  1023. return 0;
  1024. }
  1025. #ifndef _WIN32_WCE
  1026. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  1027. {
  1028. if(m_tip.IsWindow())
  1029. {
  1030. m_tip.DestroyWindow();
  1031. m_tip.m_hWnd = NULL;
  1032. }
  1033. if(m_bInternalLinkFont)
  1034. {
  1035. ::DeleteObject(m_hFontLink);
  1036. m_hFontLink = NULL;
  1037. m_bInternalLinkFont = false;
  1038. }
  1039. if(m_bInternalNormalFont)
  1040. {
  1041. ::DeleteObject(m_hFontNormal);
  1042. m_hFontNormal = NULL;
  1043. m_bInternalNormalFont = false;
  1044. }
  1045. bHandled = FALSE;
  1046. return 1;
  1047. }
  1048. LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  1049. {
  1050. MSG msg = { m_hWnd, uMsg, wParam, lParam };
  1051. if(m_tip.IsWindow() && IsUsingToolTip())
  1052. m_tip.RelayEvent(&msg);
  1053. bHandled = FALSE;
  1054. return 1;
  1055. }
  1056. #endif // !_WIN32_WCE
  1057. LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1058. {
  1059. return 1; // no background painting needed (we do it all during WM_PAINT)
  1060. }
  1061. LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  1062. {
  1063. if(!m_bPaintLabel)
  1064. {
  1065. bHandled = FALSE;
  1066. return 1;
  1067. }
  1068. T* pT = static_cast<T*>(this);
  1069. if(wParam != NULL)
  1070. {
  1071. pT->DoEraseBackground((HDC)wParam);
  1072. pT->DoPaint((HDC)wParam);
  1073. }
  1074. else
  1075. {
  1076. CPaintDC dc(m_hWnd);
  1077. pT->DoEraseBackground(dc.m_hDC);
  1078. pT->DoPaint(dc.m_hDC);
  1079. }
  1080. return 0;
  1081. }
  1082. LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  1083. {
  1084. if(m_bPaintLabel)
  1085. Invalidate();
  1086. else
  1087. bHandled = FALSE;
  1088. return 0;
  1089. }
  1090. LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  1091. {
  1092. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  1093. if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
  1094. {
  1095. ::SetCursor(m_hCursor);
  1096. if(IsUnderlineHover())
  1097. {
  1098. if(!m_bHover)
  1099. {
  1100. m_bHover = true;
  1101. InvalidateRect(&m_rcLink);
  1102. UpdateWindow();
  1103. #ifndef _WIN32_WCE
  1104. StartTrackMouseLeave();
  1105. #endif // !_WIN32_WCE
  1106. }
  1107. }
  1108. }
  1109. else
  1110. {
  1111. if(IsUnderlineHover())
  1112. {
  1113. if(m_bHover)
  1114. {
  1115. m_bHover = false;
  1116. InvalidateRect(&m_rcLink);
  1117. UpdateWindow();
  1118. }
  1119. }
  1120. bHandled = FALSE;
  1121. }
  1122. return 0;
  1123. }
  1124. #ifndef _WIN32_WCE
  1125. LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1126. {
  1127. if(IsUnderlineHover() && m_bHover)
  1128. {
  1129. m_bHover = false;
  1130. InvalidateRect(&m_rcLink);
  1131. UpdateWindow();
  1132. }
  1133. return 0;
  1134. }
  1135. #endif // !_WIN32_WCE
  1136. LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  1137. {
  1138. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  1139. if(::PtInRect(&m_rcLink, pt))
  1140. {
  1141. SetFocus();
  1142. SetCapture();
  1143. }
  1144. return 0;
  1145. }
  1146. LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  1147. {
  1148. if(GetCapture() == m_hWnd)
  1149. {
  1150. ReleaseCapture();
  1151. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  1152. if(::PtInRect(&m_rcLink, pt))
  1153. {
  1154. T* pT = static_cast<T*>(this);
  1155. pT->Navigate();
  1156. }
  1157. }
  1158. return 0;
  1159. }
  1160. LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1161. {
  1162. if(wParam == VK_RETURN || wParam == VK_SPACE)
  1163. {
  1164. T* pT = static_cast<T*>(this);
  1165. pT->Navigate();
  1166. }
  1167. return 0;
  1168. }
  1169. LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1170. {
  1171. return DLGC_WANTCHARS;
  1172. }
  1173. LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  1174. {
  1175. POINT pt = { 0, 0 };
  1176. GetCursorPos(&pt);
  1177. ScreenToClient(&pt);
  1178. if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
  1179. {
  1180. return TRUE;
  1181. }
  1182. bHandled = FALSE;
  1183. return FALSE;
  1184. }
  1185. LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1186. {
  1187. Invalidate();
  1188. UpdateWindow();
  1189. return 0;
  1190. }
  1191. LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1192. {
  1193. return (LRESULT)m_hFontNormal;
  1194. }
  1195. LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  1196. {
  1197. if(m_bInternalNormalFont)
  1198. {
  1199. ::DeleteObject(m_hFontNormal);
  1200. m_bInternalNormalFont = false;
  1201. }
  1202. bool bCreateLinkFont = m_bInternalLinkFont;
  1203. m_hFontNormal = (HFONT)wParam;
  1204. if(bCreateLinkFont || IsAutoCreateLinkFont())
  1205. CreateLinkFontFromNormal();
  1206. T* pT = static_cast<T*>(this);
  1207. pT->CalcLabelRect();
  1208. if((BOOL)lParam)
  1209. {
  1210. Invalidate();
  1211. UpdateWindow();
  1212. }
  1213. return 0;
  1214. }
  1215. LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1216. {
  1217. // If the control is subclassed or superclassed, this message can cause
  1218. // repainting without WM_PAINT. We don't use this state, so just do nothing.
  1219. return 0;
  1220. }
  1221. LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  1222. {
  1223. T* pT = static_cast<T*>(this);
  1224. pT->CalcLabelRect();
  1225. pT->Invalidate();
  1226. return 0;
  1227. }
  1228. // Implementation
  1229. void Init()
  1230. {
  1231. ATLASSERT(::IsWindow(m_hWnd));
  1232. // Check if we should paint a label
  1233. const int cchBuff = 8;
  1234. TCHAR szBuffer[cchBuff] = { 0 };
  1235. if(::GetClassName(m_hWnd, szBuffer, cchBuff))
  1236. {
  1237. if(lstrcmpi(szBuffer, _T("static")) == 0)
  1238. {
  1239. ModifyStyle(0, SS_NOTIFY); // we need this
  1240. DWORD dwStyle = GetStyle() & 0x000000FF;
  1241. #ifndef _WIN32_WCE
  1242. if(dwStyle == SS_ICON || dwStyle == SS_BLACKRECT || dwStyle == SS_GRAYRECT ||
  1243. dwStyle == SS_WHITERECT || dwStyle == SS_BLACKFRAME || dwStyle == SS_GRAYFRAME ||
  1244. dwStyle == SS_WHITEFRAME || dwStyle == SS_OWNERDRAW ||
  1245. dwStyle == SS_BITMAP || dwStyle == SS_ENHMETAFILE)
  1246. #else // CE specific
  1247. if(dwStyle == SS_ICON || dwStyle == SS_BITMAP)
  1248. #endif // _WIN32_WCE
  1249. m_bPaintLabel = false;
  1250. }
  1251. }
  1252. // create or load a cursor
  1253. #if (WINVER >= 0x0500) || defined(_WIN32_WCE)
  1254. m_hCursor = ::LoadCursor(NULL, IDC_HAND);
  1255. #else
  1256. m_hCursor = ::CreateCursor(ModuleHelper::GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane);
  1257. #endif
  1258. ATLASSERT(m_hCursor != NULL);
  1259. // set fonts
  1260. if(m_bPaintLabel)
  1261. {
  1262. if(m_hFontNormal == NULL)
  1263. {
  1264. m_hFontNormal = AtlCreateControlFont();
  1265. m_bInternalNormalFont = true;
  1266. }
  1267. if(m_hFontLink == NULL)
  1268. CreateLinkFontFromNormal();
  1269. }
  1270. #ifndef _WIN32_WCE
  1271. // create a tool tip
  1272. m_tip.Create(m_hWnd);
  1273. ATLASSERT(m_tip.IsWindow());
  1274. #endif // !_WIN32_WCE
  1275. // set label (defaults to window text)
  1276. if(m_lpstrLabel == NULL)
  1277. {
  1278. int nLen = GetWindowTextLength();
  1279. if(nLen > 0)
  1280. {
  1281. ATLTRY(m_lpstrLabel = new TCHAR[nLen + 1]);
  1282. if(m_lpstrLabel != NULL)
  1283. ATLVERIFY(GetWindowText(m_lpstrLabel, nLen + 1) > 0);
  1284. }
  1285. }
  1286. T* pT = static_cast<T*>(this);
  1287. pT->CalcLabelRect();
  1288. // set hyperlink (defaults to label), or just activate tool tip if already set
  1289. if(m_lpstrHyperLink == NULL && !IsCommandButton())
  1290. {
  1291. if(m_lpstrLabel != NULL)
  1292. SetHyperLink(m_lpstrLabel);
  1293. }
  1294. #ifndef _WIN32_WCE
  1295. else
  1296. {
  1297. m_tip.Activate(TRUE);
  1298. m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
  1299. }
  1300. #endif // !_WIN32_WCE
  1301. // set link colors
  1302. if(m_bPaintLabel)
  1303. {
  1304. CRegKeyEx rk;
  1305. LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
  1306. if(lRet == ERROR_SUCCESS)
  1307. {
  1308. const int cchValue = 12;
  1309. TCHAR szValue[cchValue] = { 0 };
  1310. ULONG ulCount = cchValue;
  1311. lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
  1312. if(lRet == ERROR_SUCCESS)
  1313. {
  1314. COLORREF clr = pT->_ParseColorString(szValue);
  1315. ATLASSERT(clr != CLR_INVALID);
  1316. if(clr != CLR_INVALID)
  1317. m_clrLink = clr;
  1318. }
  1319. ulCount = cchValue;
  1320. lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
  1321. if(lRet == ERROR_SUCCESS)
  1322. {
  1323. COLORREF clr = pT->_ParseColorString(szValue);
  1324. ATLASSERT(clr != CLR_INVALID);
  1325. if(clr != CLR_INVALID)
  1326. m_clrVisited = clr;
  1327. }
  1328. }
  1329. }
  1330. }
  1331. static COLORREF _ParseColorString(LPTSTR lpstr)
  1332. {
  1333. int c[3] = { -1, -1, -1 };
  1334. LPTSTR p = NULL;
  1335. for(int i = 0; i < 2; i++)
  1336. {
  1337. for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
  1338. {
  1339. if(*p == _T(','))
  1340. {
  1341. *p = _T('\0');
  1342. c[i] = MinCrtHelper::_atoi(lpstr);
  1343. lpstr = &p[1];
  1344. break;
  1345. }
  1346. }
  1347. if(c[i] == -1)
  1348. return CLR_INVALID;
  1349. }
  1350. if(*lpstr == _T('\0'))
  1351. return CLR_INVALID;
  1352. c[2] = MinCrtHelper::_atoi(lpstr);
  1353. return RGB(c[0], c[1], c[2]);
  1354. }
  1355. bool CalcLabelRect()
  1356. {
  1357. if(!::IsWindow(m_hWnd))
  1358. return false;
  1359. if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
  1360. return false;
  1361. CClientDC dc(m_hWnd);
  1362. RECT rcClient = { 0 };
  1363. GetClientRect(&rcClient);
  1364. m_rcLink = rcClient;
  1365. if(!m_bPaintLabel)
  1366. return true;
  1367. if(IsUsingTags())
  1368. {
  1369. // find tags and label parts
  1370. LPTSTR lpstrLeft = NULL;
  1371. int cchLeft = 0;
  1372. LPTSTR lpstrLink = NULL;
  1373. int cchLink = 0;
  1374. LPTSTR lpstrRight = NULL;
  1375. int cchRight = 0;
  1376. T* pT = static_cast<T*>(this);
  1377. pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
  1378. ATLASSERT(lpstrLink != NULL);
  1379. ATLASSERT(cchLink > 0);
  1380. // get label part rects
  1381. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  1382. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1383. RECT rcLeft = rcClient;
  1384. if(lpstrLeft != NULL)
  1385. dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
  1386. dc.SelectFont(m_hFontLink);
  1387. RECT rcLink = rcClient;
  1388. if(lpstrLeft != NULL)
  1389. rcLink.left = rcLeft.right;
  1390. dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
  1391. dc.SelectFont(hFontOld);
  1392. m_rcLink = rcLink;
  1393. }
  1394. else
  1395. {
  1396. HFONT hOldFont = NULL;
  1397. if(m_hFontLink != NULL)
  1398. hOldFont = dc.SelectFont(m_hFontLink);
  1399. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  1400. DWORD dwStyle = GetStyle();
  1401. UINT uFormat = DT_LEFT;
  1402. if (dwStyle & SS_CENTER)
  1403. uFormat = DT_CENTER;
  1404. else if (dwStyle & SS_RIGHT)
  1405. uFormat = DT_RIGHT;
  1406. uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1407. dc.DrawText(lpstrText, -1, &m_rcLink, uFormat | DT_CALCRECT);
  1408. if(m_hFontLink != NULL)
  1409. dc.SelectFont(hOldFont);
  1410. if (dwStyle & SS_CENTER)
  1411. {
  1412. int dx = (rcClient.right - m_rcLink.right) / 2;
  1413. ::OffsetRect(&m_rcLink, dx, 0);
  1414. }
  1415. else if (dwStyle & SS_RIGHT)
  1416. {
  1417. int dx = rcClient.right - m_rcLink.right;
  1418. ::OffsetRect(&m_rcLink, dx, 0);
  1419. }
  1420. }
  1421. return true;
  1422. }
  1423. void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
  1424. {
  1425. lpstrLeft = NULL;
  1426. cchLeft = 0;
  1427. lpstrLink = NULL;
  1428. cchLink = 0;
  1429. lpstrRight = NULL;
  1430. cchRight = 0;
  1431. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  1432. int cchText = lstrlen(lpstrText);
  1433. bool bOutsideLink = true;
  1434. for(int i = 0; i < cchText; i++)
  1435. {
  1436. if(lpstrText[i] != _T('<'))
  1437. continue;
  1438. if(bOutsideLink)
  1439. {
  1440. if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
  1441. {
  1442. if(i > 0)
  1443. {
  1444. lpstrLeft = lpstrText;
  1445. cchLeft = i;
  1446. }
  1447. lpstrLink = &lpstrText[i + 3];
  1448. bOutsideLink = false;
  1449. }
  1450. }
  1451. else
  1452. {
  1453. if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
  1454. {
  1455. cchLink = i - 3 - cchLeft;
  1456. if(lpstrText[i + 4] != 0)
  1457. {
  1458. lpstrRight = &lpstrText[i + 4];
  1459. cchRight = cchText - (i + 4);
  1460. break;
  1461. }
  1462. }
  1463. }
  1464. }
  1465. }
  1466. void DoEraseBackground(CDCHandle dc)
  1467. {
  1468. HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd);
  1469. if(hBrush != NULL)
  1470. {
  1471. RECT rect = { 0 };
  1472. GetClientRect(&rect);
  1473. dc.FillRect(&rect, hBrush);
  1474. }
  1475. }
  1476. void DoPaint(CDCHandle dc)
  1477. {
  1478. if(IsUsingTags())
  1479. {
  1480. // find tags and label parts
  1481. LPTSTR lpstrLeft = NULL;
  1482. int cchLeft = 0;
  1483. LPTSTR lpstrLink = NULL;
  1484. int cchLink = 0;
  1485. LPTSTR lpstrRight = NULL;
  1486. int cchRight = 0;
  1487. T* pT = static_cast<T*>(this);
  1488. pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
  1489. // get label part rects
  1490. RECT rcClient = { 0 };
  1491. GetClientRect(&rcClient);
  1492. dc.SetBkMode(TRANSPARENT);
  1493. HFONT hFontOld = dc.SelectFont(m_hFontNormal);
  1494. UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1495. if(lpstrLeft != NULL)
  1496. dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | uFormat);
  1497. COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
  1498. if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
  1499. dc.SelectFont(m_hFontLink);
  1500. else
  1501. dc.SelectFont(m_hFontNormal);
  1502. dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | uFormat);
  1503. dc.SetTextColor(clrOld);
  1504. dc.SelectFont(m_hFontNormal);
  1505. if(lpstrRight != NULL)
  1506. {
  1507. RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
  1508. dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat);
  1509. }
  1510. if(GetFocus() == m_hWnd)
  1511. dc.DrawFocusRect(&m_rcLink);
  1512. dc.SelectFont(hFontOld);
  1513. }
  1514. else
  1515. {
  1516. dc.SetBkMode(TRANSPARENT);
  1517. COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
  1518. HFONT hFontOld = NULL;
  1519. if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
  1520. hFontOld = dc.SelectFont(m_hFontLink);
  1521. else
  1522. hFontOld = dc.SelectFont(m_hFontNormal);
  1523. LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
  1524. DWORD dwStyle = GetStyle();
  1525. UINT uFormat = DT_LEFT;
  1526. if (dwStyle & SS_CENTER)
  1527. uFormat = DT_CENTER;
  1528. else if (dwStyle & SS_RIGHT)
  1529. uFormat = DT_RIGHT;
  1530. uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
  1531. dc.DrawText(lpstrText, -1, &m_rcLink, uFormat);
  1532. if(GetFocus() == m_hWnd)
  1533. dc.DrawFocusRect(&m_rcLink);
  1534. dc.SetTextColor(clrOld);
  1535. dc.SelectFont(hFontOld);
  1536. }
  1537. }
  1538. #ifndef _WIN32_WCE
  1539. BOOL StartTrackMouseLeave()
  1540. {
  1541. TRACKMOUSEEVENT tme = { 0 };
  1542. tme.cbSize = sizeof(tme);
  1543. tme.dwFlags = TME_LEAVE;
  1544. tme.hwndTrack = m_hWnd;
  1545. return _TrackMouseEvent(&tme);
  1546. }
  1547. #endif // !_WIN32_WCE
  1548. // Implementation helpers
  1549. bool IsUnderlined() const
  1550. {
  1551. return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
  1552. }
  1553. bool IsNotUnderlined() const
  1554. {
  1555. return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
  1556. }
  1557. bool IsUnderlineHover() const
  1558. {
  1559. return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
  1560. }
  1561. bool IsCommandButton() const
  1562. {
  1563. return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
  1564. }
  1565. bool IsNotifyButton() const
  1566. {
  1567. return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
  1568. }
  1569. bool IsUsingTags() const
  1570. {
  1571. return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
  1572. }
  1573. bool IsUsingTagsBold() const
  1574. {
  1575. return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
  1576. }
  1577. bool IsUsingToolTip() const
  1578. {
  1579. return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
  1580. }
  1581. bool IsAutoCreateLinkFont() const
  1582. {
  1583. return ((m_dwExtendedStyle & HLINK_AUTOCREATELINKFONT) == HLINK_AUTOCREATELINKFONT);
  1584. }
  1585. bool IsSingleLine() const
  1586. {
  1587. return ((m_dwExtendedStyle & HLINK_SINGLELINE) == HLINK_SINGLELINE);
  1588. }
  1589. };
  1590. class CHyperLink : public CHyperLinkImpl<CHyperLink>
  1591. {
  1592. public:
  1593. DECLARE_WND_CLASS(_T("WTL_HyperLink"))
  1594. };
  1595. ///////////////////////////////////////////////////////////////////////////////
  1596. // CWaitCursor - displays a wait cursor
  1597. class CWaitCursor
  1598. {
  1599. public:
  1600. // Data
  1601. HCURSOR m_hWaitCursor;
  1602. HCURSOR m_hOldCursor;
  1603. bool m_bInUse;
  1604. // Constructor/destructor
  1605. CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
  1606. {
  1607. HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
  1608. m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
  1609. ATLASSERT(m_hWaitCursor != NULL);
  1610. if(bSet)
  1611. Set();
  1612. }
  1613. ~CWaitCursor()
  1614. {
  1615. Restore();
  1616. }
  1617. // Methods
  1618. bool Set()
  1619. {
  1620. if(m_bInUse)
  1621. return false;
  1622. m_hOldCursor = ::SetCursor(m_hWaitCursor);
  1623. m_bInUse = true;
  1624. return true;
  1625. }
  1626. bool Restore()
  1627. {
  1628. if(!m_bInUse)
  1629. return false;
  1630. ::SetCursor(m_hOldCursor);
  1631. m_bInUse = false;
  1632. return true;
  1633. }
  1634. };
  1635. ///////////////////////////////////////////////////////////////////////////////
  1636. // CCustomWaitCursor - for custom and animated cursors
  1637. class CCustomWaitCursor : public CWaitCursor
  1638. {
  1639. public:
  1640. // Constructor/destructor
  1641. CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) :
  1642. CWaitCursor(false, IDC_WAIT, true)
  1643. {
  1644. if(hInstance == NULL)
  1645. hInstance = ModuleHelper::GetResourceInstance();
  1646. m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
  1647. if(bSet)
  1648. Set();
  1649. }
  1650. ~CCustomWaitCursor()
  1651. {
  1652. Restore();
  1653. #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
  1654. ::DestroyCursor(m_hWaitCursor);
  1655. #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
  1656. }
  1657. };
  1658. ///////////////////////////////////////////////////////////////////////////////
  1659. // CMultiPaneStatusBarCtrl - Status Bar with multiple panes
  1660. template <class T, class TBase = CStatusBarCtrl>
  1661. class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase >
  1662. {
  1663. public:
  1664. DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
  1665. // Data
  1666. enum { m_cxPaneMargin = 3 };
  1667. int m_nPanes;
  1668. int* m_pPane;
  1669. // Constructor/destructor
  1670. CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL)
  1671. { }
  1672. ~CMultiPaneStatusBarCtrlImpl()
  1673. {
  1674. delete [] m_pPane;
  1675. }
  1676. // Methods
  1677. HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
  1678. {
  1679. #if (_MSC_VER >= 1300)
  1680. return ATL::CWindowImpl< T, TBase >::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
  1681. #else // !(_MSC_VER >= 1300)
  1682. typedef ATL::CWindowImpl< T, TBase > _baseClass;
  1683. return _baseClass::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
  1684. #endif // !(_MSC_VER >= 1300)
  1685. }
  1686. HWND Create(HWND hWndParent, UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
  1687. {
  1688. const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
  1689. TCHAR szText[cchMax] = { 0 };
  1690. ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
  1691. return Create(hWndParent, szText, dwStyle, nID);
  1692. }
  1693. BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true)
  1694. {
  1695. ATLASSERT(::IsWindow(m_hWnd));
  1696. ATLASSERT(nPanes > 0);
  1697. m_nPanes = nPanes;
  1698. delete [] m_pPane;
  1699. m_pPane = NULL;
  1700. ATLTRY(m_pPane = new int[nPanes]);
  1701. ATLASSERT(m_pPane != NULL);
  1702. if(m_pPane == NULL)
  1703. return FALSE;
  1704. CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
  1705. int* pPanesPos = buff.Allocate(nPanes);
  1706. ATLASSERT(pPanesPos != NULL);
  1707. if(pPanesPos == NULL)
  1708. return FALSE;
  1709. SecureHelper::memcpy_x(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int));
  1710. // get status bar DC and set font
  1711. CClientDC dc(m_hWnd);
  1712. HFONT hOldFont = dc.SelectFont(GetFont());
  1713. // get status bar borders
  1714. int arrBorders[3] = { 0 };
  1715. GetBorders(arrBorders);
  1716. const int cchBuff = 128;
  1717. TCHAR szBuff[cchBuff] = { 0 };
  1718. SIZE size = { 0, 0 };
  1719. int cxLeft = arrBorders[0];
  1720. // calculate right edge of each part
  1721. for(int i = 0; i < nPanes; i++)
  1722. {
  1723. if(pPanes[i] == ID_DEFAULT_PANE)
  1724. {
  1725. // make very large, will be resized later
  1726. pPanesPos[i] = INT_MAX / 2;
  1727. }
  1728. else
  1729. {
  1730. ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
  1731. dc.GetTextExtent(szBuff, lstrlen(szBuff), &size);
  1732. T* pT = static_cast<T*>(this);
  1733. pT;
  1734. pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin;
  1735. }
  1736. cxLeft = pPanesPos[i];
  1737. }
  1738. BOOL bRet = SetParts(nPanes, pPanesPos);
  1739. if(bRet && bSetText)
  1740. {
  1741. for(int i = 0; i < nPanes; i++)
  1742. {
  1743. if(pPanes[i] != ID_DEFAULT_PANE)
  1744. {
  1745. ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
  1746. SetPaneText(m_pPane[i], szBuff);
  1747. }
  1748. }
  1749. }
  1750. dc.SelectFont(hOldFont);
  1751. return bRet;
  1752. }
  1753. bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const
  1754. {
  1755. ATLASSERT(::IsWindow(m_hWnd));
  1756. int nIndex = GetPaneIndexFromID(nPaneID);
  1757. if(nIndex == -1)
  1758. return false;
  1759. int nLength = GetTextLength(nIndex, pnType);
  1760. if(pcchLength != NULL)
  1761. *pcchLength = nLength;
  1762. return true;
  1763. }
  1764. BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const
  1765. {
  1766. ATLASSERT(::IsWindow(m_hWnd));
  1767. int nIndex = GetPaneIndexFromID(nPaneID);
  1768. if(nIndex == -1)
  1769. return FALSE;
  1770. int nLength = GetText(nIndex, lpstrText, pnType);
  1771. if(pcchLength != NULL)
  1772. *pcchLength = nLength;
  1773. return TRUE;
  1774. }
  1775. BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0)
  1776. {
  1777. ATLASSERT(::IsWindow(m_hWnd));
  1778. int nIndex = GetPaneIndexFromID(nPaneID);
  1779. if(nIndex == -1)
  1780. return FALSE;
  1781. return SetText(nIndex, lpstrText, nType);
  1782. }
  1783. BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const
  1784. {
  1785. ATLASSERT(::IsWindow(m_hWnd));
  1786. int nIndex = GetPaneIndexFromID(nPaneID);
  1787. if(nIndex == -1)
  1788. return FALSE;
  1789. return GetRect(nIndex, lpRect);
  1790. }
  1791. BOOL SetPaneWidth(int nPaneID, int cxWidth)
  1792. {
  1793. ATLASSERT(::IsWindow(m_hWnd));
  1794. ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one
  1795. int nIndex = GetPaneIndexFromID(nPaneID);
  1796. if(nIndex == -1)
  1797. return FALSE;
  1798. // get pane positions
  1799. CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
  1800. int* pPanesPos = buff.Allocate(m_nPanes);
  1801. if(pPanesPos == NULL)
  1802. return FALSE;
  1803. GetParts(m_nPanes, pPanesPos);
  1804. // calculate offset
  1805. int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]);
  1806. int cxOff = cxWidth - cxPaneWidth;
  1807. // find variable width pane
  1808. int nDef = m_nPanes;
  1809. for(int i = 0; i < m_nPanes; i++)
  1810. {
  1811. if(m_pPane[i] == ID_DEFAULT_PANE)
  1812. {
  1813. nDef = i;
  1814. break;
  1815. }
  1816. }
  1817. // resize
  1818. if(nIndex < nDef) // before default pane
  1819. {
  1820. for(int i = nIndex; i < nDef; i++)
  1821. pPanesPos[i] += cxOff;
  1822. }
  1823. else // after default one
  1824. {
  1825. for(int i = nDef; i < nIndex; i++)
  1826. pPanesPos[i] -= cxOff;
  1827. }
  1828. // set pane postions
  1829. return SetParts(m_nPanes, pPanesPos);
  1830. }
  1831. #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
  1832. BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const
  1833. {
  1834. ATLASSERT(::IsWindow(m_hWnd));
  1835. int nIndex = GetPaneIndexFromID(nPaneID);
  1836. if(nIndex == -1)
  1837. return FALSE;
  1838. GetTipText(nIndex, lpstrText, nSize);
  1839. return TRUE;
  1840. }
  1841. BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText)
  1842. {
  1843. ATLASSERT(::IsWindow(m_hWnd));
  1844. int nIndex = GetPaneIndexFromID(nPaneID);
  1845. if(nIndex == -1)
  1846. return FALSE;
  1847. SetTipText(nIndex, lpstrText);
  1848. return TRUE;
  1849. }
  1850. #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
  1851. #if ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
  1852. BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const
  1853. {
  1854. ATLASSERT(::IsWindow(m_hWnd));
  1855. int nIndex = GetPaneIndexFromID(nPaneID);
  1856. if(nIndex == -1)
  1857. return FALSE;
  1858. hIcon = GetIcon(nIndex);
  1859. return TRUE;
  1860. }
  1861. BOOL SetPaneIcon(int nPaneID, HICON hIcon)
  1862. {
  1863. ATLASSERT(::IsWindow(m_hWnd));
  1864. int nIndex = GetPaneIndexFromID(nPaneID);
  1865. if(nIndex == -1)
  1866. return FALSE;
  1867. return SetIcon(nIndex, hIcon);
  1868. }
  1869. #endif // ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
  1870. // Message map and handlers
  1871. BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >)
  1872. MESSAGE_HANDLER(WM_SIZE, OnSize)
  1873. END_MSG_MAP()
  1874. LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  1875. {
  1876. LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
  1877. if(wParam != SIZE_MINIMIZED && m_nPanes > 0)
  1878. {
  1879. T* pT = static_cast<T*>(this);
  1880. pT->UpdatePanesLayout();
  1881. }
  1882. return lRet;
  1883. }
  1884. // Implementation
  1885. BOOL UpdatePanesLayout()
  1886. {
  1887. // get pane positions
  1888. CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
  1889. int* pPanesPos = buff.Allocate(m_nPanes);
  1890. ATLASSERT(pPanesPos != NULL);
  1891. if(pPanesPos == NULL)
  1892. return FALSE;
  1893. int nRet = GetParts(m_nPanes, pPanesPos);
  1894. ATLASSERT(nRet == m_nPanes);
  1895. if(nRet != m_nPanes)
  1896. return FALSE;
  1897. // calculate offset
  1898. RECT rcClient = { 0 };
  1899. GetClientRect(&rcClient);
  1900. int cxOff = rcClient.right - pPanesPos[m_nPanes - 1];
  1901. #ifndef _WIN32_WCE
  1902. // Move panes left if size grip box is present
  1903. if((GetStyle() & SBARS_SIZEGRIP) != 0)
  1904. cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE);
  1905. #endif // !_WIN32_WCE
  1906. // find variable width pane
  1907. int i;
  1908. for(i = 0; i < m_nPanes; i++)
  1909. {
  1910. if(m_pPane[i] == ID_DEFAULT_PANE)
  1911. break;
  1912. }
  1913. // resize all panes from the variable one to the right
  1914. if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1]))
  1915. {
  1916. for(; i < m_nPanes; i++)
  1917. pPanesPos[i] += cxOff;
  1918. }
  1919. // set pane postions
  1920. return SetParts(m_nPanes, pPanesPos);
  1921. }
  1922. int GetPaneIndexFromID(int nPaneID) const
  1923. {
  1924. for(int i = 0; i < m_nPanes; i++)
  1925. {
  1926. if(m_pPane[i] == nPaneID)
  1927. return i;
  1928. }
  1929. return -1; // not found
  1930. }
  1931. };
  1932. class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl<CMultiPaneStatusBarCtrl>
  1933. {
  1934. public:
  1935. DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName())
  1936. };
  1937. ///////////////////////////////////////////////////////////////////////////////
  1938. // CPaneContainer - provides header with title and close button for panes
  1939. // pane container extended styles
  1940. #define PANECNT_NOCLOSEBUTTON 0x00000001
  1941. #define PANECNT_VERTICAL 0x00000002
  1942. #define PANECNT_FLATBORDER 0x00000004
  1943. #define PANECNT_NOBORDER 0x00000008
  1944. template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
  1945. class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
  1946. {
  1947. public:
  1948. DECLARE_WND_CLASS_EX(NULL, 0, -1)
  1949. // Constants
  1950. enum
  1951. {
  1952. m_cxyBorder = 2,
  1953. m_cxyTextOffset = 4,
  1954. m_cxyBtnOffset = 1,
  1955. m_cchTitle = 80,
  1956. m_cxImageTB = 13,
  1957. m_cyImageTB = 11,
  1958. m_cxyBtnAddTB = 7,
  1959. m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset,
  1960. m_xBtnImageLeft = 6,
  1961. m_yBtnImageTop = 5,
  1962. m_xBtnImageRight = 12,
  1963. m_yBtnImageBottom = 11,
  1964. m_nCloseBtnID = ID_PANE_CLOSE
  1965. };
  1966. // Data members
  1967. CToolBarCtrl m_tb;
  1968. ATL::CWindow m_wndClient;
  1969. int m_cxyHeader;
  1970. TCHAR m_szTitle[m_cchTitle];
  1971. DWORD m_dwExtendedStyle; // Pane container specific extended styles
  1972. HFONT m_hFont;
  1973. bool m_bInternalFont;
  1974. // Constructor
  1975. CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0), m_hFont(NULL), m_bInternalFont(false)
  1976. {
  1977. m_szTitle[0] = 0;
  1978. }
  1979. // Attributes
  1980. DWORD GetPaneContainerExtendedStyle() const
  1981. {
  1982. return m_dwExtendedStyle;
  1983. }
  1984. DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  1985. {
  1986. DWORD dwPrevStyle = m_dwExtendedStyle;
  1987. if(dwMask == 0)
  1988. m_dwExtendedStyle = dwExtendedStyle;
  1989. else
  1990. m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  1991. if(m_hWnd != NULL)
  1992. {
  1993. T* pT = static_cast<T*>(this);
  1994. bool bUpdate = false;
  1995. if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
  1996. {
  1997. pT->CreateCloseButton();
  1998. bUpdate = true;
  1999. }
  2000. else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
  2001. {
  2002. pT->DestroyCloseButton();
  2003. bUpdate = true;
  2004. }
  2005. if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
  2006. {
  2007. pT->CalcSize();
  2008. bUpdate = true;
  2009. }
  2010. if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) !=
  2011. (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border
  2012. {
  2013. bUpdate = true;
  2014. }
  2015. if(bUpdate)
  2016. pT->UpdateLayout();
  2017. }
  2018. return dwPrevStyle;
  2019. }
  2020. HWND GetClient() const
  2021. {
  2022. return m_wndClient;
  2023. }
  2024. HWND SetClient(HWND hWndClient)
  2025. {
  2026. HWND hWndOldClient = m_wndClient;
  2027. m_wndClient = hWndClient;
  2028. if(m_hWnd != NULL)
  2029. {
  2030. T* pT = static_cast<T*>(this);
  2031. pT->UpdateLayout();
  2032. }
  2033. return hWndOldClient;
  2034. }
  2035. BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const
  2036. {
  2037. ATLASSERT(lpstrTitle != NULL);
  2038. errno_t nRet = SecureHelper::strncpy_x(lpstrTitle, cchLength, m_szTitle, _TRUNCATE);
  2039. return (nRet == 0 || nRet == STRUNCATE);
  2040. }
  2041. BOOL SetTitle(LPCTSTR lpstrTitle)
  2042. {
  2043. ATLASSERT(lpstrTitle != NULL);
  2044. errno_t nRet = SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
  2045. bool bRet = (nRet == 0 || nRet == STRUNCATE);
  2046. if(bRet && m_hWnd != NULL)
  2047. {
  2048. T* pT = static_cast<T*>(this);
  2049. pT->UpdateLayout();
  2050. }
  2051. return bRet;
  2052. }
  2053. int GetTitleLength() const
  2054. {
  2055. return lstrlen(m_szTitle);
  2056. }
  2057. // Methods
  2058. HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
  2059. DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
  2060. {
  2061. if(lpstrTitle != NULL)
  2062. SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
  2063. #if (_MSC_VER >= 1300)
  2064. return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
  2065. #else // !(_MSC_VER >= 1300)
  2066. typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
  2067. return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
  2068. #endif // !(_MSC_VER >= 1300)
  2069. }
  2070. HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
  2071. DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
  2072. {
  2073. if(uTitleID != 0U)
  2074. ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
  2075. #if (_MSC_VER >= 1300)
  2076. return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
  2077. #else // !(_MSC_VER >= 1300)
  2078. typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
  2079. return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
  2080. #endif // !(_MSC_VER >= 1300)
  2081. }
  2082. BOOL EnableCloseButton(BOOL bEnable)
  2083. {
  2084. ATLASSERT(::IsWindow(m_hWnd));
  2085. T* pT = static_cast<T*>(this);
  2086. pT; // avoid level 4 warning
  2087. return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE;
  2088. }
  2089. void UpdateLayout()
  2090. {
  2091. RECT rcClient = { 0 };
  2092. GetClientRect(&rcClient);
  2093. T* pT = static_cast<T*>(this);
  2094. pT->UpdateLayout(rcClient.right, rcClient.bottom);
  2095. }
  2096. // Message map and handlers
  2097. BEGIN_MSG_MAP(CPaneContainerImpl)
  2098. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  2099. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  2100. MESSAGE_HANDLER(WM_SIZE, OnSize)
  2101. MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
  2102. MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
  2103. MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
  2104. MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
  2105. MESSAGE_HANDLER(WM_PAINT, OnPaint)
  2106. #ifndef _WIN32_WCE
  2107. MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
  2108. #endif // !_WIN32_WCE
  2109. MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
  2110. MESSAGE_HANDLER(WM_COMMAND, OnCommand)
  2111. FORWARD_NOTIFICATIONS()
  2112. END_MSG_MAP()
  2113. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2114. {
  2115. if(m_hFont == NULL)
  2116. {
  2117. // The same as AtlCreateControlFont() for horizontal pane
  2118. #ifndef _WIN32_WCE
  2119. LOGFONT lf = { 0 };
  2120. ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE);
  2121. if(IsVertical())
  2122. lf.lfEscapement = 900; // 90 degrees
  2123. m_hFont = ::CreateFontIndirect(&lf);
  2124. #else // CE specific
  2125. m_hFont = (HFONT)::GetStockObject(SYSTEM_FONT);
  2126. if(IsVertical())
  2127. {
  2128. CLogFont lf(m_hFont);
  2129. lf.lfEscapement = 900; // 90 degrees
  2130. m_hFont = ::CreateFontIndirect(&lf);
  2131. }
  2132. #endif // _WIN32_WCE
  2133. m_bInternalFont = true;
  2134. }
  2135. T* pT = static_cast<T*>(this);
  2136. pT->CalcSize();
  2137. if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)
  2138. pT->CreateCloseButton();
  2139. return 0;
  2140. }
  2141. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2142. {
  2143. if(m_bInternalFont)
  2144. {
  2145. ::DeleteObject(m_hFont);
  2146. m_hFont = NULL;
  2147. m_bInternalFont = false;
  2148. }
  2149. return 0;
  2150. }
  2151. LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  2152. {
  2153. T* pT = static_cast<T*>(this);
  2154. pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  2155. return 0;
  2156. }
  2157. LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2158. {
  2159. if(m_wndClient.m_hWnd != NULL)
  2160. m_wndClient.SetFocus();
  2161. return 0;
  2162. }
  2163. LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2164. {
  2165. return (LRESULT)m_hFont;
  2166. }
  2167. LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  2168. {
  2169. if(m_bInternalFont)
  2170. {
  2171. ::DeleteObject(m_hFont);
  2172. m_bInternalFont = false;
  2173. }
  2174. m_hFont = (HFONT)wParam;
  2175. T* pT = static_cast<T*>(this);
  2176. pT->CalcSize();
  2177. if((BOOL)lParam != FALSE)
  2178. pT->UpdateLayout();
  2179. return 0;
  2180. }
  2181. LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2182. {
  2183. return 1; // no background needed
  2184. }
  2185. LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  2186. {
  2187. T* pT = static_cast<T*>(this);
  2188. if(wParam != NULL)
  2189. {
  2190. pT->DrawPaneTitle((HDC)wParam);
  2191. if(m_wndClient.m_hWnd == NULL) // no client window
  2192. pT->DrawPane((HDC)wParam);
  2193. }
  2194. else
  2195. {
  2196. CPaintDC dc(m_hWnd);
  2197. pT->DrawPaneTitle(dc.m_hDC);
  2198. if(m_wndClient.m_hWnd == NULL) // no client window
  2199. pT->DrawPane(dc.m_hDC);
  2200. }
  2201. return 0;
  2202. }
  2203. LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  2204. {
  2205. if(m_tb.m_hWnd == NULL)
  2206. {
  2207. bHandled = FALSE;
  2208. return 1;
  2209. }
  2210. T* pT = static_cast<T*>(this);
  2211. pT;
  2212. LPNMHDR lpnmh = (LPNMHDR)lParam;
  2213. LRESULT lRet = 0;
  2214. // pass toolbar custom draw notifications to the base class
  2215. if(lpnmh->code == NM_CUSTOMDRAW && lpnmh->hwndFrom == m_tb.m_hWnd)
  2216. lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
  2217. #ifndef _WIN32_WCE
  2218. // tooltip notifications come with the tooltip window handle and button ID,
  2219. // pass them to the parent if we don't handle them
  2220. else if(lpnmh->code == TTN_GETDISPINFO && lpnmh->idFrom == pT->m_nCloseBtnID)
  2221. bHandled = pT->GetToolTipText(lpnmh);
  2222. #endif // !_WIN32_WCE
  2223. // only let notifications not from the toolbar go to the parent
  2224. else if(lpnmh->hwndFrom != m_tb.m_hWnd && lpnmh->idFrom != pT->m_nCloseBtnID)
  2225. bHandled = FALSE;
  2226. return lRet;
  2227. }
  2228. LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  2229. {
  2230. // if command comes from the close button, substitute HWND of the pane container instead
  2231. if(m_tb.m_hWnd != NULL && (HWND)lParam == m_tb.m_hWnd)
  2232. return ::SendMessage(GetParent(), WM_COMMAND, wParam, (LPARAM)m_hWnd);
  2233. bHandled = FALSE;
  2234. return 1;
  2235. }
  2236. // Custom draw overrides
  2237. DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
  2238. {
  2239. return CDRF_NOTIFYITEMDRAW; // we need per-item notifications
  2240. }
  2241. DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
  2242. {
  2243. CDCHandle dc = lpNMCustomDraw->hdc;
  2244. #if (_WIN32_IE >= 0x0400)
  2245. RECT& rc = lpNMCustomDraw->rc;
  2246. #else // !(_WIN32_IE >= 0x0400)
  2247. RECT rc;
  2248. m_tb.GetItemRect(0, &rc);
  2249. #endif // !(_WIN32_IE >= 0x0400)
  2250. dc.FillRect(&rc, COLOR_3DFACE);
  2251. return CDRF_NOTIFYPOSTPAINT;
  2252. }
  2253. DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
  2254. {
  2255. CDCHandle dc = lpNMCustomDraw->hdc;
  2256. #if (_WIN32_IE >= 0x0400)
  2257. RECT& rc = lpNMCustomDraw->rc;
  2258. #else // !(_WIN32_IE >= 0x0400)
  2259. RECT rc = { 0 };
  2260. m_tb.GetItemRect(0, &rc);
  2261. #endif // !(_WIN32_IE >= 0x0400)
  2262. RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 };
  2263. ::OffsetRect(&rcImage, rc.left, rc.top);
  2264. T* pT = static_cast<T*>(this);
  2265. if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0)
  2266. {
  2267. RECT rcShadow = rcImage;
  2268. ::OffsetRect(&rcShadow, 1, 1);
  2269. CPen pen1;
  2270. pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT));
  2271. pT->DrawButtonImage(dc, rcShadow, pen1);
  2272. CPen pen2;
  2273. pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW));
  2274. pT->DrawButtonImage(dc, rcImage, pen2);
  2275. }
  2276. else
  2277. {
  2278. if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0)
  2279. ::OffsetRect(&rcImage, 1, 1);
  2280. CPen pen;
  2281. pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
  2282. pT->DrawButtonImage(dc, rcImage, pen);
  2283. }
  2284. return CDRF_DODEFAULT; // continue with the default item painting
  2285. }
  2286. // Implementation - overrideable methods
  2287. void UpdateLayout(int cxWidth, int cyHeight)
  2288. {
  2289. ATLASSERT(::IsWindow(m_hWnd));
  2290. RECT rect = { 0 };
  2291. if(IsVertical())
  2292. {
  2293. ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight);
  2294. if(m_tb.m_hWnd != NULL)
  2295. m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  2296. if(m_wndClient.m_hWnd != NULL)
  2297. m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER);
  2298. else
  2299. rect.right = cxWidth;
  2300. }
  2301. else
  2302. {
  2303. ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader);
  2304. if(m_tb.m_hWnd != NULL)
  2305. m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
  2306. if(m_wndClient.m_hWnd != NULL)
  2307. m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER);
  2308. else
  2309. rect.bottom = cyHeight;
  2310. }
  2311. InvalidateRect(&rect);
  2312. }
  2313. void CreateCloseButton()
  2314. {
  2315. ATLASSERT(m_tb.m_hWnd == NULL);
  2316. // create toolbar for the "x" button
  2317. m_tb.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NOMOVEY | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, 0);
  2318. ATLASSERT(m_tb.IsWindow());
  2319. if(m_tb.m_hWnd != NULL)
  2320. {
  2321. T* pT = static_cast<T*>(this);
  2322. pT; // avoid level 4 warning
  2323. m_tb.SetButtonStructSize();
  2324. TBBUTTON tbbtn = { 0 };
  2325. tbbtn.idCommand = pT->m_nCloseBtnID;
  2326. tbbtn.fsState = TBSTATE_ENABLED;
  2327. tbbtn.fsStyle = TBSTYLE_BUTTON;
  2328. m_tb.AddButtons(1, &tbbtn);
  2329. m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB);
  2330. m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB);
  2331. if(IsVertical())
  2332. m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOACTIVATE);
  2333. else
  2334. m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  2335. }
  2336. }
  2337. void DestroyCloseButton()
  2338. {
  2339. if(m_tb.m_hWnd != NULL)
  2340. m_tb.DestroyWindow();
  2341. }
  2342. void CalcSize()
  2343. {
  2344. T* pT = static_cast<T*>(this);
  2345. CFontHandle font = pT->GetTitleFont();
  2346. if(font.IsNull())
  2347. font = (HFONT)::GetStockObject(SYSTEM_FONT);
  2348. LOGFONT lf = { 0 };
  2349. font.GetLogFont(lf);
  2350. if(IsVertical())
  2351. {
  2352. m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder;
  2353. }
  2354. else
  2355. {
  2356. int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset;
  2357. int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset;
  2358. m_cxyHeader = max(cyFont, cyBtn);
  2359. }
  2360. }
  2361. HFONT GetTitleFont() const
  2362. {
  2363. return m_hFont;
  2364. }
  2365. #ifndef _WIN32_WCE
  2366. BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
  2367. {
  2368. return FALSE;
  2369. }
  2370. #endif // !_WIN32_WCE
  2371. void DrawPaneTitle(CDCHandle dc)
  2372. {
  2373. RECT rect = { 0 };
  2374. GetClientRect(&rect);
  2375. UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST;
  2376. if(IsVertical())
  2377. {
  2378. rect.right = rect.left + m_cxyHeader;
  2379. uBorder |= BF_BOTTOM;
  2380. }
  2381. else
  2382. {
  2383. rect.bottom = rect.top + m_cxyHeader;
  2384. uBorder |= BF_RIGHT;
  2385. }
  2386. if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0)
  2387. {
  2388. if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0)
  2389. uBorder |= BF_FLAT;
  2390. dc.DrawEdge(&rect, EDGE_ETCHED, uBorder);
  2391. }
  2392. dc.FillRect(&rect, COLOR_3DFACE);
  2393. // draw title text
  2394. dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
  2395. dc.SetBkMode(TRANSPARENT);
  2396. T* pT = static_cast<T*>(this);
  2397. HFONT hFontOld = dc.SelectFont(pT->GetTitleFont());
  2398. #ifdef _WIN32_WCE
  2399. const UINT DT_END_ELLIPSIS = 0;
  2400. #endif // _WIN32_WCE
  2401. if(IsVertical())
  2402. {
  2403. rect.top += m_cxyTextOffset;
  2404. rect.bottom -= m_cxyTextOffset;
  2405. if(m_tb.m_hWnd != NULL)
  2406. rect.top += m_cxToolBar;;
  2407. RECT rcCalc = { rect.left, rect.bottom, rect.right, rect.top };
  2408. int cxFont = dc.DrawText(m_szTitle, -1, &rcCalc, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT);
  2409. RECT rcText = { 0 };
  2410. rcText.left = (rect.right - rect.left - cxFont) / 2;
  2411. rcText.right = rcText.left + (rect.bottom - rect.top);
  2412. rcText.top = rect.bottom;
  2413. rcText.bottom = rect.top;
  2414. dc.DrawText(m_szTitle, -1, &rcText, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS);
  2415. }
  2416. else
  2417. {
  2418. rect.left += m_cxyTextOffset;
  2419. rect.right -= m_cxyTextOffset;
  2420. if(m_tb.m_hWnd != NULL)
  2421. rect.right -= m_cxToolBar;;
  2422. dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
  2423. }
  2424. dc.SelectFont(hFontOld);
  2425. }
  2426. // called only if pane is empty
  2427. void DrawPane(CDCHandle dc)
  2428. {
  2429. RECT rect = { 0 };
  2430. GetClientRect(&rect);
  2431. if(IsVertical())
  2432. rect.left += m_cxyHeader;
  2433. else
  2434. rect.top += m_cxyHeader;
  2435. if((GetExStyle() & WS_EX_CLIENTEDGE) == 0)
  2436. dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
  2437. dc.FillRect(&rect, COLOR_APPWORKSPACE);
  2438. }
  2439. // drawing helper - draws "x" button image
  2440. void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
  2441. {
  2442. #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
  2443. HPEN hPenOld = dc.SelectPen(hPen);
  2444. dc.MoveTo(rcImage.left, rcImage.top);
  2445. dc.LineTo(rcImage.right, rcImage.bottom);
  2446. dc.MoveTo(rcImage.left + 1, rcImage.top);
  2447. dc.LineTo(rcImage.right + 1, rcImage.bottom);
  2448. dc.MoveTo(rcImage.left, rcImage.bottom - 1);
  2449. dc.LineTo(rcImage.right, rcImage.top - 1);
  2450. dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
  2451. dc.LineTo(rcImage.right + 1, rcImage.top - 1);
  2452. dc.SelectPen(hPenOld);
  2453. #else // (_WIN32_WCE < 400)
  2454. rcImage;
  2455. hPen;
  2456. // no support for the "x" button image
  2457. #endif // (_WIN32_WCE < 400)
  2458. }
  2459. bool IsVertical() const
  2460. {
  2461. return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
  2462. }
  2463. };
  2464. class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
  2465. {
  2466. public:
  2467. DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
  2468. };
  2469. ///////////////////////////////////////////////////////////////////////////////
  2470. // CSortListViewCtrl - implements sorting for a listview control
  2471. // sort listview extended styles
  2472. #define SORTLV_USESHELLBITMAPS 0x00000001
  2473. // Notification sent to parent when sort column is changed by user clicking header.
  2474. #define SLVN_SORTCHANGED LVN_LAST
  2475. // A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification
  2476. typedef struct tagNMSORTLISTVIEW
  2477. {
  2478. NMHDR hdr;
  2479. int iNewSortColumn;
  2480. int iOldSortColumn;
  2481. } NMSORTLISTVIEW, *LPNMSORTLISTVIEW;
  2482. // Column sort types. Can be set on a per-column basis with the SetColumnSortType method.
  2483. enum
  2484. {
  2485. LVCOLSORT_NONE,
  2486. LVCOLSORT_TEXT, // default
  2487. LVCOLSORT_TEXTNOCASE,
  2488. LVCOLSORT_LONG,
  2489. LVCOLSORT_DOUBLE,
  2490. LVCOLSORT_DECIMAL,
  2491. LVCOLSORT_DATETIME,
  2492. LVCOLSORT_DATE,
  2493. LVCOLSORT_TIME,
  2494. LVCOLSORT_CUSTOM,
  2495. LVCOLSORT_LAST = LVCOLSORT_CUSTOM
  2496. };
  2497. template <class T>
  2498. class CSortListViewImpl
  2499. {
  2500. public:
  2501. enum
  2502. {
  2503. m_cchCmpTextMax = 32, // overrideable
  2504. m_cxSortImage = 16,
  2505. m_cySortImage = 15,
  2506. m_cxSortArrow = 11,
  2507. m_cySortArrow = 6,
  2508. m_iSortUp = 0, // index of sort bitmaps
  2509. m_iSortDown = 1,
  2510. m_nShellSortUpID = 133
  2511. };
  2512. // passed to LVCompare functions as lParam1 and lParam2
  2513. struct LVCompareParam
  2514. {
  2515. int iItem;
  2516. DWORD_PTR dwItemData;
  2517. union
  2518. {
  2519. long lValue;
  2520. double dblValue;
  2521. DECIMAL decValue;
  2522. LPCTSTR pszValue;
  2523. };
  2524. };
  2525. // passed to LVCompare functions as the lParamSort parameter
  2526. struct LVSortInfo
  2527. {
  2528. T* pT;
  2529. int iSortCol;
  2530. bool bDescending;
  2531. };
  2532. bool m_bSortDescending;
  2533. bool m_bCommCtrl6;
  2534. int m_iSortColumn;
  2535. CBitmap m_bmSort[2];
  2536. int m_fmtOldSortCol;
  2537. HBITMAP m_hbmOldSortCol;
  2538. DWORD m_dwSortLVExtendedStyle;
  2539. ATL::CSimpleArray<WORD> m_arrColSortType;
  2540. bool m_bUseWaitCursor;
  2541. CSortListViewImpl() :
  2542. m_bSortDescending(false),
  2543. m_bCommCtrl6(false),
  2544. m_iSortColumn(-1),
  2545. m_fmtOldSortCol(0),
  2546. m_hbmOldSortCol(NULL),
  2547. m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS),
  2548. m_bUseWaitCursor(true)
  2549. {
  2550. #ifndef _WIN32_WCE
  2551. DWORD dwMajor = 0;
  2552. DWORD dwMinor = 0;
  2553. HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
  2554. m_bCommCtrl6 = SUCCEEDED(hRet) && dwMajor >= 6;
  2555. #endif // !_WIN32_WCE
  2556. }
  2557. // Attributes
  2558. void SetSortColumn(int iCol)
  2559. {
  2560. T* pT = static_cast<T*>(this);
  2561. ATLASSERT(::IsWindow(pT->m_hWnd));
  2562. CHeaderCtrl header = pT->GetHeader();
  2563. ATLASSERT(header.m_hWnd != NULL);
  2564. ATLASSERT(iCol >= -1 && iCol < m_arrColSortType.GetSize());
  2565. int iOldSortCol = m_iSortColumn;
  2566. m_iSortColumn = iCol;
  2567. if(m_bCommCtrl6)
  2568. {
  2569. #ifndef HDF_SORTUP
  2570. const int HDF_SORTUP = 0x0400;
  2571. #endif // HDF_SORTUP
  2572. #ifndef HDF_SORTDOWN
  2573. const int HDF_SORTDOWN = 0x0200;
  2574. #endif // HDF_SORTDOWN
  2575. const int nMask = HDF_SORTUP | HDF_SORTDOWN;
  2576. HDITEM hditem = { HDI_FORMAT };
  2577. if(iOldSortCol != iCol && iOldSortCol >= 0 && header.GetItem(iOldSortCol, &hditem))
  2578. {
  2579. hditem.fmt &= ~nMask;
  2580. header.SetItem(iOldSortCol, &hditem);
  2581. }
  2582. if(iCol >= 0 && header.GetItem(iCol, &hditem))
  2583. {
  2584. hditem.fmt &= ~nMask;
  2585. hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP;
  2586. header.SetItem(iCol, &hditem);
  2587. }
  2588. return;
  2589. }
  2590. if(m_bmSort[m_iSortUp].IsNull())
  2591. pT->CreateSortBitmaps();
  2592. // restore previous sort column's bitmap, if any, and format
  2593. HDITEM hditem = { HDI_BITMAP | HDI_FORMAT };
  2594. if(iOldSortCol != iCol && iOldSortCol >= 0)
  2595. {
  2596. hditem.hbm = m_hbmOldSortCol;
  2597. hditem.fmt = m_fmtOldSortCol;
  2598. header.SetItem(iOldSortCol, &hditem);
  2599. }
  2600. // save new sort column's bitmap and format, and add our sort bitmap
  2601. if(iCol >= 0 && header.GetItem(iCol, &hditem))
  2602. {
  2603. if(iOldSortCol != iCol)
  2604. {
  2605. m_fmtOldSortCol = hditem.fmt;
  2606. m_hbmOldSortCol = hditem.hbm;
  2607. }
  2608. hditem.fmt &= ~HDF_IMAGE;
  2609. hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
  2610. int i = m_bSortDescending ? m_iSortDown : m_iSortUp;
  2611. hditem.hbm = m_bmSort[i];
  2612. header.SetItem(iCol, &hditem);
  2613. }
  2614. }
  2615. int GetSortColumn() const
  2616. {
  2617. return m_iSortColumn;
  2618. }
  2619. void SetColumnSortType(int iCol, WORD wType)
  2620. {
  2621. ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
  2622. ATLASSERT(wType >= LVCOLSORT_NONE && wType <= LVCOLSORT_LAST);
  2623. m_arrColSortType[iCol] = wType;
  2624. }
  2625. WORD GetColumnSortType(int iCol) const
  2626. {
  2627. ATLASSERT((iCol >= 0) && iCol < m_arrColSortType.GetSize());
  2628. return m_arrColSortType[iCol];
  2629. }
  2630. int GetColumnCount() const
  2631. {
  2632. const T* pT = static_cast<const T*>(this);
  2633. ATLASSERT(::IsWindow(pT->m_hWnd));
  2634. CHeaderCtrl header = pT->GetHeader();
  2635. return header.m_hWnd != NULL ? header.GetItemCount() : 0;
  2636. }
  2637. bool IsSortDescending() const
  2638. {
  2639. return m_bSortDescending;
  2640. }
  2641. DWORD GetSortListViewExtendedStyle() const
  2642. {
  2643. return m_dwSortLVExtendedStyle;
  2644. }
  2645. DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
  2646. {
  2647. DWORD dwPrevStyle = m_dwSortLVExtendedStyle;
  2648. if(dwMask == 0)
  2649. m_dwSortLVExtendedStyle = dwExtendedStyle;
  2650. else
  2651. m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
  2652. return dwPrevStyle;
  2653. }
  2654. // Operations
  2655. bool DoSortItems(int iCol, bool bDescending = false)
  2656. {
  2657. T* pT = static_cast<T*>(this);
  2658. ATLASSERT(::IsWindow(pT->m_hWnd));
  2659. ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
  2660. WORD wType = m_arrColSortType[iCol];
  2661. if(wType == LVCOLSORT_NONE)
  2662. return false;
  2663. int nCount = pT->GetItemCount();
  2664. if(nCount < 2)
  2665. {
  2666. m_bSortDescending = bDescending;
  2667. SetSortColumn(iCol);
  2668. return true;
  2669. }
  2670. CWaitCursor waitCursor(false);
  2671. if(m_bUseWaitCursor)
  2672. waitCursor.Set();
  2673. LVCompareParam* pParam = NULL;
  2674. ATLTRY(pParam = new LVCompareParam[nCount]);
  2675. PFNLVCOMPARE pFunc = NULL;
  2676. TCHAR pszTemp[pT->m_cchCmpTextMax] = { 0 };
  2677. bool bStrValue = false;
  2678. switch(wType)
  2679. {
  2680. case LVCOLSORT_TEXT:
  2681. pFunc = (PFNLVCOMPARE)pT->LVCompareText;
  2682. case LVCOLSORT_TEXTNOCASE:
  2683. if(pFunc == NULL)
  2684. pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase;
  2685. case LVCOLSORT_CUSTOM:
  2686. {
  2687. if(pFunc == NULL)
  2688. pFunc = (PFNLVCOMPARE)pT->LVCompareCustom;
  2689. for(int i = 0; i < nCount; i++)
  2690. {
  2691. pParam[i].iItem = i;
  2692. pParam[i].dwItemData = pT->GetItemData(i);
  2693. pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax];
  2694. pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax);
  2695. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2696. }
  2697. bStrValue = true;
  2698. }
  2699. break;
  2700. case LVCOLSORT_LONG:
  2701. {
  2702. pFunc = (PFNLVCOMPARE)pT->LVCompareLong;
  2703. for(int i = 0; i < nCount; i++)
  2704. {
  2705. pParam[i].iItem = i;
  2706. pParam[i].dwItemData = pT->GetItemData(i);
  2707. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2708. pParam[i].lValue = pT->StrToLong(pszTemp);
  2709. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2710. }
  2711. }
  2712. break;
  2713. case LVCOLSORT_DOUBLE:
  2714. {
  2715. pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
  2716. for(int i = 0; i < nCount; i++)
  2717. {
  2718. pParam[i].iItem = i;
  2719. pParam[i].dwItemData = pT->GetItemData(i);
  2720. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2721. pParam[i].dblValue = pT->StrToDouble(pszTemp);
  2722. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2723. }
  2724. }
  2725. break;
  2726. case LVCOLSORT_DECIMAL:
  2727. {
  2728. pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal;
  2729. for(int i = 0; i < nCount; i++)
  2730. {
  2731. pParam[i].iItem = i;
  2732. pParam[i].dwItemData = pT->GetItemData(i);
  2733. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2734. pT->StrToDecimal(pszTemp, &pParam[i].decValue);
  2735. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2736. }
  2737. }
  2738. break;
  2739. case LVCOLSORT_DATETIME:
  2740. case LVCOLSORT_DATE:
  2741. case LVCOLSORT_TIME:
  2742. {
  2743. pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
  2744. DWORD dwFlags = LOCALE_NOUSEROVERRIDE;
  2745. if(wType == LVCOLSORT_DATE)
  2746. dwFlags |= VAR_DATEVALUEONLY;
  2747. else if(wType == LVCOLSORT_TIME)
  2748. dwFlags |= VAR_TIMEVALUEONLY;
  2749. for(int i = 0; i < nCount; i++)
  2750. {
  2751. pParam[i].iItem = i;
  2752. pParam[i].dwItemData = pT->GetItemData(i);
  2753. pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
  2754. pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags);
  2755. pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
  2756. }
  2757. }
  2758. break;
  2759. default:
  2760. ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n"));
  2761. break;
  2762. } // switch(wType)
  2763. ATLASSERT(pFunc != NULL);
  2764. LVSortInfo lvsi = { pT, iCol, bDescending };
  2765. bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE);
  2766. for(int i = 0; i < nCount; i++)
  2767. {
  2768. DWORD_PTR dwItemData = pT->GetItemData(i);
  2769. LVCompareParam* p = (LVCompareParam*)dwItemData;
  2770. ATLASSERT(p != NULL);
  2771. if(bStrValue)
  2772. delete [] (TCHAR*)p->pszValue;
  2773. pT->SetItemData(i, p->dwItemData);
  2774. }
  2775. delete [] pParam;
  2776. if(bRet)
  2777. {
  2778. m_bSortDescending = bDescending;
  2779. SetSortColumn(iCol);
  2780. }
  2781. if(m_bUseWaitCursor)
  2782. waitCursor.Restore();
  2783. return bRet;
  2784. }
  2785. void CreateSortBitmaps()
  2786. {
  2787. if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0)
  2788. {
  2789. bool bFree = false;
  2790. LPCTSTR pszModule = _T("shell32.dll");
  2791. HINSTANCE hShell = ::GetModuleHandle(pszModule);
  2792. if (hShell == NULL)
  2793. {
  2794. hShell = ::LoadLibrary(pszModule);
  2795. bFree = true;
  2796. }
  2797. if (hShell != NULL)
  2798. {
  2799. bool bSuccess = true;
  2800. for(int i = m_iSortUp; i <= m_iSortDown; i++)
  2801. {
  2802. if(!m_bmSort[i].IsNull())
  2803. m_bmSort[i].DeleteObject();
  2804. m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i),
  2805. #ifndef _WIN32_WCE
  2806. IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
  2807. #else // CE specific
  2808. IMAGE_BITMAP, 0, 0, 0);
  2809. #endif // _WIN32_WCE
  2810. if(m_bmSort[i].IsNull())
  2811. {
  2812. bSuccess = false;
  2813. break;
  2814. }
  2815. }
  2816. if(bFree)
  2817. ::FreeLibrary(hShell);
  2818. if(bSuccess)
  2819. return;
  2820. }
  2821. }
  2822. T* pT = static_cast<T*>(this);
  2823. for(int i = m_iSortUp; i <= m_iSortDown; i++)
  2824. {
  2825. if(!m_bmSort[i].IsNull())
  2826. m_bmSort[i].DeleteObject();
  2827. CDC dcMem;
  2828. CClientDC dc(::GetDesktopWindow());
  2829. dcMem.CreateCompatibleDC(dc.m_hDC);
  2830. m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage);
  2831. HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]);
  2832. RECT rc = {0,0,m_cxSortImage, m_cySortImage};
  2833. pT->DrawSortBitmap(dcMem.m_hDC, i, &rc);
  2834. dcMem.SelectBitmap(hbmOld);
  2835. dcMem.DeleteDC();
  2836. }
  2837. }
  2838. void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol)
  2839. {
  2840. T* pT = static_cast<T*>(this);
  2841. int nID = pT->GetDlgCtrlID();
  2842. NMSORTLISTVIEW nm = { { pT->m_hWnd, nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol };
  2843. ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm);
  2844. }
  2845. // Overrideables
  2846. int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/)
  2847. {
  2848. // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members.
  2849. // If item1 > item2 return 1, if item1 < item2 return -1, else return 0.
  2850. return 0;
  2851. }
  2852. void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc)
  2853. {
  2854. dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE));
  2855. HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
  2856. CPen pen;
  2857. pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW));
  2858. HPEN hpenOld = dc.SelectPen(pen);
  2859. POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 };
  2860. if(iBitmap == m_iSortUp)
  2861. {
  2862. POINT pts[3] =
  2863. {
  2864. { ptOrg.x + m_cxSortArrow / 2, ptOrg.y },
  2865. { ptOrg.x, ptOrg.y + m_cySortArrow - 1 },
  2866. { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 }
  2867. };
  2868. dc.Polygon(pts, 3);
  2869. }
  2870. else
  2871. {
  2872. POINT pts[3] =
  2873. {
  2874. { ptOrg.x, ptOrg.y },
  2875. { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 },
  2876. { ptOrg.x + m_cxSortArrow - 1, ptOrg.y }
  2877. };
  2878. dc.Polygon(pts, 3);
  2879. }
  2880. dc.SelectBrush(hbrOld);
  2881. dc.SelectPen(hpenOld);
  2882. }
  2883. double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags)
  2884. {
  2885. ATLASSERT(lpstr != NULL);
  2886. if(lpstr == NULL || lpstr[0] == _T('\0'))
  2887. return 0;
  2888. USES_CONVERSION;
  2889. HRESULT hRet = E_FAIL;
  2890. DATE dRet = 0;
  2891. if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet)))
  2892. {
  2893. ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet);
  2894. dRet = 0;
  2895. }
  2896. return dRet;
  2897. }
  2898. long StrToLong(LPCTSTR lpstr)
  2899. {
  2900. ATLASSERT(lpstr != NULL);
  2901. if(lpstr == NULL || lpstr[0] == _T('\0'))
  2902. return 0;
  2903. USES_CONVERSION;
  2904. HRESULT hRet = E_FAIL;
  2905. long lRet = 0;
  2906. if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet)))
  2907. {
  2908. ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet);
  2909. lRet = 0;
  2910. }
  2911. return lRet;
  2912. }
  2913. double StrToDouble(LPCTSTR lpstr)
  2914. {
  2915. ATLASSERT(lpstr != NULL);
  2916. if(lpstr == NULL || lpstr[0] == _T('\0'))
  2917. return 0;
  2918. USES_CONVERSION;
  2919. HRESULT hRet = E_FAIL;
  2920. double dblRet = 0;
  2921. if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet)))
  2922. {
  2923. ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet);
  2924. dblRet = 0;
  2925. }
  2926. return dblRet;
  2927. }
  2928. bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal)
  2929. {
  2930. ATLASSERT(lpstr != NULL);
  2931. ATLASSERT(pDecimal != NULL);
  2932. if(lpstr == NULL || pDecimal == NULL)
  2933. return false;
  2934. USES_CONVERSION;
  2935. HRESULT hRet = E_FAIL;
  2936. if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal)))
  2937. {
  2938. ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet);
  2939. pDecimal->Lo64 = 0;
  2940. pDecimal->Hi32 = 0;
  2941. pDecimal->signscale = 0;
  2942. return false;
  2943. }
  2944. return true;
  2945. }
  2946. // Overrideable PFNLVCOMPARE functions
  2947. static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2948. {
  2949. ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
  2950. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2951. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2952. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2953. int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue);
  2954. return pInfo->bDescending ? -nRet : nRet;
  2955. }
  2956. static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2957. {
  2958. ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
  2959. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2960. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2961. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2962. int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue);
  2963. return pInfo->bDescending ? -nRet : nRet;
  2964. }
  2965. static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2966. {
  2967. ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
  2968. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2969. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2970. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2971. int nRet = 0;
  2972. if(pParam1->lValue > pParam2->lValue)
  2973. nRet = 1;
  2974. else if(pParam1->lValue < pParam2->lValue)
  2975. nRet = -1;
  2976. return pInfo->bDescending ? -nRet : nRet;
  2977. }
  2978. static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2979. {
  2980. ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
  2981. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2982. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2983. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2984. int nRet = 0;
  2985. if(pParam1->dblValue > pParam2->dblValue)
  2986. nRet = 1;
  2987. else if(pParam1->dblValue < pParam2->dblValue)
  2988. nRet = -1;
  2989. return pInfo->bDescending ? -nRet : nRet;
  2990. }
  2991. static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  2992. {
  2993. ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
  2994. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  2995. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  2996. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  2997. int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol);
  2998. return pInfo->bDescending ? -nRet : nRet;
  2999. }
  3000. #ifndef _WIN32_WCE
  3001. static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  3002. {
  3003. ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
  3004. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  3005. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  3006. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  3007. int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue);
  3008. nRet--;
  3009. return pInfo->bDescending ? -nRet : nRet;
  3010. }
  3011. #else
  3012. // Compare mantissas, ignore sign and scale
  3013. static int CompareMantissas(const DECIMAL& decLeft, const DECIMAL& decRight)
  3014. {
  3015. if (decLeft.Hi32 < decRight.Hi32)
  3016. {
  3017. return -1;
  3018. }
  3019. if (decLeft.Hi32 > decRight.Hi32)
  3020. {
  3021. return 1;
  3022. }
  3023. // Here, decLeft.Hi32 == decRight.Hi32
  3024. if (decLeft.Lo64 < decRight.Lo64)
  3025. {
  3026. return -1;
  3027. }
  3028. if (decLeft.Lo64 > decRight.Lo64)
  3029. {
  3030. return 1;
  3031. }
  3032. return 0;
  3033. }
  3034. // return values: VARCMP_LT, VARCMP_EQ, VARCMP_GT, VARCMP_NULL
  3035. static HRESULT VarDecCmp(const DECIMAL* pdecLeft, const DECIMAL* pdecRight)
  3036. {
  3037. static const ULONG powersOfTen[] =
  3038. {
  3039. 10ul,
  3040. 100ul,
  3041. 1000ul,
  3042. 10000ul,
  3043. 100000ul,
  3044. 1000000ul,
  3045. 10000000ul,
  3046. 100000000ul,
  3047. 1000000000ul
  3048. };
  3049. static const int largestPower = sizeof(powersOfTen) / sizeof(powersOfTen[0]);
  3050. if (!pdecLeft || !pdecRight)
  3051. {
  3052. return VARCMP_NULL;
  3053. }
  3054. // Degenerate case - at least one comparand is of the form
  3055. // [+-]0*10^N (denormalized zero)
  3056. bool bLeftZero = (!pdecLeft->Lo64 && !pdecLeft->Hi32);
  3057. bool bRightZero = (!pdecRight->Lo64 && !pdecRight->Hi32);
  3058. if (bLeftZero && bRightZero)
  3059. {
  3060. return VARCMP_EQ;
  3061. }
  3062. bool bLeftNeg = ((pdecLeft->sign & DECIMAL_NEG) != 0);
  3063. bool bRightNeg = ((pdecRight->sign & DECIMAL_NEG) != 0);
  3064. if (bLeftZero)
  3065. {
  3066. return (bRightNeg ? VARCMP_GT : VARCMP_LT);
  3067. }
  3068. // This also covers the case where the comparands have different signs
  3069. if (bRightZero || bLeftNeg != bRightNeg)
  3070. {
  3071. return (bLeftNeg ? VARCMP_LT : VARCMP_GT);
  3072. }
  3073. // Here both comparands have the same sign and need to be compared
  3074. // on mantissa and scale. The result is obvious when
  3075. // 1. Scales are equal (then compare mantissas)
  3076. // 2. A number with smaller scale is also the one with larger mantissa
  3077. // (then this number is obviously larger)
  3078. // In the remaining case, we would multiply the number with smaller
  3079. // scale by 10 and simultaneously increment its scale (which amounts to
  3080. // adding trailing zeros after decimal point), until the numbers fall under
  3081. // one of the two cases above
  3082. DECIMAL temp;
  3083. bool bInvert = bLeftNeg; // the final result needs to be inverted
  3084. if (pdecLeft->scale < pdecRight->scale)
  3085. {
  3086. temp = *pdecLeft;
  3087. }
  3088. else
  3089. {
  3090. temp = *pdecRight;
  3091. pdecRight = pdecLeft;
  3092. bInvert = !bInvert;
  3093. }
  3094. // Now temp is the number with smaller (or equal) scale, and
  3095. // we can modify it freely without touching original parameters
  3096. int comp;
  3097. while ((comp = CompareMantissas(temp, *pdecRight)) < 0 &&
  3098. temp.scale < pdecRight->scale)
  3099. {
  3100. // Multiply by an appropriate power of 10
  3101. int scaleDiff = pdecRight->scale - temp.scale;
  3102. if (scaleDiff > largestPower)
  3103. {
  3104. // Keep the multiplier representable in 32bit
  3105. scaleDiff = largestPower;
  3106. }
  3107. DWORDLONG power = powersOfTen[scaleDiff - 1];
  3108. // Multiply temp's mantissa by power
  3109. DWORDLONG product = temp.Lo32 * power;
  3110. ULONG carry = static_cast<ULONG>(product >> 32);
  3111. temp.Lo32 = static_cast<ULONG>(product);
  3112. product = temp.Mid32 * power + carry;
  3113. carry = static_cast<ULONG>(product >> 32);
  3114. temp.Mid32 = static_cast<ULONG>(product);
  3115. product = temp.Hi32 * power + carry;
  3116. if (static_cast<ULONG>(product >> 32))
  3117. {
  3118. // Multiplication overflowed - pdecLeft is clearly larger
  3119. break;
  3120. }
  3121. temp.Hi32 = static_cast<ULONG>(product);
  3122. temp.scale = (BYTE)(temp.scale + scaleDiff);
  3123. }
  3124. if (temp.scale < pdecRight->scale)
  3125. {
  3126. comp = 1;
  3127. }
  3128. if (bInvert)
  3129. {
  3130. comp = -comp;
  3131. }
  3132. return (comp > 0 ? VARCMP_GT : comp < 0 ? VARCMP_LT : VARCMP_EQ);
  3133. }
  3134. static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  3135. {
  3136. ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
  3137. LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
  3138. LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
  3139. LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
  3140. int nRet = (int)VarDecCmp(&pParam1->decValue, &pParam2->decValue);
  3141. nRet--;
  3142. return pInfo->bDescending ? -nRet : nRet;
  3143. }
  3144. #endif // !_WIN32_WCE
  3145. BEGIN_MSG_MAP(CSortListViewImpl)
  3146. MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn)
  3147. MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn)
  3148. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick)
  3149. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick)
  3150. MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
  3151. END_MSG_MAP()
  3152. LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  3153. {
  3154. T* pT = static_cast<T*>(this);
  3155. LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
  3156. if(lRet == -1)
  3157. return -1;
  3158. WORD wType = 0;
  3159. m_arrColSortType.Add(wType);
  3160. int nCount = m_arrColSortType.GetSize();
  3161. ATLASSERT(nCount == GetColumnCount());
  3162. for(int i = nCount - 1; i > lRet; i--)
  3163. m_arrColSortType[i] = m_arrColSortType[i - 1];
  3164. m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT;
  3165. if(lRet <= m_iSortColumn)
  3166. m_iSortColumn++;
  3167. return lRet;
  3168. }
  3169. LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  3170. {
  3171. T* pT = static_cast<T*>(this);
  3172. LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
  3173. if(lRet == 0)
  3174. return 0;
  3175. int iCol = (int)wParam;
  3176. if(m_iSortColumn == iCol)
  3177. m_iSortColumn = -1;
  3178. else if(m_iSortColumn > iCol)
  3179. m_iSortColumn--;
  3180. m_arrColSortType.RemoveAt(iCol);
  3181. return lRet;
  3182. }
  3183. LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
  3184. {
  3185. LPNMHEADER p = (LPNMHEADER)pnmh;
  3186. if(p->iButton == 0)
  3187. {
  3188. int iOld = m_iSortColumn;
  3189. bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false;
  3190. if(DoSortItems(p->iItem, bDescending))
  3191. NotifyParentSortChanged(p->iItem, iOld);
  3192. }
  3193. bHandled = FALSE;
  3194. return 0;
  3195. }
  3196. LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  3197. {
  3198. #ifndef _WIN32_WCE
  3199. if(wParam == SPI_SETNONCLIENTMETRICS)
  3200. GetSystemSettings();
  3201. #else // CE specific
  3202. wParam; // avoid level 4 warning
  3203. GetSystemSettings();
  3204. #endif // _WIN32_WCE
  3205. bHandled = FALSE;
  3206. return 0;
  3207. }
  3208. void GetSystemSettings()
  3209. {
  3210. if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull())
  3211. {
  3212. T* pT = static_cast<T*>(this);
  3213. pT->CreateSortBitmaps();
  3214. if(m_iSortColumn != -1)
  3215. SetSortColumn(m_iSortColumn);
  3216. }
  3217. }
  3218. };
  3219. typedef ATL::CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SHOWSELALWAYS , WS_EX_CLIENTEDGE> CSortListViewCtrlTraits;
  3220. template <class T, class TBase = CListViewCtrl, class TWinTraits = CSortListViewCtrlTraits>
  3221. class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl<T, TBase, TWinTraits>, public CSortListViewImpl<T>
  3222. {
  3223. public:
  3224. DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
  3225. bool SortItems(int iCol, bool bDescending = false)
  3226. {
  3227. return DoSortItems(iCol, bDescending);
  3228. }
  3229. BEGIN_MSG_MAP(CSortListViewCtrlImpl)
  3230. MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl<T>::OnInsertColumn)
  3231. MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl<T>::OnDeleteColumn)
  3232. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl<T>::OnHeaderItemClick)
  3233. NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl<T>::OnHeaderItemClick)
  3234. MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl<T>::OnSettingChange)
  3235. END_MSG_MAP()
  3236. };
  3237. class CSortListViewCtrl : public CSortListViewCtrlImpl<CSortListViewCtrl>
  3238. {
  3239. public:
  3240. DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName())
  3241. };
  3242. ///////////////////////////////////////////////////////////////////////////////
  3243. // CTabView - implements tab view window
  3244. // TabView Notifications
  3245. #define TBVN_PAGEACTIVATED (0U-741)
  3246. #define TBVN_CONTEXTMENU (0U-742)
  3247. // Notification data for TBVN_CONTEXTMENU
  3248. struct TBVCONTEXTMENUINFO
  3249. {
  3250. NMHDR hdr;
  3251. POINT pt;
  3252. };
  3253. typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
  3254. template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
  3255. class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
  3256. {
  3257. public:
  3258. DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE)
  3259. // Declarations and enums
  3260. struct TABVIEWPAGE
  3261. {
  3262. HWND hWnd;
  3263. LPTSTR lpstrTitle;
  3264. LPVOID pData;
  3265. };
  3266. struct TCITEMEXTRA
  3267. {
  3268. TCITEMHEADER tciheader;
  3269. TABVIEWPAGE tvpage;
  3270. operator LPTCITEM() { return (LPTCITEM)this; }
  3271. };
  3272. enum
  3273. {
  3274. m_nTabID = 1313,
  3275. m_cxMoveMark = 6,
  3276. m_cyMoveMark = 3,
  3277. m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
  3278. };
  3279. // Data members
  3280. ATL::CContainedWindowT<CTabCtrl> m_tab;
  3281. int m_cyTabHeight;
  3282. int m_nActivePage;
  3283. int m_nInsertItem;
  3284. POINT m_ptStartDrag;
  3285. CMenuHandle m_menu;
  3286. int m_cchTabTextLength;
  3287. int m_nMenuItemsCount;
  3288. ATL::CWindow m_wndTitleBar;
  3289. LPTSTR m_lpstrTitleBarBase;
  3290. int m_cchTitleBarLength;
  3291. CImageList m_ilDrag;
  3292. bool m_bDestroyPageOnRemove:1;
  3293. bool m_bDestroyImageList:1;
  3294. bool m_bActivePageMenuItem:1;
  3295. bool m_bActiveAsDefaultMenuItem:1;
  3296. bool m_bEmptyMenuItem:1;
  3297. bool m_bWindowsMenuItem:1;
  3298. bool m_bNoTabDrag:1;
  3299. // internal
  3300. bool m_bTabCapture:1;
  3301. bool m_bTabDrag:1;
  3302. bool m_bInternalFont:1;
  3303. // Constructor/destructor
  3304. CTabViewImpl() :
  3305. m_nActivePage(-1),
  3306. m_cyTabHeight(0),
  3307. m_tab(this, 1),
  3308. m_nInsertItem(-1),
  3309. m_cchTabTextLength(30),
  3310. m_nMenuItemsCount(10),
  3311. m_lpstrTitleBarBase(NULL),
  3312. m_cchTitleBarLength(100),
  3313. m_bDestroyPageOnRemove(true),
  3314. m_bDestroyImageList(true),
  3315. m_bActivePageMenuItem(true),
  3316. m_bActiveAsDefaultMenuItem(false),
  3317. m_bEmptyMenuItem(false),
  3318. m_bWindowsMenuItem(false),
  3319. m_bNoTabDrag(false),
  3320. m_bTabCapture(false),
  3321. m_bTabDrag(false),
  3322. m_bInternalFont(false)
  3323. {
  3324. m_ptStartDrag.x = 0;
  3325. m_ptStartDrag.y = 0;
  3326. }
  3327. ~CTabViewImpl()
  3328. {
  3329. delete [] m_lpstrTitleBarBase;
  3330. }
  3331. // Message filter function - to be called from PreTranslateMessage of the main window
  3332. BOOL PreTranslateMessage(MSG* pMsg)
  3333. {
  3334. if(IsWindow() == FALSE)
  3335. return FALSE;
  3336. BOOL bRet = FALSE;
  3337. // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
  3338. int nCount = GetPageCount();
  3339. if(nCount > 0)
  3340. {
  3341. bool bControl = (::GetKeyState(VK_CONTROL) < 0);
  3342. if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
  3343. {
  3344. if(nCount > 1)
  3345. {
  3346. int nPage = m_nActivePage;
  3347. bool bShift = (::GetKeyState(VK_SHIFT) < 0);
  3348. if(bShift)
  3349. nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
  3350. else
  3351. nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
  3352. SetActivePage(nPage);
  3353. T* pT = static_cast<T*>(this);
  3354. pT->OnPageActivated(m_nActivePage);
  3355. }
  3356. bRet = TRUE;
  3357. }
  3358. }
  3359. // If we are doing drag-drop, check for Escape key that cancels it
  3360. if(bRet == FALSE)
  3361. {
  3362. if(m_bTabCapture && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
  3363. {
  3364. ::ReleaseCapture();
  3365. bRet = TRUE;
  3366. }
  3367. }
  3368. // Pass the message to the active page
  3369. if(bRet == FALSE)
  3370. {
  3371. if(m_nActivePage != -1)
  3372. bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
  3373. }
  3374. return bRet;
  3375. }
  3376. // Attributes
  3377. int GetPageCount() const
  3378. {
  3379. ATLASSERT(::IsWindow(m_hWnd));
  3380. return m_tab.GetItemCount();
  3381. }
  3382. int GetActivePage() const
  3383. {
  3384. return m_nActivePage;
  3385. }
  3386. void SetActivePage(int nPage)
  3387. {
  3388. ATLASSERT(::IsWindow(m_hWnd));
  3389. ATLASSERT(IsValidPageIndex(nPage));
  3390. T* pT = static_cast<T*>(this);
  3391. SetRedraw(FALSE);
  3392. if(m_nActivePage != -1)
  3393. ::ShowWindow(GetPageHWND(m_nActivePage), FALSE);
  3394. m_nActivePage = nPage;
  3395. m_tab.SetCurSel(m_nActivePage);
  3396. ::ShowWindow(GetPageHWND(m_nActivePage), TRUE);
  3397. pT->UpdateLayout();
  3398. SetRedraw(TRUE);
  3399. RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
  3400. if(::GetFocus() != m_tab.m_hWnd)
  3401. ::SetFocus(GetPageHWND(m_nActivePage));
  3402. pT->UpdateTitleBar();
  3403. pT->UpdateMenu();
  3404. }
  3405. HIMAGELIST GetImageList() const
  3406. {
  3407. ATLASSERT(::IsWindow(m_hWnd));
  3408. return m_tab.GetImageList();
  3409. }
  3410. HIMAGELIST SetImageList(HIMAGELIST hImageList)
  3411. {
  3412. ATLASSERT(::IsWindow(m_hWnd));
  3413. return m_tab.SetImageList(hImageList);
  3414. }
  3415. void SetWindowMenu(HMENU hMenu)
  3416. {
  3417. ATLASSERT(::IsWindow(m_hWnd));
  3418. m_menu = hMenu;
  3419. T* pT = static_cast<T*>(this);
  3420. pT->UpdateMenu();
  3421. }
  3422. void SetTitleBarWindow(HWND hWnd)
  3423. {
  3424. ATLASSERT(::IsWindow(m_hWnd));
  3425. delete [] m_lpstrTitleBarBase;
  3426. m_lpstrTitleBarBase = NULL;
  3427. m_wndTitleBar = hWnd;
  3428. if(hWnd == NULL)
  3429. return;
  3430. int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
  3431. ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
  3432. if(m_lpstrTitleBarBase != NULL)
  3433. {
  3434. m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
  3435. T* pT = static_cast<T*>(this);
  3436. pT->UpdateTitleBar();
  3437. }
  3438. }
  3439. // Page attributes
  3440. HWND GetPageHWND(int nPage) const
  3441. {
  3442. ATLASSERT(::IsWindow(m_hWnd));
  3443. ATLASSERT(IsValidPageIndex(nPage));
  3444. TCITEMEXTRA tcix = { 0 };
  3445. tcix.tciheader.mask = TCIF_PARAM;
  3446. m_tab.GetItem(nPage, tcix);
  3447. return tcix.tvpage.hWnd;
  3448. }
  3449. LPCTSTR GetPageTitle(int nPage) const
  3450. {
  3451. ATLASSERT(::IsWindow(m_hWnd));
  3452. ATLASSERT(IsValidPageIndex(nPage));
  3453. TCITEMEXTRA tcix = { 0 };
  3454. tcix.tciheader.mask = TCIF_PARAM;
  3455. if(m_tab.GetItem(nPage, tcix) == FALSE)
  3456. return NULL;
  3457. return tcix.tvpage.lpstrTitle;
  3458. }
  3459. bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
  3460. {
  3461. ATLASSERT(::IsWindow(m_hWnd));
  3462. ATLASSERT(IsValidPageIndex(nPage));
  3463. T* pT = static_cast<T*>(this);
  3464. int cchBuff = lstrlen(lpstrTitle) + 1;
  3465. LPTSTR lpstrBuff = NULL;
  3466. ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
  3467. if(lpstrBuff == NULL)
  3468. return false;
  3469. SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
  3470. TCITEMEXTRA tcix = { 0 };
  3471. tcix.tciheader.mask = TCIF_PARAM;
  3472. if(m_tab.GetItem(nPage, tcix) == FALSE)
  3473. return false;
  3474. CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3475. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  3476. if(lpstrTabText == NULL)
  3477. return false;
  3478. delete [] tcix.tvpage.lpstrTitle;
  3479. pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
  3480. tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
  3481. tcix.tciheader.pszText = lpstrTabText;
  3482. tcix.tvpage.lpstrTitle = lpstrBuff;
  3483. if(m_tab.SetItem(nPage, tcix) == FALSE)
  3484. return false;
  3485. pT->UpdateTitleBar();
  3486. pT->UpdateMenu();
  3487. return true;
  3488. }
  3489. LPVOID GetPageData(int nPage) const
  3490. {
  3491. ATLASSERT(::IsWindow(m_hWnd));
  3492. ATLASSERT(IsValidPageIndex(nPage));
  3493. TCITEMEXTRA tcix = { 0 };
  3494. tcix.tciheader.mask = TCIF_PARAM;
  3495. m_tab.GetItem(nPage, tcix);
  3496. return tcix.tvpage.pData;
  3497. }
  3498. LPVOID SetPageData(int nPage, LPVOID pData)
  3499. {
  3500. ATLASSERT(::IsWindow(m_hWnd));
  3501. ATLASSERT(IsValidPageIndex(nPage));
  3502. TCITEMEXTRA tcix = { 0 };
  3503. tcix.tciheader.mask = TCIF_PARAM;
  3504. m_tab.GetItem(nPage, tcix);
  3505. LPVOID pDataOld = tcix.tvpage.pData;
  3506. tcix.tvpage.pData = pData;
  3507. m_tab.SetItem(nPage, tcix);
  3508. return pDataOld;
  3509. }
  3510. int GetPageImage(int nPage) const
  3511. {
  3512. ATLASSERT(::IsWindow(m_hWnd));
  3513. ATLASSERT(IsValidPageIndex(nPage));
  3514. TCITEMEXTRA tcix = { 0 };
  3515. tcix.tciheader.mask = TCIF_IMAGE;
  3516. m_tab.GetItem(nPage, tcix);
  3517. return tcix.tciheader.iImage;
  3518. }
  3519. int SetPageImage(int nPage, int nImage)
  3520. {
  3521. ATLASSERT(::IsWindow(m_hWnd));
  3522. ATLASSERT(IsValidPageIndex(nPage));
  3523. TCITEMEXTRA tcix = { 0 };
  3524. tcix.tciheader.mask = TCIF_IMAGE;
  3525. m_tab.GetItem(nPage, tcix);
  3526. int nImageOld = tcix.tciheader.iImage;
  3527. tcix.tciheader.iImage = nImage;
  3528. m_tab.SetItem(nPage, tcix);
  3529. return nImageOld;
  3530. }
  3531. // Operations
  3532. bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
  3533. {
  3534. return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
  3535. }
  3536. bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
  3537. {
  3538. ATLASSERT(::IsWindow(m_hWnd));
  3539. ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage));
  3540. T* pT = static_cast<T*>(this);
  3541. int cchBuff = lstrlen(lpstrTitle) + 1;
  3542. LPTSTR lpstrBuff = NULL;
  3543. ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
  3544. if(lpstrBuff == NULL)
  3545. return false;
  3546. SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
  3547. CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3548. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  3549. if(lpstrTabText == NULL)
  3550. return false;
  3551. pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
  3552. SetRedraw(FALSE);
  3553. TCITEMEXTRA tcix = { 0 };
  3554. tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
  3555. tcix.tciheader.pszText = lpstrTabText;
  3556. tcix.tciheader.iImage = nImage;
  3557. tcix.tvpage.hWnd = hWndView;
  3558. tcix.tvpage.lpstrTitle = lpstrBuff;
  3559. tcix.tvpage.pData = pData;
  3560. int nItem = m_tab.InsertItem(nPage, tcix);
  3561. if(nItem == -1)
  3562. {
  3563. delete [] lpstrBuff;
  3564. SetRedraw(TRUE);
  3565. return false;
  3566. }
  3567. SetActivePage(nItem);
  3568. pT->OnPageActivated(m_nActivePage);
  3569. if(GetPageCount() == 1)
  3570. pT->ShowTabControl(true);
  3571. pT->UpdateLayout();
  3572. SetRedraw(TRUE);
  3573. RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
  3574. return true;
  3575. }
  3576. void RemovePage(int nPage)
  3577. {
  3578. ATLASSERT(::IsWindow(m_hWnd));
  3579. ATLASSERT(IsValidPageIndex(nPage));
  3580. T* pT = static_cast<T*>(this);
  3581. SetRedraw(FALSE);
  3582. if(GetPageCount() == 1)
  3583. pT->ShowTabControl(false);
  3584. if(m_bDestroyPageOnRemove)
  3585. ::DestroyWindow(GetPageHWND(nPage));
  3586. else
  3587. ::ShowWindow(GetPageHWND(nPage), FALSE);
  3588. LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
  3589. delete [] lpstrTitle;
  3590. ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
  3591. if(m_nActivePage == nPage)
  3592. {
  3593. m_nActivePage = -1;
  3594. if(nPage > 0)
  3595. {
  3596. SetActivePage(nPage - 1);
  3597. }
  3598. else if(GetPageCount() > 0)
  3599. {
  3600. SetActivePage(nPage);
  3601. }
  3602. else
  3603. {
  3604. SetRedraw(TRUE);
  3605. Invalidate();
  3606. UpdateWindow();
  3607. pT->UpdateTitleBar();
  3608. pT->UpdateMenu();
  3609. }
  3610. }
  3611. else
  3612. {
  3613. nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
  3614. m_nActivePage = -1;
  3615. SetActivePage(nPage);
  3616. }
  3617. pT->OnPageActivated(m_nActivePage);
  3618. }
  3619. void RemoveAllPages()
  3620. {
  3621. ATLASSERT(::IsWindow(m_hWnd));
  3622. if(GetPageCount() == 0)
  3623. return;
  3624. T* pT = static_cast<T*>(this);
  3625. SetRedraw(FALSE);
  3626. pT->ShowTabControl(false);
  3627. for(int i = 0; i < GetPageCount(); i++)
  3628. {
  3629. if(m_bDestroyPageOnRemove)
  3630. ::DestroyWindow(GetPageHWND(i));
  3631. else
  3632. ::ShowWindow(GetPageHWND(i), FALSE);
  3633. LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
  3634. delete [] lpstrTitle;
  3635. }
  3636. m_tab.DeleteAllItems();
  3637. m_nActivePage = -1;
  3638. pT->OnPageActivated(m_nActivePage);
  3639. SetRedraw(TRUE);
  3640. Invalidate();
  3641. UpdateWindow();
  3642. pT->UpdateTitleBar();
  3643. pT->UpdateMenu();
  3644. }
  3645. int PageIndexFromHwnd(HWND hWnd) const
  3646. {
  3647. int nIndex = -1;
  3648. for(int i = 0; i < GetPageCount(); i++)
  3649. {
  3650. if(GetPageHWND(i) == hWnd)
  3651. {
  3652. nIndex = i;
  3653. break;
  3654. }
  3655. }
  3656. return nIndex;
  3657. }
  3658. void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
  3659. {
  3660. ATLASSERT(::IsWindow(m_hWnd));
  3661. CMenuHandle menu = hMenu;
  3662. T* pT = static_cast<T*>(this);
  3663. pT; // avoid level 4 warning
  3664. int nFirstPos = 0;
  3665. // Find first menu item in our range
  3666. #ifndef _WIN32_WCE
  3667. for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
  3668. {
  3669. UINT nID = menu.GetMenuItemID(nFirstPos);
  3670. if((nID >= ID_WINDOW_TABFIRST && nID <= ID_WINDOW_TABLAST) || nID == ID_WINDOW_SHOWTABLIST)
  3671. break;
  3672. }
  3673. #else // CE specific
  3674. for(nFirstPos = 0; ; nFirstPos++)
  3675. {
  3676. CMenuItemInfo mii;
  3677. mii.fMask = MIIM_ID;
  3678. BOOL bRet = menu.GetMenuItemInfo(nFirstPos, TRUE, &mii);
  3679. if(bRet == FALSE)
  3680. break;
  3681. if((mii.wID >= ID_WINDOW_TABFIRST && mii.wID <= ID_WINDOW_TABLAST) || mii.wID == ID_WINDOW_SHOWTABLIST)
  3682. break;
  3683. }
  3684. #endif // _WIN32_WCE
  3685. // Remove all menu items for tab pages
  3686. BOOL bRet = TRUE;
  3687. while(bRet != FALSE)
  3688. bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
  3689. // Add separator if it's not already there
  3690. int nPageCount = GetPageCount();
  3691. if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
  3692. {
  3693. CMenuItemInfo mii;
  3694. mii.fMask = MIIM_TYPE;
  3695. menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
  3696. if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0))
  3697. {
  3698. menu.AppendMenu(MF_SEPARATOR);
  3699. nFirstPos++;
  3700. }
  3701. }
  3702. // Add menu items for all pages
  3703. if(nPageCount > 0)
  3704. {
  3705. // Append menu items for all pages
  3706. const int cchPrefix = 3; // 2 digits + space
  3707. nMenuItemsCount = min(min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
  3708. ATLASSERT(nMenuItemsCount < 100); // 2 digits only
  3709. if(nMenuItemsCount >= 100)
  3710. nMenuItemsCount = 99;
  3711. for(int i = 0; i < nMenuItemsCount; i++)
  3712. {
  3713. LPCTSTR lpstrTitle = GetPageTitle(i);
  3714. int nLen = lstrlen(lpstrTitle);
  3715. CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  3716. LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
  3717. ATLASSERT(lpstrText != NULL);
  3718. if(lpstrText != NULL)
  3719. {
  3720. LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
  3721. SecureHelper::wsprintf_x(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
  3722. menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
  3723. }
  3724. }
  3725. // Mark active page
  3726. if(bActivePageMenuItem && (m_nActivePage != -1))
  3727. {
  3728. #ifndef _WIN32_WCE
  3729. if(bActiveAsDefaultMenuItem)
  3730. {
  3731. menu.SetMenuDefaultItem((UINT)-1, TRUE);
  3732. menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
  3733. }
  3734. else
  3735. #else // CE specific
  3736. bActiveAsDefaultMenuItem; // avoid level 4 warning
  3737. #endif // _WIN32_WCE
  3738. {
  3739. menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
  3740. }
  3741. }
  3742. }
  3743. else
  3744. {
  3745. if(bEmptyMenuItem)
  3746. {
  3747. menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
  3748. menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
  3749. }
  3750. // Remove separator if nothing else is there
  3751. if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
  3752. {
  3753. CMenuItemInfo mii;
  3754. mii.fMask = MIIM_TYPE;
  3755. menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
  3756. if((mii.fType & MFT_SEPARATOR) != 0)
  3757. menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
  3758. }
  3759. }
  3760. // Add "Windows..." menu item
  3761. if(bWindowsMenuItem)
  3762. menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
  3763. }
  3764. // Message map and handlers
  3765. BEGIN_MSG_MAP(CTabViewImpl)
  3766. MESSAGE_HANDLER(WM_CREATE, OnCreate)
  3767. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  3768. MESSAGE_HANDLER(WM_SIZE, OnSize)
  3769. MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
  3770. MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
  3771. MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
  3772. NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
  3773. NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
  3774. #ifndef _WIN32_WCE
  3775. NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
  3776. #endif // !_WIN32_WCE
  3777. FORWARD_NOTIFICATIONS()
  3778. ALT_MSG_MAP(1) // tab control
  3779. MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
  3780. MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
  3781. MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
  3782. MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
  3783. MESSAGE_HANDLER(WM_RBUTTONUP, OnTabRButtonUp)
  3784. MESSAGE_HANDLER(WM_SYSKEYDOWN, OnTabSysKeyDown)
  3785. END_MSG_MAP()
  3786. LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3787. {
  3788. T* pT = static_cast<T*>(this);
  3789. pT->CreateTabControl();
  3790. return 0;
  3791. }
  3792. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3793. {
  3794. RemoveAllPages();
  3795. if(m_bDestroyImageList)
  3796. {
  3797. CImageList il = m_tab.SetImageList(NULL);
  3798. if(il.m_hImageList != NULL)
  3799. il.Destroy();
  3800. }
  3801. if(m_bInternalFont)
  3802. {
  3803. HFONT hFont = m_tab.GetFont();
  3804. m_tab.SetFont(NULL, FALSE);
  3805. ::DeleteObject(hFont);
  3806. m_bInternalFont = false;
  3807. }
  3808. return 0;
  3809. }
  3810. LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3811. {
  3812. T* pT = static_cast<T*>(this);
  3813. pT->UpdateLayout();
  3814. return 0;
  3815. }
  3816. LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3817. {
  3818. if(m_nActivePage != -1)
  3819. ::SetFocus(GetPageHWND(m_nActivePage));
  3820. return 0;
  3821. }
  3822. LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
  3823. {
  3824. return m_tab.SendMessage(WM_GETFONT);
  3825. }
  3826. LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
  3827. {
  3828. if(m_bInternalFont)
  3829. {
  3830. HFONT hFont = m_tab.GetFont();
  3831. m_tab.SetFont(NULL, FALSE);
  3832. ::DeleteObject(hFont);
  3833. m_bInternalFont = false;
  3834. }
  3835. m_tab.SendMessage(WM_SETFONT, wParam, lParam);
  3836. T* pT = static_cast<T*>(this);
  3837. m_cyTabHeight = pT->CalcTabHeight();
  3838. if((BOOL)lParam != FALSE)
  3839. pT->UpdateLayout();
  3840. return 0;
  3841. }
  3842. LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
  3843. {
  3844. SetActivePage(m_tab.GetCurSel());
  3845. T* pT = static_cast<T*>(this);
  3846. pT->OnPageActivated(m_nActivePage);
  3847. return 0;
  3848. }
  3849. LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
  3850. {
  3851. // nothing to do - this just blocks all tab control
  3852. // notifications from being propagated further
  3853. return 0;
  3854. }
  3855. #ifndef _WIN32_WCE
  3856. LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
  3857. {
  3858. LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
  3859. if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
  3860. {
  3861. T* pT = static_cast<T*>(this);
  3862. pT->UpdateTooltipText(pTTDI);
  3863. }
  3864. else
  3865. {
  3866. bHandled = FALSE;
  3867. }
  3868. return 0;
  3869. }
  3870. #endif // !_WIN32_WCE
  3871. // Tab control message handlers
  3872. LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3873. {
  3874. if(!m_bNoTabDrag && (m_tab.GetItemCount() > 1))
  3875. {
  3876. m_bTabCapture = true;
  3877. m_tab.SetCapture();
  3878. m_ptStartDrag.x = GET_X_LPARAM(lParam);
  3879. m_ptStartDrag.y = GET_Y_LPARAM(lParam);
  3880. }
  3881. bHandled = FALSE;
  3882. return 0;
  3883. }
  3884. LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3885. {
  3886. if(m_bTabCapture)
  3887. {
  3888. if(m_bTabDrag)
  3889. {
  3890. TCHITTESTINFO hti = { 0 };
  3891. hti.pt.x = GET_X_LPARAM(lParam);
  3892. hti.pt.y = GET_Y_LPARAM(lParam);
  3893. int nItem = m_tab.HitTest(&hti);
  3894. if(nItem != -1)
  3895. MovePage(m_nActivePage, nItem);
  3896. }
  3897. ::ReleaseCapture();
  3898. }
  3899. bHandled = FALSE;
  3900. return 0;
  3901. }
  3902. LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  3903. {
  3904. if(m_bTabCapture)
  3905. {
  3906. m_bTabCapture = false;
  3907. if(m_bTabDrag)
  3908. {
  3909. m_bTabDrag = false;
  3910. T* pT = static_cast<T*>(this);
  3911. pT->DrawMoveMark(-1);
  3912. #ifndef _WIN32_WCE
  3913. m_ilDrag.DragLeave(GetDesktopWindow());
  3914. #endif // !_WIN32_WCE
  3915. m_ilDrag.EndDrag();
  3916. m_ilDrag.Destroy();
  3917. m_ilDrag.m_hImageList = NULL;
  3918. }
  3919. }
  3920. bHandled = FALSE;
  3921. return 0;
  3922. }
  3923. LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
  3924. {
  3925. bHandled = FALSE;
  3926. if(m_bTabCapture)
  3927. {
  3928. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  3929. if(!m_bTabDrag)
  3930. {
  3931. #ifndef _WIN32_WCE
  3932. if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) ||
  3933. abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG))
  3934. #else // CE specific
  3935. if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= 4 ||
  3936. abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= 4)
  3937. #endif // _WIN32_WCE
  3938. {
  3939. T* pT = static_cast<T*>(this);
  3940. pT->GenerateDragImage(m_nActivePage);
  3941. int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
  3942. int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
  3943. m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
  3944. #ifndef _WIN32_WCE
  3945. POINT ptEnter = m_ptStartDrag;
  3946. m_tab.ClientToScreen(&ptEnter);
  3947. m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
  3948. #endif // !_WIN32_WCE
  3949. m_bTabDrag = true;
  3950. }
  3951. }
  3952. if(m_bTabDrag)
  3953. {
  3954. TCHITTESTINFO hti = { 0 };
  3955. hti.pt = pt;
  3956. int nItem = m_tab.HitTest(&hti);
  3957. T* pT = static_cast<T*>(this);
  3958. pT->SetMoveCursor(nItem != -1);
  3959. if(m_nInsertItem != nItem)
  3960. pT->DrawMoveMark(nItem);
  3961. m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
  3962. m_tab.ClientToScreen(&pt);
  3963. m_ilDrag.DragMove(pt);
  3964. bHandled = TRUE;
  3965. }
  3966. }
  3967. return 0;
  3968. }
  3969. LRESULT OnTabRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  3970. {
  3971. TCHITTESTINFO hti = { 0 };
  3972. hti.pt.x = GET_X_LPARAM(lParam);
  3973. hti.pt.y = GET_Y_LPARAM(lParam);
  3974. int nItem = m_tab.HitTest(&hti);
  3975. if(nItem != -1)
  3976. {
  3977. T* pT = static_cast<T*>(this);
  3978. pT->OnContextMenu(nItem, hti.pt);
  3979. }
  3980. return 0;
  3981. }
  3982. LRESULT OnTabSysKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
  3983. {
  3984. bool bShift = (::GetKeyState(VK_SHIFT) < 0);
  3985. if(wParam == VK_F10 && bShift)
  3986. {
  3987. if(m_nActivePage != -1)
  3988. {
  3989. RECT rect = { 0 };
  3990. m_tab.GetItemRect(m_nActivePage, &rect);
  3991. POINT pt = { rect.left, rect.bottom };
  3992. T* pT = static_cast<T*>(this);
  3993. pT->OnContextMenu(m_nActivePage, pt);
  3994. }
  3995. }
  3996. else
  3997. {
  3998. bHandled = FALSE;
  3999. }
  4000. return 0;
  4001. }
  4002. // Implementation helpers
  4003. bool IsValidPageIndex(int nPage) const
  4004. {
  4005. return (nPage >= 0 && nPage < GetPageCount());
  4006. }
  4007. bool MovePage(int nMovePage, int nInsertBeforePage)
  4008. {
  4009. ATLASSERT(IsValidPageIndex(nMovePage));
  4010. ATLASSERT(IsValidPageIndex(nInsertBeforePage));
  4011. if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
  4012. return false;
  4013. if(nMovePage == nInsertBeforePage)
  4014. return true; // nothing to do
  4015. CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  4016. LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
  4017. if(lpstrTabText == NULL)
  4018. return false;
  4019. TCITEMEXTRA tcix = { 0 };
  4020. tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
  4021. tcix.tciheader.pszText = lpstrTabText;
  4022. tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
  4023. BOOL bRet = m_tab.GetItem(nMovePage, tcix);
  4024. ATLASSERT(bRet != FALSE);
  4025. if(bRet == FALSE)
  4026. return false;
  4027. int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
  4028. int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
  4029. ATLASSERT(nNewItem == nInsertItem);
  4030. if(nNewItem != nInsertItem)
  4031. {
  4032. ATLVERIFY(m_tab.DeleteItem(nNewItem));
  4033. return false;
  4034. }
  4035. if(nMovePage > nInsertBeforePage)
  4036. ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
  4037. else if(nMovePage < nInsertBeforePage)
  4038. ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
  4039. SetActivePage(nInsertBeforePage);
  4040. T* pT = static_cast<T*>(this);
  4041. pT->OnPageActivated(m_nActivePage);
  4042. return true;
  4043. }
  4044. // Implementation overrideables
  4045. bool CreateTabControl()
  4046. {
  4047. #ifndef _WIN32_WCE
  4048. m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
  4049. #else // CE specific
  4050. m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, m_nTabID);
  4051. #endif // _WIN32_WCE
  4052. ATLASSERT(m_tab.m_hWnd != NULL);
  4053. if(m_tab.m_hWnd == NULL)
  4054. return false;
  4055. m_tab.SetFont(AtlCreateControlFont());
  4056. m_bInternalFont = true;
  4057. m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
  4058. T* pT = static_cast<T*>(this);
  4059. m_cyTabHeight = pT->CalcTabHeight();
  4060. return true;
  4061. }
  4062. int CalcTabHeight()
  4063. {
  4064. int nCount = m_tab.GetItemCount();
  4065. TCITEMEXTRA tcix = { 0 };
  4066. tcix.tciheader.mask = TCIF_TEXT;
  4067. tcix.tciheader.pszText = _T("NS");
  4068. int nIndex = m_tab.InsertItem(nCount, tcix);
  4069. RECT rect = { 0, 0, 1000, 1000 };
  4070. m_tab.AdjustRect(FALSE, &rect);
  4071. RECT rcWnd = { 0, 0, 1000, rect.top };
  4072. ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
  4073. int nHeight = rcWnd.bottom - rcWnd.top;
  4074. m_tab.DeleteItem(nIndex);
  4075. return nHeight;
  4076. }
  4077. void ShowTabControl(bool bShow)
  4078. {
  4079. m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
  4080. }
  4081. void UpdateLayout()
  4082. {
  4083. RECT rect;
  4084. GetClientRect(&rect);
  4085. if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
  4086. m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
  4087. if(m_nActivePage != -1)
  4088. ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, m_cyTabHeight, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
  4089. }
  4090. void UpdateMenu()
  4091. {
  4092. if(m_menu.m_hMenu != NULL)
  4093. BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
  4094. }
  4095. void UpdateTitleBar()
  4096. {
  4097. if(!m_wndTitleBar.IsWindow() || m_lpstrTitleBarBase == NULL)
  4098. return; // nothing to do
  4099. if(m_nActivePage != -1)
  4100. {
  4101. T* pT = static_cast<T*>(this);
  4102. LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
  4103. LPCTSTR lpstrDivider = pT->GetTitleDividerText();
  4104. int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
  4105. CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
  4106. LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
  4107. ATLASSERT(lpstrPageTitle != NULL);
  4108. if(lpstrPageTitle != NULL)
  4109. {
  4110. pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
  4111. SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, lpstrDivider);
  4112. SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
  4113. }
  4114. else
  4115. {
  4116. lpstrPageTitle = m_lpstrTitleBarBase;
  4117. }
  4118. m_wndTitleBar.SetWindowText(lpstrPageTitle);
  4119. }
  4120. else
  4121. {
  4122. m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
  4123. }
  4124. }
  4125. void DrawMoveMark(int nItem)
  4126. {
  4127. T* pT = static_cast<T*>(this);
  4128. if(m_nInsertItem != -1)
  4129. {
  4130. RECT rect = { 0 };
  4131. pT->GetMoveMarkRect(rect);
  4132. m_tab.InvalidateRect(&rect);
  4133. }
  4134. m_nInsertItem = nItem;
  4135. if(m_nInsertItem != -1)
  4136. {
  4137. CClientDC dc(m_tab.m_hWnd);
  4138. RECT rect = { 0 };
  4139. pT->GetMoveMarkRect(rect);
  4140. CPen pen;
  4141. pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
  4142. CBrush brush;
  4143. brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
  4144. HPEN hPenOld = dc.SelectPen(pen);
  4145. HBRUSH hBrushOld = dc.SelectBrush(brush);
  4146. int x = rect.left;
  4147. int y = rect.top;
  4148. POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
  4149. dc.Polygon(ptsTop, 3);
  4150. y = rect.bottom - 1;
  4151. POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
  4152. dc.Polygon(ptsBottom, 3);
  4153. dc.SelectPen(hPenOld);
  4154. dc.SelectBrush(hBrushOld);
  4155. }
  4156. }
  4157. void GetMoveMarkRect(RECT& rect) const
  4158. {
  4159. m_tab.GetClientRect(&rect);
  4160. RECT rcItem = { 0 };
  4161. m_tab.GetItemRect(m_nInsertItem, &rcItem);
  4162. if(m_nInsertItem <= m_nActivePage)
  4163. {
  4164. rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
  4165. rect.right = rcItem.left + m_cxMoveMark / 2;
  4166. }
  4167. else
  4168. {
  4169. rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
  4170. rect.right = rcItem.right + m_cxMoveMark / 2;
  4171. }
  4172. }
  4173. void SetMoveCursor(bool bCanMove)
  4174. {
  4175. ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
  4176. }
  4177. void GenerateDragImage(int nItem)
  4178. {
  4179. ATLASSERT(IsValidPageIndex(nItem));
  4180. #ifndef _WIN32_WCE
  4181. RECT rcItem = { 0 };
  4182. m_tab.GetItemRect(nItem, &rcItem);
  4183. ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
  4184. #else // CE specific
  4185. nItem; // avoid level 4 warning
  4186. RECT rcItem = { 0, 0, 40, 20 };
  4187. #endif // _WIN32_WCE
  4188. ATLASSERT(m_ilDrag.m_hImageList == NULL);
  4189. m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
  4190. CClientDC dc(m_hWnd);
  4191. CDC dcMem;
  4192. dcMem.CreateCompatibleDC(dc);
  4193. ATLASSERT(dcMem.m_hDC != NULL);
  4194. dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
  4195. CBitmap bmp;
  4196. bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
  4197. ATLASSERT(bmp.m_hBitmap != NULL);
  4198. HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
  4199. #ifndef _WIN32_WCE
  4200. m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
  4201. #else // CE specific
  4202. dcMem.Rectangle(&rcItem);
  4203. #endif // _WIN32_WCE
  4204. dcMem.SelectBitmap(hBmpOld);
  4205. ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
  4206. }
  4207. void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
  4208. {
  4209. if(lstrlen(lpstrTitle) >= cchShortTitle)
  4210. {
  4211. LPCTSTR lpstrEllipsis = _T("...");
  4212. int cchEllipsis = lstrlen(lpstrEllipsis);
  4213. SecureHelper::strncpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
  4214. SecureHelper::strcat_x(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
  4215. }
  4216. else
  4217. {
  4218. SecureHelper::strcpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle);
  4219. }
  4220. }
  4221. #ifndef _WIN32_WCE
  4222. void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
  4223. {
  4224. ATLASSERT(pTTDI != NULL);
  4225. pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
  4226. }
  4227. #endif // !_WIN32_WCE
  4228. // Text for menu items and title bar - override to provide different strings
  4229. static LPCTSTR GetEmptyListText()
  4230. {
  4231. return _T("(Empty)");
  4232. }
  4233. static LPCTSTR GetWindowsMenuItemText()
  4234. {
  4235. return _T("&Windows...");
  4236. }
  4237. static LPCTSTR GetTitleDividerText()
  4238. {
  4239. return _T(" - ");
  4240. }
  4241. // Notifications - override to provide different behavior
  4242. void OnPageActivated(int nPage)
  4243. {
  4244. NMHDR nmhdr = { 0 };
  4245. nmhdr.hwndFrom = m_hWnd;
  4246. nmhdr.idFrom = nPage;
  4247. nmhdr.code = TBVN_PAGEACTIVATED;
  4248. ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
  4249. }
  4250. void OnContextMenu(int nPage, POINT pt)
  4251. {
  4252. m_tab.ClientToScreen(&pt);
  4253. TBVCONTEXTMENUINFO cmi = { 0 };
  4254. cmi.hdr.hwndFrom = m_hWnd;
  4255. cmi.hdr.idFrom = nPage;
  4256. cmi.hdr.code = TBVN_CONTEXTMENU;
  4257. cmi.pt = pt;
  4258. ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&cmi);
  4259. }
  4260. };
  4261. class CTabView : public CTabViewImpl<CTabView>
  4262. {
  4263. public:
  4264. DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
  4265. };
  4266. }; // namespace WTL
  4267. #endif // __ATLCTRLX_H__