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