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