Sync DrDump crash handler with TortoiseSVN codebase
[TortoiseGit.git] / ext / CrashServer / external / WTL / Include / atlctrlx.h
blob2cd0325f76f93232090bbbadf74023b30ca986e3
1 // Windows Template Library - WTL version 9.0
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 // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php)
7 // which can be found in the file CPL.TXT at the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by
9 // the terms of this license. You must not remove this notice, or
10 // any other, from this software.
12 #ifndef __ATLCTRLX_H__
13 #define __ATLCTRLX_H__
15 #pragma once
17 #ifndef __ATLAPP_H__
18 #error atlctrlx.h requires atlapp.h to be included first
19 #endif
21 #ifndef __ATLCTRLS_H__
22 #error atlctrlx.h requires atlctrls.h to be included first
23 #endif
25 #ifndef WM_UPDATEUISTATE
26 #define WM_UPDATEUISTATE 0x0128
27 #endif // !WM_UPDATEUISTATE
30 ///////////////////////////////////////////////////////////////////////////////
31 // Classes in this file:
33 // CBitmapButtonImpl<T, TBase, TWinTraits>
34 // CBitmapButton
35 // CCheckListViewCtrlImpl<T, TBase, TWinTraits>
36 // CCheckListViewCtrl
37 // CHyperLinkImpl<T, TBase, TWinTraits>
38 // CHyperLink
39 // CWaitCursor
40 // CCustomWaitCursor
41 // CMultiPaneStatusBarCtrlImpl<T, TBase>
42 // CMultiPaneStatusBarCtrl
43 // CPaneContainerImpl<T, TBase, TWinTraits>
44 // CPaneContainer
45 // CSortListViewImpl<T>
46 // CSortListViewCtrlImpl<T, TBase, TWinTraits>
47 // CSortListViewCtrl
48 // CTabViewImpl<T, TBase, TWinTraits>
49 // CTabView
51 namespace WTL
54 ///////////////////////////////////////////////////////////////////////////////
55 // CBitmapButton - bitmap button implementation
57 #ifndef _WIN32_WCE
59 // bitmap button extended styles
60 #define BMPBTN_HOVER 0x00000001
61 #define BMPBTN_AUTO3D_SINGLE 0x00000002
62 #define BMPBTN_AUTO3D_DOUBLE 0x00000004
63 #define BMPBTN_AUTOSIZE 0x00000008
64 #define BMPBTN_SHAREIMAGELISTS 0x00000010
65 #define BMPBTN_AUTOFIRE 0x00000020
66 #define BMPBTN_CHECK 0x00000040
67 #define BMPBTN_AUTOCHECK 0x00000080
69 // Note: BMPBTN_CHECK/BMPBTN_AUTOCHECK disables BN_DOUBLECLICKED,
70 // BMPBTN_AUTOFIRE doesn't work with BMPBTN_CHECK/BMPBTN_AUTOCHECK
72 template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
73 class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
75 public:
76 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
78 enum
80 _nImageNormal = 0,
81 _nImagePushed,
82 _nImageFocusOrHover,
83 _nImageDisabled,
85 _nImageCount = 4,
88 enum
90 ID_TIMER_FIRST = 1000,
91 ID_TIMER_REPEAT = 1001
94 // Bitmap button specific extended styles
95 DWORD m_dwExtendedStyle;
97 CImageList m_ImageList;
98 int m_nImage[_nImageCount];
100 CToolTipCtrl m_tip;
101 LPTSTR m_lpstrToolTipText;
103 // Internal states
104 unsigned m_fMouseOver:1;
105 unsigned m_fFocus:1;
106 unsigned m_fPressed:1;
107 unsigned m_fChecked:1;
110 // Constructor/Destructor
111 CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
112 m_dwExtendedStyle(dwExtendedStyle), m_ImageList(hImageList),
113 m_lpstrToolTipText(NULL),
114 m_fMouseOver(0), m_fFocus(0), m_fPressed(0), m_fChecked(0)
116 m_nImage[_nImageNormal] = -1;
117 m_nImage[_nImagePushed] = -1;
118 m_nImage[_nImageFocusOrHover] = -1;
119 m_nImage[_nImageDisabled] = -1;
121 #ifdef _DEBUG
122 if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode())
123 ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n"));
124 #endif // _DEBUG
127 ~CBitmapButtonImpl()
129 if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
130 m_ImageList.Destroy();
131 delete [] m_lpstrToolTipText;
134 // overridden to provide proper initialization
135 BOOL SubclassWindow(HWND hWnd)
137 #if (_MSC_VER >= 1300)
138 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
139 #else // !(_MSC_VER >= 1300)
140 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
141 BOOL bRet = _baseClass::SubclassWindow(hWnd);
142 #endif // !(_MSC_VER >= 1300)
143 if(bRet != FALSE)
145 T* pT = static_cast<T*>(this);
146 pT->Init();
149 return bRet;
152 // Attributes
153 DWORD GetBitmapButtonExtendedStyle() const
155 return m_dwExtendedStyle;
158 DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
160 DWORD dwPrevStyle = m_dwExtendedStyle;
161 if(dwMask == 0)
162 m_dwExtendedStyle = dwExtendedStyle;
163 else
164 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
166 #ifdef _DEBUG
167 if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode())
168 ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n"));
169 #endif // _DEBUG
171 return dwPrevStyle;
174 HIMAGELIST GetImageList() const
176 return m_ImageList;
179 HIMAGELIST SetImageList(HIMAGELIST hImageList)
181 HIMAGELIST hImageListPrev = m_ImageList;
182 m_ImageList = hImageList;
183 if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd))
184 SizeToImage();
186 return hImageListPrev;
189 int GetToolTipTextLength() const
191 return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
194 bool GetToolTipText(LPTSTR lpstrText, int nLength) const
196 ATLASSERT(lpstrText != NULL);
197 if(m_lpstrToolTipText == NULL)
198 return false;
200 errno_t nRet = SecureHelper::strncpy_x(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
202 return (nRet == 0 || nRet == STRUNCATE);
205 bool SetToolTipText(LPCTSTR lpstrText)
207 if(m_lpstrToolTipText != NULL)
209 delete [] m_lpstrToolTipText;
210 m_lpstrToolTipText = NULL;
213 if(lpstrText == NULL)
215 if(m_tip.IsWindow())
216 m_tip.Activate(FALSE);
217 return true;
220 int cchLen = lstrlen(lpstrText) + 1;
221 ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
222 if(m_lpstrToolTipText == NULL)
223 return false;
225 SecureHelper::strcpy_x(m_lpstrToolTipText, cchLen, lpstrText);
226 if(m_tip.IsWindow())
228 m_tip.Activate(TRUE);
229 m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
232 return true;
235 bool GetCheck() const
237 return (m_fChecked == 1);
240 void SetCheck(bool bCheck, bool bUpdate = true)
242 m_fChecked = bCheck ? 1 : 0;
244 if(bUpdate)
246 Invalidate();
247 UpdateWindow();
251 // Operations
252 void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
254 if(nNormal != -1)
255 m_nImage[_nImageNormal] = nNormal;
256 if(nPushed != -1)
257 m_nImage[_nImagePushed] = nPushed;
258 if(nFocusOrHover != -1)
259 m_nImage[_nImageFocusOrHover] = nFocusOrHover;
260 if(nDisabled != -1)
261 m_nImage[_nImageDisabled] = nDisabled;
264 BOOL SizeToImage()
266 ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL);
267 int cx = 0;
268 int cy = 0;
269 if(!m_ImageList.GetIconSize(cx, cy))
270 return FALSE;
271 return ResizeClient(cx, cy);
274 // Overrideables
275 void DoPaint(CDCHandle dc)
277 ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set
278 ATLASSERT(m_nImage[0] != -1); // main bitmap must be set
280 // set bitmap according to the current button state
281 bool bHover = IsHoverMode();
282 bool bPressed = (m_fPressed == 1) || (IsCheckMode() && (m_fChecked == 1));
283 int nImage = -1;
284 if(!IsWindowEnabled())
285 nImage = m_nImage[_nImageDisabled];
286 else if(bPressed)
287 nImage = m_nImage[_nImagePushed];
288 else if((!bHover && (m_fFocus == 1)) || (bHover && (m_fMouseOver == 1)))
289 nImage = m_nImage[_nImageFocusOrHover];
291 // if none is set, use default one
292 if(nImage == -1)
293 nImage = m_nImage[_nImageNormal];
295 // draw the button image
296 bool bAuto3D = (m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0;
297 int xyPos = (bPressed && bAuto3D && (m_nImage[_nImagePushed] == -1)) ? 1 : 0;
298 m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
300 // draw 3D border if required
301 if(bAuto3D)
303 RECT rect = { 0 };
304 GetClientRect(&rect);
306 if(bPressed)
307 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
308 else if(!bHover || (m_fMouseOver == 1))
309 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
311 if(!bHover && (m_fFocus == 1))
313 ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
314 dc.DrawFocusRect(&rect);
319 // Message map and handlers
320 BEGIN_MSG_MAP(CBitmapButtonImpl)
321 MESSAGE_HANDLER(WM_CREATE, OnCreate)
322 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
323 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
324 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
325 MESSAGE_HANDLER(WM_PAINT, OnPaint)
326 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
327 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
328 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
329 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
330 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
331 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
332 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
333 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
334 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
335 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
336 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
337 MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
338 MESSAGE_HANDLER(WM_TIMER, OnTimer)
339 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
340 END_MSG_MAP()
342 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
344 T* pT = static_cast<T*>(this);
345 pT->Init();
347 bHandled = FALSE;
348 return 1;
351 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
353 if(m_tip.IsWindow())
355 m_tip.DestroyWindow();
356 m_tip.m_hWnd = NULL;
358 bHandled = FALSE;
359 return 1;
362 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
364 MSG msg = { m_hWnd, uMsg, wParam, lParam };
365 if(m_tip.IsWindow())
366 m_tip.RelayEvent(&msg);
367 bHandled = FALSE;
368 return 1;
371 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
373 return 1; // no background needed
376 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
378 T* pT = static_cast<T*>(this);
379 if(wParam != NULL)
381 pT->DoPaint((HDC)wParam);
383 else
385 CPaintDC dc(m_hWnd);
386 pT->DoPaint(dc.m_hDC);
388 return 0;
391 LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
393 m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
394 Invalidate();
395 UpdateWindow();
396 bHandled = FALSE;
397 return 1;
400 LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
402 LRESULT lRet = 0;
403 if(IsHoverMode())
404 SetCapture();
405 else
406 lRet = DefWindowProc(uMsg, wParam, lParam);
407 if(::GetCapture() == m_hWnd)
409 m_fPressed = 1;
410 Invalidate();
411 UpdateWindow();
413 if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && !IsCheckMode())
415 int nElapse = 250;
416 int nDelay = 0;
417 if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
418 nElapse += nDelay * 250; // all milli-seconds
419 SetTimer(ID_TIMER_FIRST, nElapse);
421 return lRet;
424 LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
426 LRESULT lRet = 0;
427 if(!IsHoverMode() && !IsCheckMode())
428 lRet = DefWindowProc(uMsg, wParam, lParam);
429 if(::GetCapture() != m_hWnd)
430 SetCapture();
431 if(m_fPressed == 0)
433 m_fPressed = 1;
434 Invalidate();
435 UpdateWindow();
437 return lRet;
440 LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
442 if(((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0) && (m_fPressed == 1))
443 SetCheck(!GetCheck(), false);
445 LRESULT lRet = 0;
446 if(!IsHoverMode() && !IsCheckMode())
447 lRet = DefWindowProc(uMsg, wParam, lParam);
448 if(::GetCapture() == m_hWnd)
450 if((IsHoverMode() || IsCheckMode()) && (m_fPressed == 1))
451 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
452 ::ReleaseCapture();
454 return lRet;
457 LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
459 if(m_fPressed == 1)
461 m_fPressed = 0;
462 Invalidate();
463 UpdateWindow();
465 bHandled = FALSE;
466 return 1;
469 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
471 Invalidate();
472 UpdateWindow();
473 bHandled = FALSE;
474 return 1;
477 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
479 if(::GetCapture() == m_hWnd)
481 POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
482 ClientToScreen(&ptCursor);
483 RECT rect = { 0 };
484 GetWindowRect(&rect);
485 unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
486 if(m_fPressed != uPressed)
488 m_fPressed = uPressed;
489 Invalidate();
490 UpdateWindow();
493 else if(IsHoverMode() && m_fMouseOver == 0)
495 m_fMouseOver = 1;
496 Invalidate();
497 UpdateWindow();
498 StartTrackMouseLeave();
500 bHandled = FALSE;
501 return 1;
504 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
506 if(m_fMouseOver == 1)
508 m_fMouseOver = 0;
509 Invalidate();
510 UpdateWindow();
512 return 0;
515 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
517 if(wParam == VK_SPACE && IsHoverMode())
518 return 0; // ignore if in hover mode
519 if(wParam == VK_SPACE && m_fPressed == 0)
521 m_fPressed = 1;
522 Invalidate();
523 UpdateWindow();
525 bHandled = FALSE;
526 return 1;
529 LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
531 if(wParam == VK_SPACE && IsHoverMode())
532 return 0; // ignore if in hover mode
533 if(wParam == VK_SPACE && m_fPressed == 1)
535 m_fPressed = 0;
536 if((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0)
537 SetCheck(!GetCheck(), false);
538 Invalidate();
539 UpdateWindow();
541 bHandled = FALSE;
542 return 1;
545 LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
547 ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
548 switch(wParam) // timer ID
550 case ID_TIMER_FIRST:
551 KillTimer(ID_TIMER_FIRST);
552 if(m_fPressed == 1)
554 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
555 int nElapse = 250;
556 int nRepeat = 40;
557 if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
558 nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated
559 SetTimer(ID_TIMER_REPEAT, nElapse);
561 break;
562 case ID_TIMER_REPEAT:
563 if(m_fPressed == 1)
564 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
565 else if(::GetCapture() != m_hWnd)
566 KillTimer(ID_TIMER_REPEAT);
567 break;
568 default: // not our timer
569 break;
571 return 0;
574 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
576 // If the control is subclassed or superclassed, this message can cause
577 // repainting without WM_PAINT. We don't use this state, so just do nothing.
578 return 0;
581 // Implementation
582 void Init()
584 // We need this style to prevent Windows from painting the button
585 ModifyStyle(0, BS_OWNERDRAW);
587 // create a tool tip
588 m_tip.Create(m_hWnd);
589 ATLASSERT(m_tip.IsWindow());
590 if(m_tip.IsWindow() && m_lpstrToolTipText != NULL)
592 m_tip.Activate(TRUE);
593 m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
596 if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0)
597 SizeToImage();
600 BOOL StartTrackMouseLeave()
602 TRACKMOUSEEVENT tme = { 0 };
603 tme.cbSize = sizeof(tme);
604 tme.dwFlags = TME_LEAVE;
605 tme.hwndTrack = m_hWnd;
606 return _TrackMouseEvent(&tme);
609 bool IsHoverMode() const
611 return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
614 bool IsCheckMode() const
616 return ((m_dwExtendedStyle & (BMPBTN_CHECK | BMPBTN_AUTOCHECK)) != 0);
620 class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
622 public:
623 DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
625 CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
626 CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
630 #endif // !_WIN32_WCE
633 ///////////////////////////////////////////////////////////////////////////////
634 // CCheckListCtrlView - list view control with check boxes
636 template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
637 class CCheckListViewCtrlImplTraits
639 public:
640 static DWORD GetWndStyle(DWORD dwStyle)
642 return (dwStyle == 0) ? t_dwStyle : dwStyle;
645 static DWORD GetWndExStyle(DWORD dwExStyle)
647 return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
650 static DWORD GetExtendedLVStyle()
652 return t_dwExListViewStyle;
656 typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT> CCheckListViewCtrlTraits;
658 template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
659 class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits >
661 public:
662 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
664 // Attributes
665 static DWORD GetExtendedLVStyle()
667 return TWinTraits::GetExtendedLVStyle();
670 // Operations
671 BOOL SubclassWindow(HWND hWnd)
673 #if (_MSC_VER >= 1300)
674 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
675 #else // !(_MSC_VER >= 1300)
676 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
677 BOOL bRet = _baseClass::SubclassWindow(hWnd);
678 #endif // !(_MSC_VER >= 1300)
679 if(bRet != FALSE)
681 T* pT = static_cast<T*>(this);
682 pT->Init();
685 return bRet;
688 void CheckSelectedItems(int nCurrItem)
690 // first check if this item is selected
691 LVITEM lvi = { 0 };
692 lvi.iItem = nCurrItem;
693 lvi.iSubItem = 0;
694 lvi.mask = LVIF_STATE;
695 lvi.stateMask = LVIS_SELECTED;
696 GetItem(&lvi);
697 // if item is not selected, don't do anything
698 if(!(lvi.state & LVIS_SELECTED))
699 return;
700 // new check state will be reverse of the current state,
701 BOOL bCheck = !GetCheckState(nCurrItem);
702 int nItem = -1;
703 int nOldItem = -1;
704 while((nItem = GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
706 if(nItem != nCurrItem)
707 SetCheckState(nItem, bCheck);
708 nOldItem = nItem;
712 // Implementation
713 void Init()
715 T* pT = static_cast<T*>(this);
716 pT; // avoid level 4 warning
717 ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
718 SetExtendedListViewStyle(pT->GetExtendedLVStyle());
721 // Message map and handlers
722 BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
723 MESSAGE_HANDLER(WM_CREATE, OnCreate)
724 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
725 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
726 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
727 END_MSG_MAP()
729 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
731 // first let list view control initialize everything
732 LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
733 if(lRet == 0)
735 T* pT = static_cast<T*>(this);
736 pT->Init();
739 return lRet;
742 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
744 POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
745 LVHITTESTINFO lvh = { 0 };
746 lvh.pt = ptMsg;
747 if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0)
749 T* pT = static_cast<T*>(this);
750 pT->CheckSelectedItems(lvh.iItem);
752 bHandled = FALSE;
753 return 1;
756 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
758 if(wParam == VK_SPACE)
760 int nCurrItem = GetNextItem(-1, LVNI_FOCUSED);
761 if(nCurrItem != -1 && ::GetKeyState(VK_CONTROL) >= 0)
763 T* pT = static_cast<T*>(this);
764 pT->CheckSelectedItems(nCurrItem);
767 bHandled = FALSE;
768 return 1;
772 class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
774 public:
775 DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
779 ///////////////////////////////////////////////////////////////////////////////
780 // CHyperLink - hyper link control implementation
782 #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
783 __declspec(selectany) struct
785 enum { cxWidth = 32, cyHeight = 32 };
786 int xHotSpot;
787 int yHotSpot;
788 unsigned char arrANDPlane[cxWidth * cyHeight / 8];
789 unsigned char arrXORPlane[cxWidth * cyHeight / 8];
790 } _AtlHyperLink_CursorData =
792 5, 0,
794 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF,
795 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF,
796 0xF0, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF,
797 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF,
798 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF,
799 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
800 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
801 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
804 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
805 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x06, 0xD8, 0x00, 0x00,
806 0x06, 0xDA, 0x00, 0x00, 0x06, 0xDB, 0x00, 0x00, 0x67, 0xFB, 0x00, 0x00, 0x77, 0xFF, 0x00, 0x00,
807 0x37, 0xFF, 0x00, 0x00, 0x17, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00,
808 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00,
809 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
810 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
811 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
814 #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
816 #define HLINK_UNDERLINED 0x00000000
817 #define HLINK_NOTUNDERLINED 0x00000001
818 #define HLINK_UNDERLINEHOVER 0x00000002
819 #define HLINK_COMMANDBUTTON 0x00000004
820 #define HLINK_NOTIFYBUTTON 0x0000000C
821 #define HLINK_USETAGS 0x00000010
822 #define HLINK_USETAGSBOLD 0x00000030
823 #define HLINK_NOTOOLTIP 0x00000040
824 #define HLINK_AUTOCREATELINKFONT 0x00000080
825 #define HLINK_SINGLELINE 0x00000100
827 // Notes:
828 // - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
829 // - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
831 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
832 class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
834 public:
835 LPTSTR m_lpstrLabel;
836 LPTSTR m_lpstrHyperLink;
838 HCURSOR m_hCursor;
839 HFONT m_hFontLink;
840 HFONT m_hFontNormal;
842 RECT m_rcLink;
843 #ifndef _WIN32_WCE
844 CToolTipCtrl m_tip;
845 #endif // !_WIN32_WCE
847 COLORREF m_clrLink;
848 COLORREF m_clrVisited;
850 DWORD m_dwExtendedStyle; // Hyper Link specific extended styles
852 bool m_bPaintLabel:1;
853 bool m_bVisited:1;
854 bool m_bHover:1;
855 bool m_bInternalLinkFont:1;
856 bool m_bInternalNormalFont:1;
859 // Constructor/Destructor
860 CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) :
861 m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
862 m_hCursor(NULL), m_hFontLink(NULL), m_hFontNormal(NULL),
863 m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
864 m_dwExtendedStyle(dwExtendedStyle),
865 m_bPaintLabel(true), m_bVisited(false),
866 m_bHover(false), m_bInternalLinkFont(false), m_bInternalNormalFont(false)
868 ::SetRectEmpty(&m_rcLink);
871 ~CHyperLinkImpl()
873 delete [] m_lpstrLabel;
874 delete [] m_lpstrHyperLink;
875 #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
876 // It was created, not loaded, so we have to destroy it
877 if(m_hCursor != NULL)
878 ::DestroyCursor(m_hCursor);
879 #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
882 // Attributes
883 DWORD GetHyperLinkExtendedStyle() const
885 return m_dwExtendedStyle;
888 DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
890 DWORD dwPrevStyle = m_dwExtendedStyle;
891 if(dwMask == 0)
892 m_dwExtendedStyle = dwExtendedStyle;
893 else
894 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
895 return dwPrevStyle;
898 bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
900 if(m_lpstrLabel == NULL)
901 return false;
902 ATLASSERT(lpstrBuffer != NULL);
903 if(nLength <= lstrlen(m_lpstrLabel))
904 return false;
906 SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrLabel);
908 return true;
911 bool SetLabel(LPCTSTR lpstrLabel)
913 delete [] m_lpstrLabel;
914 m_lpstrLabel = NULL;
915 int cchLen = lstrlen(lpstrLabel) + 1;
916 ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
917 if(m_lpstrLabel == NULL)
918 return false;
920 SecureHelper::strcpy_x(m_lpstrLabel, cchLen, lpstrLabel);
921 T* pT = static_cast<T*>(this);
922 pT->CalcLabelRect();
924 if(m_hWnd != NULL)
925 SetWindowText(lpstrLabel); // Set this for accessibility
927 return true;
930 bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
932 if(m_lpstrHyperLink == NULL)
933 return false;
934 ATLASSERT(lpstrBuffer != NULL);
935 if(nLength <= lstrlen(m_lpstrHyperLink))
936 return false;
938 SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrHyperLink);
940 return true;
943 bool SetHyperLink(LPCTSTR lpstrLink)
945 delete [] m_lpstrHyperLink;
946 m_lpstrHyperLink = NULL;
947 int cchLen = lstrlen(lpstrLink) + 1;
948 ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
949 if(m_lpstrHyperLink == NULL)
950 return false;
952 SecureHelper::strcpy_x(m_lpstrHyperLink, cchLen, lpstrLink);
953 if(m_lpstrLabel == NULL)
955 T* pT = static_cast<T*>(this);
956 pT->CalcLabelRect();
958 #ifndef _WIN32_WCE
959 if(m_tip.IsWindow())
961 m_tip.Activate(TRUE);
962 m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
964 #endif // !_WIN32_WCE
965 return true;
968 HFONT GetLinkFont() const
970 return m_hFontLink;
973 void SetLinkFont(HFONT hFont)
975 if(m_bInternalLinkFont)
977 ::DeleteObject(m_hFontLink);
978 m_bInternalLinkFont = false;
981 m_hFontLink = hFont;
983 T* pT = static_cast<T*>(this);
984 pT->CalcLabelRect();
987 int GetIdealHeight() const
989 ATLASSERT(::IsWindow(m_hWnd));
990 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
991 return -1;
992 if(!m_bPaintLabel)
993 return -1;
995 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
997 CClientDC dc(m_hWnd);
998 RECT rect = { 0 };
999 GetClientRect(&rect);
1000 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1001 RECT rcText = rect;
1002 dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | uFormat | DT_CALCRECT);
1003 dc.SelectFont(m_hFontLink);
1004 RECT rcLink = rect;
1005 dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
1006 dc.SelectFont(hFontOld);
1007 return __max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
1010 bool GetIdealSize(SIZE& size) const
1012 int cx = 0, cy = 0;
1013 bool bRet = GetIdealSize(cx, cy);
1014 if(bRet)
1016 size.cx = cx;
1017 size.cy = cy;
1019 return bRet;
1022 bool GetIdealSize(int& cx, int& cy) const
1024 ATLASSERT(::IsWindow(m_hWnd));
1025 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
1026 return false;
1027 if(!m_bPaintLabel)
1028 return false;
1030 CClientDC dc(m_hWnd);
1031 RECT rcClient = { 0 };
1032 GetClientRect(&rcClient);
1033 RECT rcAll = rcClient;
1035 if(IsUsingTags())
1037 // find tags and label parts
1038 LPTSTR lpstrLeft = NULL;
1039 int cchLeft = 0;
1040 LPTSTR lpstrLink = NULL;
1041 int cchLink = 0;
1042 LPTSTR lpstrRight = NULL;
1043 int cchRight = 0;
1045 const T* pT = static_cast<const T*>(this);
1046 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
1048 // get label part rects
1049 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1051 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1052 RECT rcLeft = rcClient;
1053 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
1055 dc.SelectFont(m_hFontLink);
1056 RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
1057 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
1059 dc.SelectFont(m_hFontNormal);
1060 RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
1061 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat | DT_CALCRECT);
1063 dc.SelectFont(hFontOld);
1065 int cyMax = __max(rcLeft.bottom, __max(rcLink.bottom, rcRight.bottom));
1066 ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
1068 else
1070 HFONT hOldFont = NULL;
1071 if(m_hFontLink != NULL)
1072 hOldFont = dc.SelectFont(m_hFontLink);
1073 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1074 DWORD dwStyle = GetStyle();
1075 UINT uFormat = DT_LEFT;
1076 if (dwStyle & SS_CENTER)
1077 uFormat = DT_CENTER;
1078 else if (dwStyle & SS_RIGHT)
1079 uFormat = DT_RIGHT;
1080 uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1081 dc.DrawText(lpstrText, -1, &rcAll, uFormat | DT_CALCRECT);
1082 if(m_hFontLink != NULL)
1083 dc.SelectFont(hOldFont);
1084 if (dwStyle & SS_CENTER)
1086 int dx = (rcClient.right - rcAll.right) / 2;
1087 ::OffsetRect(&rcAll, dx, 0);
1089 else if (dwStyle & SS_RIGHT)
1091 int dx = rcClient.right - rcAll.right;
1092 ::OffsetRect(&rcAll, dx, 0);
1096 cx = rcAll.right - rcAll.left;
1097 cy = rcAll.bottom - rcAll.top;
1099 return true;
1102 // for command buttons only
1103 bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
1105 ATLASSERT(IsCommandButton());
1106 return GetHyperLink(lpstrBuffer, nLength);
1109 bool SetToolTipText(LPCTSTR lpstrToolTipText)
1111 ATLASSERT(IsCommandButton());
1112 return SetHyperLink(lpstrToolTipText);
1115 // Operations
1116 BOOL SubclassWindow(HWND hWnd)
1118 ATLASSERT(m_hWnd == NULL);
1119 ATLASSERT(::IsWindow(hWnd));
1120 if(m_hFontNormal == NULL)
1121 m_hFontNormal = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L);
1123 #if (_MSC_VER >= 1300)
1124 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
1125 #else // !(_MSC_VER >= 1300)
1126 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
1127 BOOL bRet = _baseClass::SubclassWindow(hWnd);
1128 #endif // !(_MSC_VER >= 1300)
1129 if(bRet != FALSE)
1131 T* pT = static_cast<T*>(this);
1132 pT->Init();
1135 return bRet;
1138 bool Navigate()
1140 ATLASSERT(::IsWindow(m_hWnd));
1141 bool bRet = true;
1142 if(IsNotifyButton())
1144 NMHDR nmhdr = { m_hWnd, GetDlgCtrlID(), NM_CLICK };
1145 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
1147 else if(IsCommandButton())
1149 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
1151 else
1153 ATLASSERT(m_lpstrHyperLink != NULL);
1154 #ifndef _WIN32_WCE
1155 DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
1156 bRet = (dwRet > 32);
1157 #else // CE specific
1158 SHELLEXECUTEINFO shExeInfo = { sizeof(SHELLEXECUTEINFO), 0, 0, L"open", m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL, 0, 0, 0, 0, 0, 0, 0 };
1159 ::ShellExecuteEx(&shExeInfo);
1160 DWORD_PTR dwRet = (DWORD_PTR)shExeInfo.hInstApp;
1161 bRet = (dwRet == 0) || (dwRet > 32);
1162 #endif // _WIN32_WCE
1163 ATLASSERT(bRet);
1164 if(bRet)
1166 m_bVisited = true;
1167 Invalidate();
1170 return bRet;
1173 void CreateLinkFontFromNormal()
1175 if(m_bInternalLinkFont)
1177 ::DeleteObject(m_hFontLink);
1178 m_bInternalLinkFont = false;
1181 CFontHandle font = (m_hFontNormal != NULL) ? m_hFontNormal : (HFONT)::GetStockObject(SYSTEM_FONT);
1182 LOGFONT lf = { 0 };
1183 font.GetLogFont(&lf);
1185 if(IsUsingTagsBold())
1186 lf.lfWeight = FW_BOLD;
1187 else if(!IsNotUnderlined())
1188 lf.lfUnderline = TRUE;
1190 m_hFontLink = ::CreateFontIndirect(&lf);
1191 m_bInternalLinkFont = true;
1192 ATLASSERT(m_hFontLink != NULL);
1195 // Message map and handlers
1196 BEGIN_MSG_MAP(CHyperLinkImpl)
1197 MESSAGE_HANDLER(WM_CREATE, OnCreate)
1198 #ifndef _WIN32_WCE
1199 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
1200 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
1201 #endif // !_WIN32_WCE
1202 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1203 MESSAGE_HANDLER(WM_PAINT, OnPaint)
1204 #ifndef _WIN32_WCE
1205 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
1206 #endif // !_WIN32_WCE
1207 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
1208 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
1209 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
1210 #ifndef _WIN32_WCE
1211 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
1212 #endif // !_WIN32_WCE
1213 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
1214 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
1215 MESSAGE_HANDLER(WM_CHAR, OnChar)
1216 MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
1217 MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
1218 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
1219 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
1220 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
1221 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
1222 MESSAGE_HANDLER(WM_SIZE, OnSize)
1223 END_MSG_MAP()
1225 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1227 T* pT = static_cast<T*>(this);
1228 pT->Init();
1229 return 0;
1232 #ifndef _WIN32_WCE
1233 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1235 if(m_tip.IsWindow())
1237 m_tip.DestroyWindow();
1238 m_tip.m_hWnd = NULL;
1241 if(m_bInternalLinkFont)
1243 ::DeleteObject(m_hFontLink);
1244 m_hFontLink = NULL;
1245 m_bInternalLinkFont = false;
1248 if(m_bInternalNormalFont)
1250 ::DeleteObject(m_hFontNormal);
1251 m_hFontNormal = NULL;
1252 m_bInternalNormalFont = false;
1255 bHandled = FALSE;
1256 return 1;
1259 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1261 MSG msg = { m_hWnd, uMsg, wParam, lParam };
1262 if(m_tip.IsWindow() && IsUsingToolTip())
1263 m_tip.RelayEvent(&msg);
1264 bHandled = FALSE;
1265 return 1;
1267 #endif // !_WIN32_WCE
1269 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1271 return 1; // no background painting needed (we do it all during WM_PAINT)
1274 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
1276 if(!m_bPaintLabel)
1278 bHandled = FALSE;
1279 return 1;
1282 T* pT = static_cast<T*>(this);
1283 if(wParam != NULL)
1285 pT->DoEraseBackground((HDC)wParam);
1286 pT->DoPaint((HDC)wParam);
1288 else
1290 CPaintDC dc(m_hWnd);
1291 pT->DoEraseBackground(dc.m_hDC);
1292 pT->DoPaint(dc.m_hDC);
1295 return 0;
1298 LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1300 if(m_bPaintLabel)
1301 Invalidate();
1302 else
1303 bHandled = FALSE;
1304 return 0;
1307 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1309 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1310 if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
1312 ::SetCursor(m_hCursor);
1313 if(IsUnderlineHover())
1315 if(!m_bHover)
1317 m_bHover = true;
1318 InvalidateRect(&m_rcLink);
1319 UpdateWindow();
1320 #ifndef _WIN32_WCE
1321 StartTrackMouseLeave();
1322 #endif // !_WIN32_WCE
1326 else
1328 if(IsUnderlineHover())
1330 if(m_bHover)
1332 m_bHover = false;
1333 InvalidateRect(&m_rcLink);
1334 UpdateWindow();
1337 bHandled = FALSE;
1339 return 0;
1342 #ifndef _WIN32_WCE
1343 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1345 if(IsUnderlineHover() && m_bHover)
1347 m_bHover = false;
1348 InvalidateRect(&m_rcLink);
1349 UpdateWindow();
1351 return 0;
1353 #endif // !_WIN32_WCE
1355 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
1357 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1358 if(::PtInRect(&m_rcLink, pt))
1360 SetFocus();
1361 SetCapture();
1363 return 0;
1366 LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
1368 if(GetCapture() == m_hWnd)
1370 ReleaseCapture();
1371 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1372 if(::PtInRect(&m_rcLink, pt))
1374 T* pT = static_cast<T*>(this);
1375 pT->Navigate();
1378 return 0;
1381 LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1383 if(wParam == VK_RETURN || wParam == VK_SPACE)
1385 T* pT = static_cast<T*>(this);
1386 pT->Navigate();
1388 return 0;
1391 LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1393 return DLGC_WANTCHARS;
1396 LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1398 POINT pt = { 0, 0 };
1399 GetCursorPos(&pt);
1400 ScreenToClient(&pt);
1401 if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
1403 return TRUE;
1405 bHandled = FALSE;
1406 return FALSE;
1409 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1411 Invalidate();
1412 UpdateWindow();
1413 return 0;
1416 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1418 return (LRESULT)m_hFontNormal;
1421 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
1423 if(m_bInternalNormalFont)
1425 ::DeleteObject(m_hFontNormal);
1426 m_bInternalNormalFont = false;
1429 bool bCreateLinkFont = m_bInternalLinkFont;
1431 m_hFontNormal = (HFONT)wParam;
1433 if(bCreateLinkFont || IsAutoCreateLinkFont())
1434 CreateLinkFontFromNormal();
1436 T* pT = static_cast<T*>(this);
1437 pT->CalcLabelRect();
1439 if((BOOL)lParam)
1441 Invalidate();
1442 UpdateWindow();
1445 return 0;
1448 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1450 // If the control is subclassed or superclassed, this message can cause
1451 // repainting without WM_PAINT. We don't use this state, so just do nothing.
1452 return 0;
1455 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1457 T* pT = static_cast<T*>(this);
1458 pT->CalcLabelRect();
1459 pT->Invalidate();
1460 return 0;
1463 // Implementation
1464 void Init()
1466 ATLASSERT(::IsWindow(m_hWnd));
1468 // Check if we should paint a label
1469 const int cchBuff = 8;
1470 TCHAR szBuffer[cchBuff] = { 0 };
1471 if(::GetClassName(m_hWnd, szBuffer, cchBuff))
1473 if(lstrcmpi(szBuffer, _T("static")) == 0)
1475 ModifyStyle(0, SS_NOTIFY); // we need this
1476 DWORD dwStyle = GetStyle() & 0x000000FF;
1477 #ifndef _WIN32_WCE
1478 if(dwStyle == SS_ICON || dwStyle == SS_BLACKRECT || dwStyle == SS_GRAYRECT ||
1479 dwStyle == SS_WHITERECT || dwStyle == SS_BLACKFRAME || dwStyle == SS_GRAYFRAME ||
1480 dwStyle == SS_WHITEFRAME || dwStyle == SS_OWNERDRAW ||
1481 dwStyle == SS_BITMAP || dwStyle == SS_ENHMETAFILE)
1482 #else // CE specific
1483 if(dwStyle == SS_ICON || dwStyle == SS_BITMAP)
1484 #endif // _WIN32_WCE
1485 m_bPaintLabel = false;
1489 // create or load a cursor
1490 #if (WINVER >= 0x0500) || defined(_WIN32_WCE)
1491 m_hCursor = ::LoadCursor(NULL, IDC_HAND);
1492 #else
1493 m_hCursor = ::CreateCursor(ModuleHelper::GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane);
1494 #endif
1495 ATLASSERT(m_hCursor != NULL);
1497 // set fonts
1498 if(m_bPaintLabel)
1500 if(m_hFontNormal == NULL)
1502 m_hFontNormal = AtlCreateControlFont();
1503 m_bInternalNormalFont = true;
1506 if(m_hFontLink == NULL)
1507 CreateLinkFontFromNormal();
1510 #ifndef _WIN32_WCE
1511 // create a tool tip
1512 m_tip.Create(m_hWnd);
1513 ATLASSERT(m_tip.IsWindow());
1514 #endif // !_WIN32_WCE
1516 // set label (defaults to window text)
1517 if(m_lpstrLabel == NULL)
1519 int nLen = GetWindowTextLength();
1520 if(nLen > 0)
1522 ATLTRY(m_lpstrLabel = new TCHAR[nLen + 1]);
1523 if(m_lpstrLabel != NULL)
1524 ATLVERIFY(GetWindowText(m_lpstrLabel, nLen + 1) > 0);
1528 T* pT = static_cast<T*>(this);
1529 pT->CalcLabelRect();
1531 // set hyperlink (defaults to label), or just activate tool tip if already set
1532 if(m_lpstrHyperLink == NULL && !IsCommandButton())
1534 if(m_lpstrLabel != NULL)
1535 SetHyperLink(m_lpstrLabel);
1537 #ifndef _WIN32_WCE
1538 else
1540 m_tip.Activate(TRUE);
1541 m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
1543 #endif // !_WIN32_WCE
1545 // set link colors
1546 if(m_bPaintLabel)
1548 CRegKeyEx rk;
1549 LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
1550 if(lRet == ERROR_SUCCESS)
1552 const int cchValue = 12;
1553 TCHAR szValue[cchValue] = { 0 };
1554 ULONG ulCount = cchValue;
1555 lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
1556 if(lRet == ERROR_SUCCESS)
1558 COLORREF clr = pT->_ParseColorString(szValue);
1559 ATLASSERT(clr != CLR_INVALID);
1560 if(clr != CLR_INVALID)
1561 m_clrLink = clr;
1564 ulCount = cchValue;
1565 lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
1566 if(lRet == ERROR_SUCCESS)
1568 COLORREF clr = pT->_ParseColorString(szValue);
1569 ATLASSERT(clr != CLR_INVALID);
1570 if(clr != CLR_INVALID)
1571 m_clrVisited = clr;
1577 static COLORREF _ParseColorString(LPTSTR lpstr)
1579 int c[3] = { -1, -1, -1 };
1580 LPTSTR p = NULL;
1581 for(int i = 0; i < 2; i++)
1583 for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
1585 if(*p == _T(','))
1587 *p = _T('\0');
1588 c[i] = MinCrtHelper::_atoi(lpstr);
1589 lpstr = &p[1];
1590 break;
1593 if(c[i] == -1)
1594 return CLR_INVALID;
1596 if(*lpstr == _T('\0'))
1597 return CLR_INVALID;
1598 c[2] = MinCrtHelper::_atoi(lpstr);
1600 return RGB(c[0], c[1], c[2]);
1603 bool CalcLabelRect()
1605 if(!::IsWindow(m_hWnd))
1606 return false;
1607 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
1608 return false;
1610 CClientDC dc(m_hWnd);
1611 RECT rcClient = { 0 };
1612 GetClientRect(&rcClient);
1613 m_rcLink = rcClient;
1614 if(!m_bPaintLabel)
1615 return true;
1617 if(IsUsingTags())
1619 // find tags and label parts
1620 LPTSTR lpstrLeft = NULL;
1621 int cchLeft = 0;
1622 LPTSTR lpstrLink = NULL;
1623 int cchLink = 0;
1624 LPTSTR lpstrRight = NULL;
1625 int cchRight = 0;
1627 T* pT = static_cast<T*>(this);
1628 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
1629 ATLASSERT(lpstrLink != NULL);
1630 ATLASSERT(cchLink > 0);
1632 // get label part rects
1633 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1635 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1637 RECT rcLeft = rcClient;
1638 if(lpstrLeft != NULL)
1639 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
1641 dc.SelectFont(m_hFontLink);
1642 RECT rcLink = rcClient;
1643 if(lpstrLeft != NULL)
1644 rcLink.left = rcLeft.right;
1645 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
1647 dc.SelectFont(hFontOld);
1649 m_rcLink = rcLink;
1651 else
1653 HFONT hOldFont = NULL;
1654 if(m_hFontLink != NULL)
1655 hOldFont = dc.SelectFont(m_hFontLink);
1656 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1657 DWORD dwStyle = GetStyle();
1658 UINT uFormat = DT_LEFT;
1659 if (dwStyle & SS_CENTER)
1660 uFormat = DT_CENTER;
1661 else if (dwStyle & SS_RIGHT)
1662 uFormat = DT_RIGHT;
1663 uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1664 dc.DrawText(lpstrText, -1, &m_rcLink, uFormat | DT_CALCRECT);
1665 if(m_hFontLink != NULL)
1666 dc.SelectFont(hOldFont);
1667 if (dwStyle & SS_CENTER)
1669 int dx = (rcClient.right - m_rcLink.right) / 2;
1670 ::OffsetRect(&m_rcLink, dx, 0);
1672 else if (dwStyle & SS_RIGHT)
1674 int dx = rcClient.right - m_rcLink.right;
1675 ::OffsetRect(&m_rcLink, dx, 0);
1679 return true;
1682 void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
1684 lpstrLeft = NULL;
1685 cchLeft = 0;
1686 lpstrLink = NULL;
1687 cchLink = 0;
1688 lpstrRight = NULL;
1689 cchRight = 0;
1691 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1692 int cchText = lstrlen(lpstrText);
1693 bool bOutsideLink = true;
1694 for(int i = 0; i < cchText; i++)
1696 if(lpstrText[i] != _T('<'))
1697 continue;
1699 if(bOutsideLink)
1701 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
1703 if(i > 0)
1705 lpstrLeft = lpstrText;
1706 cchLeft = i;
1708 lpstrLink = &lpstrText[i + 3];
1709 bOutsideLink = false;
1712 else
1714 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
1716 cchLink = i - 3 - cchLeft;
1717 if(lpstrText[i + 4] != 0)
1719 lpstrRight = &lpstrText[i + 4];
1720 cchRight = cchText - (i + 4);
1721 break;
1729 void DoEraseBackground(CDCHandle dc)
1731 HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd);
1732 if(hBrush != NULL)
1734 RECT rect = { 0 };
1735 GetClientRect(&rect);
1736 dc.FillRect(&rect, hBrush);
1740 void DoPaint(CDCHandle dc)
1742 if(IsUsingTags())
1744 // find tags and label parts
1745 LPTSTR lpstrLeft = NULL;
1746 int cchLeft = 0;
1747 LPTSTR lpstrLink = NULL;
1748 int cchLink = 0;
1749 LPTSTR lpstrRight = NULL;
1750 int cchRight = 0;
1752 T* pT = static_cast<T*>(this);
1753 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
1755 // get label part rects
1756 RECT rcClient = { 0 };
1757 GetClientRect(&rcClient);
1759 dc.SetBkMode(TRANSPARENT);
1760 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1762 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1764 if(lpstrLeft != NULL)
1765 dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | uFormat);
1767 COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
1768 if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
1769 dc.SelectFont(m_hFontLink);
1770 else
1771 dc.SelectFont(m_hFontNormal);
1773 dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | uFormat);
1775 dc.SetTextColor(clrOld);
1776 dc.SelectFont(m_hFontNormal);
1777 if(lpstrRight != NULL)
1779 RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
1780 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat);
1783 if(GetFocus() == m_hWnd)
1784 dc.DrawFocusRect(&m_rcLink);
1786 dc.SelectFont(hFontOld);
1788 else
1790 dc.SetBkMode(TRANSPARENT);
1791 COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
1793 HFONT hFontOld = NULL;
1794 if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
1795 hFontOld = dc.SelectFont(m_hFontLink);
1796 else
1797 hFontOld = dc.SelectFont(m_hFontNormal);
1799 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1801 DWORD dwStyle = GetStyle();
1802 UINT uFormat = DT_LEFT;
1803 if (dwStyle & SS_CENTER)
1804 uFormat = DT_CENTER;
1805 else if (dwStyle & SS_RIGHT)
1806 uFormat = DT_RIGHT;
1807 uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1809 dc.DrawText(lpstrText, -1, &m_rcLink, uFormat);
1811 if(GetFocus() == m_hWnd)
1812 dc.DrawFocusRect(&m_rcLink);
1814 dc.SetTextColor(clrOld);
1815 dc.SelectFont(hFontOld);
1819 #ifndef _WIN32_WCE
1820 BOOL StartTrackMouseLeave()
1822 TRACKMOUSEEVENT tme = { 0 };
1823 tme.cbSize = sizeof(tme);
1824 tme.dwFlags = TME_LEAVE;
1825 tme.hwndTrack = m_hWnd;
1826 return _TrackMouseEvent(&tme);
1828 #endif // !_WIN32_WCE
1830 // Implementation helpers
1831 bool IsUnderlined() const
1833 return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
1836 bool IsNotUnderlined() const
1838 return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
1841 bool IsUnderlineHover() const
1843 return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
1846 bool IsCommandButton() const
1848 return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
1851 bool IsNotifyButton() const
1853 return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
1856 bool IsUsingTags() const
1858 return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
1861 bool IsUsingTagsBold() const
1863 return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
1866 bool IsUsingToolTip() const
1868 return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
1871 bool IsAutoCreateLinkFont() const
1873 return ((m_dwExtendedStyle & HLINK_AUTOCREATELINKFONT) == HLINK_AUTOCREATELINKFONT);
1876 bool IsSingleLine() const
1878 return ((m_dwExtendedStyle & HLINK_SINGLELINE) == HLINK_SINGLELINE);
1882 class CHyperLink : public CHyperLinkImpl<CHyperLink>
1884 public:
1885 DECLARE_WND_CLASS(_T("WTL_HyperLink"))
1889 ///////////////////////////////////////////////////////////////////////////////
1890 // CWaitCursor - displays a wait cursor
1892 class CWaitCursor
1894 public:
1895 // Data
1896 HCURSOR m_hWaitCursor;
1897 HCURSOR m_hOldCursor;
1898 bool m_bInUse;
1900 // Constructor/destructor
1901 CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
1903 HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
1904 m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
1905 ATLASSERT(m_hWaitCursor != NULL);
1907 if(bSet)
1908 Set();
1911 ~CWaitCursor()
1913 Restore();
1916 // Methods
1917 bool Set()
1919 if(m_bInUse)
1920 return false;
1921 m_hOldCursor = ::SetCursor(m_hWaitCursor);
1922 m_bInUse = true;
1923 return true;
1926 bool Restore()
1928 if(!m_bInUse)
1929 return false;
1930 ::SetCursor(m_hOldCursor);
1931 m_bInUse = false;
1932 return true;
1937 ///////////////////////////////////////////////////////////////////////////////
1938 // CCustomWaitCursor - for custom and animated cursors
1940 class CCustomWaitCursor : public CWaitCursor
1942 public:
1943 // Constructor/destructor
1944 CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) :
1945 CWaitCursor(false, IDC_WAIT, true)
1947 if(hInstance == NULL)
1948 hInstance = ModuleHelper::GetResourceInstance();
1949 m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
1951 if(bSet)
1952 Set();
1955 ~CCustomWaitCursor()
1957 Restore();
1958 #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
1959 ::DestroyCursor(m_hWaitCursor);
1960 #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
1965 ///////////////////////////////////////////////////////////////////////////////
1966 // CMultiPaneStatusBarCtrl - Status Bar with multiple panes
1968 template <class T, class TBase = CStatusBarCtrl>
1969 class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase >
1971 public:
1972 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
1974 // Data
1975 enum { m_cxPaneMargin = 3 };
1977 int m_nPanes;
1978 int* m_pPane;
1980 // Constructor/destructor
1981 CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL)
1984 ~CMultiPaneStatusBarCtrlImpl()
1986 delete [] m_pPane;
1989 // Methods
1990 HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
1992 #if (_MSC_VER >= 1300)
1993 return ATL::CWindowImpl< T, TBase >::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
1994 #else // !(_MSC_VER >= 1300)
1995 typedef ATL::CWindowImpl< T, TBase > _baseClass;
1996 return _baseClass::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
1997 #endif // !(_MSC_VER >= 1300)
2000 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)
2002 const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
2003 TCHAR szText[cchMax] = { 0 };
2004 ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
2005 return Create(hWndParent, szText, dwStyle, nID);
2008 BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true)
2010 ATLASSERT(::IsWindow(m_hWnd));
2011 ATLASSERT(nPanes > 0);
2013 m_nPanes = nPanes;
2014 delete [] m_pPane;
2015 m_pPane = NULL;
2017 ATLTRY(m_pPane = new int[nPanes]);
2018 ATLASSERT(m_pPane != NULL);
2019 if(m_pPane == NULL)
2020 return FALSE;
2022 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
2023 int* pPanesPos = buff.Allocate(nPanes);
2024 ATLASSERT(pPanesPos != NULL);
2025 if(pPanesPos == NULL)
2026 return FALSE;
2028 SecureHelper::memcpy_x(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int));
2030 // get status bar DC and set font
2031 CClientDC dc(m_hWnd);
2032 HFONT hOldFont = dc.SelectFont(GetFont());
2034 // get status bar borders
2035 int arrBorders[3] = { 0 };
2036 GetBorders(arrBorders);
2038 const int cchBuff = 128;
2039 TCHAR szBuff[cchBuff] = { 0 };
2040 SIZE size = { 0, 0 };
2041 int cxLeft = arrBorders[0];
2043 // calculate right edge of each part
2044 for(int i = 0; i < nPanes; i++)
2046 if(pPanes[i] == ID_DEFAULT_PANE)
2048 // make very large, will be resized later
2049 pPanesPos[i] = INT_MAX / 2;
2051 else
2053 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
2054 dc.GetTextExtent(szBuff, lstrlen(szBuff), &size);
2055 T* pT = static_cast<T*>(this);
2057 pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin;
2059 cxLeft = pPanesPos[i];
2062 BOOL bRet = SetParts(nPanes, pPanesPos);
2064 if(bRet && bSetText)
2066 for(int i = 0; i < nPanes; i++)
2068 if(pPanes[i] != ID_DEFAULT_PANE)
2070 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
2071 SetPaneText(m_pPane[i], szBuff);
2076 dc.SelectFont(hOldFont);
2077 return bRet;
2080 bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const
2082 ATLASSERT(::IsWindow(m_hWnd));
2083 int nIndex = GetPaneIndexFromID(nPaneID);
2084 if(nIndex == -1)
2085 return false;
2087 int nLength = GetTextLength(nIndex, pnType);
2088 if(pcchLength != NULL)
2089 *pcchLength = nLength;
2091 return true;
2094 BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const
2096 ATLASSERT(::IsWindow(m_hWnd));
2097 int nIndex = GetPaneIndexFromID(nPaneID);
2098 if(nIndex == -1)
2099 return FALSE;
2101 int nLength = GetText(nIndex, lpstrText, pnType);
2102 if(pcchLength != NULL)
2103 *pcchLength = nLength;
2105 return TRUE;
2108 BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0)
2110 ATLASSERT(::IsWindow(m_hWnd));
2111 int nIndex = GetPaneIndexFromID(nPaneID);
2112 if(nIndex == -1)
2113 return FALSE;
2115 return SetText(nIndex, lpstrText, nType);
2118 BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const
2120 ATLASSERT(::IsWindow(m_hWnd));
2121 int nIndex = GetPaneIndexFromID(nPaneID);
2122 if(nIndex == -1)
2123 return FALSE;
2125 return GetRect(nIndex, lpRect);
2128 BOOL SetPaneWidth(int nPaneID, int cxWidth)
2130 ATLASSERT(::IsWindow(m_hWnd));
2131 ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one
2132 int nIndex = GetPaneIndexFromID(nPaneID);
2133 if(nIndex == -1)
2134 return FALSE;
2136 // get pane positions
2137 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
2138 int* pPanesPos = buff.Allocate(m_nPanes);
2139 if(pPanesPos == NULL)
2140 return FALSE;
2141 GetParts(m_nPanes, pPanesPos);
2142 // calculate offset
2143 int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]);
2144 int cxOff = cxWidth - cxPaneWidth;
2145 // find variable width pane
2146 int nDef = m_nPanes;
2147 for(int i = 0; i < m_nPanes; i++)
2149 if(m_pPane[i] == ID_DEFAULT_PANE)
2151 nDef = i;
2152 break;
2155 // resize
2156 if(nIndex < nDef) // before default pane
2158 for(int i = nIndex; i < nDef; i++)
2159 pPanesPos[i] += cxOff;
2162 else // after default one
2164 for(int i = nDef; i < nIndex; i++)
2165 pPanesPos[i] -= cxOff;
2167 // set pane postions
2168 return SetParts(m_nPanes, pPanesPos);
2171 #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
2172 BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const
2174 ATLASSERT(::IsWindow(m_hWnd));
2175 int nIndex = GetPaneIndexFromID(nPaneID);
2176 if(nIndex == -1)
2177 return FALSE;
2179 GetTipText(nIndex, lpstrText, nSize);
2180 return TRUE;
2183 BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText)
2185 ATLASSERT(::IsWindow(m_hWnd));
2186 int nIndex = GetPaneIndexFromID(nPaneID);
2187 if(nIndex == -1)
2188 return FALSE;
2190 SetTipText(nIndex, lpstrText);
2191 return TRUE;
2193 #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
2195 #if ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
2196 BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const
2198 ATLASSERT(::IsWindow(m_hWnd));
2199 int nIndex = GetPaneIndexFromID(nPaneID);
2200 if(nIndex == -1)
2201 return FALSE;
2203 hIcon = GetIcon(nIndex);
2204 return TRUE;
2207 BOOL SetPaneIcon(int nPaneID, HICON hIcon)
2209 ATLASSERT(::IsWindow(m_hWnd));
2210 int nIndex = GetPaneIndexFromID(nPaneID);
2211 if(nIndex == -1)
2212 return FALSE;
2214 return SetIcon(nIndex, hIcon);
2216 #endif // ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
2218 // Message map and handlers
2219 BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >)
2220 MESSAGE_HANDLER(WM_SIZE, OnSize)
2221 END_MSG_MAP()
2223 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
2225 LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
2226 if(wParam != SIZE_MINIMIZED && m_nPanes > 0)
2228 T* pT = static_cast<T*>(this);
2229 pT->UpdatePanesLayout();
2231 return lRet;
2234 // Implementation
2235 BOOL UpdatePanesLayout()
2237 // get pane positions
2238 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
2239 int* pPanesPos = buff.Allocate(m_nPanes);
2240 ATLASSERT(pPanesPos != NULL);
2241 if(pPanesPos == NULL)
2242 return FALSE;
2243 int nRet = GetParts(m_nPanes, pPanesPos);
2244 ATLASSERT(nRet == m_nPanes);
2245 if(nRet != m_nPanes)
2246 return FALSE;
2247 // calculate offset
2248 RECT rcClient = { 0 };
2249 GetClientRect(&rcClient);
2250 int cxOff = rcClient.right - pPanesPos[m_nPanes - 1];
2251 #ifndef _WIN32_WCE
2252 // Move panes left if size grip box is present
2253 if((GetStyle() & SBARS_SIZEGRIP) != 0)
2254 cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE);
2255 #endif // !_WIN32_WCE
2256 // find variable width pane
2257 int i;
2258 for(i = 0; i < m_nPanes; i++)
2260 if(m_pPane[i] == ID_DEFAULT_PANE)
2261 break;
2263 // resize all panes from the variable one to the right
2264 if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1]))
2266 for(; i < m_nPanes; i++)
2267 pPanesPos[i] += cxOff;
2269 // set pane postions
2270 return SetParts(m_nPanes, pPanesPos);
2273 int GetPaneIndexFromID(int nPaneID) const
2275 for(int i = 0; i < m_nPanes; i++)
2277 if(m_pPane[i] == nPaneID)
2278 return i;
2281 return -1; // not found
2285 class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl<CMultiPaneStatusBarCtrl>
2287 public:
2288 DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName())
2292 ///////////////////////////////////////////////////////////////////////////////
2293 // CPaneContainer - provides header with title and close button for panes
2295 // pane container extended styles
2296 #define PANECNT_NOCLOSEBUTTON 0x00000001
2297 #define PANECNT_VERTICAL 0x00000002
2298 #define PANECNT_FLATBORDER 0x00000004
2299 #define PANECNT_NOBORDER 0x00000008
2301 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
2302 class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
2304 public:
2305 DECLARE_WND_CLASS_EX(NULL, 0, -1)
2307 // Constants
2308 enum
2310 m_cxyBorder = 2,
2311 m_cxyTextOffset = 4,
2312 m_cxyBtnOffset = 1,
2314 m_cchTitle = 80,
2316 m_cxImageTB = 13,
2317 m_cyImageTB = 11,
2318 m_cxyBtnAddTB = 7,
2320 m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset,
2322 m_xBtnImageLeft = 6,
2323 m_yBtnImageTop = 5,
2324 m_xBtnImageRight = 12,
2325 m_yBtnImageBottom = 11,
2327 m_nCloseBtnID = ID_PANE_CLOSE
2330 // Data members
2331 CToolBarCtrl m_tb;
2332 ATL::CWindow m_wndClient;
2333 int m_cxyHeader;
2334 TCHAR m_szTitle[m_cchTitle];
2335 DWORD m_dwExtendedStyle; // Pane container specific extended styles
2336 HFONT m_hFont;
2337 bool m_bInternalFont;
2340 // Constructor
2341 CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0), m_hFont(NULL), m_bInternalFont(false)
2343 m_szTitle[0] = 0;
2346 // Attributes
2347 DWORD GetPaneContainerExtendedStyle() const
2349 return m_dwExtendedStyle;
2352 DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
2354 DWORD dwPrevStyle = m_dwExtendedStyle;
2355 if(dwMask == 0)
2356 m_dwExtendedStyle = dwExtendedStyle;
2357 else
2358 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
2359 if(m_hWnd != NULL)
2361 T* pT = static_cast<T*>(this);
2362 bool bUpdate = false;
2364 if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
2366 pT->CreateCloseButton();
2367 bUpdate = true;
2369 else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
2371 pT->DestroyCloseButton();
2372 bUpdate = true;
2375 if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
2377 pT->CalcSize();
2378 bUpdate = true;
2381 if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) !=
2382 (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border
2384 bUpdate = true;
2387 if(bUpdate)
2388 pT->UpdateLayout();
2390 return dwPrevStyle;
2393 HWND GetClient() const
2395 return m_wndClient;
2398 HWND SetClient(HWND hWndClient)
2400 HWND hWndOldClient = m_wndClient;
2401 m_wndClient = hWndClient;
2402 if(m_hWnd != NULL)
2404 T* pT = static_cast<T*>(this);
2405 pT->UpdateLayout();
2407 return hWndOldClient;
2410 BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const
2412 ATLASSERT(lpstrTitle != NULL);
2414 errno_t nRet = SecureHelper::strncpy_x(lpstrTitle, cchLength, m_szTitle, _TRUNCATE);
2416 return (nRet == 0 || nRet == STRUNCATE);
2419 BOOL SetTitle(LPCTSTR lpstrTitle)
2421 ATLASSERT(lpstrTitle != NULL);
2423 errno_t nRet = SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
2424 bool bRet = (nRet == 0 || nRet == STRUNCATE);
2425 if(bRet && m_hWnd != NULL)
2427 T* pT = static_cast<T*>(this);
2428 pT->UpdateLayout();
2431 return bRet;
2434 int GetTitleLength() const
2436 return lstrlen(m_szTitle);
2439 // Methods
2440 HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
2441 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
2443 if(lpstrTitle != NULL)
2444 SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
2445 #if (_MSC_VER >= 1300)
2446 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2447 #else // !(_MSC_VER >= 1300)
2448 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
2449 return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2450 #endif // !(_MSC_VER >= 1300)
2453 HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
2454 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
2456 if(uTitleID != 0U)
2457 ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
2458 #if (_MSC_VER >= 1300)
2459 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2460 #else // !(_MSC_VER >= 1300)
2461 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
2462 return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2463 #endif // !(_MSC_VER >= 1300)
2466 BOOL SubclassWindow(HWND hWnd)
2468 #if (_MSC_VER >= 1300)
2469 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
2470 #else // !(_MSC_VER >= 1300)
2471 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
2472 BOOL bRet = _baseClass::SubclassWindow(hWnd);
2473 #endif // !(_MSC_VER >= 1300)
2474 if(bRet != FALSE)
2476 T* pT = static_cast<T*>(this);
2477 pT->Init();
2479 RECT rect = { 0 };
2480 GetClientRect(&rect);
2481 pT->UpdateLayout(rect.right, rect.bottom);
2484 return bRet;
2487 BOOL EnableCloseButton(BOOL bEnable)
2489 ATLASSERT(::IsWindow(m_hWnd));
2490 T* pT = static_cast<T*>(this);
2491 pT; // avoid level 4 warning
2492 return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE;
2495 void UpdateLayout()
2497 RECT rcClient = { 0 };
2498 GetClientRect(&rcClient);
2499 T* pT = static_cast<T*>(this);
2500 pT->UpdateLayout(rcClient.right, rcClient.bottom);
2503 // Message map and handlers
2504 BEGIN_MSG_MAP(CPaneContainerImpl)
2505 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2506 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
2507 MESSAGE_HANDLER(WM_SIZE, OnSize)
2508 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
2509 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
2510 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
2511 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2512 MESSAGE_HANDLER(WM_PAINT, OnPaint)
2513 #ifndef _WIN32_WCE
2514 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
2515 #endif // !_WIN32_WCE
2516 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2517 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2518 FORWARD_NOTIFICATIONS()
2519 END_MSG_MAP()
2521 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2523 T* pT = static_cast<T*>(this);
2524 pT->Init();
2526 return 0;
2529 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2531 if(m_bInternalFont)
2533 ::DeleteObject(m_hFont);
2534 m_hFont = NULL;
2535 m_bInternalFont = false;
2538 return 0;
2541 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
2543 T* pT = static_cast<T*>(this);
2544 pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
2545 return 0;
2548 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2550 if(m_wndClient.m_hWnd != NULL)
2551 m_wndClient.SetFocus();
2552 return 0;
2555 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2557 return (LRESULT)m_hFont;
2560 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
2562 if(m_bInternalFont)
2564 ::DeleteObject(m_hFont);
2565 m_bInternalFont = false;
2568 m_hFont = (HFONT)wParam;
2570 T* pT = static_cast<T*>(this);
2571 pT->CalcSize();
2573 if((BOOL)lParam != FALSE)
2574 pT->UpdateLayout();
2576 return 0;
2579 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2581 return 1; // no background needed
2584 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2586 T* pT = static_cast<T*>(this);
2587 if(wParam != NULL)
2589 pT->DrawPaneTitle((HDC)wParam);
2591 if(m_wndClient.m_hWnd == NULL) // no client window
2592 pT->DrawPane((HDC)wParam);
2594 else
2596 CPaintDC dc(m_hWnd);
2597 pT->DrawPaneTitle(dc.m_hDC);
2599 if(m_wndClient.m_hWnd == NULL) // no client window
2600 pT->DrawPane(dc.m_hDC);
2603 return 0;
2606 LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
2608 if(m_tb.m_hWnd == NULL)
2610 bHandled = FALSE;
2611 return 1;
2614 T* pT = static_cast<T*>(this);
2616 LPNMHDR lpnmh = (LPNMHDR)lParam;
2617 LRESULT lRet = 0;
2619 // pass toolbar custom draw notifications to the base class
2620 if(lpnmh->code == NM_CUSTOMDRAW && lpnmh->hwndFrom == m_tb.m_hWnd)
2621 lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
2622 #ifndef _WIN32_WCE
2623 // tooltip notifications come with the tooltip window handle and button ID,
2624 // pass them to the parent if we don't handle them
2625 else if(lpnmh->code == TTN_GETDISPINFO && lpnmh->idFrom == pT->m_nCloseBtnID)
2626 bHandled = pT->GetToolTipText(lpnmh);
2627 #endif // !_WIN32_WCE
2628 // only let notifications not from the toolbar go to the parent
2629 else if(lpnmh->hwndFrom != m_tb.m_hWnd && lpnmh->idFrom != pT->m_nCloseBtnID)
2630 bHandled = FALSE;
2632 return lRet;
2635 LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2637 // if command comes from the close button, substitute HWND of the pane container instead
2638 if(m_tb.m_hWnd != NULL && (HWND)lParam == m_tb.m_hWnd)
2639 return ::SendMessage(GetParent(), WM_COMMAND, wParam, (LPARAM)m_hWnd);
2641 bHandled = FALSE;
2642 return 1;
2645 // Custom draw overrides
2646 DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
2648 return CDRF_NOTIFYITEMDRAW; // we need per-item notifications
2651 DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
2653 CDCHandle dc = lpNMCustomDraw->hdc;
2654 #if (_WIN32_IE >= 0x0400)
2655 RECT& rc = lpNMCustomDraw->rc;
2656 #else // !(_WIN32_IE >= 0x0400)
2657 RECT rc = { 0 };
2658 m_tb.GetItemRect(0, &rc);
2659 #endif // !(_WIN32_IE >= 0x0400)
2661 dc.FillRect(&rc, COLOR_3DFACE);
2663 return CDRF_NOTIFYPOSTPAINT;
2666 DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
2668 CDCHandle dc = lpNMCustomDraw->hdc;
2669 #if (_WIN32_IE >= 0x0400)
2670 RECT& rc = lpNMCustomDraw->rc;
2671 #else // !(_WIN32_IE >= 0x0400)
2672 RECT rc = { 0 };
2673 m_tb.GetItemRect(0, &rc);
2674 #endif // !(_WIN32_IE >= 0x0400)
2676 RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 };
2677 ::OffsetRect(&rcImage, rc.left, rc.top);
2678 T* pT = static_cast<T*>(this);
2680 if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0)
2682 RECT rcShadow = rcImage;
2683 ::OffsetRect(&rcShadow, 1, 1);
2684 CPen pen1;
2685 pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT));
2686 pT->DrawButtonImage(dc, rcShadow, pen1);
2687 CPen pen2;
2688 pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW));
2689 pT->DrawButtonImage(dc, rcImage, pen2);
2691 else
2693 if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0)
2694 ::OffsetRect(&rcImage, 1, 1);
2695 CPen pen;
2696 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
2697 pT->DrawButtonImage(dc, rcImage, pen);
2700 return CDRF_DODEFAULT; // continue with the default item painting
2703 // Implementation - overrideable methods
2704 void Init()
2706 if(m_hFont == NULL)
2708 // The same as AtlCreateControlFont() for horizontal pane
2709 #ifndef _WIN32_WCE
2710 LOGFONT lf = { 0 };
2711 ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE);
2712 if(IsVertical())
2713 lf.lfEscapement = 900; // 90 degrees
2714 m_hFont = ::CreateFontIndirect(&lf);
2715 #else // CE specific
2716 m_hFont = (HFONT)::GetStockObject(SYSTEM_FONT);
2717 if(IsVertical())
2719 CLogFont lf(m_hFont);
2720 lf.lfEscapement = 900; // 90 degrees
2721 m_hFont = ::CreateFontIndirect(&lf);
2723 #endif // _WIN32_WCE
2724 m_bInternalFont = true;
2727 T* pT = static_cast<T*>(this);
2728 pT->CalcSize();
2730 if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)
2731 pT->CreateCloseButton();
2734 void UpdateLayout(int cxWidth, int cyHeight)
2736 ATLASSERT(::IsWindow(m_hWnd));
2737 RECT rect = { 0 };
2739 if(IsVertical())
2741 ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight);
2742 if(m_tb.m_hWnd != NULL)
2743 m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
2745 if(m_wndClient.m_hWnd != NULL)
2746 m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER);
2747 else
2748 rect.right = cxWidth;
2750 else
2752 ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader);
2753 if(m_tb.m_hWnd != NULL)
2754 m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
2756 if(m_wndClient.m_hWnd != NULL)
2757 m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER);
2758 else
2759 rect.bottom = cyHeight;
2762 InvalidateRect(&rect);
2765 void CreateCloseButton()
2767 ATLASSERT(m_tb.m_hWnd == NULL);
2768 // create toolbar for the "x" button
2769 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);
2770 ATLASSERT(m_tb.IsWindow());
2772 if(m_tb.m_hWnd != NULL)
2774 T* pT = static_cast<T*>(this);
2775 pT; // avoid level 4 warning
2777 m_tb.SetButtonStructSize();
2779 TBBUTTON tbbtn = { 0 };
2780 tbbtn.idCommand = pT->m_nCloseBtnID;
2781 tbbtn.fsState = TBSTATE_ENABLED;
2782 tbbtn.fsStyle = BTNS_BUTTON;
2783 m_tb.AddButtons(1, &tbbtn);
2785 m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB);
2786 m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB);
2788 if(IsVertical())
2789 m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOACTIVATE);
2790 else
2791 m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
2795 void DestroyCloseButton()
2797 if(m_tb.m_hWnd != NULL)
2798 m_tb.DestroyWindow();
2801 void CalcSize()
2803 T* pT = static_cast<T*>(this);
2804 CFontHandle font = pT->GetTitleFont();
2805 if(font.IsNull())
2806 font = (HFONT)::GetStockObject(SYSTEM_FONT);
2807 LOGFONT lf = { 0 };
2808 font.GetLogFont(lf);
2809 if(IsVertical())
2811 m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder;
2813 else
2815 int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset;
2816 int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset;
2817 m_cxyHeader = __max(cyFont, cyBtn);
2821 HFONT GetTitleFont() const
2823 return m_hFont;
2826 #ifndef _WIN32_WCE
2827 BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
2829 return FALSE;
2831 #endif // !_WIN32_WCE
2833 void DrawPaneTitle(CDCHandle dc)
2835 RECT rect = { 0 };
2836 GetClientRect(&rect);
2838 UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST;
2839 if(IsVertical())
2841 rect.right = rect.left + m_cxyHeader;
2842 uBorder |= BF_BOTTOM;
2844 else
2846 rect.bottom = rect.top + m_cxyHeader;
2847 uBorder |= BF_RIGHT;
2850 if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0)
2852 if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0)
2853 uBorder |= BF_FLAT;
2854 dc.DrawEdge(&rect, EDGE_ETCHED, uBorder);
2856 dc.FillRect(&rect, COLOR_3DFACE);
2858 // draw title text
2859 dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
2860 dc.SetBkMode(TRANSPARENT);
2861 T* pT = static_cast<T*>(this);
2862 HFONT hFontOld = dc.SelectFont(pT->GetTitleFont());
2863 #if defined(_WIN32_WCE) && !defined(DT_END_ELLIPSIS)
2864 const UINT DT_END_ELLIPSIS = 0;
2865 #endif // defined(_WIN32_WCE) && !defined(DT_END_ELLIPSIS)
2867 if(IsVertical())
2869 rect.top += m_cxyTextOffset;
2870 rect.bottom -= m_cxyTextOffset;
2871 if(m_tb.m_hWnd != NULL)
2872 rect.top += m_cxToolBar;;
2874 RECT rcCalc = { rect.left, rect.bottom, rect.right, rect.top };
2875 int cxFont = dc.DrawText(m_szTitle, -1, &rcCalc, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT);
2876 RECT rcText = { 0 };
2877 rcText.left = (rect.right - rect.left - cxFont) / 2;
2878 rcText.right = rcText.left + (rect.bottom - rect.top);
2879 rcText.top = rect.bottom;
2880 rcText.bottom = rect.top;
2881 dc.DrawText(m_szTitle, -1, &rcText, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS);
2883 else
2885 rect.left += m_cxyTextOffset;
2886 rect.right -= m_cxyTextOffset;
2887 if(m_tb.m_hWnd != NULL)
2888 rect.right -= m_cxToolBar;;
2890 dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
2893 dc.SelectFont(hFontOld);
2896 // called only if pane is empty
2897 void DrawPane(CDCHandle dc)
2899 RECT rect = { 0 };
2900 GetClientRect(&rect);
2901 if(IsVertical())
2902 rect.left += m_cxyHeader;
2903 else
2904 rect.top += m_cxyHeader;
2905 if((GetExStyle() & WS_EX_CLIENTEDGE) == 0)
2906 dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
2907 dc.FillRect(&rect, COLOR_APPWORKSPACE);
2910 // drawing helper - draws "x" button image
2911 void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
2913 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
2914 HPEN hPenOld = dc.SelectPen(hPen);
2916 dc.MoveTo(rcImage.left, rcImage.top);
2917 dc.LineTo(rcImage.right, rcImage.bottom);
2918 dc.MoveTo(rcImage.left + 1, rcImage.top);
2919 dc.LineTo(rcImage.right + 1, rcImage.bottom);
2921 dc.MoveTo(rcImage.left, rcImage.bottom - 1);
2922 dc.LineTo(rcImage.right, rcImage.top - 1);
2923 dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
2924 dc.LineTo(rcImage.right + 1, rcImage.top - 1);
2926 dc.SelectPen(hPenOld);
2927 #else // (_WIN32_WCE < 400)
2928 rcImage;
2929 hPen;
2930 // no support for the "x" button image
2931 #endif // (_WIN32_WCE < 400)
2934 bool IsVertical() const
2936 return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
2940 class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
2942 public:
2943 DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
2947 ///////////////////////////////////////////////////////////////////////////////
2948 // CSortListViewCtrl - implements sorting for a listview control
2950 // sort listview extended styles
2951 #define SORTLV_USESHELLBITMAPS 0x00000001
2953 // Notification sent to parent when sort column is changed by user clicking header.
2954 #define SLVN_SORTCHANGED LVN_LAST
2956 // A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification
2957 typedef struct tagNMSORTLISTVIEW
2959 NMHDR hdr;
2960 int iNewSortColumn;
2961 int iOldSortColumn;
2962 } NMSORTLISTVIEW, *LPNMSORTLISTVIEW;
2964 // Column sort types. Can be set on a per-column basis with the SetColumnSortType method.
2965 enum
2967 LVCOLSORT_NONE,
2968 LVCOLSORT_TEXT, // default
2969 LVCOLSORT_TEXTNOCASE,
2970 LVCOLSORT_LONG,
2971 LVCOLSORT_DOUBLE,
2972 LVCOLSORT_DECIMAL,
2973 LVCOLSORT_DATETIME,
2974 LVCOLSORT_DATE,
2975 LVCOLSORT_TIME,
2976 LVCOLSORT_CUSTOM,
2977 LVCOLSORT_LAST = LVCOLSORT_CUSTOM
2981 template <class T>
2982 class CSortListViewImpl
2984 public:
2985 enum
2987 m_cchCmpTextMax = 32, // overrideable
2988 m_cxSortImage = 16,
2989 m_cySortImage = 15,
2990 m_cxSortArrow = 11,
2991 m_cySortArrow = 6,
2992 m_iSortUp = 0, // index of sort bitmaps
2993 m_iSortDown = 1,
2994 m_nShellSortUpID = 133
2997 // passed to LVCompare functions as lParam1 and lParam2
2998 struct LVCompareParam
3000 int iItem;
3001 DWORD_PTR dwItemData;
3002 union
3004 long lValue;
3005 double dblValue;
3006 DECIMAL decValue;
3007 LPCTSTR pszValue;
3011 // passed to LVCompare functions as the lParamSort parameter
3012 struct LVSortInfo
3014 T* pT;
3015 int iSortCol;
3016 bool bDescending;
3019 bool m_bSortDescending;
3020 bool m_bCommCtrl6;
3021 int m_iSortColumn;
3022 CBitmap m_bmSort[2];
3023 int m_fmtOldSortCol;
3024 HBITMAP m_hbmOldSortCol;
3025 DWORD m_dwSortLVExtendedStyle;
3026 ATL::CSimpleArray<WORD> m_arrColSortType;
3027 bool m_bUseWaitCursor;
3029 CSortListViewImpl() :
3030 m_bSortDescending(false),
3031 m_bCommCtrl6(false),
3032 m_iSortColumn(-1),
3033 m_fmtOldSortCol(0),
3034 m_hbmOldSortCol(NULL),
3035 m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS),
3036 m_bUseWaitCursor(true)
3038 #ifndef _WIN32_WCE
3039 DWORD dwMajor = 0;
3040 DWORD dwMinor = 0;
3041 HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
3042 m_bCommCtrl6 = SUCCEEDED(hRet) && dwMajor >= 6;
3043 #endif // !_WIN32_WCE
3046 // Attributes
3047 void SetSortColumn(int iCol)
3049 T* pT = static_cast<T*>(this);
3050 ATLASSERT(::IsWindow(pT->m_hWnd));
3051 CHeaderCtrl header = pT->GetHeader();
3052 ATLASSERT(header.m_hWnd != NULL);
3053 ATLASSERT(iCol >= -1 && iCol < m_arrColSortType.GetSize());
3055 int iOldSortCol = m_iSortColumn;
3056 m_iSortColumn = iCol;
3057 if(m_bCommCtrl6)
3059 #ifndef HDF_SORTUP
3060 const int HDF_SORTUP = 0x0400;
3061 #endif // HDF_SORTUP
3062 #ifndef HDF_SORTDOWN
3063 const int HDF_SORTDOWN = 0x0200;
3064 #endif // HDF_SORTDOWN
3065 const int nMask = HDF_SORTUP | HDF_SORTDOWN;
3066 HDITEM hditem = { HDI_FORMAT };
3067 if(iOldSortCol != iCol && iOldSortCol >= 0 && header.GetItem(iOldSortCol, &hditem))
3069 hditem.fmt &= ~nMask;
3070 header.SetItem(iOldSortCol, &hditem);
3072 if(iCol >= 0 && header.GetItem(iCol, &hditem))
3074 hditem.fmt &= ~nMask;
3075 hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP;
3076 header.SetItem(iCol, &hditem);
3078 return;
3081 if(m_bmSort[m_iSortUp].IsNull())
3082 pT->CreateSortBitmaps();
3084 // restore previous sort column's bitmap, if any, and format
3085 HDITEM hditem = { HDI_BITMAP | HDI_FORMAT };
3086 if(iOldSortCol != iCol && iOldSortCol >= 0)
3088 hditem.hbm = m_hbmOldSortCol;
3089 hditem.fmt = m_fmtOldSortCol;
3090 header.SetItem(iOldSortCol, &hditem);
3093 // save new sort column's bitmap and format, and add our sort bitmap
3094 if(iCol >= 0 && header.GetItem(iCol, &hditem))
3096 if(iOldSortCol != iCol)
3098 m_fmtOldSortCol = hditem.fmt;
3099 m_hbmOldSortCol = hditem.hbm;
3101 hditem.fmt &= ~HDF_IMAGE;
3102 hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
3103 int i = m_bSortDescending ? m_iSortDown : m_iSortUp;
3104 hditem.hbm = m_bmSort[i];
3105 header.SetItem(iCol, &hditem);
3109 int GetSortColumn() const
3111 return m_iSortColumn;
3114 void SetColumnSortType(int iCol, WORD wType)
3116 ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
3117 ATLASSERT(wType >= LVCOLSORT_NONE && wType <= LVCOLSORT_LAST);
3118 m_arrColSortType[iCol] = wType;
3121 WORD GetColumnSortType(int iCol) const
3123 ATLASSERT((iCol >= 0) && iCol < m_arrColSortType.GetSize());
3124 return m_arrColSortType[iCol];
3127 int GetColumnCount() const
3129 const T* pT = static_cast<const T*>(this);
3130 ATLASSERT(::IsWindow(pT->m_hWnd));
3131 CHeaderCtrl header = pT->GetHeader();
3132 return header.m_hWnd != NULL ? header.GetItemCount() : 0;
3135 bool IsSortDescending() const
3137 return m_bSortDescending;
3140 DWORD GetSortListViewExtendedStyle() const
3142 return m_dwSortLVExtendedStyle;
3145 DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
3147 DWORD dwPrevStyle = m_dwSortLVExtendedStyle;
3148 if(dwMask == 0)
3149 m_dwSortLVExtendedStyle = dwExtendedStyle;
3150 else
3151 m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
3152 return dwPrevStyle;
3155 // Operations
3156 bool DoSortItems(int iCol, bool bDescending = false)
3158 T* pT = static_cast<T*>(this);
3159 ATLASSERT(::IsWindow(pT->m_hWnd));
3160 ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
3162 WORD wType = m_arrColSortType[iCol];
3163 if(wType == LVCOLSORT_NONE)
3164 return false;
3166 int nCount = pT->GetItemCount();
3167 if(nCount < 2)
3169 m_bSortDescending = bDescending;
3170 SetSortColumn(iCol);
3171 return true;
3174 CWaitCursor waitCursor(false);
3175 if(m_bUseWaitCursor)
3176 waitCursor.Set();
3178 LVCompareParam* pParam = NULL;
3179 ATLTRY(pParam = new LVCompareParam[nCount]);
3180 PFNLVCOMPARE pFunc = NULL;
3181 TCHAR pszTemp[pT->m_cchCmpTextMax] = { 0 };
3182 bool bStrValue = false;
3184 switch(wType)
3186 case LVCOLSORT_TEXT:
3187 pFunc = (PFNLVCOMPARE)pT->LVCompareText;
3188 case LVCOLSORT_TEXTNOCASE:
3189 if(pFunc == NULL)
3190 pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase;
3191 case LVCOLSORT_CUSTOM:
3193 if(pFunc == NULL)
3194 pFunc = (PFNLVCOMPARE)pT->LVCompareCustom;
3196 for(int i = 0; i < nCount; i++)
3198 pParam[i].iItem = i;
3199 pParam[i].dwItemData = pT->GetItemData(i);
3200 pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax];
3201 pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax);
3202 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3204 bStrValue = true;
3206 break;
3207 case LVCOLSORT_LONG:
3209 pFunc = (PFNLVCOMPARE)pT->LVCompareLong;
3210 for(int i = 0; i < nCount; i++)
3212 pParam[i].iItem = i;
3213 pParam[i].dwItemData = pT->GetItemData(i);
3214 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3215 pParam[i].lValue = pT->StrToLong(pszTemp);
3216 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3219 break;
3220 case LVCOLSORT_DOUBLE:
3222 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
3223 for(int i = 0; i < nCount; i++)
3225 pParam[i].iItem = i;
3226 pParam[i].dwItemData = pT->GetItemData(i);
3227 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3228 pParam[i].dblValue = pT->StrToDouble(pszTemp);
3229 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3232 break;
3233 case LVCOLSORT_DECIMAL:
3235 pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal;
3236 for(int i = 0; i < nCount; i++)
3238 pParam[i].iItem = i;
3239 pParam[i].dwItemData = pT->GetItemData(i);
3240 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3241 pT->StrToDecimal(pszTemp, &pParam[i].decValue);
3242 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3245 break;
3246 case LVCOLSORT_DATETIME:
3247 case LVCOLSORT_DATE:
3248 case LVCOLSORT_TIME:
3250 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
3251 DWORD dwFlags = LOCALE_NOUSEROVERRIDE;
3252 if(wType == LVCOLSORT_DATE)
3253 dwFlags |= VAR_DATEVALUEONLY;
3254 else if(wType == LVCOLSORT_TIME)
3255 dwFlags |= VAR_TIMEVALUEONLY;
3256 for(int i = 0; i < nCount; i++)
3258 pParam[i].iItem = i;
3259 pParam[i].dwItemData = pT->GetItemData(i);
3260 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3261 pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags);
3262 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3265 break;
3266 default:
3267 ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n"));
3268 break;
3269 } // switch(wType)
3271 ATLASSERT(pFunc != NULL);
3272 LVSortInfo lvsi = { pT, iCol, bDescending };
3273 bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE);
3274 for(int i = 0; i < nCount; i++)
3276 DWORD_PTR dwItemData = pT->GetItemData(i);
3277 LVCompareParam* p = (LVCompareParam*)dwItemData;
3278 ATLASSERT(p != NULL);
3279 if(bStrValue)
3280 delete [] (TCHAR*)p->pszValue;
3281 pT->SetItemData(i, p->dwItemData);
3283 delete [] pParam;
3285 if(bRet)
3287 m_bSortDescending = bDescending;
3288 SetSortColumn(iCol);
3291 if(m_bUseWaitCursor)
3292 waitCursor.Restore();
3294 return bRet;
3297 void CreateSortBitmaps()
3299 if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0)
3301 bool bFree = false;
3302 LPCTSTR pszModule = _T("shell32.dll");
3303 HINSTANCE hShell = ::GetModuleHandle(pszModule);
3305 if (hShell == NULL)
3307 hShell = ::LoadLibrary(pszModule);
3308 bFree = true;
3311 if (hShell != NULL)
3313 bool bSuccess = true;
3314 for(int i = m_iSortUp; i <= m_iSortDown; i++)
3316 if(!m_bmSort[i].IsNull())
3317 m_bmSort[i].DeleteObject();
3318 m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i),
3319 #ifndef _WIN32_WCE
3320 IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
3321 #else // CE specific
3322 IMAGE_BITMAP, 0, 0, 0);
3323 #endif // _WIN32_WCE
3324 if(m_bmSort[i].IsNull())
3326 bSuccess = false;
3327 break;
3330 if(bFree)
3331 ::FreeLibrary(hShell);
3332 if(bSuccess)
3333 return;
3337 T* pT = static_cast<T*>(this);
3338 for(int i = m_iSortUp; i <= m_iSortDown; i++)
3340 if(!m_bmSort[i].IsNull())
3341 m_bmSort[i].DeleteObject();
3343 CDC dcMem;
3344 CClientDC dc(::GetDesktopWindow());
3345 dcMem.CreateCompatibleDC(dc.m_hDC);
3346 m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage);
3347 HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]);
3348 RECT rc = { 0, 0, m_cxSortImage, m_cySortImage };
3349 pT->DrawSortBitmap(dcMem.m_hDC, i, &rc);
3350 dcMem.SelectBitmap(hbmOld);
3351 dcMem.DeleteDC();
3355 void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol)
3357 T* pT = static_cast<T*>(this);
3358 int nID = pT->GetDlgCtrlID();
3359 NMSORTLISTVIEW nm = { { pT->m_hWnd, nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol };
3360 ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm);
3363 // Overrideables
3364 int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/)
3366 // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members.
3367 // If item1 > item2 return 1, if item1 < item2 return -1, else return 0.
3368 return 0;
3371 void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc)
3373 dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE));
3374 HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
3375 CPen pen;
3376 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW));
3377 HPEN hpenOld = dc.SelectPen(pen);
3378 POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 };
3379 if(iBitmap == m_iSortUp)
3381 POINT pts[3] =
3383 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y },
3384 { ptOrg.x, ptOrg.y + m_cySortArrow - 1 },
3385 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 }
3387 dc.Polygon(pts, 3);
3389 else
3391 POINT pts[3] =
3393 { ptOrg.x, ptOrg.y },
3394 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 },
3395 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y }
3397 dc.Polygon(pts, 3);
3399 dc.SelectBrush(hbrOld);
3400 dc.SelectPen(hpenOld);
3403 double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags)
3405 ATLASSERT(lpstr != NULL);
3406 if(lpstr == NULL || lpstr[0] == _T('\0'))
3407 return 0;
3409 USES_CONVERSION;
3410 HRESULT hRet = E_FAIL;
3411 DATE dRet = 0;
3412 if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet)))
3414 ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet);
3415 dRet = 0;
3417 return dRet;
3420 long StrToLong(LPCTSTR lpstr)
3422 ATLASSERT(lpstr != NULL);
3423 if(lpstr == NULL || lpstr[0] == _T('\0'))
3424 return 0;
3426 USES_CONVERSION;
3427 HRESULT hRet = E_FAIL;
3428 long lRet = 0;
3429 if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet)))
3431 ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet);
3432 lRet = 0;
3434 return lRet;
3437 double StrToDouble(LPCTSTR lpstr)
3439 ATLASSERT(lpstr != NULL);
3440 if(lpstr == NULL || lpstr[0] == _T('\0'))
3441 return 0;
3443 USES_CONVERSION;
3444 HRESULT hRet = E_FAIL;
3445 double dblRet = 0;
3446 if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet)))
3448 ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet);
3449 dblRet = 0;
3451 return dblRet;
3454 bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal)
3456 ATLASSERT(lpstr != NULL);
3457 ATLASSERT(pDecimal != NULL);
3458 if(lpstr == NULL || pDecimal == NULL)
3459 return false;
3461 USES_CONVERSION;
3462 HRESULT hRet = E_FAIL;
3463 if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal)))
3465 ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet);
3466 pDecimal->Lo64 = 0;
3467 pDecimal->Hi32 = 0;
3468 pDecimal->signscale = 0;
3469 return false;
3471 return true;
3474 // Overrideable PFNLVCOMPARE functions
3475 static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3477 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3479 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3480 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3481 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3483 int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue);
3484 return pInfo->bDescending ? -nRet : nRet;
3487 static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3489 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3491 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3492 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3493 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3495 int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue);
3496 return pInfo->bDescending ? -nRet : nRet;
3499 static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3501 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3503 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3504 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3505 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3507 int nRet = 0;
3508 if(pParam1->lValue > pParam2->lValue)
3509 nRet = 1;
3510 else if(pParam1->lValue < pParam2->lValue)
3511 nRet = -1;
3512 return pInfo->bDescending ? -nRet : nRet;
3515 static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3517 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3519 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3520 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3521 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3523 int nRet = 0;
3524 if(pParam1->dblValue > pParam2->dblValue)
3525 nRet = 1;
3526 else if(pParam1->dblValue < pParam2->dblValue)
3527 nRet = -1;
3528 return pInfo->bDescending ? -nRet : nRet;
3531 static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3533 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3535 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3536 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3537 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3539 int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol);
3540 return pInfo->bDescending ? -nRet : nRet;
3543 #ifndef _WIN32_WCE
3544 static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3546 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3548 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3549 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3550 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3552 int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue);
3553 nRet--;
3554 return pInfo->bDescending ? -nRet : nRet;
3556 #else
3557 // Compare mantissas, ignore sign and scale
3558 static int CompareMantissas(const DECIMAL& decLeft, const DECIMAL& decRight)
3560 if (decLeft.Hi32 < decRight.Hi32)
3562 return -1;
3564 if (decLeft.Hi32 > decRight.Hi32)
3566 return 1;
3568 // Here, decLeft.Hi32 == decRight.Hi32
3569 if (decLeft.Lo64 < decRight.Lo64)
3571 return -1;
3573 if (decLeft.Lo64 > decRight.Lo64)
3575 return 1;
3577 return 0;
3580 // return values: VARCMP_LT, VARCMP_EQ, VARCMP_GT, VARCMP_NULL
3581 static HRESULT VarDecCmp(const DECIMAL* pdecLeft, const DECIMAL* pdecRight)
3583 static const ULONG powersOfTen[] =
3585 10ul,
3586 100ul,
3587 1000ul,
3588 10000ul,
3589 100000ul,
3590 1000000ul,
3591 10000000ul,
3592 100000000ul,
3593 1000000000ul
3595 static const int largestPower = sizeof(powersOfTen) / sizeof(powersOfTen[0]);
3596 if (!pdecLeft || !pdecRight)
3598 return VARCMP_NULL;
3601 // Degenerate case - at least one comparand is of the form
3602 // [+-]0*10^N (denormalized zero)
3603 bool bLeftZero = (!pdecLeft->Lo64 && !pdecLeft->Hi32);
3604 bool bRightZero = (!pdecRight->Lo64 && !pdecRight->Hi32);
3605 if (bLeftZero && bRightZero)
3607 return VARCMP_EQ;
3609 bool bLeftNeg = ((pdecLeft->sign & DECIMAL_NEG) != 0);
3610 bool bRightNeg = ((pdecRight->sign & DECIMAL_NEG) != 0);
3611 if (bLeftZero)
3613 return (bRightNeg ? VARCMP_GT : VARCMP_LT);
3615 // This also covers the case where the comparands have different signs
3616 if (bRightZero || bLeftNeg != bRightNeg)
3618 return (bLeftNeg ? VARCMP_LT : VARCMP_GT);
3621 // Here both comparands have the same sign and need to be compared
3622 // on mantissa and scale. The result is obvious when
3623 // 1. Scales are equal (then compare mantissas)
3624 // 2. A number with smaller scale is also the one with larger mantissa
3625 // (then this number is obviously larger)
3626 // In the remaining case, we would multiply the number with smaller
3627 // scale by 10 and simultaneously increment its scale (which amounts to
3628 // adding trailing zeros after decimal point), until the numbers fall under
3629 // one of the two cases above
3630 DECIMAL temp;
3631 bool bInvert = bLeftNeg; // the final result needs to be inverted
3632 if (pdecLeft->scale < pdecRight->scale)
3634 temp = *pdecLeft;
3636 else
3638 temp = *pdecRight;
3639 pdecRight = pdecLeft;
3640 bInvert = !bInvert;
3643 // Now temp is the number with smaller (or equal) scale, and
3644 // we can modify it freely without touching original parameters
3645 int comp;
3646 while ((comp = CompareMantissas(temp, *pdecRight)) < 0 &&
3647 temp.scale < pdecRight->scale)
3649 // Multiply by an appropriate power of 10
3650 int scaleDiff = pdecRight->scale - temp.scale;
3651 if (scaleDiff > largestPower)
3653 // Keep the multiplier representable in 32bit
3654 scaleDiff = largestPower;
3656 DWORDLONG power = powersOfTen[scaleDiff - 1];
3657 // Multiply temp's mantissa by power
3658 DWORDLONG product = temp.Lo32 * power;
3659 ULONG carry = static_cast<ULONG>(product >> 32);
3660 temp.Lo32 = static_cast<ULONG>(product);
3661 product = temp.Mid32 * power + carry;
3662 carry = static_cast<ULONG>(product >> 32);
3663 temp.Mid32 = static_cast<ULONG>(product);
3664 product = temp.Hi32 * power + carry;
3665 if (static_cast<ULONG>(product >> 32))
3667 // Multiplication overflowed - pdecLeft is clearly larger
3668 break;
3670 temp.Hi32 = static_cast<ULONG>(product);
3671 temp.scale = (BYTE)(temp.scale + scaleDiff);
3673 if (temp.scale < pdecRight->scale)
3675 comp = 1;
3677 if (bInvert)
3679 comp = -comp;
3681 return (comp > 0 ? VARCMP_GT : comp < 0 ? VARCMP_LT : VARCMP_EQ);
3684 static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3686 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3688 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3689 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3690 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3692 int nRet = (int)VarDecCmp(&pParam1->decValue, &pParam2->decValue);
3693 nRet--;
3694 return pInfo->bDescending ? -nRet : nRet;
3696 #endif // !_WIN32_WCE
3698 BEGIN_MSG_MAP(CSortListViewImpl)
3699 MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn)
3700 MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn)
3701 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick)
3702 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick)
3703 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
3704 END_MSG_MAP()
3706 LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
3708 T* pT = static_cast<T*>(this);
3709 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
3710 if(lRet == -1)
3711 return -1;
3713 WORD wType = 0;
3714 m_arrColSortType.Add(wType);
3715 int nCount = m_arrColSortType.GetSize();
3716 ATLASSERT(nCount == GetColumnCount());
3718 for(int i = nCount - 1; i > lRet; i--)
3719 m_arrColSortType[i] = m_arrColSortType[i - 1];
3720 m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT;
3722 if(lRet <= m_iSortColumn)
3723 m_iSortColumn++;
3725 return lRet;
3728 LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
3730 T* pT = static_cast<T*>(this);
3731 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
3732 if(lRet == 0)
3733 return 0;
3735 int iCol = (int)wParam;
3736 if(m_iSortColumn == iCol)
3737 m_iSortColumn = -1;
3738 else if(m_iSortColumn > iCol)
3739 m_iSortColumn--;
3740 m_arrColSortType.RemoveAt(iCol);
3742 return lRet;
3745 LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
3747 LPNMHEADER p = (LPNMHEADER)pnmh;
3748 if(p->iButton == 0)
3750 int iOld = m_iSortColumn;
3751 bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false;
3752 if(DoSortItems(p->iItem, bDescending))
3753 NotifyParentSortChanged(p->iItem, iOld);
3755 bHandled = FALSE;
3756 return 0;
3759 LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
3761 #ifndef _WIN32_WCE
3762 if(wParam == SPI_SETNONCLIENTMETRICS)
3763 GetSystemSettings();
3764 #else // CE specific
3765 wParam; // avoid level 4 warning
3766 GetSystemSettings();
3767 #endif // _WIN32_WCE
3768 bHandled = FALSE;
3769 return 0;
3772 void GetSystemSettings()
3774 if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull())
3776 T* pT = static_cast<T*>(this);
3777 pT->CreateSortBitmaps();
3778 if(m_iSortColumn != -1)
3779 SetSortColumn(m_iSortColumn);
3786 typedef ATL::CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SHOWSELALWAYS , WS_EX_CLIENTEDGE> CSortListViewCtrlTraits;
3788 template <class T, class TBase = CListViewCtrl, class TWinTraits = CSortListViewCtrlTraits>
3789 class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl<T, TBase, TWinTraits>, public CSortListViewImpl<T>
3791 public:
3792 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
3794 bool SortItems(int iCol, bool bDescending = false)
3796 return DoSortItems(iCol, bDescending);
3799 BEGIN_MSG_MAP(CSortListViewCtrlImpl)
3800 MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl<T>::OnInsertColumn)
3801 MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl<T>::OnDeleteColumn)
3802 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl<T>::OnHeaderItemClick)
3803 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl<T>::OnHeaderItemClick)
3804 MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl<T>::OnSettingChange)
3805 END_MSG_MAP()
3808 class CSortListViewCtrl : public CSortListViewCtrlImpl<CSortListViewCtrl>
3810 public:
3811 DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName())
3815 ///////////////////////////////////////////////////////////////////////////////
3816 // CTabView - implements tab view window
3818 // TabView Notifications
3819 #define TBVN_PAGEACTIVATED (0U-741)
3820 #define TBVN_CONTEXTMENU (0U-742)
3822 // Notification data for TBVN_CONTEXTMENU
3823 struct TBVCONTEXTMENUINFO
3825 NMHDR hdr;
3826 POINT pt;
3829 typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
3832 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
3833 class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
3835 public:
3836 DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE)
3838 // Declarations and enums
3839 struct TABVIEWPAGE
3841 HWND hWnd;
3842 LPTSTR lpstrTitle;
3843 LPVOID pData;
3846 struct TCITEMEXTRA
3848 TCITEMHEADER tciheader;
3849 TABVIEWPAGE tvpage;
3851 operator LPTCITEM() { return (LPTCITEM)this; }
3854 enum
3856 m_nTabID = 1313,
3857 m_cxMoveMark = 6,
3858 m_cyMoveMark = 3,
3859 m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
3862 // Data members
3863 ATL::CContainedWindowT<CTabCtrl> m_tab;
3864 int m_cyTabHeight;
3866 int m_nActivePage;
3868 int m_nInsertItem;
3869 POINT m_ptStartDrag;
3871 CMenuHandle m_menu;
3873 int m_cchTabTextLength;
3875 int m_nMenuItemsCount;
3877 ATL::CWindow m_wndTitleBar;
3878 LPTSTR m_lpstrTitleBarBase;
3879 int m_cchTitleBarLength;
3881 CImageList m_ilDrag;
3883 bool m_bDestroyPageOnRemove:1;
3884 bool m_bDestroyImageList:1;
3885 bool m_bActivePageMenuItem:1;
3886 bool m_bActiveAsDefaultMenuItem:1;
3887 bool m_bEmptyMenuItem:1;
3888 bool m_bWindowsMenuItem:1;
3889 bool m_bNoTabDrag:1;
3890 // internal
3891 bool m_bTabCapture:1;
3892 bool m_bTabDrag:1;
3893 bool m_bInternalFont:1;
3895 // Constructor/destructor
3896 CTabViewImpl() :
3897 m_nActivePage(-1),
3898 m_cyTabHeight(0),
3899 m_tab(this, 1),
3900 m_nInsertItem(-1),
3901 m_cchTabTextLength(30),
3902 m_nMenuItemsCount(10),
3903 m_lpstrTitleBarBase(NULL),
3904 m_cchTitleBarLength(100),
3905 m_bDestroyPageOnRemove(true),
3906 m_bDestroyImageList(true),
3907 m_bActivePageMenuItem(true),
3908 m_bActiveAsDefaultMenuItem(false),
3909 m_bEmptyMenuItem(false),
3910 m_bWindowsMenuItem(false),
3911 m_bNoTabDrag(false),
3912 m_bTabCapture(false),
3913 m_bTabDrag(false),
3914 m_bInternalFont(false)
3916 m_ptStartDrag.x = 0;
3917 m_ptStartDrag.y = 0;
3920 ~CTabViewImpl()
3922 delete [] m_lpstrTitleBarBase;
3925 // Message filter function - to be called from PreTranslateMessage of the main window
3926 BOOL PreTranslateMessage(MSG* pMsg)
3928 if(IsWindow() == FALSE)
3929 return FALSE;
3931 BOOL bRet = FALSE;
3933 // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
3934 int nCount = GetPageCount();
3935 if(nCount > 0)
3937 bool bControl = (::GetKeyState(VK_CONTROL) < 0);
3938 if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
3940 if(nCount > 1)
3942 int nPage = m_nActivePage;
3943 bool bShift = (::GetKeyState(VK_SHIFT) < 0);
3944 if(bShift)
3945 nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
3946 else
3947 nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
3949 SetActivePage(nPage);
3950 T* pT = static_cast<T*>(this);
3951 pT->OnPageActivated(m_nActivePage);
3954 bRet = TRUE;
3958 // If we are doing drag-drop, check for Escape key that cancels it
3959 if(bRet == FALSE)
3961 if(m_bTabCapture && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
3963 ::ReleaseCapture();
3964 bRet = TRUE;
3968 // Pass the message to the active page
3969 if(bRet == FALSE)
3971 if(m_nActivePage != -1)
3972 bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
3975 return bRet;
3978 // Attributes
3979 int GetPageCount() const
3981 ATLASSERT(::IsWindow(m_hWnd));
3982 return m_tab.GetItemCount();
3985 int GetActivePage() const
3987 return m_nActivePage;
3990 void SetActivePage(int nPage)
3992 ATLASSERT(::IsWindow(m_hWnd));
3993 ATLASSERT(IsValidPageIndex(nPage));
3995 T* pT = static_cast<T*>(this);
3997 SetRedraw(FALSE);
3999 if(m_nActivePage != -1)
4000 ::ShowWindow(GetPageHWND(m_nActivePage), FALSE);
4001 m_nActivePage = nPage;
4002 m_tab.SetCurSel(m_nActivePage);
4003 ::ShowWindow(GetPageHWND(m_nActivePage), TRUE);
4005 pT->UpdateLayout();
4007 SetRedraw(TRUE);
4008 RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
4010 if(::GetFocus() != m_tab.m_hWnd)
4011 ::SetFocus(GetPageHWND(m_nActivePage));
4013 pT->UpdateTitleBar();
4014 pT->UpdateMenu();
4017 HIMAGELIST GetImageList() const
4019 ATLASSERT(::IsWindow(m_hWnd));
4020 return m_tab.GetImageList();
4023 HIMAGELIST SetImageList(HIMAGELIST hImageList)
4025 ATLASSERT(::IsWindow(m_hWnd));
4026 return m_tab.SetImageList(hImageList);
4029 void SetWindowMenu(HMENU hMenu)
4031 ATLASSERT(::IsWindow(m_hWnd));
4033 m_menu = hMenu;
4035 T* pT = static_cast<T*>(this);
4036 pT->UpdateMenu();
4039 void SetTitleBarWindow(HWND hWnd)
4041 ATLASSERT(::IsWindow(m_hWnd));
4043 delete [] m_lpstrTitleBarBase;
4044 m_lpstrTitleBarBase = NULL;
4046 m_wndTitleBar = hWnd;
4047 if(hWnd == NULL)
4048 return;
4050 int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
4051 ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
4052 if(m_lpstrTitleBarBase != NULL)
4054 m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
4055 T* pT = static_cast<T*>(this);
4056 pT->UpdateTitleBar();
4060 // Page attributes
4061 HWND GetPageHWND(int nPage) const
4063 ATLASSERT(::IsWindow(m_hWnd));
4064 ATLASSERT(IsValidPageIndex(nPage));
4066 TCITEMEXTRA tcix = { 0 };
4067 tcix.tciheader.mask = TCIF_PARAM;
4068 m_tab.GetItem(nPage, tcix);
4070 return tcix.tvpage.hWnd;
4073 LPCTSTR GetPageTitle(int nPage) const
4075 ATLASSERT(::IsWindow(m_hWnd));
4076 ATLASSERT(IsValidPageIndex(nPage));
4078 TCITEMEXTRA tcix = { 0 };
4079 tcix.tciheader.mask = TCIF_PARAM;
4080 if(m_tab.GetItem(nPage, tcix) == FALSE)
4081 return NULL;
4083 return tcix.tvpage.lpstrTitle;
4086 bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
4088 ATLASSERT(::IsWindow(m_hWnd));
4089 ATLASSERT(IsValidPageIndex(nPage));
4091 T* pT = static_cast<T*>(this);
4093 int cchBuff = lstrlen(lpstrTitle) + 1;
4094 LPTSTR lpstrBuff = NULL;
4095 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
4096 if(lpstrBuff == NULL)
4097 return false;
4099 SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
4100 TCITEMEXTRA tcix = { 0 };
4101 tcix.tciheader.mask = TCIF_PARAM;
4102 if(m_tab.GetItem(nPage, tcix) == FALSE)
4103 return false;
4105 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4106 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
4107 if(lpstrTabText == NULL)
4108 return false;
4110 delete [] tcix.tvpage.lpstrTitle;
4112 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
4114 tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
4115 tcix.tciheader.pszText = lpstrTabText;
4116 tcix.tvpage.lpstrTitle = lpstrBuff;
4117 if(m_tab.SetItem(nPage, tcix) == FALSE)
4118 return false;
4120 pT->UpdateTitleBar();
4121 pT->UpdateMenu();
4123 return true;
4126 LPVOID GetPageData(int nPage) const
4128 ATLASSERT(::IsWindow(m_hWnd));
4129 ATLASSERT(IsValidPageIndex(nPage));
4131 TCITEMEXTRA tcix = { 0 };
4132 tcix.tciheader.mask = TCIF_PARAM;
4133 m_tab.GetItem(nPage, tcix);
4135 return tcix.tvpage.pData;
4138 LPVOID SetPageData(int nPage, LPVOID pData)
4140 ATLASSERT(::IsWindow(m_hWnd));
4141 ATLASSERT(IsValidPageIndex(nPage));
4143 TCITEMEXTRA tcix = { 0 };
4144 tcix.tciheader.mask = TCIF_PARAM;
4145 m_tab.GetItem(nPage, tcix);
4146 LPVOID pDataOld = tcix.tvpage.pData;
4148 tcix.tvpage.pData = pData;
4149 m_tab.SetItem(nPage, tcix);
4151 return pDataOld;
4154 int GetPageImage(int nPage) const
4156 ATLASSERT(::IsWindow(m_hWnd));
4157 ATLASSERT(IsValidPageIndex(nPage));
4159 TCITEMEXTRA tcix = { 0 };
4160 tcix.tciheader.mask = TCIF_IMAGE;
4161 m_tab.GetItem(nPage, tcix);
4163 return tcix.tciheader.iImage;
4166 int SetPageImage(int nPage, int nImage)
4168 ATLASSERT(::IsWindow(m_hWnd));
4169 ATLASSERT(IsValidPageIndex(nPage));
4171 TCITEMEXTRA tcix = { 0 };
4172 tcix.tciheader.mask = TCIF_IMAGE;
4173 m_tab.GetItem(nPage, tcix);
4174 int nImageOld = tcix.tciheader.iImage;
4176 tcix.tciheader.iImage = nImage;
4177 m_tab.SetItem(nPage, tcix);
4179 return nImageOld;
4182 // Operations
4183 bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
4185 return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
4188 bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
4190 ATLASSERT(::IsWindow(m_hWnd));
4191 ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage));
4193 T* pT = static_cast<T*>(this);
4195 int cchBuff = lstrlen(lpstrTitle) + 1;
4196 LPTSTR lpstrBuff = NULL;
4197 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
4198 if(lpstrBuff == NULL)
4199 return false;
4201 SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
4203 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4204 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
4205 if(lpstrTabText == NULL)
4206 return false;
4208 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
4210 SetRedraw(FALSE);
4212 TCITEMEXTRA tcix = { 0 };
4213 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
4214 tcix.tciheader.pszText = lpstrTabText;
4215 tcix.tciheader.iImage = nImage;
4216 tcix.tvpage.hWnd = hWndView;
4217 tcix.tvpage.lpstrTitle = lpstrBuff;
4218 tcix.tvpage.pData = pData;
4219 int nItem = m_tab.InsertItem(nPage, tcix);
4220 if(nItem == -1)
4222 delete [] lpstrBuff;
4223 SetRedraw(TRUE);
4224 return false;
4227 // adjust active page index, if inserted before it
4228 if(nPage <= m_nActivePage)
4229 m_nActivePage++;
4231 SetActivePage(nItem);
4232 pT->OnPageActivated(m_nActivePage);
4234 if(GetPageCount() == 1)
4235 pT->ShowTabControl(true);
4237 pT->UpdateLayout();
4239 SetRedraw(TRUE);
4240 RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
4242 return true;
4245 void RemovePage(int nPage)
4247 ATLASSERT(::IsWindow(m_hWnd));
4248 ATLASSERT(IsValidPageIndex(nPage));
4250 T* pT = static_cast<T*>(this);
4252 SetRedraw(FALSE);
4254 if(GetPageCount() == 1)
4255 pT->ShowTabControl(false);
4257 if(m_bDestroyPageOnRemove)
4258 ::DestroyWindow(GetPageHWND(nPage));
4259 else
4260 ::ShowWindow(GetPageHWND(nPage), FALSE);
4261 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
4262 delete [] lpstrTitle;
4264 ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
4266 if(m_nActivePage == nPage)
4268 m_nActivePage = -1;
4270 if(nPage > 0)
4272 SetActivePage(nPage - 1);
4274 else if(GetPageCount() > 0)
4276 SetActivePage(nPage);
4278 else
4280 SetRedraw(TRUE);
4281 Invalidate();
4282 UpdateWindow();
4283 pT->UpdateTitleBar();
4284 pT->UpdateMenu();
4287 else
4289 nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
4290 m_nActivePage = -1;
4291 SetActivePage(nPage);
4294 pT->OnPageActivated(m_nActivePage);
4297 void RemoveAllPages()
4299 ATLASSERT(::IsWindow(m_hWnd));
4301 if(GetPageCount() == 0)
4302 return;
4304 T* pT = static_cast<T*>(this);
4306 SetRedraw(FALSE);
4308 pT->ShowTabControl(false);
4310 for(int i = 0; i < GetPageCount(); i++)
4312 if(m_bDestroyPageOnRemove)
4313 ::DestroyWindow(GetPageHWND(i));
4314 else
4315 ::ShowWindow(GetPageHWND(i), FALSE);
4316 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
4317 delete [] lpstrTitle;
4319 m_tab.DeleteAllItems();
4321 m_nActivePage = -1;
4322 pT->OnPageActivated(m_nActivePage);
4324 SetRedraw(TRUE);
4325 Invalidate();
4326 UpdateWindow();
4328 pT->UpdateTitleBar();
4329 pT->UpdateMenu();
4332 int PageIndexFromHwnd(HWND hWnd) const
4334 int nIndex = -1;
4336 for(int i = 0; i < GetPageCount(); i++)
4338 if(GetPageHWND(i) == hWnd)
4340 nIndex = i;
4341 break;
4345 return nIndex;
4348 void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
4350 ATLASSERT(::IsWindow(m_hWnd));
4352 CMenuHandle menu = hMenu;
4353 T* pT = static_cast<T*>(this);
4354 pT; // avoid level 4 warning
4355 int nFirstPos = 0;
4357 // Find first menu item in our range
4358 #ifndef _WIN32_WCE
4359 for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
4361 UINT nID = menu.GetMenuItemID(nFirstPos);
4362 if((nID >= ID_WINDOW_TABFIRST && nID <= ID_WINDOW_TABLAST) || nID == ID_WINDOW_SHOWTABLIST)
4363 break;
4365 #else // CE specific
4366 for(nFirstPos = 0; ; nFirstPos++)
4368 CMenuItemInfo mii;
4369 mii.fMask = MIIM_ID;
4370 BOOL bRet = menu.GetMenuItemInfo(nFirstPos, TRUE, &mii);
4371 if(bRet == FALSE)
4372 break;
4373 if((mii.wID >= ID_WINDOW_TABFIRST && mii.wID <= ID_WINDOW_TABLAST) || mii.wID == ID_WINDOW_SHOWTABLIST)
4374 break;
4376 #endif // _WIN32_WCE
4378 // Remove all menu items for tab pages
4379 BOOL bRet = TRUE;
4380 while(bRet != FALSE)
4381 bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
4383 // Add separator if it's not already there
4384 int nPageCount = GetPageCount();
4385 if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
4387 CMenuItemInfo mii;
4388 mii.fMask = MIIM_TYPE;
4389 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
4390 if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0))
4392 menu.AppendMenu(MF_SEPARATOR);
4393 nFirstPos++;
4397 // Add menu items for all pages
4398 if(nPageCount > 0)
4400 // Append menu items for all pages
4401 const int cchPrefix = 3; // 2 digits + space
4402 nMenuItemsCount = __min(__min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
4403 ATLASSERT(nMenuItemsCount < 100); // 2 digits only
4404 if(nMenuItemsCount >= 100)
4405 nMenuItemsCount = 99;
4407 for(int i = 0; i < nMenuItemsCount; i++)
4409 LPCTSTR lpstrTitle = GetPageTitle(i);
4410 int nLen = lstrlen(lpstrTitle);
4411 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4412 LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
4413 ATLASSERT(lpstrText != NULL);
4414 if(lpstrText != NULL)
4416 LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
4417 SecureHelper::wsprintf_x(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
4418 menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
4422 // Mark active page
4423 if(bActivePageMenuItem && (m_nActivePage != -1))
4425 #ifndef _WIN32_WCE
4426 if(bActiveAsDefaultMenuItem)
4428 menu.SetMenuDefaultItem((UINT)-1, TRUE);
4429 menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
4431 else
4432 #else // CE specific
4433 bActiveAsDefaultMenuItem; // avoid level 4 warning
4434 #endif // _WIN32_WCE
4436 menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
4440 else
4442 if(bEmptyMenuItem)
4444 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
4445 menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
4448 // Remove separator if nothing else is there
4449 if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
4451 CMenuItemInfo mii;
4452 mii.fMask = MIIM_TYPE;
4453 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
4454 if((mii.fType & MFT_SEPARATOR) != 0)
4455 menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
4459 // Add "Windows..." menu item
4460 if(bWindowsMenuItem)
4461 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
4464 BOOL SubclassWindow(HWND hWnd)
4466 #if (_MSC_VER >= 1300)
4467 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
4468 #else // !(_MSC_VER >= 1300)
4469 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
4470 BOOL bRet = _baseClass::SubclassWindow(hWnd);
4471 #endif // !(_MSC_VER >= 1300)
4472 if(bRet != FALSE)
4474 T* pT = static_cast<T*>(this);
4475 pT->CreateTabControl();
4476 pT->UpdateLayout();
4479 return bRet;
4482 // Message map and handlers
4483 BEGIN_MSG_MAP(CTabViewImpl)
4484 MESSAGE_HANDLER(WM_CREATE, OnCreate)
4485 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
4486 MESSAGE_HANDLER(WM_SIZE, OnSize)
4487 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
4488 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
4489 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
4490 NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
4491 NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
4492 #ifndef _WIN32_WCE
4493 NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
4494 #endif // !_WIN32_WCE
4495 FORWARD_NOTIFICATIONS()
4496 ALT_MSG_MAP(1) // tab control
4497 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
4498 MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
4499 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
4500 MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
4501 MESSAGE_HANDLER(WM_RBUTTONUP, OnTabRButtonUp)
4502 MESSAGE_HANDLER(WM_SYSKEYDOWN, OnTabSysKeyDown)
4503 END_MSG_MAP()
4505 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4507 T* pT = static_cast<T*>(this);
4508 pT->CreateTabControl();
4510 return 0;
4513 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4515 RemoveAllPages();
4517 if(m_bDestroyImageList)
4519 CImageList il = m_tab.SetImageList(NULL);
4520 if(il.m_hImageList != NULL)
4521 il.Destroy();
4524 if(m_bInternalFont)
4526 HFONT hFont = m_tab.GetFont();
4527 m_tab.SetFont(NULL, FALSE);
4528 ::DeleteObject(hFont);
4529 m_bInternalFont = false;
4532 return 0;
4535 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4537 T* pT = static_cast<T*>(this);
4538 pT->UpdateLayout();
4539 return 0;
4542 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4544 if(m_nActivePage != -1)
4545 ::SetFocus(GetPageHWND(m_nActivePage));
4546 return 0;
4549 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4551 return m_tab.SendMessage(WM_GETFONT);
4554 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
4556 if(m_bInternalFont)
4558 HFONT hFont = m_tab.GetFont();
4559 m_tab.SetFont(NULL, FALSE);
4560 ::DeleteObject(hFont);
4561 m_bInternalFont = false;
4564 m_tab.SendMessage(WM_SETFONT, wParam, lParam);
4566 T* pT = static_cast<T*>(this);
4567 m_cyTabHeight = pT->CalcTabHeight();
4569 if((BOOL)lParam != FALSE)
4570 pT->UpdateLayout();
4572 return 0;
4575 LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
4577 SetActivePage(m_tab.GetCurSel());
4578 T* pT = static_cast<T*>(this);
4579 pT->OnPageActivated(m_nActivePage);
4581 return 0;
4584 LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
4586 // nothing to do - this just blocks all tab control
4587 // notifications from being propagated further
4588 return 0;
4591 #ifndef _WIN32_WCE
4592 LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
4594 LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
4595 if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
4597 T* pT = static_cast<T*>(this);
4598 pT->UpdateTooltipText(pTTDI);
4600 else
4602 bHandled = FALSE;
4605 return 0;
4607 #endif // !_WIN32_WCE
4609 // Tab control message handlers
4610 LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
4612 if(!m_bNoTabDrag && (m_tab.GetItemCount() > 1))
4614 m_bTabCapture = true;
4615 m_tab.SetCapture();
4617 m_ptStartDrag.x = GET_X_LPARAM(lParam);
4618 m_ptStartDrag.y = GET_Y_LPARAM(lParam);
4621 bHandled = FALSE;
4622 return 0;
4625 LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
4627 if(m_bTabCapture)
4629 if(m_bTabDrag)
4631 TCHITTESTINFO hti = { 0 };
4632 hti.pt.x = GET_X_LPARAM(lParam);
4633 hti.pt.y = GET_Y_LPARAM(lParam);
4634 int nItem = m_tab.HitTest(&hti);
4635 if(nItem != -1)
4636 MovePage(m_nActivePage, nItem);
4639 ::ReleaseCapture();
4642 bHandled = FALSE;
4643 return 0;
4646 LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
4648 if(m_bTabCapture)
4650 m_bTabCapture = false;
4652 if(m_bTabDrag)
4654 m_bTabDrag = false;
4655 T* pT = static_cast<T*>(this);
4656 pT->DrawMoveMark(-1);
4658 #ifndef _WIN32_WCE
4659 m_ilDrag.DragLeave(GetDesktopWindow());
4660 #endif // !_WIN32_WCE
4661 m_ilDrag.EndDrag();
4663 m_ilDrag.Destroy();
4664 m_ilDrag.m_hImageList = NULL;
4668 bHandled = FALSE;
4669 return 0;
4672 LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
4674 bHandled = FALSE;
4676 if(m_bTabCapture)
4678 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
4680 if(!m_bTabDrag)
4682 #ifndef _WIN32_WCE
4683 if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) ||
4684 abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG))
4685 #else // CE specific
4686 if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= 4 ||
4687 abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= 4)
4688 #endif // _WIN32_WCE
4690 T* pT = static_cast<T*>(this);
4691 pT->GenerateDragImage(m_nActivePage);
4693 int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
4694 int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
4695 m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
4696 #ifndef _WIN32_WCE
4697 POINT ptEnter = m_ptStartDrag;
4698 m_tab.ClientToScreen(&ptEnter);
4699 m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
4700 #endif // !_WIN32_WCE
4702 m_bTabDrag = true;
4706 if(m_bTabDrag)
4708 TCHITTESTINFO hti = { 0 };
4709 hti.pt = pt;
4710 int nItem = m_tab.HitTest(&hti);
4712 T* pT = static_cast<T*>(this);
4713 pT->SetMoveCursor(nItem != -1);
4715 if(m_nInsertItem != nItem)
4716 pT->DrawMoveMark(nItem);
4718 m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
4719 m_tab.ClientToScreen(&pt);
4720 m_ilDrag.DragMove(pt);
4722 bHandled = TRUE;
4726 return 0;
4729 LRESULT OnTabRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
4731 TCHITTESTINFO hti = { 0 };
4732 hti.pt.x = GET_X_LPARAM(lParam);
4733 hti.pt.y = GET_Y_LPARAM(lParam);
4734 int nItem = m_tab.HitTest(&hti);
4735 if(nItem != -1)
4737 T* pT = static_cast<T*>(this);
4738 pT->OnContextMenu(nItem, hti.pt);
4741 return 0;
4744 LRESULT OnTabSysKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
4746 bool bShift = (::GetKeyState(VK_SHIFT) < 0);
4747 if(wParam == VK_F10 && bShift)
4749 if(m_nActivePage != -1)
4751 RECT rect = { 0 };
4752 m_tab.GetItemRect(m_nActivePage, &rect);
4753 POINT pt = { rect.left, rect.bottom };
4754 T* pT = static_cast<T*>(this);
4755 pT->OnContextMenu(m_nActivePage, pt);
4758 else
4760 bHandled = FALSE;
4763 return 0;
4766 // Implementation helpers
4767 bool IsValidPageIndex(int nPage) const
4769 return (nPage >= 0 && nPage < GetPageCount());
4772 bool MovePage(int nMovePage, int nInsertBeforePage)
4774 ATLASSERT(IsValidPageIndex(nMovePage));
4775 ATLASSERT(IsValidPageIndex(nInsertBeforePage));
4777 if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
4778 return false;
4780 if(nMovePage == nInsertBeforePage)
4781 return true; // nothing to do
4783 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4784 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
4785 if(lpstrTabText == NULL)
4786 return false;
4787 TCITEMEXTRA tcix = { 0 };
4788 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
4789 tcix.tciheader.pszText = lpstrTabText;
4790 tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
4791 BOOL bRet = m_tab.GetItem(nMovePage, tcix);
4792 ATLASSERT(bRet != FALSE);
4793 if(bRet == FALSE)
4794 return false;
4796 int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
4797 int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
4798 ATLASSERT(nNewItem == nInsertItem);
4799 if(nNewItem != nInsertItem)
4801 ATLVERIFY(m_tab.DeleteItem(nNewItem));
4802 return false;
4805 if(nMovePage > nInsertBeforePage)
4806 ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
4807 else if(nMovePage < nInsertBeforePage)
4808 ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
4810 SetActivePage(nInsertBeforePage);
4811 T* pT = static_cast<T*>(this);
4812 pT->OnPageActivated(m_nActivePage);
4814 return true;
4817 // Implementation overrideables
4818 bool CreateTabControl()
4820 #ifndef _WIN32_WCE
4821 m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
4822 #else // CE specific
4823 m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, m_nTabID);
4824 #endif // _WIN32_WCE
4825 ATLASSERT(m_tab.m_hWnd != NULL);
4826 if(m_tab.m_hWnd == NULL)
4827 return false;
4829 m_tab.SetFont(AtlCreateControlFont());
4830 m_bInternalFont = true;
4832 m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
4834 T* pT = static_cast<T*>(this);
4835 m_cyTabHeight = pT->CalcTabHeight();
4837 return true;
4840 int CalcTabHeight()
4842 int nCount = m_tab.GetItemCount();
4843 TCITEMEXTRA tcix = { 0 };
4844 tcix.tciheader.mask = TCIF_TEXT;
4845 tcix.tciheader.pszText = _T("NS");
4846 int nIndex = m_tab.InsertItem(nCount, tcix);
4848 RECT rect = { 0, 0, 1000, 1000 };
4849 m_tab.AdjustRect(FALSE, &rect);
4851 RECT rcWnd = { 0, 0, 1000, rect.top };
4852 ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
4854 int nHeight = rcWnd.bottom - rcWnd.top;
4856 m_tab.DeleteItem(nIndex);
4858 return nHeight;
4861 void ShowTabControl(bool bShow)
4863 m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
4864 T* pT = static_cast<T*>(this);
4865 pT->UpdateLayout();
4868 void UpdateLayout()
4870 RECT rect = { 0 };
4871 GetClientRect(&rect);
4873 int cyOffset = 0;
4874 if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
4876 m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
4877 cyOffset = m_cyTabHeight;
4880 if(m_nActivePage != -1)
4881 ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, cyOffset, rect.right - rect.left, rect.bottom - rect.top - cyOffset, SWP_NOZORDER);
4884 void UpdateMenu()
4886 if(m_menu.m_hMenu != NULL)
4887 BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
4890 void UpdateTitleBar()
4892 if(!m_wndTitleBar.IsWindow() || m_lpstrTitleBarBase == NULL)
4893 return; // nothing to do
4895 if(m_nActivePage != -1)
4897 T* pT = static_cast<T*>(this);
4898 LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
4899 LPCTSTR lpstrDivider = pT->GetTitleDividerText();
4900 int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
4901 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4902 LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
4903 ATLASSERT(lpstrPageTitle != NULL);
4904 if(lpstrPageTitle != NULL)
4906 pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
4907 SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, lpstrDivider);
4908 SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
4910 else
4912 lpstrPageTitle = m_lpstrTitleBarBase;
4915 m_wndTitleBar.SetWindowText(lpstrPageTitle);
4917 else
4919 m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
4923 void DrawMoveMark(int nItem)
4925 T* pT = static_cast<T*>(this);
4927 if(m_nInsertItem != -1)
4929 RECT rect = { 0 };
4930 pT->GetMoveMarkRect(rect);
4931 m_tab.InvalidateRect(&rect);
4934 m_nInsertItem = nItem;
4936 if(m_nInsertItem != -1)
4938 CClientDC dc(m_tab.m_hWnd);
4940 RECT rect = { 0 };
4941 pT->GetMoveMarkRect(rect);
4943 CPen pen;
4944 pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
4945 CBrush brush;
4946 brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
4948 HPEN hPenOld = dc.SelectPen(pen);
4949 HBRUSH hBrushOld = dc.SelectBrush(brush);
4951 int x = rect.left;
4952 int y = rect.top;
4953 POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
4954 dc.Polygon(ptsTop, 3);
4956 y = rect.bottom - 1;
4957 POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
4958 dc.Polygon(ptsBottom, 3);
4960 dc.SelectPen(hPenOld);
4961 dc.SelectBrush(hBrushOld);
4965 void GetMoveMarkRect(RECT& rect) const
4967 m_tab.GetClientRect(&rect);
4969 RECT rcItem = { 0 };
4970 m_tab.GetItemRect(m_nInsertItem, &rcItem);
4972 if(m_nInsertItem <= m_nActivePage)
4974 rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
4975 rect.right = rcItem.left + m_cxMoveMark / 2;
4977 else
4979 rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
4980 rect.right = rcItem.right + m_cxMoveMark / 2;
4984 void SetMoveCursor(bool bCanMove)
4986 ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
4989 void GenerateDragImage(int nItem)
4991 ATLASSERT(IsValidPageIndex(nItem));
4993 #ifndef _WIN32_WCE
4994 RECT rcItem = { 0 };
4995 m_tab.GetItemRect(nItem, &rcItem);
4996 ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
4997 #else // CE specific
4998 nItem; // avoid level 4 warning
4999 RECT rcItem = { 0, 0, 40, 20 };
5000 #endif // _WIN32_WCE
5002 ATLASSERT(m_ilDrag.m_hImageList == NULL);
5003 m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
5005 CClientDC dc(m_hWnd);
5006 CDC dcMem;
5007 dcMem.CreateCompatibleDC(dc);
5008 ATLASSERT(dcMem.m_hDC != NULL);
5009 dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
5011 CBitmap bmp;
5012 bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
5013 ATLASSERT(bmp.m_hBitmap != NULL);
5015 HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
5016 #ifndef _WIN32_WCE
5017 m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
5018 #else // CE specific
5019 dcMem.Rectangle(&rcItem);
5020 #endif // _WIN32_WCE
5021 dcMem.SelectBitmap(hBmpOld);
5023 ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
5026 void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
5028 if(lstrlen(lpstrTitle) >= cchShortTitle)
5030 LPCTSTR lpstrEllipsis = _T("...");
5031 int cchEllipsis = lstrlen(lpstrEllipsis);
5032 SecureHelper::strncpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
5033 SecureHelper::strcat_x(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
5035 else
5037 SecureHelper::strcpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle);
5041 #ifndef _WIN32_WCE
5042 void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
5044 ATLASSERT(pTTDI != NULL);
5045 pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
5047 #endif // !_WIN32_WCE
5049 // Text for menu items and title bar - override to provide different strings
5050 static LPCTSTR GetEmptyListText()
5052 return _T("(Empty)");
5055 static LPCTSTR GetWindowsMenuItemText()
5057 return _T("&Windows...");
5060 static LPCTSTR GetTitleDividerText()
5062 return _T(" - ");
5065 // Notifications - override to provide different behavior
5066 void OnPageActivated(int nPage)
5068 NMHDR nmhdr = { 0 };
5069 nmhdr.hwndFrom = m_hWnd;
5070 nmhdr.idFrom = nPage;
5071 nmhdr.code = TBVN_PAGEACTIVATED;
5072 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
5075 void OnContextMenu(int nPage, POINT pt)
5077 m_tab.ClientToScreen(&pt);
5079 TBVCONTEXTMENUINFO cmi = { 0 };
5080 cmi.hdr.hwndFrom = m_hWnd;
5081 cmi.hdr.idFrom = nPage;
5082 cmi.hdr.code = TBVN_CONTEXTMENU;
5083 cmi.pt = pt;
5084 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&cmi);
5088 class CTabView : public CTabViewImpl<CTabView>
5090 public:
5091 DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
5094 }; // namespace WTL
5096 #endif // __ATLCTRLX_H__