1 // Windows Template Library - WTL version 8.0
\r
2 // Copyright (C) Microsoft Corporation. All rights reserved.
\r
4 // This file is a part of the Windows Template Library.
\r
5 // The use and distribution terms for this software are covered by the
\r
6 // Common Public License 1.0 (http://opensource.org/osi3.0/licenses/cpl1.0.php)
\r
7 // which can be found in the file CPL.TXT at the root of this distribution.
\r
8 // By using this software in any fashion, you are agreeing to be bound by
\r
9 // the terms of this license. You must not remove this notice, or
\r
10 // any other, from this software.
\r
12 #ifndef __ATLCTRLX_H__
\r
13 #define __ATLCTRLX_H__
\r
18 #error ATL requires C++ compilation (use a .cpp suffix)
\r
21 #ifndef __ATLAPP_H__
\r
22 #error atlctrlx.h requires atlapp.h to be included first
\r
25 #ifndef __ATLCTRLS_H__
\r
26 #error atlctrlx.h requires atlctrls.h to be included first
\r
29 #ifndef WM_UPDATEUISTATE
\r
30 #define WM_UPDATEUISTATE 0x0128
\r
31 #endif // !WM_UPDATEUISTATE
\r
34 ///////////////////////////////////////////////////////////////////////////////
\r
35 // Classes in this file:
\r
37 // CBitmapButtonImpl<T, TBase, TWinTraits>
\r
39 // CCheckListViewCtrlImpl<T, TBase, TWinTraits>
\r
40 // CCheckListViewCtrl
\r
41 // CHyperLinkImpl<T, TBase, TWinTraits>
\r
44 // CCustomWaitCursor
\r
45 // CMultiPaneStatusBarCtrlImpl<T, TBase>
\r
46 // CMultiPaneStatusBarCtrl
\r
47 // CPaneContainerImpl<T, TBase, TWinTraits>
\r
49 // CSortListViewImpl<T>
\r
50 // CSortListViewCtrlImpl<T, TBase, TWinTraits>
\r
51 // CSortListViewCtrl
\r
52 // CTabViewImpl<T, TBase, TWinTraits>
\r
58 ///////////////////////////////////////////////////////////////////////////////
\r
59 // CBitmapButton - bitmap button implementation
\r
63 // bitmap button extended styles
\r
64 #define BMPBTN_HOVER 0x00000001
\r
65 #define BMPBTN_AUTO3D_SINGLE 0x00000002
\r
66 #define BMPBTN_AUTO3D_DOUBLE 0x00000004
\r
67 #define BMPBTN_AUTOSIZE 0x00000008
\r
68 #define BMPBTN_SHAREIMAGELISTS 0x00000010
\r
69 #define BMPBTN_AUTOFIRE 0x00000020
\r
71 template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
\r
72 class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits>
\r
75 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
\r
81 _nImageFocusOrHover,
\r
89 ID_TIMER_FIRST = 1000,
\r
90 ID_TIMER_REPEAT = 1001
\r
93 // Bitmap button specific extended styles
\r
94 DWORD m_dwExtendedStyle;
\r
96 CImageList m_ImageList;
\r
97 int m_nImage[_nImageCount];
\r
100 LPTSTR m_lpstrToolTipText;
\r
103 unsigned m_fMouseOver:1;
\r
104 unsigned m_fFocus:1;
\r
105 unsigned m_fPressed:1;
\r
108 // Constructor/Destructor
\r
109 CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
\r
110 m_ImageList(hImageList), m_dwExtendedStyle(dwExtendedStyle),
\r
111 m_lpstrToolTipText(NULL),
\r
112 m_fMouseOver(0), m_fFocus(0), m_fPressed(0)
\r
114 m_nImage[_nImageNormal] = -1;
\r
115 m_nImage[_nImagePushed] = -1;
\r
116 m_nImage[_nImageFocusOrHover] = -1;
\r
117 m_nImage[_nImageDisabled] = -1;
\r
120 ~CBitmapButtonImpl()
\r
122 if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
\r
123 m_ImageList.Destroy();
\r
124 delete [] m_lpstrToolTipText;
\r
127 // overridden to provide proper initialization
\r
128 BOOL SubclassWindow(HWND hWnd)
\r
130 #if (_MSC_VER >= 1300)
\r
131 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
\r
132 #else // !(_MSC_VER >= 1300)
\r
133 typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
\r
134 BOOL bRet = _baseClass::SubclassWindow(hWnd);
\r
135 #endif // !(_MSC_VER >= 1300)
\r
142 DWORD GetBitmapButtonExtendedStyle() const
\r
144 return m_dwExtendedStyle;
\r
147 DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
\r
149 DWORD dwPrevStyle = m_dwExtendedStyle;
\r
151 m_dwExtendedStyle = dwExtendedStyle;
\r
153 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
\r
154 return dwPrevStyle;
\r
157 HIMAGELIST GetImageList() const
\r
159 return m_ImageList;
\r
162 HIMAGELIST SetImageList(HIMAGELIST hImageList)
\r
164 HIMAGELIST hImageListPrev = m_ImageList;
\r
165 m_ImageList = hImageList;
\r
166 if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd))
\r
168 return hImageListPrev;
\r
171 int GetToolTipTextLength() const
\r
173 return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
\r
176 bool GetToolTipText(LPTSTR lpstrText, int nLength) const
\r
178 ATLASSERT(lpstrText != NULL);
\r
179 if(m_lpstrToolTipText == NULL)
\r
182 errno_t nRet = SecureHelper::strncpy_x(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
\r
184 return (nRet == 0 || nRet == STRUNCATE);
\r
187 bool SetToolTipText(LPCTSTR lpstrText)
\r
189 if(m_lpstrToolTipText != NULL)
\r
191 delete [] m_lpstrToolTipText;
\r
192 m_lpstrToolTipText = NULL;
\r
195 if(lpstrText == NULL)
\r
197 if(m_tip.IsWindow())
\r
198 m_tip.Activate(FALSE);
\r
202 int cchLen = lstrlen(lpstrText) + 1;
\r
203 ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
\r
204 if(m_lpstrToolTipText == NULL)
\r
207 SecureHelper::strcpy_x(m_lpstrToolTipText, cchLen, lpstrText);
\r
208 if(m_tip.IsWindow())
\r
210 m_tip.Activate(TRUE);
\r
211 m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
\r
218 void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
\r
221 m_nImage[_nImageNormal] = nNormal;
\r
223 m_nImage[_nImagePushed] = nPushed;
\r
224 if(nFocusOrHover != -1)
\r
225 m_nImage[_nImageFocusOrHover] = nFocusOrHover;
\r
226 if(nDisabled != -1)
\r
227 m_nImage[_nImageDisabled] = nDisabled;
\r
232 ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL);
\r
235 if(!m_ImageList.GetIconSize(cx, cy))
\r
237 return ResizeClient(cx, cy);
\r
241 void DoPaint(CDCHandle dc)
\r
243 ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set
\r
244 ATLASSERT(m_nImage[0] != -1); // main bitmap must be set
\r
246 // set bitmap according to the current button state
\r
248 bool bHover = IsHoverMode();
\r
249 if(!IsWindowEnabled())
\r
250 nImage = m_nImage[_nImageDisabled];
\r
251 else if(m_fPressed == 1)
\r
252 nImage = m_nImage[_nImagePushed];
\r
253 else if((!bHover && m_fFocus == 1) || (bHover && m_fMouseOver == 1))
\r
254 nImage = m_nImage[_nImageFocusOrHover];
\r
255 if(nImage == -1) // not there, use default one
\r
256 nImage = m_nImage[_nImageNormal];
\r
258 // draw the button image
\r
260 if((m_fPressed == 1) && ((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0) && (m_nImage[_nImagePushed] == -1))
\r
262 m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
\r
264 // draw 3D border if required
\r
265 if((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0)
\r
268 GetClientRect(&rect);
\r
270 if(m_fPressed == 1)
\r
271 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
\r
272 else if(!bHover || m_fMouseOver == 1)
\r
273 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
\r
275 if(!bHover && m_fFocus == 1)
\r
277 ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
\r
278 dc.DrawFocusRect(&rect);
\r
283 // Message map and handlers
\r
284 BEGIN_MSG_MAP(CBitmapButtonImpl)
\r
285 MESSAGE_HANDLER(WM_CREATE, OnCreate)
\r
286 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
\r
287 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
\r
288 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
\r
289 MESSAGE_HANDLER(WM_PAINT, OnPaint)
\r
290 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
\r
291 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
\r
292 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
\r
293 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
\r
294 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
\r
295 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
\r
296 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
\r
297 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
\r
298 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
\r
299 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
\r
300 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
\r
301 MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
\r
302 MESSAGE_HANDLER(WM_TIMER, OnTimer)
\r
303 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
\r
306 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
313 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
315 if(m_tip.IsWindow())
\r
317 m_tip.DestroyWindow();
\r
318 m_tip.m_hWnd = NULL;
\r
324 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
\r
326 MSG msg = { m_hWnd, uMsg, wParam, lParam };
\r
327 if(m_tip.IsWindow())
\r
328 m_tip.RelayEvent(&msg);
\r
333 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
335 return 1; // no background needed
\r
338 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
340 T* pT = static_cast<T*>(this);
\r
343 pT->DoPaint((HDC)wParam);
\r
347 CPaintDC dc(m_hWnd);
\r
348 pT->DoPaint(dc.m_hDC);
\r
353 LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
355 m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
\r
362 LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
368 lRet = DefWindowProc(uMsg, wParam, lParam);
\r
369 if(::GetCapture() == m_hWnd)
\r
375 if((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0)
\r
379 if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
\r
380 nElapse += nDelay * 250; // all milli-seconds
\r
381 SetTimer(ID_TIMER_FIRST, nElapse);
\r
386 LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
390 lRet = DefWindowProc(uMsg, wParam, lParam);
\r
391 if(::GetCapture() != m_hWnd)
\r
393 if(m_fPressed == 0)
\r
402 LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
405 bool bHover = IsHoverMode();
\r
407 lRet = DefWindowProc(uMsg, wParam, lParam);
\r
408 if(::GetCapture() == m_hWnd)
\r
410 if(bHover && m_fPressed == 1)
\r
411 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
\r
412 ::ReleaseCapture();
\r
417 LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
419 if(m_fPressed == 1)
\r
429 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
437 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
\r
439 if(::GetCapture() == m_hWnd)
\r
441 POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
\r
442 ClientToScreen(&ptCursor);
\r
444 GetWindowRect(&rect);
\r
445 unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
\r
446 if(m_fPressed != uPressed)
\r
448 m_fPressed = uPressed;
\r
453 else if(IsHoverMode() && m_fMouseOver == 0)
\r
458 StartTrackMouseLeave();
\r
464 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
466 if(m_fMouseOver == 1)
\r
475 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
\r
477 if(wParam == VK_SPACE && IsHoverMode())
\r
478 return 0; // ignore if in hover mode
\r
479 if(wParam == VK_SPACE && m_fPressed == 0)
\r
489 LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
\r
491 if(wParam == VK_SPACE && IsHoverMode())
\r
492 return 0; // ignore if in hover mode
\r
493 if(wParam == VK_SPACE && m_fPressed == 1)
\r
503 LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
505 ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
\r
506 switch(wParam) // timer ID
\r
508 case ID_TIMER_FIRST:
\r
509 KillTimer(ID_TIMER_FIRST);
\r
510 if(m_fPressed == 1)
\r
512 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
\r
515 if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
\r
516 nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated
\r
517 SetTimer(ID_TIMER_REPEAT, nElapse);
\r
520 case ID_TIMER_REPEAT:
\r
521 if(m_fPressed == 1)
\r
522 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
\r
523 else if(::GetCapture() != m_hWnd)
\r
524 KillTimer(ID_TIMER_REPEAT);
\r
526 default: // not our timer
\r
532 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
534 // If the control is subclassed or superclassed, this message can cause
\r
535 // repainting without WM_PAINT. We don't use this state, so just do nothing.
\r
542 // We need this style to prevent Windows from painting the button
\r
543 ModifyStyle(0, BS_OWNERDRAW);
\r
545 // create a tool tip
\r
546 m_tip.Create(m_hWnd);
\r
547 ATLASSERT(m_tip.IsWindow());
\r
548 if(m_tip.IsWindow() && m_lpstrToolTipText != NULL)
\r
550 m_tip.Activate(TRUE);
\r
551 m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
\r
554 if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0)
\r
558 BOOL StartTrackMouseLeave()
\r
560 TRACKMOUSEEVENT tme = { 0 };
\r
561 tme.cbSize = sizeof(tme);
\r
562 tme.dwFlags = TME_LEAVE;
\r
563 tme.hwndTrack = m_hWnd;
\r
564 return _TrackMouseEvent(&tme);
\r
567 bool IsHoverMode() const
\r
569 return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
\r
574 class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
\r
577 DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
\r
579 CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
\r
580 CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
\r
584 #endif // !_WIN32_WCE
\r
587 ///////////////////////////////////////////////////////////////////////////////
\r
588 // CCheckListCtrlView - list view control with check boxes
\r
590 template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
\r
591 class CCheckListViewCtrlImplTraits
\r
594 static DWORD GetWndStyle(DWORD dwStyle)
\r
596 return (dwStyle == 0) ? t_dwStyle : dwStyle;
\r
599 static DWORD GetWndExStyle(DWORD dwExStyle)
\r
601 return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
\r
604 static DWORD GetExtendedLVStyle()
\r
606 return t_dwExListViewStyle;
\r
610 typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT> CCheckListViewCtrlTraits;
\r
612 template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
\r
613 class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
\r
616 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
\r
619 static DWORD GetExtendedLVStyle()
\r
621 return TWinTraits::GetExtendedLVStyle();
\r
625 BOOL SubclassWindow(HWND hWnd)
\r
627 #if (_MSC_VER >= 1300)
\r
628 BOOL bRet = ATL::CWindowImplBaseT< TBase, TWinTraits>::SubclassWindow(hWnd);
\r
629 #else // !(_MSC_VER >= 1300)
\r
630 typedef ATL::CWindowImplBaseT< TBase, TWinTraits> _baseClass;
\r
631 BOOL bRet = _baseClass::SubclassWindow(hWnd);
\r
632 #endif // !(_MSC_VER >= 1300)
\r
635 T* pT = static_cast<T*>(this);
\r
637 ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
\r
638 SetExtendedListViewStyle(pT->GetExtendedLVStyle());
\r
643 void CheckSelectedItems(int nCurrItem)
\r
645 // first check if this item is selected
\r
646 LVITEM lvi = { 0 };
\r
647 lvi.iItem = nCurrItem;
\r
649 lvi.mask = LVIF_STATE;
\r
650 lvi.stateMask = LVIS_SELECTED;
\r
652 // if item is not selected, don't do anything
\r
653 if(!(lvi.state & LVIS_SELECTED))
\r
655 // new check state will be reverse of the current state,
\r
656 BOOL bCheck = !GetCheckState(nCurrItem);
\r
659 while((nItem = GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
\r
661 if(nItem != nCurrItem)
\r
662 SetCheckState(nItem, bCheck);
\r
668 BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
\r
669 MESSAGE_HANDLER(WM_CREATE, OnCreate)
\r
670 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
\r
671 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
\r
672 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
\r
675 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
677 // first let list view control initialize everything
\r
678 LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
\r
679 T* pT = static_cast<T*>(this);
\r
681 ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
\r
682 SetExtendedListViewStyle(pT->GetExtendedLVStyle());
\r
686 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
\r
688 POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
\r
689 LVHITTESTINFO lvh = { 0 };
\r
691 if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0)
\r
693 T* pT = static_cast<T*>(this);
\r
694 pT->CheckSelectedItems(lvh.iItem);
\r
700 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
\r
702 if(wParam == VK_SPACE)
\r
704 int nCurrItem = GetNextItem(-1, LVNI_FOCUSED);
\r
705 if(nCurrItem != -1 && ::GetKeyState(VK_CONTROL) >= 0)
\r
707 T* pT = static_cast<T*>(this);
\r
708 pT->CheckSelectedItems(nCurrItem);
\r
716 class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
\r
719 DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
\r
723 ///////////////////////////////////////////////////////////////////////////////
\r
724 // CHyperLink - hyper link control implementation
\r
726 #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
\r
727 __declspec(selectany) struct
\r
729 enum { cxWidth = 32, cyHeight = 32 };
\r
732 unsigned char arrANDPlane[cxWidth * cyHeight / 8];
\r
733 unsigned char arrXORPlane[cxWidth * cyHeight / 8];
\r
734 } _AtlHyperLink_CursorData =
\r
738 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF,
\r
739 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF,
\r
740 0xF0, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF,
\r
741 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF,
\r
742 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF,
\r
743 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
\r
744 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
\r
745 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
\r
748 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
\r
749 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x06, 0xD8, 0x00, 0x00,
\r
750 0x06, 0xDA, 0x00, 0x00, 0x06, 0xDB, 0x00, 0x00, 0x67, 0xFB, 0x00, 0x00, 0x77, 0xFF, 0x00, 0x00,
\r
751 0x37, 0xFF, 0x00, 0x00, 0x17, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00,
\r
752 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00,
\r
753 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
\r
754 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
\r
755 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
\r
758 #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
\r
760 #define HLINK_UNDERLINED 0x00000000
\r
761 #define HLINK_NOTUNDERLINED 0x00000001
\r
762 #define HLINK_UNDERLINEHOVER 0x00000002
\r
763 #define HLINK_COMMANDBUTTON 0x00000004
\r
764 #define HLINK_NOTIFYBUTTON 0x0000000C
\r
765 #define HLINK_USETAGS 0x00000010
\r
766 #define HLINK_USETAGSBOLD 0x00000030
\r
767 #define HLINK_NOTOOLTIP 0x00000040
\r
770 // - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
\r
771 // - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
\r
773 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
\r
774 class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
\r
777 LPTSTR m_lpstrLabel;
\r
778 LPTSTR m_lpstrHyperLink;
\r
782 HFONT m_hFontNormal;
\r
786 CToolTipCtrl m_tip;
\r
787 #endif // !_WIN32_WCE
\r
789 COLORREF m_clrLink;
\r
790 COLORREF m_clrVisited;
\r
792 DWORD m_dwExtendedStyle; // Hyper Link specific extended styles
\r
794 bool m_bPaintLabel:1;
\r
797 bool m_bInternalLinkFont:1;
\r
800 // Constructor/Destructor
\r
801 CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) :
\r
802 m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
\r
803 m_hCursor(NULL), m_hFont(NULL), m_hFontNormal(NULL),
\r
804 m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
\r
805 m_dwExtendedStyle(dwExtendedStyle),
\r
806 m_bPaintLabel(true), m_bVisited(false),
\r
807 m_bHover(false), m_bInternalLinkFont(false)
\r
809 ::SetRectEmpty(&m_rcLink);
\r
814 delete [] m_lpstrLabel;
\r
815 delete [] m_lpstrHyperLink;
\r
816 if(m_bInternalLinkFont && m_hFont != NULL)
\r
817 ::DeleteObject(m_hFont);
\r
818 #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
\r
819 // It was created, not loaded, so we have to destroy it
\r
820 if(m_hCursor != NULL)
\r
821 ::DestroyCursor(m_hCursor);
\r
822 #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
\r
826 DWORD GetHyperLinkExtendedStyle() const
\r
828 return m_dwExtendedStyle;
\r
831 DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
\r
833 DWORD dwPrevStyle = m_dwExtendedStyle;
\r
835 m_dwExtendedStyle = dwExtendedStyle;
\r
837 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
\r
838 return dwPrevStyle;
\r
841 bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
\r
843 if(m_lpstrLabel == NULL)
\r
845 ATLASSERT(lpstrBuffer != NULL);
\r
846 if(nLength <= lstrlen(m_lpstrLabel))
\r
849 SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrLabel);
\r
854 bool SetLabel(LPCTSTR lpstrLabel)
\r
856 delete [] m_lpstrLabel;
\r
857 m_lpstrLabel = NULL;
\r
858 int cchLen = lstrlen(lpstrLabel) + 1;
\r
859 ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
\r
860 if(m_lpstrLabel == NULL)
\r
863 SecureHelper::strcpy_x(m_lpstrLabel, cchLen, lpstrLabel);
\r
864 T* pT = static_cast<T*>(this);
\r
865 pT->CalcLabelRect();
\r
868 SetWindowText(lpstrLabel); // Set this for accessibility
\r
873 bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
\r
875 if(m_lpstrHyperLink == NULL)
\r
877 ATLASSERT(lpstrBuffer != NULL);
\r
878 if(nLength <= lstrlen(m_lpstrHyperLink))
\r
881 SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrHyperLink);
\r
886 bool SetHyperLink(LPCTSTR lpstrLink)
\r
888 delete [] m_lpstrHyperLink;
\r
889 m_lpstrHyperLink = NULL;
\r
890 int cchLen = lstrlen(lpstrLink) + 1;
\r
891 ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
\r
892 if(m_lpstrHyperLink == NULL)
\r
895 SecureHelper::strcpy_x(m_lpstrHyperLink, cchLen, lpstrLink);
\r
896 if(m_lpstrLabel == NULL)
\r
898 T* pT = static_cast<T*>(this);
\r
899 pT->CalcLabelRect();
\r
902 if(m_tip.IsWindow())
\r
904 m_tip.Activate(TRUE);
\r
905 m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
\r
907 #endif // !_WIN32_WCE
\r
911 HFONT GetLinkFont() const
\r
916 void SetLinkFont(HFONT hFont)
\r
918 if(m_bInternalLinkFont && m_hFont != NULL)
\r
920 ::DeleteObject(m_hFont);
\r
921 m_bInternalLinkFont = false;
\r
926 int GetIdealHeight() const
\r
928 ATLASSERT(::IsWindow(m_hWnd));
\r
929 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
\r
934 CClientDC dc(m_hWnd);
\r
936 GetClientRect(&rect);
\r
937 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
\r
938 RECT rcText = rect;
\r
939 dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
\r
940 dc.SelectFont(m_hFont);
\r
941 RECT rcLink = rect;
\r
942 dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
\r
943 dc.SelectFont(hFontOld);
\r
944 return max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
\r
947 bool GetIdealSize(SIZE& size) const
\r
949 int cx = 0, cy = 0;
\r
950 bool bRet = GetIdealSize(cx, cy);
\r
959 bool GetIdealSize(int& cx, int& cy) const
\r
961 ATLASSERT(::IsWindow(m_hWnd));
\r
962 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
\r
967 CClientDC dc(m_hWnd);
\r
968 RECT rcClient = { 0 };
\r
969 GetClientRect(&rcClient);
\r
970 RECT rcAll = rcClient;
\r
974 // find tags and label parts
\r
975 LPTSTR lpstrLeft = NULL;
\r
977 LPTSTR lpstrLink = NULL;
\r
979 LPTSTR lpstrRight = NULL;
\r
982 const T* pT = static_cast<const T*>(this);
\r
983 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
\r
985 // get label part rects
\r
986 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
\r
987 RECT rcLeft = rcClient;
\r
988 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
\r
990 dc.SelectFont(m_hFont);
\r
991 RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
\r
992 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
\r
994 dc.SelectFont(m_hFontNormal);
\r
995 RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
\r
996 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
\r
998 dc.SelectFont(hFontOld);
\r
1000 int cyMax = max(rcLeft.bottom, max(rcLink.bottom, rcRight.bottom));
\r
1001 ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
\r
1005 HFONT hOldFont = NULL;
\r
1006 if(m_hFont != NULL)
\r
1007 hOldFont = dc.SelectFont(m_hFont);
\r
1008 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
\r
1009 DWORD dwStyle = GetStyle();
\r
1010 int nDrawStyle = DT_LEFT;
\r
1011 if (dwStyle & SS_CENTER)
\r
1012 nDrawStyle = DT_CENTER;
\r
1013 else if (dwStyle & SS_RIGHT)
\r
1014 nDrawStyle = DT_RIGHT;
\r
1015 dc.DrawText(lpstrText, -1, &rcAll, nDrawStyle | DT_WORDBREAK | DT_CALCRECT);
\r
1016 if(m_hFont != NULL)
\r
1017 dc.SelectFont(hOldFont);
\r
1018 if (dwStyle & SS_CENTER)
\r
1020 int dx = (rcClient.right - rcAll.right) / 2;
\r
1021 ::OffsetRect(&rcAll, dx, 0);
\r
1023 else if (dwStyle & SS_RIGHT)
\r
1025 int dx = rcClient.right - rcAll.right;
\r
1026 ::OffsetRect(&rcAll, dx, 0);
\r
1030 cx = rcAll.right - rcAll.left;
\r
1031 cy = rcAll.bottom - rcAll.top;
\r
1036 // for command buttons only
\r
1037 bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
\r
1039 ATLASSERT(IsCommandButton());
\r
1040 return GetHyperLink(lpstrBuffer, nLength);
\r
1043 bool SetToolTipText(LPCTSTR lpstrToolTipText)
\r
1045 ATLASSERT(IsCommandButton());
\r
1046 return SetHyperLink(lpstrToolTipText);
\r
1050 BOOL SubclassWindow(HWND hWnd)
\r
1052 ATLASSERT(m_hWnd == NULL);
\r
1053 ATLASSERT(::IsWindow(hWnd));
\r
1054 #if (_MSC_VER >= 1300)
\r
1055 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
\r
1056 #else // !(_MSC_VER >= 1300)
\r
1057 typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
\r
1058 BOOL bRet = _baseClass::SubclassWindow(hWnd);
\r
1059 #endif // !(_MSC_VER >= 1300)
\r
1062 T* pT = static_cast<T*>(this);
\r
1070 ATLASSERT(::IsWindow(m_hWnd));
\r
1072 if(IsNotifyButton())
\r
1074 NMHDR nmhdr = { m_hWnd, GetDlgCtrlID(), NM_CLICK };
\r
1075 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
\r
1077 else if(IsCommandButton())
\r
1079 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
\r
1083 ATLASSERT(m_lpstrHyperLink != NULL);
\r
1084 #ifndef _WIN32_WCE
\r
1085 DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
\r
1086 bRet = (dwRet > 32);
\r
1087 #else // CE specific
\r
1088 SHELLEXECUTEINFO shExeInfo = { sizeof(SHELLEXECUTEINFO), 0, 0, L"open", m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL, 0, 0, 0, 0, 0, 0, 0 };
\r
1089 ::ShellExecuteEx(&shExeInfo);
\r
1090 DWORD_PTR dwRet = (DWORD_PTR)shExeInfo.hInstApp;
\r
1091 bRet = (dwRet == 0) || (dwRet > 32);
\r
1092 #endif // _WIN32_WCE
\r
1096 m_bVisited = true;
\r
1103 // Message map and handlers
\r
1104 BEGIN_MSG_MAP(CHyperLinkImpl)
\r
1105 MESSAGE_HANDLER(WM_CREATE, OnCreate)
\r
1106 #ifndef _WIN32_WCE
\r
1107 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
\r
1108 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
\r
1109 #endif // !_WIN32_WCE
\r
1110 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
\r
1111 MESSAGE_HANDLER(WM_PAINT, OnPaint)
\r
1112 #ifndef _WIN32_WCE
\r
1113 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
\r
1114 #endif // !_WIN32_WCE
\r
1115 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
\r
1116 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
\r
1117 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
\r
1118 #ifndef _WIN32_WCE
\r
1119 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
\r
1120 #endif // !_WIN32_WCE
\r
1121 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
\r
1122 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
\r
1123 MESSAGE_HANDLER(WM_CHAR, OnChar)
\r
1124 MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
\r
1125 MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
\r
1126 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
\r
1127 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
\r
1128 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
\r
1129 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
\r
1130 MESSAGE_HANDLER(WM_SIZE, OnSize)
\r
1133 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1135 T* pT = static_cast<T*>(this);
\r
1140 #ifndef _WIN32_WCE
\r
1141 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
1143 if(m_tip.IsWindow())
\r
1145 m_tip.DestroyWindow();
\r
1146 m_tip.m_hWnd = NULL;
\r
1152 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
\r
1154 MSG msg = { m_hWnd, uMsg, wParam, lParam };
\r
1155 if(m_tip.IsWindow() && IsUsingToolTip())
\r
1156 m_tip.RelayEvent(&msg);
\r
1160 #endif // !_WIN32_WCE
\r
1162 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1164 return 1; // no background painting needed (we do it all during WM_PAINT)
\r
1167 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
\r
1169 if(!m_bPaintLabel)
\r
1175 T* pT = static_cast<T*>(this);
\r
1176 if(wParam != NULL)
\r
1178 pT->DoEraseBackground((HDC)wParam);
\r
1179 pT->DoPaint((HDC)wParam);
\r
1183 CPaintDC dc(m_hWnd);
\r
1184 pT->DoEraseBackground(dc.m_hDC);
\r
1185 pT->DoPaint(dc.m_hDC);
\r
1191 LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
1200 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
\r
1202 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
\r
1203 if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
\r
1205 ::SetCursor(m_hCursor);
\r
1206 if(IsUnderlineHover())
\r
1211 InvalidateRect(&m_rcLink);
\r
1213 #ifndef _WIN32_WCE
\r
1214 StartTrackMouseLeave();
\r
1215 #endif // !_WIN32_WCE
\r
1221 if(IsUnderlineHover())
\r
1226 InvalidateRect(&m_rcLink);
\r
1235 #ifndef _WIN32_WCE
\r
1236 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1238 if(IsUnderlineHover() && m_bHover)
\r
1241 InvalidateRect(&m_rcLink);
\r
1246 #endif // !_WIN32_WCE
\r
1248 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
\r
1250 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
\r
1251 if(::PtInRect(&m_rcLink, pt))
\r
1259 LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
\r
1261 if(GetCapture() == m_hWnd)
\r
1264 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
\r
1265 if(::PtInRect(&m_rcLink, pt))
\r
1267 T* pT = static_cast<T*>(this);
\r
1274 LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1276 if(wParam == VK_RETURN || wParam == VK_SPACE)
\r
1278 T* pT = static_cast<T*>(this);
\r
1284 LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1286 return DLGC_WANTCHARS;
\r
1289 LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
1291 POINT pt = { 0, 0 };
\r
1292 GetCursorPos(&pt);
\r
1293 ScreenToClient(&pt);
\r
1294 if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
\r
1302 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1309 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1311 return (LRESULT)m_hFontNormal;
\r
1314 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
1316 m_hFontNormal = (HFONT)wParam;
\r
1325 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1327 // If the control is subclassed or superclassed, this message can cause
\r
1328 // repainting without WM_PAINT. We don't use this state, so just do nothing.
\r
1332 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
1334 T* pT = static_cast<T*>(this);
\r
1335 pT->CalcLabelRect();
\r
1343 ATLASSERT(::IsWindow(m_hWnd));
\r
1345 // Check if we should paint a label
\r
1346 const int cchBuff = 8;
\r
1347 TCHAR szBuffer[cchBuff] = { 0 };
\r
1348 if(::GetClassName(m_hWnd, szBuffer, cchBuff))
\r
1350 if(lstrcmpi(szBuffer, _T("static")) == 0)
\r
1352 ModifyStyle(0, SS_NOTIFY); // we need this
\r
1353 DWORD dwStyle = GetStyle() & 0x000000FF;
\r
1354 #ifndef _WIN32_WCE
\r
1355 if(dwStyle == SS_ICON || dwStyle == SS_BLACKRECT || dwStyle == SS_GRAYRECT ||
\r
1356 dwStyle == SS_WHITERECT || dwStyle == SS_BLACKFRAME || dwStyle == SS_GRAYFRAME ||
\r
1357 dwStyle == SS_WHITEFRAME || dwStyle == SS_OWNERDRAW ||
\r
1358 dwStyle == SS_BITMAP || dwStyle == SS_ENHMETAFILE)
\r
1359 #else // CE specific
\r
1360 if(dwStyle == SS_ICON || dwStyle == SS_BITMAP)
\r
1361 #endif // _WIN32_WCE
\r
1362 m_bPaintLabel = false;
\r
1366 // create or load a cursor
\r
1367 #if (WINVER >= 0x0500) || defined(_WIN32_WCE)
\r
1368 m_hCursor = ::LoadCursor(NULL, IDC_HAND);
\r
1370 m_hCursor = ::CreateCursor(ModuleHelper::GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane);
\r
1372 ATLASSERT(m_hCursor != NULL);
\r
1377 ATL::CWindow wnd = GetParent();
\r
1378 m_hFontNormal = wnd.GetFont();
\r
1379 if(m_hFontNormal == NULL)
\r
1380 m_hFontNormal = (HFONT)::GetStockObject(SYSTEM_FONT);
\r
1381 if(m_hFontNormal != NULL && m_hFont == NULL)
\r
1383 LOGFONT lf = { 0 };
\r
1384 CFontHandle font = m_hFontNormal;
\r
1385 font.GetLogFont(&lf);
\r
1386 if(IsUsingTagsBold())
\r
1387 lf.lfWeight = FW_BOLD;
\r
1388 else if(!IsNotUnderlined())
\r
1389 lf.lfUnderline = TRUE;
\r
1390 m_hFont = ::CreateFontIndirect(&lf);
\r
1391 m_bInternalLinkFont = true;
\r
1392 ATLASSERT(m_hFont != NULL);
\r
1396 #ifndef _WIN32_WCE
\r
1397 // create a tool tip
\r
1398 m_tip.Create(m_hWnd);
\r
1399 ATLASSERT(m_tip.IsWindow());
\r
1400 #endif // !_WIN32_WCE
\r
1402 // set label (defaults to window text)
\r
1403 if(m_lpstrLabel == NULL)
\r
1405 int nLen = GetWindowTextLength();
\r
1408 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
1409 LPTSTR lpstrText = buff.Allocate(nLen + 1);
\r
1410 ATLASSERT(lpstrText != NULL);
\r
1411 if((lpstrText != NULL) && (GetWindowText(lpstrText, nLen + 1) > 0))
\r
1412 SetLabel(lpstrText);
\r
1416 T* pT = static_cast<T*>(this);
\r
1417 pT->CalcLabelRect();
\r
1419 // set hyperlink (defaults to label), or just activate tool tip if already set
\r
1420 if(m_lpstrHyperLink == NULL && !IsCommandButton())
\r
1422 if(m_lpstrLabel != NULL)
\r
1423 SetHyperLink(m_lpstrLabel);
\r
1425 #ifndef _WIN32_WCE
\r
1428 m_tip.Activate(TRUE);
\r
1429 m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
\r
1431 #endif // !_WIN32_WCE
\r
1433 // set link colors
\r
1437 LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
\r
1440 const int cchValue = 12;
\r
1441 TCHAR szValue[cchValue] = { 0 };
\r
1442 #if (_ATL_VER >= 0x0700)
\r
1443 ULONG ulCount = cchValue;
\r
1444 lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
\r
1446 DWORD dwCount = cchValue * sizeof(TCHAR);
\r
1447 lRet = rk.QueryValue(szValue, _T("Anchor Color"), &dwCount);
\r
1451 COLORREF clr = pT->_ParseColorString(szValue);
\r
1452 ATLASSERT(clr != CLR_INVALID);
\r
1453 if(clr != CLR_INVALID)
\r
1457 #if (_ATL_VER >= 0x0700)
\r
1458 ulCount = cchValue;
\r
1459 lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
\r
1461 dwCount = cchValue * sizeof(TCHAR);
\r
1462 lRet = rk.QueryValue(szValue, _T("Anchor Color Visited"), &dwCount);
\r
1466 COLORREF clr = pT->_ParseColorString(szValue);
\r
1467 ATLASSERT(clr != CLR_INVALID);
\r
1468 if(clr != CLR_INVALID)
\r
1469 m_clrVisited = clr;
\r
1475 static COLORREF _ParseColorString(LPTSTR lpstr)
\r
1477 int c[3] = { -1, -1, -1 };
\r
1479 for(int i = 0; i < 2; i++)
\r
1481 for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
\r
1486 c[i] = T::_xttoi(lpstr);
\r
1492 return CLR_INVALID;
\r
1494 if(*lpstr == _T('\0'))
\r
1495 return CLR_INVALID;
\r
1496 c[2] = T::_xttoi(lpstr);
\r
1498 return RGB(c[0], c[1], c[2]);
\r
1501 bool CalcLabelRect()
\r
1503 if(!::IsWindow(m_hWnd))
\r
1505 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
\r
1508 CClientDC dc(m_hWnd);
\r
1509 RECT rcClient = { 0 };
\r
1510 GetClientRect(&rcClient);
\r
1511 m_rcLink = rcClient;
\r
1512 if(!m_bPaintLabel)
\r
1517 // find tags and label parts
\r
1518 LPTSTR lpstrLeft = NULL;
\r
1520 LPTSTR lpstrLink = NULL;
\r
1522 LPTSTR lpstrRight = NULL;
\r
1525 T* pT = static_cast<T*>(this);
\r
1526 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
\r
1527 ATLASSERT(lpstrLink != NULL);
\r
1528 ATLASSERT(cchLink > 0);
\r
1530 // get label part rects
\r
1531 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
\r
1533 RECT rcLeft = rcClient;
\r
1534 if(lpstrLeft != NULL)
\r
1535 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
\r
1537 dc.SelectFont(m_hFont);
\r
1538 RECT rcLink = rcClient;
\r
1539 if(lpstrLeft != NULL)
\r
1540 rcLink.left = rcLeft.right;
\r
1541 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
\r
1543 dc.SelectFont(hFontOld);
\r
1545 m_rcLink = rcLink;
\r
1549 HFONT hOldFont = NULL;
\r
1550 if(m_hFont != NULL)
\r
1551 hOldFont = dc.SelectFont(m_hFont);
\r
1552 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
\r
1553 DWORD dwStyle = GetStyle();
\r
1554 int nDrawStyle = DT_LEFT;
\r
1555 if (dwStyle & SS_CENTER)
\r
1556 nDrawStyle = DT_CENTER;
\r
1557 else if (dwStyle & SS_RIGHT)
\r
1558 nDrawStyle = DT_RIGHT;
\r
1559 dc.DrawText(lpstrText, -1, &m_rcLink, nDrawStyle | DT_WORDBREAK | DT_CALCRECT);
\r
1560 if(m_hFont != NULL)
\r
1561 dc.SelectFont(hOldFont);
\r
1562 if (dwStyle & SS_CENTER)
\r
1564 int dx = (rcClient.right - m_rcLink.right) / 2;
\r
1565 ::OffsetRect(&m_rcLink, dx, 0);
\r
1567 else if (dwStyle & SS_RIGHT)
\r
1569 int dx = rcClient.right - m_rcLink.right;
\r
1570 ::OffsetRect(&m_rcLink, dx, 0);
\r
1577 void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
\r
1583 lpstrRight = NULL;
\r
1586 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
\r
1587 int cchText = lstrlen(lpstrText);
\r
1588 bool bOutsideLink = true;
\r
1589 for(int i = 0; i < cchText; i++)
\r
1591 if(lpstrText[i] != _T('<'))
\r
1596 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
\r
1600 lpstrLeft = lpstrText;
\r
1603 lpstrLink = &lpstrText[i + 3];
\r
1604 bOutsideLink = false;
\r
1609 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
\r
1611 cchLink = i - 3 - cchLeft;
\r
1612 if(lpstrText[i + 4] != 0)
\r
1614 lpstrRight = &lpstrText[i + 4];
\r
1615 cchRight = cchText - (i + 4);
\r
1624 void DoEraseBackground(CDCHandle dc)
\r
1626 HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd);
\r
1627 if(hBrush != NULL)
\r
1629 RECT rect = { 0 };
\r
1630 GetClientRect(&rect);
\r
1631 dc.FillRect(&rect, hBrush);
\r
1635 void DoPaint(CDCHandle dc)
\r
1639 // find tags and label parts
\r
1640 LPTSTR lpstrLeft = NULL;
\r
1642 LPTSTR lpstrLink = NULL;
\r
1644 LPTSTR lpstrRight = NULL;
\r
1647 T* pT = static_cast<T*>(this);
\r
1648 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
\r
1650 // get label part rects
\r
1651 RECT rcClient = { 0 };
\r
1652 GetClientRect(&rcClient);
\r
1654 dc.SetBkMode(TRANSPARENT);
\r
1655 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
\r
1657 if(lpstrLeft != NULL)
\r
1658 dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | DT_WORDBREAK);
\r
1660 COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
\r
1661 if(m_hFont != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
\r
1662 dc.SelectFont(m_hFont);
\r
1664 dc.SelectFont(m_hFontNormal);
\r
1666 dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | DT_WORDBREAK);
\r
1668 dc.SetTextColor(clrOld);
\r
1669 dc.SelectFont(m_hFontNormal);
\r
1670 if(lpstrRight != NULL)
\r
1672 RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
\r
1673 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | DT_WORDBREAK);
\r
1676 if(GetFocus() == m_hWnd)
\r
1677 dc.DrawFocusRect(&m_rcLink);
\r
1679 dc.SelectFont(hFontOld);
\r
1683 dc.SetBkMode(TRANSPARENT);
\r
1684 COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
\r
1686 HFONT hFontOld = NULL;
\r
1687 if(m_hFont != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
\r
1688 hFontOld = dc.SelectFont(m_hFont);
\r
1690 hFontOld = dc.SelectFont(m_hFontNormal);
\r
1692 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
\r
1694 DWORD dwStyle = GetStyle();
\r
1695 int nDrawStyle = DT_LEFT;
\r
1696 if (dwStyle & SS_CENTER)
\r
1697 nDrawStyle = DT_CENTER;
\r
1698 else if (dwStyle & SS_RIGHT)
\r
1699 nDrawStyle = DT_RIGHT;
\r
1701 dc.DrawText(lpstrText, -1, &m_rcLink, nDrawStyle | DT_WORDBREAK);
\r
1703 if(GetFocus() == m_hWnd)
\r
1704 dc.DrawFocusRect(&m_rcLink);
\r
1706 dc.SetTextColor(clrOld);
\r
1707 dc.SelectFont(hFontOld);
\r
1711 #ifndef _WIN32_WCE
\r
1712 BOOL StartTrackMouseLeave()
\r
1714 TRACKMOUSEEVENT tme = { 0 };
\r
1715 tme.cbSize = sizeof(tme);
\r
1716 tme.dwFlags = TME_LEAVE;
\r
1717 tme.hwndTrack = m_hWnd;
\r
1718 return _TrackMouseEvent(&tme);
\r
1720 #endif // !_WIN32_WCE
\r
1722 // Implementation helpers
\r
1723 bool IsUnderlined() const
\r
1725 return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
\r
1728 bool IsNotUnderlined() const
\r
1730 return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
\r
1733 bool IsUnderlineHover() const
\r
1735 return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
\r
1738 bool IsCommandButton() const
\r
1740 return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
\r
1743 bool IsNotifyButton() const
\r
1745 return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
\r
1748 bool IsUsingTags() const
\r
1750 return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
\r
1753 bool IsUsingTagsBold() const
\r
1755 return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
\r
1758 bool IsUsingToolTip() const
\r
1760 return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
\r
1763 static int _xttoi(const TCHAR* nptr)
\r
1765 #ifndef _ATL_MIN_CRT
\r
1766 return _ttoi(nptr);
\r
1767 #else // _ATL_MIN_CRT
\r
1768 while(*nptr == _T(' ')) // skip spaces
\r
1771 int c = (int)(_TUCHAR)*nptr++;
\r
1772 int sign = c; // save sign indication
\r
1773 if (c == _T('-') || c == _T('+'))
\r
1774 c = (int)(_TUCHAR)*nptr++; // skip sign
\r
1777 while((TCHAR)c >= _T('0') && (TCHAR)c <= _T('9'))
\r
1779 total = 10 * total + ((TCHAR)c - _T('0')); // accumulate digit
\r
1780 c = (int)(_TUCHAR)*nptr++; // get next char
\r
1783 // return result, negated if necessary
\r
1784 return ((TCHAR)sign != _T('-')) ? total : -total;
\r
1785 #endif // _ATL_MIN_CRT
\r
1790 class CHyperLink : public CHyperLinkImpl<CHyperLink>
\r
1793 DECLARE_WND_CLASS(_T("WTL_HyperLink"))
\r
1797 ///////////////////////////////////////////////////////////////////////////////
\r
1798 // CWaitCursor - displays a wait cursor
\r
1804 HCURSOR m_hWaitCursor;
\r
1805 HCURSOR m_hOldCursor;
\r
1808 // Constructor/destructor
\r
1809 CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
\r
1811 HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
\r
1812 m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
\r
1813 ATLASSERT(m_hWaitCursor != NULL);
\r
1829 m_hOldCursor = ::SetCursor(m_hWaitCursor);
\r
1838 ::SetCursor(m_hOldCursor);
\r
1845 ///////////////////////////////////////////////////////////////////////////////
\r
1846 // CCustomWaitCursor - for custom and animated cursors
\r
1848 class CCustomWaitCursor : public CWaitCursor
\r
1851 // Constructor/destructor
\r
1852 CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) :
\r
1853 CWaitCursor(false, IDC_WAIT, true)
\r
1855 if(hInstance == NULL)
\r
1856 hInstance = ModuleHelper::GetResourceInstance();
\r
1857 m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
\r
1863 ~CCustomWaitCursor()
\r
1866 #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
\r
1867 ::DestroyCursor(m_hWaitCursor);
\r
1868 #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
\r
1873 ///////////////////////////////////////////////////////////////////////////////
\r
1874 // CMultiPaneStatusBarCtrl - Status Bar with multiple panes
\r
1876 template <class T, class TBase = CStatusBarCtrl>
\r
1877 class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase >
\r
1880 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
\r
1883 enum { m_cxPaneMargin = 3 };
\r
1888 // Constructor/destructor
\r
1889 CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL)
\r
1892 ~CMultiPaneStatusBarCtrlImpl()
\r
1894 delete [] m_pPane;
\r
1898 HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
\r
1900 #if (_MSC_VER >= 1300)
\r
1901 return ATL::CWindowImpl< T, TBase >::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
\r
1902 #else // !(_MSC_VER >= 1300)
\r
1903 typedef ATL::CWindowImpl< T, TBase > _baseClass;
\r
1904 return _baseClass::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
\r
1905 #endif // !(_MSC_VER >= 1300)
\r
1908 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)
\r
1910 const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
\r
1911 TCHAR szText[cchMax];
\r
1913 ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
\r
1914 return Create(hWndParent, szText, dwStyle, nID);
\r
1917 BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true)
\r
1919 ATLASSERT(::IsWindow(m_hWnd));
\r
1920 ATLASSERT(nPanes > 0);
\r
1922 m_nPanes = nPanes;
\r
1923 delete [] m_pPane;
\r
1926 ATLTRY(m_pPane = new int[nPanes]);
\r
1927 ATLASSERT(m_pPane != NULL);
\r
1928 if(m_pPane == NULL)
\r
1931 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
1932 int* pPanesPos = buff.Allocate(nPanes);
\r
1933 ATLASSERT(pPanesPos != NULL);
\r
1934 if(pPanesPos == NULL)
\r
1937 SecureHelper::memcpy_x(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int));
\r
1939 // get status bar DC and set font
\r
1940 CClientDC dc(m_hWnd);
\r
1941 HFONT hOldFont = dc.SelectFont(GetFont());
\r
1943 // get status bar borders
\r
1944 int arrBorders[3] = { 0 };
\r
1945 GetBorders(arrBorders);
\r
1947 const int cchBuff = 128;
\r
1948 TCHAR szBuff[cchBuff] = { 0 };
\r
1949 SIZE size = { 0, 0 };
\r
1950 int cxLeft = arrBorders[0];
\r
1952 // calculate right edge of each part
\r
1953 for(int i = 0; i < nPanes; i++)
\r
1955 if(pPanes[i] == ID_DEFAULT_PANE)
\r
1957 // make very large, will be resized later
\r
1958 pPanesPos[i] = INT_MAX / 2;
\r
1962 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
\r
1963 dc.GetTextExtent(szBuff, lstrlen(szBuff), &size);
\r
1964 T* pT = static_cast<T*>(this);
\r
1966 pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin;
\r
1968 cxLeft = pPanesPos[i];
\r
1971 BOOL bRet = SetParts(nPanes, pPanesPos);
\r
1973 if(bRet && bSetText)
\r
1975 for(int i = 0; i < nPanes; i++)
\r
1977 if(pPanes[i] != ID_DEFAULT_PANE)
\r
1979 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
\r
1980 SetPaneText(m_pPane[i], szBuff);
\r
1985 dc.SelectFont(hOldFont);
\r
1989 bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const
\r
1991 ATLASSERT(::IsWindow(m_hWnd));
\r
1992 int nIndex = GetPaneIndexFromID(nPaneID);
\r
1996 int nLength = GetTextLength(nIndex, pnType);
\r
1997 if(pcchLength != NULL)
\r
1998 *pcchLength = nLength;
\r
2003 BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const
\r
2005 ATLASSERT(::IsWindow(m_hWnd));
\r
2006 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2010 int nLength = GetText(nIndex, lpstrText, pnType);
\r
2011 if(pcchLength != NULL)
\r
2012 *pcchLength = nLength;
\r
2017 BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0)
\r
2019 ATLASSERT(::IsWindow(m_hWnd));
\r
2020 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2024 return SetText(nIndex, lpstrText, nType);
\r
2027 BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const
\r
2029 ATLASSERT(::IsWindow(m_hWnd));
\r
2030 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2034 return GetRect(nIndex, lpRect);
\r
2037 BOOL SetPaneWidth(int nPaneID, int cxWidth)
\r
2039 ATLASSERT(::IsWindow(m_hWnd));
\r
2040 ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one
\r
2041 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2045 // get pane positions
\r
2046 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
2047 int* pPanesPos = buff.Allocate(m_nPanes);
\r
2048 if(pPanesPos == NULL)
\r
2050 GetParts(m_nPanes, pPanesPos);
\r
2051 // calculate offset
\r
2052 int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]);
\r
2053 int cxOff = cxWidth - cxPaneWidth;
\r
2054 // find variable width pane
\r
2055 int nDef = m_nPanes;
\r
2056 for(int i = 0; i < m_nPanes; i++)
\r
2058 if(m_pPane[i] == ID_DEFAULT_PANE)
\r
2065 if(nIndex < nDef) // before default pane
\r
2067 for(int i = nIndex; i < nDef; i++)
\r
2068 pPanesPos[i] += cxOff;
\r
2071 else // after default one
\r
2073 for(int i = nDef; i < nIndex; i++)
\r
2074 pPanesPos[i] -= cxOff;
\r
2076 // set pane postions
\r
2077 return SetParts(m_nPanes, pPanesPos);
\r
2080 #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
\r
2081 BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const
\r
2083 ATLASSERT(::IsWindow(m_hWnd));
\r
2084 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2088 GetTipText(nIndex, lpstrText, nSize);
\r
2092 BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText)
\r
2094 ATLASSERT(::IsWindow(m_hWnd));
\r
2095 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2099 SetTipText(nIndex, lpstrText);
\r
2102 #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
\r
2104 #if ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
\r
2105 BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const
\r
2107 ATLASSERT(::IsWindow(m_hWnd));
\r
2108 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2112 hIcon = GetIcon(nIndex);
\r
2116 BOOL SetPaneIcon(int nPaneID, HICON hIcon)
\r
2118 ATLASSERT(::IsWindow(m_hWnd));
\r
2119 int nIndex = GetPaneIndexFromID(nPaneID);
\r
2123 return SetIcon(nIndex, hIcon);
\r
2125 #endif // ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
\r
2127 // Message map and handlers
\r
2128 BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >)
\r
2129 MESSAGE_HANDLER(WM_SIZE, OnSize)
\r
2132 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
2134 LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
\r
2135 if(wParam != SIZE_MINIMIZED && m_nPanes > 0)
\r
2137 T* pT = static_cast<T*>(this);
\r
2138 pT->UpdatePanesLayout();
\r
2144 BOOL UpdatePanesLayout()
\r
2146 // get pane positions
\r
2147 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
2148 int* pPanesPos = buff.Allocate(m_nPanes);
\r
2149 ATLASSERT(pPanesPos != NULL);
\r
2150 if(pPanesPos == NULL)
\r
2152 int nRet = GetParts(m_nPanes, pPanesPos);
\r
2153 ATLASSERT(nRet == m_nPanes);
\r
2154 if(nRet != m_nPanes)
\r
2156 // calculate offset
\r
2157 RECT rcClient = { 0 };
\r
2158 GetClientRect(&rcClient);
\r
2159 int cxOff = rcClient.right - pPanesPos[m_nPanes - 1];
\r
2160 #ifndef _WIN32_WCE
\r
2161 // Move panes left if size grip box is present
\r
2162 if((GetStyle() & SBARS_SIZEGRIP) != 0)
\r
2163 cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE);
\r
2164 #endif // !_WIN32_WCE
\r
2165 // find variable width pane
\r
2167 for(i = 0; i < m_nPanes; i++)
\r
2169 if(m_pPane[i] == ID_DEFAULT_PANE)
\r
2172 // resize all panes from the variable one to the right
\r
2173 if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1]))
\r
2175 for(; i < m_nPanes; i++)
\r
2176 pPanesPos[i] += cxOff;
\r
2178 // set pane postions
\r
2179 return SetParts(m_nPanes, pPanesPos);
\r
2182 int GetPaneIndexFromID(int nPaneID) const
\r
2184 for(int i = 0; i < m_nPanes; i++)
\r
2186 if(m_pPane[i] == nPaneID)
\r
2190 return -1; // not found
\r
2194 class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl<CMultiPaneStatusBarCtrl>
\r
2197 DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName())
\r
2201 ///////////////////////////////////////////////////////////////////////////////
\r
2202 // CPaneContainer - provides header with title and close button for panes
\r
2204 // pane container extended styles
\r
2205 #define PANECNT_NOCLOSEBUTTON 0x00000001
\r
2206 #define PANECNT_VERTICAL 0x00000002
\r
2207 #define PANECNT_FLATBORDER 0x00000004
\r
2208 #define PANECNT_NOBORDER 0x00000008
\r
2210 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
\r
2211 class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
\r
2214 DECLARE_WND_CLASS_EX(NULL, 0, -1)
\r
2220 m_cxyTextOffset = 4,
\r
2221 m_cxyBtnOffset = 1,
\r
2227 m_cxyBtnAddTB = 7,
\r
2229 m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset,
\r
2231 m_xBtnImageLeft = 6,
\r
2232 m_yBtnImageTop = 5,
\r
2233 m_xBtnImageRight = 12,
\r
2234 m_yBtnImageBottom = 11,
\r
2236 m_nCloseBtnID = ID_PANE_CLOSE
\r
2240 CToolBarCtrl m_tb;
\r
2241 ATL::CWindow m_wndClient;
\r
2243 TCHAR m_szTitle[m_cchTitle];
\r
2244 DWORD m_dwExtendedStyle; // Pane container specific extended styles
\r
2248 CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0)
\r
2254 DWORD GetPaneContainerExtendedStyle() const
\r
2256 return m_dwExtendedStyle;
\r
2259 DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
\r
2261 DWORD dwPrevStyle = m_dwExtendedStyle;
\r
2263 m_dwExtendedStyle = dwExtendedStyle;
\r
2265 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
\r
2266 if(m_hWnd != NULL)
\r
2268 T* pT = static_cast<T*>(this);
\r
2269 bool bUpdate = false;
\r
2271 if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
\r
2273 pT->CreateCloseButton();
\r
2276 else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
\r
2278 pT->DestroyCloseButton();
\r
2282 if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
\r
2288 if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) !=
\r
2289 (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border
\r
2295 pT->UpdateLayout();
\r
2297 return dwPrevStyle;
\r
2300 HWND GetClient() const
\r
2302 return m_wndClient;
\r
2305 HWND SetClient(HWND hWndClient)
\r
2307 HWND hWndOldClient = m_wndClient;
\r
2308 m_wndClient = hWndClient;
\r
2309 if(m_hWnd != NULL)
\r
2311 T* pT = static_cast<T*>(this);
\r
2312 pT->UpdateLayout();
\r
2314 return hWndOldClient;
\r
2317 BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const
\r
2319 ATLASSERT(lpstrTitle != NULL);
\r
2321 errno_t nRet = SecureHelper::strncpy_x(lpstrTitle, cchLength, m_szTitle, _TRUNCATE);
\r
2323 return (nRet == 0 || nRet == STRUNCATE);
\r
2326 BOOL SetTitle(LPCTSTR lpstrTitle)
\r
2328 ATLASSERT(lpstrTitle != NULL);
\r
2330 errno_t nRet = SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
\r
2331 bool bRet = (nRet == 0 || nRet == STRUNCATE);
\r
2332 if(bRet && m_hWnd != NULL)
\r
2334 T* pT = static_cast<T*>(this);
\r
2335 pT->UpdateLayout();
\r
2341 int GetTitleLength() const
\r
2343 return lstrlen(m_szTitle);
\r
2347 HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
\r
2348 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
\r
2350 if(lpstrTitle != NULL)
\r
2351 SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
\r
2352 #if (_MSC_VER >= 1300)
\r
2353 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
\r
2354 #else // !(_MSC_VER >= 1300)
\r
2355 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
\r
2356 return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
\r
2357 #endif // !(_MSC_VER >= 1300)
\r
2360 HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
\r
2361 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
\r
2363 if(uTitleID != 0U)
\r
2364 ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
\r
2365 #if (_MSC_VER >= 1300)
\r
2366 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
\r
2367 #else // !(_MSC_VER >= 1300)
\r
2368 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
\r
2369 return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
\r
2370 #endif // !(_MSC_VER >= 1300)
\r
2373 BOOL EnableCloseButton(BOOL bEnable)
\r
2375 ATLASSERT(::IsWindow(m_hWnd));
\r
2376 T* pT = static_cast<T*>(this);
\r
2377 pT; // avoid level 4 warning
\r
2378 return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE;
\r
2381 void UpdateLayout()
\r
2383 RECT rcClient = { 0 };
\r
2384 GetClientRect(&rcClient);
\r
2385 T* pT = static_cast<T*>(this);
\r
2386 pT->UpdateLayout(rcClient.right, rcClient.bottom);
\r
2389 // Message map and handlers
\r
2390 BEGIN_MSG_MAP(CPaneContainerImpl)
\r
2391 MESSAGE_HANDLER(WM_CREATE, OnCreate)
\r
2392 MESSAGE_HANDLER(WM_SIZE, OnSize)
\r
2393 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
\r
2394 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
\r
2395 MESSAGE_HANDLER(WM_PAINT, OnPaint)
\r
2396 #ifndef _WIN32_WCE
\r
2397 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
\r
2398 #endif // !_WIN32_WCE
\r
2399 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
\r
2400 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
\r
2401 FORWARD_NOTIFICATIONS()
\r
2404 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
2406 T* pT = static_cast<T*>(this);
\r
2409 if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)
\r
2410 pT->CreateCloseButton();
\r
2415 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
\r
2417 T* pT = static_cast<T*>(this);
\r
2418 pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
\r
2422 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
2424 if(m_wndClient.m_hWnd != NULL)
\r
2425 m_wndClient.SetFocus();
\r
2429 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
2431 return 1; // no background needed
\r
2434 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
2436 T* pT = static_cast<T*>(this);
\r
2437 if(wParam != NULL)
\r
2439 pT->DrawPaneTitle((HDC)wParam);
\r
2441 if(m_wndClient.m_hWnd == NULL) // no client window
\r
2442 pT->DrawPane((HDC)wParam);
\r
2446 CPaintDC dc(m_hWnd);
\r
2447 pT->DrawPaneTitle(dc.m_hDC);
\r
2449 if(m_wndClient.m_hWnd == NULL) // no client window
\r
2450 pT->DrawPane(dc.m_hDC);
\r
2456 LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
\r
2458 if(m_tb.m_hWnd == NULL)
\r
2464 T* pT = static_cast<T*>(this);
\r
2466 LPNMHDR lpnmh = (LPNMHDR)lParam;
\r
2469 // pass toolbar custom draw notifications to the base class
\r
2470 if(lpnmh->code == NM_CUSTOMDRAW && lpnmh->hwndFrom == m_tb.m_hWnd)
\r
2471 lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
\r
2472 #ifndef _WIN32_WCE
\r
2473 // tooltip notifications come with the tooltip window handle and button ID,
\r
2474 // pass them to the parent if we don't handle them
\r
2475 else if(lpnmh->code == TTN_GETDISPINFO && lpnmh->idFrom == pT->m_nCloseBtnID)
\r
2476 bHandled = pT->GetToolTipText(lpnmh);
\r
2477 #endif // !_WIN32_WCE
\r
2478 // only let notifications not from the toolbar go to the parent
\r
2479 else if(lpnmh->hwndFrom != m_tb.m_hWnd && lpnmh->idFrom != pT->m_nCloseBtnID)
\r
2485 LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
\r
2487 // if command comes from the close button, substitute HWND of the pane container instead
\r
2488 if(m_tb.m_hWnd != NULL && (HWND)lParam == m_tb.m_hWnd)
\r
2489 return ::SendMessage(GetParent(), WM_COMMAND, wParam, (LPARAM)m_hWnd);
\r
2495 // Custom draw overrides
\r
2496 DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
\r
2498 return CDRF_NOTIFYITEMDRAW; // we need per-item notifications
\r
2501 DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
\r
2503 CDCHandle dc = lpNMCustomDraw->hdc;
\r
2504 #if (_WIN32_IE >= 0x0400)
\r
2505 RECT& rc = lpNMCustomDraw->rc;
\r
2506 #else // !(_WIN32_IE >= 0x0400)
\r
2508 m_tb.GetItemRect(0, &rc);
\r
2509 #endif // !(_WIN32_IE >= 0x0400)
\r
2511 dc.FillRect(&rc, COLOR_3DFACE);
\r
2513 return CDRF_NOTIFYPOSTPAINT;
\r
2516 DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
\r
2518 CDCHandle dc = lpNMCustomDraw->hdc;
\r
2519 #if (_WIN32_IE >= 0x0400)
\r
2520 RECT& rc = lpNMCustomDraw->rc;
\r
2521 #else // !(_WIN32_IE >= 0x0400)
\r
2523 m_tb.GetItemRect(0, &rc);
\r
2524 #endif // !(_WIN32_IE >= 0x0400)
\r
2526 RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 };
\r
2527 ::OffsetRect(&rcImage, rc.left, rc.top);
\r
2528 T* pT = static_cast<T*>(this);
\r
2530 if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0)
\r
2532 RECT rcShadow = rcImage;
\r
2533 ::OffsetRect(&rcShadow, 1, 1);
\r
2535 pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT));
\r
2536 pT->DrawButtonImage(dc, rcShadow, pen1);
\r
2538 pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW));
\r
2539 pT->DrawButtonImage(dc, rcImage, pen2);
\r
2543 if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0)
\r
2544 ::OffsetRect(&rcImage, 1, 1);
\r
2546 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
\r
2547 pT->DrawButtonImage(dc, rcImage, pen);
\r
2550 return CDRF_DODEFAULT; // continue with the default item painting
\r
2553 // Implementation - overrideable methods
\r
2554 void UpdateLayout(int cxWidth, int cyHeight)
\r
2556 ATLASSERT(::IsWindow(m_hWnd));
\r
2557 RECT rect = { 0 };
\r
2561 ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight);
\r
2562 if(m_tb.m_hWnd != NULL)
\r
2563 m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
\r
2565 if(m_wndClient.m_hWnd != NULL)
\r
2566 m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER);
\r
2568 rect.right = cxWidth;
\r
2572 ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader);
\r
2573 if(m_tb.m_hWnd != NULL)
\r
2574 m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
\r
2576 if(m_wndClient.m_hWnd != NULL)
\r
2577 m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER);
\r
2579 rect.bottom = cyHeight;
\r
2582 InvalidateRect(&rect);
\r
2585 void CreateCloseButton()
\r
2587 ATLASSERT(m_tb.m_hWnd == NULL);
\r
2588 // create toolbar for the "x" button
\r
2589 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);
\r
2590 ATLASSERT(m_tb.IsWindow());
\r
2592 if(m_tb.m_hWnd != NULL)
\r
2594 T* pT = static_cast<T*>(this);
\r
2595 pT; // avoid level 4 warning
\r
2597 m_tb.SetButtonStructSize();
\r
2599 TBBUTTON tbbtn = { 0 };
\r
2600 tbbtn.idCommand = pT->m_nCloseBtnID;
\r
2601 tbbtn.fsState = TBSTATE_ENABLED;
\r
2602 tbbtn.fsStyle = TBSTYLE_BUTTON;
\r
2603 m_tb.AddButtons(1, &tbbtn);
\r
2605 m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB);
\r
2606 m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB);
\r
2609 m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOACTIVATE);
\r
2611 m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
\r
2615 void DestroyCloseButton()
\r
2617 if(m_tb.m_hWnd != NULL)
\r
2618 m_tb.DestroyWindow();
\r
2623 T* pT = static_cast<T*>(this);
\r
2624 CFontHandle font = pT->GetTitleFont();
\r
2625 LOGFONT lf = { 0 };
\r
2626 font.GetLogFont(lf);
\r
2629 m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder;
\r
2633 int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset;
\r
2634 int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset;
\r
2635 m_cxyHeader = max(cyFont, cyBtn);
\r
2639 HFONT GetTitleFont() const
\r
2641 return AtlGetDefaultGuiFont();
\r
2644 #ifndef _WIN32_WCE
\r
2645 BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
\r
2649 #endif // !_WIN32_WCE
\r
2651 void DrawPaneTitle(CDCHandle dc)
\r
2653 RECT rect = { 0 };
\r
2654 GetClientRect(&rect);
\r
2656 UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST;
\r
2659 rect.right = rect.left + m_cxyHeader;
\r
2660 uBorder |= BF_BOTTOM;
\r
2664 rect.bottom = rect.top + m_cxyHeader;
\r
2665 uBorder |= BF_RIGHT;
\r
2668 if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0)
\r
2670 if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0)
\r
2671 uBorder |= BF_FLAT;
\r
2672 dc.DrawEdge(&rect, EDGE_ETCHED, uBorder);
\r
2674 dc.FillRect(&rect, COLOR_3DFACE);
\r
2676 if(!IsVertical()) // draw title only for horizontal pane container
\r
2678 dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
\r
2679 dc.SetBkMode(TRANSPARENT);
\r
2680 T* pT = static_cast<T*>(this);
\r
2681 HFONT hFontOld = dc.SelectFont(pT->GetTitleFont());
\r
2682 rect.left += m_cxyTextOffset;
\r
2683 rect.right -= m_cxyTextOffset;
\r
2684 if(m_tb.m_hWnd != NULL)
\r
2685 rect.right -= m_cxToolBar;;
\r
2686 #ifndef _WIN32_WCE
\r
2687 dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
\r
2688 #else // CE specific
\r
2689 dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER);
\r
2690 #endif // _WIN32_WCE
\r
2691 dc.SelectFont(hFontOld);
\r
2695 // called only if pane is empty
\r
2696 void DrawPane(CDCHandle dc)
\r
2698 RECT rect = { 0 };
\r
2699 GetClientRect(&rect);
\r
2701 rect.left += m_cxyHeader;
\r
2703 rect.top += m_cxyHeader;
\r
2704 if((GetExStyle() & WS_EX_CLIENTEDGE) == 0)
\r
2705 dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
\r
2706 dc.FillRect(&rect, COLOR_APPWORKSPACE);
\r
2709 // drawing helper - draws "x" button image
\r
2710 void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
\r
2712 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
\r
2713 HPEN hPenOld = dc.SelectPen(hPen);
\r
2715 dc.MoveTo(rcImage.left, rcImage.top);
\r
2716 dc.LineTo(rcImage.right, rcImage.bottom);
\r
2717 dc.MoveTo(rcImage.left + 1, rcImage.top);
\r
2718 dc.LineTo(rcImage.right + 1, rcImage.bottom);
\r
2720 dc.MoveTo(rcImage.left, rcImage.bottom - 1);
\r
2721 dc.LineTo(rcImage.right, rcImage.top - 1);
\r
2722 dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
\r
2723 dc.LineTo(rcImage.right + 1, rcImage.top - 1);
\r
2725 dc.SelectPen(hPenOld);
\r
2726 #else // (_WIN32_WCE < 400)
\r
2729 // no support for the "x" button image
\r
2730 #endif // (_WIN32_WCE < 400)
\r
2733 bool IsVertical() const
\r
2735 return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
\r
2739 class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
\r
2742 DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
\r
2746 ///////////////////////////////////////////////////////////////////////////////
\r
2747 // CSortListViewCtrl - implements sorting for a listview control
\r
2749 // sort listview extended styles
\r
2750 #define SORTLV_USESHELLBITMAPS 0x00000001
\r
2752 // Notification sent to parent when sort column is changed by user clicking header.
\r
2753 #define SLVN_SORTCHANGED LVN_LAST
\r
2755 // A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification
\r
2756 typedef struct tagNMSORTLISTVIEW
\r
2759 int iNewSortColumn;
\r
2760 int iOldSortColumn;
\r
2761 } NMSORTLISTVIEW, *LPNMSORTLISTVIEW;
\r
2763 // Column sort types. Can be set on a per-column basis with the SetColumnSortType method.
\r
2767 LVCOLSORT_TEXT, // default
\r
2768 LVCOLSORT_TEXTNOCASE,
\r
2771 LVCOLSORT_DECIMAL,
\r
2772 LVCOLSORT_DATETIME,
\r
2776 LVCOLSORT_LAST = LVCOLSORT_CUSTOM
\r
2780 template <class T>
\r
2781 class CSortListViewImpl
\r
2786 m_cchCmpTextMax = 32, // overrideable
\r
2787 m_cxSortImage = 16,
\r
2788 m_cySortImage = 15,
\r
2789 m_cxSortArrow = 11,
\r
2790 m_cySortArrow = 6,
\r
2791 m_iSortUp = 0, // index of sort bitmaps
\r
2793 m_nShellSortUpID = 133
\r
2796 // passed to LVCompare functions as lParam1 and lParam2
\r
2797 struct LVCompareParam
\r
2800 DWORD_PTR dwItemData;
\r
2810 // passed to LVCompare functions as the lParamSort parameter
\r
2818 bool m_bSortDescending;
\r
2819 bool m_bCommCtrl6;
\r
2820 int m_iSortColumn;
\r
2821 CBitmap m_bmSort[2];
\r
2822 int m_fmtOldSortCol;
\r
2823 HBITMAP m_hbmOldSortCol;
\r
2824 DWORD m_dwSortLVExtendedStyle;
\r
2825 ATL::CSimpleArray<WORD> m_arrColSortType;
\r
2826 bool m_bUseWaitCursor;
\r
2828 CSortListViewImpl() :
\r
2829 m_bSortDescending(false),
\r
2830 m_bCommCtrl6(false),
\r
2831 m_iSortColumn(-1),
\r
2832 m_fmtOldSortCol(0),
\r
2833 m_hbmOldSortCol(NULL),
\r
2834 m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS),
\r
2835 m_bUseWaitCursor(true)
\r
2837 #ifndef _WIN32_WCE
\r
2838 DWORD dwMajor = 0;
\r
2839 DWORD dwMinor = 0;
\r
2840 HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
\r
2841 m_bCommCtrl6 = SUCCEEDED(hRet) && dwMajor >= 6;
\r
2842 #endif // !_WIN32_WCE
\r
2846 void SetSortColumn(int iCol)
\r
2848 T* pT = static_cast<T*>(this);
\r
2849 ATLASSERT(::IsWindow(pT->m_hWnd));
\r
2850 CHeaderCtrl header = pT->GetHeader();
\r
2851 ATLASSERT(header.m_hWnd != NULL);
\r
2852 ATLASSERT(iCol >= -1 && iCol < m_arrColSortType.GetSize());
\r
2854 int iOldSortCol = m_iSortColumn;
\r
2855 m_iSortColumn = iCol;
\r
2858 #ifndef HDF_SORTUP
\r
2859 const int HDF_SORTUP = 0x0400;
\r
2860 #endif // HDF_SORTUP
\r
2861 #ifndef HDF_SORTDOWN
\r
2862 const int HDF_SORTDOWN = 0x0200;
\r
2863 #endif // HDF_SORTDOWN
\r
2864 const int nMask = HDF_SORTUP | HDF_SORTDOWN;
\r
2865 HDITEM hditem = { HDI_FORMAT };
\r
2866 if(iOldSortCol != iCol && iOldSortCol >= 0 && header.GetItem(iOldSortCol, &hditem))
\r
2868 hditem.fmt &= ~nMask;
\r
2869 header.SetItem(iOldSortCol, &hditem);
\r
2871 if(iCol >= 0 && header.GetItem(iCol, &hditem))
\r
2873 hditem.fmt &= ~nMask;
\r
2874 hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP;
\r
2875 header.SetItem(iCol, &hditem);
\r
2880 if(m_bmSort[m_iSortUp].IsNull())
\r
2881 pT->CreateSortBitmaps();
\r
2883 // restore previous sort column's bitmap, if any, and format
\r
2884 HDITEM hditem = { HDI_BITMAP | HDI_FORMAT };
\r
2885 if(iOldSortCol != iCol && iOldSortCol >= 0)
\r
2887 hditem.hbm = m_hbmOldSortCol;
\r
2888 hditem.fmt = m_fmtOldSortCol;
\r
2889 header.SetItem(iOldSortCol, &hditem);
\r
2892 // save new sort column's bitmap and format, and add our sort bitmap
\r
2893 if(iCol >= 0 && header.GetItem(iCol, &hditem))
\r
2895 if(iOldSortCol != iCol)
\r
2897 m_fmtOldSortCol = hditem.fmt;
\r
2898 m_hbmOldSortCol = hditem.hbm;
\r
2900 hditem.fmt &= ~HDF_IMAGE;
\r
2901 hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
\r
2902 int i = m_bSortDescending ? m_iSortDown : m_iSortUp;
\r
2903 hditem.hbm = m_bmSort[i];
\r
2904 header.SetItem(iCol, &hditem);
\r
2908 int GetSortColumn() const
\r
2910 return m_iSortColumn;
\r
2913 void SetColumnSortType(int iCol, WORD wType)
\r
2915 ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
\r
2916 ATLASSERT(wType >= LVCOLSORT_NONE && wType <= LVCOLSORT_LAST);
\r
2917 m_arrColSortType[iCol] = wType;
\r
2920 WORD GetColumnSortType(int iCol) const
\r
2922 ATLASSERT((iCol >= 0) && iCol < m_arrColSortType.GetSize());
\r
2923 return m_arrColSortType[iCol];
\r
2926 int GetColumnCount() const
\r
2928 const T* pT = static_cast<const T*>(this);
\r
2929 ATLASSERT(::IsWindow(pT->m_hWnd));
\r
2930 CHeaderCtrl header = pT->GetHeader();
\r
2931 return header.m_hWnd != NULL ? header.GetItemCount() : 0;
\r
2934 bool IsSortDescending() const
\r
2936 return m_bSortDescending;
\r
2939 DWORD GetSortListViewExtendedStyle() const
\r
2941 return m_dwSortLVExtendedStyle;
\r
2944 DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
\r
2946 DWORD dwPrevStyle = m_dwSortLVExtendedStyle;
\r
2948 m_dwSortLVExtendedStyle = dwExtendedStyle;
\r
2950 m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
\r
2951 return dwPrevStyle;
\r
2955 bool DoSortItems(int iCol, bool bDescending = false)
\r
2957 T* pT = static_cast<T*>(this);
\r
2958 ATLASSERT(::IsWindow(pT->m_hWnd));
\r
2959 ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
\r
2961 WORD wType = m_arrColSortType[iCol];
\r
2962 if(wType == LVCOLSORT_NONE)
\r
2965 int nCount = pT->GetItemCount();
\r
2968 m_bSortDescending = bDescending;
\r
2969 SetSortColumn(iCol);
\r
2973 CWaitCursor waitCursor(false);
\r
2974 if(m_bUseWaitCursor)
\r
2977 LVCompareParam* pParam = NULL;
\r
2978 ATLTRY(pParam = new LVCompareParam[nCount]);
\r
2979 PFNLVCOMPARE pFunc = NULL;
\r
2980 TCHAR pszTemp[pT->m_cchCmpTextMax];
\r
2981 bool bStrValue = false;
\r
2985 case LVCOLSORT_TEXT:
\r
2986 pFunc = (PFNLVCOMPARE)pT->LVCompareText;
\r
2987 case LVCOLSORT_TEXTNOCASE:
\r
2989 pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase;
\r
2990 case LVCOLSORT_CUSTOM:
\r
2993 pFunc = (PFNLVCOMPARE)pT->LVCompareCustom;
\r
2995 for(int i = 0; i < nCount; i++)
\r
2997 pParam[i].iItem = i;
\r
2998 pParam[i].dwItemData = pT->GetItemData(i);
\r
2999 pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax];
\r
3000 pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax);
\r
3001 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
\r
3006 case LVCOLSORT_LONG:
\r
3008 pFunc = (PFNLVCOMPARE)pT->LVCompareLong;
\r
3009 for(int i = 0; i < nCount; i++)
\r
3011 pParam[i].iItem = i;
\r
3012 pParam[i].dwItemData = pT->GetItemData(i);
\r
3013 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
\r
3014 pParam[i].lValue = pT->StrToLong(pszTemp);
\r
3015 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
\r
3019 case LVCOLSORT_DOUBLE:
\r
3021 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
\r
3022 for(int i = 0; i < nCount; i++)
\r
3024 pParam[i].iItem = i;
\r
3025 pParam[i].dwItemData = pT->GetItemData(i);
\r
3026 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
\r
3027 pParam[i].dblValue = pT->StrToDouble(pszTemp);
\r
3028 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
\r
3032 case LVCOLSORT_DECIMAL:
\r
3034 pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal;
\r
3035 for(int i = 0; i < nCount; i++)
\r
3037 pParam[i].iItem = i;
\r
3038 pParam[i].dwItemData = pT->GetItemData(i);
\r
3039 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
\r
3040 pT->StrToDecimal(pszTemp, &pParam[i].decValue);
\r
3041 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
\r
3045 case LVCOLSORT_DATETIME:
\r
3046 case LVCOLSORT_DATE:
\r
3047 case LVCOLSORT_TIME:
\r
3049 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
\r
3050 DWORD dwFlags = LOCALE_NOUSEROVERRIDE;
\r
3051 if(wType == LVCOLSORT_DATE)
\r
3052 dwFlags |= VAR_DATEVALUEONLY;
\r
3053 else if(wType == LVCOLSORT_TIME)
\r
3054 dwFlags |= VAR_TIMEVALUEONLY;
\r
3055 for(int i = 0; i < nCount; i++)
\r
3057 pParam[i].iItem = i;
\r
3058 pParam[i].dwItemData = pT->GetItemData(i);
\r
3059 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
\r
3060 pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags);
\r
3061 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
\r
3066 ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n"));
\r
3068 } // switch(wType)
\r
3070 ATLASSERT(pFunc != NULL);
\r
3071 LVSortInfo lvsi = { pT, iCol, bDescending };
\r
3072 bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE);
\r
3073 for(int i = 0; i < nCount; i++)
\r
3075 DWORD_PTR dwItemData = pT->GetItemData(i);
\r
3076 LVCompareParam* p = (LVCompareParam*)dwItemData;
\r
3077 ATLASSERT(p != NULL);
\r
3079 delete [] (TCHAR*)p->pszValue;
\r
3080 pT->SetItemData(i, p->dwItemData);
\r
3086 m_bSortDescending = bDescending;
\r
3087 SetSortColumn(iCol);
\r
3090 if(m_bUseWaitCursor)
\r
3091 waitCursor.Restore();
\r
3096 void CreateSortBitmaps()
\r
3098 if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0)
\r
3100 bool bFree = false;
\r
3101 LPCTSTR pszModule = _T("shell32.dll");
\r
3102 HINSTANCE hShell = ::GetModuleHandle(pszModule);
\r
3104 if (hShell == NULL)
\r
3106 hShell = ::LoadLibrary(pszModule);
\r
3110 if (hShell != NULL)
\r
3112 bool bSuccess = true;
\r
3113 for(int i = m_iSortUp; i <= m_iSortDown; i++)
\r
3115 if(!m_bmSort[i].IsNull())
\r
3116 m_bmSort[i].DeleteObject();
\r
3117 m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i),
\r
3118 #ifndef _WIN32_WCE
\r
3119 IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
\r
3120 #else // CE specific
\r
3121 IMAGE_BITMAP, 0, 0, 0);
\r
3122 #endif // _WIN32_WCE
\r
3123 if(m_bmSort[i].IsNull())
\r
3130 ::FreeLibrary(hShell);
\r
3136 T* pT = static_cast<T*>(this);
\r
3137 for(int i = m_iSortUp; i <= m_iSortDown; i++)
\r
3139 if(!m_bmSort[i].IsNull())
\r
3140 m_bmSort[i].DeleteObject();
\r
3143 CClientDC dc(::GetDesktopWindow());
\r
3144 dcMem.CreateCompatibleDC(dc.m_hDC);
\r
3145 m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage);
\r
3146 HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]);
\r
3147 RECT rc = {0,0,m_cxSortImage, m_cySortImage};
\r
3148 pT->DrawSortBitmap(dcMem.m_hDC, i, &rc);
\r
3149 dcMem.SelectBitmap(hbmOld);
\r
3154 void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol)
\r
3156 T* pT = static_cast<T*>(this);
\r
3157 int nID = pT->GetDlgCtrlID();
\r
3158 NMSORTLISTVIEW nm = { { pT->m_hWnd, nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol };
\r
3159 ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm);
\r
3163 int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/)
\r
3165 // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members.
\r
3166 // If item1 > item2 return 1, if item1 < item2 return -1, else return 0.
\r
3170 void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc)
\r
3172 dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE));
\r
3173 HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
\r
3175 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW));
\r
3176 HPEN hpenOld = dc.SelectPen(pen);
\r
3177 POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 };
\r
3178 if(iBitmap == m_iSortUp)
\r
3182 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y },
\r
3183 { ptOrg.x, ptOrg.y + m_cySortArrow - 1 },
\r
3184 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 }
\r
3186 dc.Polygon(pts, 3);
\r
3192 { ptOrg.x, ptOrg.y },
\r
3193 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 },
\r
3194 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y }
\r
3196 dc.Polygon(pts, 3);
\r
3198 dc.SelectBrush(hbrOld);
\r
3199 dc.SelectPen(hpenOld);
\r
3202 double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags)
\r
3204 ATLASSERT(lpstr != NULL);
\r
3205 if(lpstr == NULL || lpstr[0] == _T('\0'))
\r
3209 HRESULT hRet = E_FAIL;
\r
3211 if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet)))
\r
3213 ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet);
\r
3219 long StrToLong(LPCTSTR lpstr)
\r
3221 ATLASSERT(lpstr != NULL);
\r
3222 if(lpstr == NULL || lpstr[0] == _T('\0'))
\r
3226 HRESULT hRet = E_FAIL;
\r
3228 if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet)))
\r
3230 ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet);
\r
3236 double StrToDouble(LPCTSTR lpstr)
\r
3238 ATLASSERT(lpstr != NULL);
\r
3239 if(lpstr == NULL || lpstr[0] == _T('\0'))
\r
3243 HRESULT hRet = E_FAIL;
\r
3244 double dblRet = 0;
\r
3245 if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet)))
\r
3247 ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet);
\r
3253 bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal)
\r
3255 ATLASSERT(lpstr != NULL);
\r
3256 ATLASSERT(pDecimal != NULL);
\r
3257 if(lpstr == NULL || pDecimal == NULL)
\r
3261 HRESULT hRet = E_FAIL;
\r
3262 if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal)))
\r
3264 ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet);
\r
3265 pDecimal->Lo64 = 0;
\r
3266 pDecimal->Hi32 = 0;
\r
3267 pDecimal->signscale = 0;
\r
3273 // Overrideable PFNLVCOMPARE functions
\r
3274 static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
3276 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
\r
3278 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
\r
3279 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
\r
3280 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
\r
3282 int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue);
\r
3283 return pInfo->bDescending ? -nRet : nRet;
\r
3286 static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
3288 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
\r
3290 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
\r
3291 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
\r
3292 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
\r
3294 int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue);
\r
3295 return pInfo->bDescending ? -nRet : nRet;
\r
3298 static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
3300 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
\r
3302 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
\r
3303 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
\r
3304 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
\r
3307 if(pParam1->lValue > pParam2->lValue)
\r
3309 else if(pParam1->lValue < pParam2->lValue)
\r
3311 return pInfo->bDescending ? -nRet : nRet;
\r
3314 static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
3316 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
\r
3318 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
\r
3319 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
\r
3320 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
\r
3323 if(pParam1->dblValue > pParam2->dblValue)
\r
3325 else if(pParam1->dblValue < pParam2->dblValue)
\r
3327 return pInfo->bDescending ? -nRet : nRet;
\r
3330 static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
3332 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
\r
3334 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
\r
3335 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
\r
3336 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
\r
3338 int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol);
\r
3339 return pInfo->bDescending ? -nRet : nRet;
\r
3342 #ifndef _WIN32_WCE
\r
3343 static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
3345 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
\r
3347 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
\r
3348 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
\r
3349 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
\r
3351 int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue);
\r
3353 return pInfo->bDescending ? -nRet : nRet;
\r
3356 // Compare mantissas, ignore sign and scale
\r
3357 static int CompareMantissas(const DECIMAL& decLeft, const DECIMAL& decRight)
\r
3359 if (decLeft.Hi32 < decRight.Hi32)
\r
3363 if (decLeft.Hi32 > decRight.Hi32)
\r
3367 // Here, decLeft.Hi32 == decRight.Hi32
\r
3368 if (decLeft.Lo64 < decRight.Lo64)
\r
3372 if (decLeft.Lo64 > decRight.Lo64)
\r
3379 // return values: VARCMP_LT, VARCMP_EQ, VARCMP_GT, VARCMP_NULL
\r
3380 static HRESULT VarDecCmp(const DECIMAL* pdecLeft, const DECIMAL* pdecRight)
\r
3382 static const ULONG powersOfTen[] =
\r
3394 static const int largestPower = sizeof(powersOfTen) / sizeof(powersOfTen[0]);
\r
3395 if (!pdecLeft || !pdecRight)
\r
3397 return VARCMP_NULL;
\r
3400 // Degenerate case - at least one comparand is of the form
\r
3401 // [+-]0*10^N (denormalized zero)
\r
3402 bool bLeftZero = (!pdecLeft->Lo64 && !pdecLeft->Hi32);
\r
3403 bool bRightZero = (!pdecRight->Lo64 && !pdecRight->Hi32);
\r
3404 if (bLeftZero && bRightZero)
\r
3408 bool bLeftNeg = ((pdecLeft->sign & DECIMAL_NEG) != 0);
\r
3409 bool bRightNeg = ((pdecRight->sign & DECIMAL_NEG) != 0);
\r
3412 return (bRightNeg ? VARCMP_GT : VARCMP_LT);
\r
3414 // This also covers the case where the comparands have different signs
\r
3415 if (bRightZero || bLeftNeg != bRightNeg)
\r
3417 return (bLeftNeg ? VARCMP_LT : VARCMP_GT);
\r
3420 // Here both comparands have the same sign and need to be compared
\r
3421 // on mantissa and scale. The result is obvious when
\r
3422 // 1. Scales are equal (then compare mantissas)
\r
3423 // 2. A number with smaller scale is also the one with larger mantissa
\r
3424 // (then this number is obviously larger)
\r
3425 // In the remaining case, we would multiply the number with smaller
\r
3426 // scale by 10 and simultaneously increment its scale (which amounts to
\r
3427 // adding trailing zeros after decimal point), until the numbers fall under
\r
3428 // one of the two cases above
\r
3430 bool bInvert = bLeftNeg; // the final result needs to be inverted
\r
3431 if (pdecLeft->scale < pdecRight->scale)
\r
3437 temp = *pdecRight;
\r
3438 pdecRight = pdecLeft;
\r
3439 bInvert = !bInvert;
\r
3442 // Now temp is the number with smaller (or equal) scale, and
\r
3443 // we can modify it freely without touching original parameters
\r
3445 while ((comp = CompareMantissas(temp, *pdecRight)) < 0 &&
\r
3446 temp.scale < pdecRight->scale)
\r
3448 // Multiply by an appropriate power of 10
\r
3449 int scaleDiff = pdecRight->scale - temp.scale;
\r
3450 if (scaleDiff > largestPower)
\r
3452 // Keep the multiplier representable in 32bit
\r
3453 scaleDiff = largestPower;
\r
3455 DWORDLONG power = powersOfTen[scaleDiff - 1];
\r
3456 // Multiply temp's mantissa by power
\r
3457 DWORDLONG product = temp.Lo32 * power;
\r
3458 ULONG carry = static_cast<ULONG>(product >> 32);
\r
3459 temp.Lo32 = static_cast<ULONG>(product);
\r
3460 product = temp.Mid32 * power + carry;
\r
3461 carry = static_cast<ULONG>(product >> 32);
\r
3462 temp.Mid32 = static_cast<ULONG>(product);
\r
3463 product = temp.Hi32 * power + carry;
\r
3464 if (static_cast<ULONG>(product >> 32))
\r
3466 // Multiplication overflowed - pdecLeft is clearly larger
\r
3469 temp.Hi32 = static_cast<ULONG>(product);
\r
3470 temp.scale = (BYTE)(temp.scale + scaleDiff);
\r
3472 if (temp.scale < pdecRight->scale)
\r
3480 return (comp > 0 ? VARCMP_GT : comp < 0 ? VARCMP_LT : VARCMP_EQ);
\r
3483 static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
\r
3485 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
\r
3487 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
\r
3488 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
\r
3489 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
\r
3491 int nRet = (int)VarDecCmp(&pParam1->decValue, &pParam2->decValue);
\r
3493 return pInfo->bDescending ? -nRet : nRet;
\r
3495 #endif // !_WIN32_WCE
\r
3497 BEGIN_MSG_MAP(CSortListViewImpl)
\r
3498 MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn)
\r
3499 MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn)
\r
3500 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick)
\r
3501 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick)
\r
3502 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
\r
3505 LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
3507 T* pT = static_cast<T*>(this);
\r
3508 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
\r
3513 m_arrColSortType.Add(wType);
\r
3514 int nCount = m_arrColSortType.GetSize();
\r
3515 ATLASSERT(nCount == GetColumnCount());
\r
3517 for(int i = nCount - 1; i > lRet; i--)
\r
3518 m_arrColSortType[i] = m_arrColSortType[i - 1];
\r
3519 m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT;
\r
3521 if(lRet <= m_iSortColumn)
\r
3527 LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
\r
3529 T* pT = static_cast<T*>(this);
\r
3530 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
\r
3534 int iCol = (int)wParam;
\r
3535 if(m_iSortColumn == iCol)
\r
3536 m_iSortColumn = -1;
\r
3537 else if(m_iSortColumn > iCol)
\r
3539 m_arrColSortType.RemoveAt(iCol);
\r
3544 LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
\r
3546 LPNMHEADER p = (LPNMHEADER)pnmh;
\r
3547 if(p->iButton == 0)
\r
3549 int iOld = m_iSortColumn;
\r
3550 bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false;
\r
3551 if(DoSortItems(p->iItem, bDescending))
\r
3552 NotifyParentSortChanged(p->iItem, iOld);
\r
3558 LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
\r
3560 #ifndef _WIN32_WCE
\r
3561 if(wParam == SPI_SETNONCLIENTMETRICS)
\r
3562 GetSystemSettings();
\r
3563 #else // CE specific
\r
3564 wParam; // avoid level 4 warning
\r
3565 GetSystemSettings();
\r
3566 #endif // _WIN32_WCE
\r
3571 void GetSystemSettings()
\r
3573 if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull())
\r
3575 T* pT = static_cast<T*>(this);
\r
3576 pT->CreateSortBitmaps();
\r
3577 if(m_iSortColumn != -1)
\r
3578 SetSortColumn(m_iSortColumn);
\r
3585 typedef ATL::CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SHOWSELALWAYS , WS_EX_CLIENTEDGE> CSortListViewCtrlTraits;
\r
3587 template <class T, class TBase = CListViewCtrl, class TWinTraits = CSortListViewCtrlTraits>
\r
3588 class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl<T, TBase, TWinTraits>, public CSortListViewImpl<T>
\r
3591 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
\r
3593 bool SortItems(int iCol, bool bDescending = false)
\r
3595 return DoSortItems(iCol, bDescending);
\r
3598 BEGIN_MSG_MAP(CSortListViewCtrlImpl)
\r
3599 MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl<T>::OnInsertColumn)
\r
3600 MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl<T>::OnDeleteColumn)
\r
3601 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl<T>::OnHeaderItemClick)
\r
3602 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl<T>::OnHeaderItemClick)
\r
3603 MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl<T>::OnSettingChange)
\r
3607 class CSortListViewCtrl : public CSortListViewCtrlImpl<CSortListViewCtrl>
\r
3610 DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName())
\r
3614 ///////////////////////////////////////////////////////////////////////////////
\r
3615 // CTabView - implements tab view window
\r
3617 // TabView Notifications
\r
3618 #define TBVN_PAGEACTIVATED (0U-741)
\r
3619 #define TBVN_CONTEXTMENU (0U-742)
\r
3621 // Notification data for TBVN_CONTEXTMENU
\r
3622 struct TBVCONTEXTMENUINFO
\r
3628 typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
\r
3631 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
\r
3632 class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
\r
3635 DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE)
\r
3637 // Declarations and enums
\r
3638 struct TABVIEWPAGE
\r
3641 LPTSTR lpstrTitle;
\r
3645 struct TCITEMEXTRA
\r
3647 TCITEMHEADER tciheader;
\r
3648 TABVIEWPAGE tvpage;
\r
3650 operator LPTCITEM() { return (LPTCITEM)this; }
\r
3658 m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
\r
3662 ATL::CContainedWindowT<CTabCtrl> m_tab;
\r
3663 int m_cyTabHeight;
\r
3665 int m_nActivePage;
\r
3667 int m_nInsertItem;
\r
3668 POINT m_ptStartDrag;
\r
3670 CMenuHandle m_menu;
\r
3672 int m_cchTabTextLength;
\r
3674 int m_nMenuItemsCount;
\r
3676 ATL::CWindow m_wndTitleBar;
\r
3677 LPTSTR m_lpstrTitleBarBase;
\r
3678 int m_cchTitleBarLength;
\r
3680 CImageList m_ilDrag;
\r
3682 bool m_bDestroyPageOnRemove:1;
\r
3683 bool m_bDestroyImageList:1;
\r
3684 bool m_bActivePageMenuItem:1;
\r
3685 bool m_bActiveAsDefaultMenuItem:1;
\r
3686 bool m_bEmptyMenuItem:1;
\r
3687 bool m_bWindowsMenuItem:1;
\r
3689 bool m_bTabCapture:1;
\r
3690 bool m_bTabDrag:1;
\r
3692 // Constructor/destructor
\r
3694 m_nActivePage(-1),
\r
3695 m_cyTabHeight(0),
\r
3697 m_nInsertItem(-1),
\r
3698 m_cchTabTextLength(30),
\r
3699 m_nMenuItemsCount(10),
\r
3700 m_lpstrTitleBarBase(NULL),
\r
3701 m_cchTitleBarLength(100),
\r
3702 m_bDestroyPageOnRemove(true),
\r
3703 m_bDestroyImageList(true),
\r
3704 m_bActivePageMenuItem(true),
\r
3705 m_bActiveAsDefaultMenuItem(false),
\r
3706 m_bEmptyMenuItem(false),
\r
3707 m_bWindowsMenuItem(false),
\r
3708 m_bTabCapture(false),
\r
3711 m_ptStartDrag.x = 0;
\r
3712 m_ptStartDrag.y = 0;
\r
3717 delete [] m_lpstrTitleBarBase;
\r
3720 // Message filter function - to be called from PreTranslateMessage of the main window
\r
3721 BOOL PreTranslateMessage(MSG* pMsg)
\r
3723 if(IsWindow() == FALSE)
\r
3726 BOOL bRet = FALSE;
\r
3728 // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
\r
3729 int nCount = GetPageCount();
\r
3732 bool bControl = (::GetKeyState(VK_CONTROL) < 0);
\r
3733 if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
\r
3737 int nPage = m_nActivePage;
\r
3738 bool bShift = (::GetKeyState(VK_SHIFT) < 0);
\r
3740 nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
\r
3742 nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
\r
3744 SetActivePage(nPage);
\r
3745 T* pT = static_cast<T*>(this);
\r
3746 pT->OnPageActivated(m_nActivePage);
\r
3753 // If we are doing drag-drop, check for Escape key that cancels it
\r
3756 if(m_bTabCapture && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
\r
3758 ::ReleaseCapture();
\r
3763 // Pass the message to the active page
\r
3766 if(m_nActivePage != -1)
\r
3767 bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
\r
3774 int GetPageCount() const
\r
3776 ATLASSERT(::IsWindow(m_hWnd));
\r
3777 return m_tab.GetItemCount();
\r
3780 int GetActivePage() const
\r
3782 return m_nActivePage;
\r
3785 void SetActivePage(int nPage)
\r
3787 ATLASSERT(::IsWindow(m_hWnd));
\r
3788 ATLASSERT(IsValidPageIndex(nPage));
\r
3790 T* pT = static_cast<T*>(this);
\r
3794 if(m_nActivePage != -1)
\r
3795 ::ShowWindow(GetPageHWND(m_nActivePage), FALSE);
\r
3796 m_nActivePage = nPage;
\r
3797 m_tab.SetCurSel(m_nActivePage);
\r
3798 ::ShowWindow(GetPageHWND(m_nActivePage), TRUE);
\r
3800 pT->UpdateLayout();
\r
3803 RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
\r
3805 if(::GetFocus() != m_tab.m_hWnd)
\r
3806 ::SetFocus(GetPageHWND(m_nActivePage));
\r
3808 pT->UpdateTitleBar();
\r
3812 HIMAGELIST GetImageList() const
\r
3814 ATLASSERT(::IsWindow(m_hWnd));
\r
3815 return m_tab.GetImageList();
\r
3818 HIMAGELIST SetImageList(HIMAGELIST hImageList)
\r
3820 ATLASSERT(::IsWindow(m_hWnd));
\r
3821 return m_tab.SetImageList(hImageList);
\r
3824 void SetWindowMenu(HMENU hMenu)
\r
3826 ATLASSERT(::IsWindow(m_hWnd));
\r
3830 T* pT = static_cast<T*>(this);
\r
3834 void SetTitleBarWindow(HWND hWnd)
\r
3836 ATLASSERT(::IsWindow(m_hWnd));
\r
3838 delete [] m_lpstrTitleBarBase;
\r
3839 m_lpstrTitleBarBase = NULL;
\r
3841 m_wndTitleBar = hWnd;
\r
3845 int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
\r
3846 ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
\r
3847 if(m_lpstrTitleBarBase != NULL)
\r
3849 m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
\r
3850 T* pT = static_cast<T*>(this);
\r
3851 pT->UpdateTitleBar();
\r
3855 // Page attributes
\r
3856 HWND GetPageHWND(int nPage) const
\r
3858 ATLASSERT(::IsWindow(m_hWnd));
\r
3859 ATLASSERT(IsValidPageIndex(nPage));
\r
3861 TCITEMEXTRA tcix = { 0 };
\r
3862 tcix.tciheader.mask = TCIF_PARAM;
\r
3863 m_tab.GetItem(nPage, tcix);
\r
3865 return tcix.tvpage.hWnd;
\r
3868 LPCTSTR GetPageTitle(int nPage) const
\r
3870 ATLASSERT(::IsWindow(m_hWnd));
\r
3871 ATLASSERT(IsValidPageIndex(nPage));
\r
3873 TCITEMEXTRA tcix = { 0 };
\r
3874 tcix.tciheader.mask = TCIF_PARAM;
\r
3875 if(m_tab.GetItem(nPage, tcix) == FALSE)
\r
3878 return tcix.tvpage.lpstrTitle;
\r
3881 bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
\r
3883 ATLASSERT(::IsWindow(m_hWnd));
\r
3884 ATLASSERT(IsValidPageIndex(nPage));
\r
3886 T* pT = static_cast<T*>(this);
\r
3888 int cchBuff = lstrlen(lpstrTitle) + 1;
\r
3889 LPTSTR lpstrBuff = NULL;
\r
3890 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
\r
3891 if(lpstrBuff == NULL)
\r
3894 SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
\r
3895 TCITEMEXTRA tcix = { 0 };
\r
3896 tcix.tciheader.mask = TCIF_PARAM;
\r
3897 if(m_tab.GetItem(nPage, tcix) == FALSE)
\r
3900 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
3901 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
\r
3902 if(lpstrTabText == NULL)
\r
3905 delete [] tcix.tvpage.lpstrTitle;
\r
3907 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
\r
3909 tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
\r
3910 tcix.tciheader.pszText = lpstrTabText;
\r
3911 tcix.tvpage.lpstrTitle = lpstrBuff;
\r
3912 if(m_tab.SetItem(nPage, tcix) == FALSE)
\r
3915 pT->UpdateTitleBar();
\r
3921 LPVOID GetPageData(int nPage) const
\r
3923 ATLASSERT(::IsWindow(m_hWnd));
\r
3924 ATLASSERT(IsValidPageIndex(nPage));
\r
3926 TCITEMEXTRA tcix = { 0 };
\r
3927 tcix.tciheader.mask = TCIF_PARAM;
\r
3928 m_tab.GetItem(nPage, tcix);
\r
3930 return tcix.tvpage.pData;
\r
3933 LPVOID SetPageData(int nPage, LPVOID pData)
\r
3935 ATLASSERT(::IsWindow(m_hWnd));
\r
3936 ATLASSERT(IsValidPageIndex(nPage));
\r
3938 TCITEMEXTRA tcix = { 0 };
\r
3939 tcix.tciheader.mask = TCIF_PARAM;
\r
3940 m_tab.GetItem(nPage, tcix);
\r
3941 LPVOID pDataOld = tcix.tvpage.pData;
\r
3943 tcix.tvpage.pData = pData;
\r
3944 m_tab.SetItem(nPage, tcix);
\r
3949 int GetPageImage(int nPage) const
\r
3951 ATLASSERT(::IsWindow(m_hWnd));
\r
3952 ATLASSERT(IsValidPageIndex(nPage));
\r
3954 TCITEMEXTRA tcix = { 0 };
\r
3955 tcix.tciheader.mask = TCIF_IMAGE;
\r
3956 m_tab.GetItem(nPage, tcix);
\r
3958 return tcix.tciheader.iImage;
\r
3961 int SetPageImage(int nPage, int nImage)
\r
3963 ATLASSERT(::IsWindow(m_hWnd));
\r
3964 ATLASSERT(IsValidPageIndex(nPage));
\r
3966 TCITEMEXTRA tcix = { 0 };
\r
3967 tcix.tciheader.mask = TCIF_IMAGE;
\r
3968 m_tab.GetItem(nPage, tcix);
\r
3969 int nImageOld = tcix.tciheader.iImage;
\r
3971 tcix.tciheader.iImage = nImage;
\r
3972 m_tab.SetItem(nPage, tcix);
\r
3978 bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
\r
3980 return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
\r
3983 bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
\r
3985 ATLASSERT(::IsWindow(m_hWnd));
\r
3986 ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage));
\r
3988 T* pT = static_cast<T*>(this);
\r
3990 int cchBuff = lstrlen(lpstrTitle) + 1;
\r
3991 LPTSTR lpstrBuff = NULL;
\r
3992 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
\r
3993 if(lpstrBuff == NULL)
\r
3996 SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
\r
3998 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
3999 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
\r
4000 if(lpstrTabText == NULL)
\r
4003 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
\r
4007 TCITEMEXTRA tcix = { 0 };
\r
4008 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
\r
4009 tcix.tciheader.pszText = lpstrTabText;
\r
4010 tcix.tciheader.iImage = nImage;
\r
4011 tcix.tvpage.hWnd = hWndView;
\r
4012 tcix.tvpage.lpstrTitle = lpstrBuff;
\r
4013 tcix.tvpage.pData = pData;
\r
4014 int nItem = m_tab.InsertItem(nPage, tcix);
\r
4017 delete [] lpstrBuff;
\r
4022 SetActivePage(nItem);
\r
4023 pT->OnPageActivated(m_nActivePage);
\r
4025 if(GetPageCount() == 1)
\r
4026 pT->ShowTabControl(true);
\r
4028 pT->UpdateLayout();
\r
4031 RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
\r
4036 void RemovePage(int nPage)
\r
4038 ATLASSERT(::IsWindow(m_hWnd));
\r
4039 ATLASSERT(IsValidPageIndex(nPage));
\r
4041 T* pT = static_cast<T*>(this);
\r
4045 if(GetPageCount() == 1)
\r
4046 pT->ShowTabControl(false);
\r
4048 if(m_bDestroyPageOnRemove)
\r
4049 ::DestroyWindow(GetPageHWND(nPage));
\r
4051 ::ShowWindow(GetPageHWND(nPage), FALSE);
\r
4052 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
\r
4053 delete [] lpstrTitle;
\r
4055 ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
\r
4057 if(m_nActivePage == nPage)
\r
4059 m_nActivePage = -1;
\r
4063 SetActivePage(nPage - 1);
\r
4065 else if(GetPageCount() > 0)
\r
4067 SetActivePage(nPage);
\r
4074 pT->UpdateTitleBar();
\r
4080 nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
\r
4081 m_nActivePage = -1;
\r
4082 SetActivePage(nPage);
\r
4085 pT->OnPageActivated(m_nActivePage);
\r
4088 void RemoveAllPages()
\r
4090 ATLASSERT(::IsWindow(m_hWnd));
\r
4092 if(GetPageCount() == 0)
\r
4095 T* pT = static_cast<T*>(this);
\r
4099 pT->ShowTabControl(false);
\r
4101 for(int i = 0; i < GetPageCount(); i++)
\r
4103 if(m_bDestroyPageOnRemove)
\r
4104 ::DestroyWindow(GetPageHWND(i));
\r
4106 ::ShowWindow(GetPageHWND(i), FALSE);
\r
4107 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
\r
4108 delete [] lpstrTitle;
\r
4110 m_tab.DeleteAllItems();
\r
4112 m_nActivePage = -1;
\r
4113 pT->OnPageActivated(m_nActivePage);
\r
4119 pT->UpdateTitleBar();
\r
4123 int PageIndexFromHwnd(HWND hWnd) const
\r
4127 for(int i = 0; i < GetPageCount(); i++)
\r
4129 if(GetPageHWND(i) == hWnd)
\r
4139 void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
\r
4141 ATLASSERT(::IsWindow(m_hWnd));
\r
4143 CMenuHandle menu = hMenu;
\r
4144 T* pT = static_cast<T*>(this);
\r
4145 pT; // avoid level 4 warning
\r
4146 int nFirstPos = 0;
\r
4148 // Find first menu item in our range
\r
4149 #ifndef _WIN32_WCE
\r
4150 for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
\r
4152 UINT nID = menu.GetMenuItemID(nFirstPos);
\r
4153 if((nID >= ID_WINDOW_TABFIRST && nID <= ID_WINDOW_TABLAST) || nID == ID_WINDOW_SHOWTABLIST)
\r
4156 #else // CE specific
\r
4157 for(nFirstPos = 0; ; nFirstPos++)
\r
4159 CMenuItemInfo mii;
\r
4160 mii.fMask = MIIM_ID;
\r
4161 BOOL bRet = menu.GetMenuItemInfo(nFirstPos, TRUE, &mii);
\r
4164 if((mii.wID >= ID_WINDOW_TABFIRST && mii.wID <= ID_WINDOW_TABLAST) || mii.wID == ID_WINDOW_SHOWTABLIST)
\r
4167 #endif // _WIN32_WCE
\r
4169 // Remove all menu items for tab pages
\r
4171 while(bRet != FALSE)
\r
4172 bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
\r
4174 // Add separator if it's not already there
\r
4175 int nPageCount = GetPageCount();
\r
4176 if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
\r
4178 CMenuItemInfo mii;
\r
4179 mii.fMask = MIIM_TYPE;
\r
4180 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
\r
4181 if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0))
\r
4183 menu.AppendMenu(MF_SEPARATOR);
\r
4188 // Add menu items for all pages
\r
4189 if(nPageCount > 0)
\r
4191 // Append menu items for all pages
\r
4192 const int cchPrefix = 3; // 2 digits + space
\r
4193 nMenuItemsCount = min(min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
\r
4194 ATLASSERT(nMenuItemsCount < 100); // 2 digits only
\r
4195 if(nMenuItemsCount >= 100)
\r
4196 nMenuItemsCount = 99;
\r
4198 for(int i = 0; i < nMenuItemsCount; i++)
\r
4200 LPCTSTR lpstrTitle = GetPageTitle(i);
\r
4201 int nLen = lstrlen(lpstrTitle);
\r
4202 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
4203 LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
\r
4204 ATLASSERT(lpstrText != NULL);
\r
4205 if(lpstrText != NULL)
\r
4207 LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
\r
4208 SecureHelper::wsprintf_x(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
\r
4209 menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
\r
4213 // Mark active page
\r
4214 if(bActivePageMenuItem && (m_nActivePage != -1))
\r
4216 #ifndef _WIN32_WCE
\r
4217 if(bActiveAsDefaultMenuItem)
\r
4219 menu.SetMenuDefaultItem((UINT)-1, TRUE);
\r
4220 menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
\r
4223 #else // CE specific
\r
4224 bActiveAsDefaultMenuItem; // avoid level 4 warning
\r
4225 #endif // _WIN32_WCE
\r
4227 menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
\r
4233 if(bEmptyMenuItem)
\r
4235 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
\r
4236 menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
\r
4239 // Remove separator if nothing else is there
\r
4240 if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
\r
4242 CMenuItemInfo mii;
\r
4243 mii.fMask = MIIM_TYPE;
\r
4244 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
\r
4245 if((mii.fType & MFT_SEPARATOR) != 0)
\r
4246 menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
\r
4250 // Add "Windows..." menu item
\r
4251 if(bWindowsMenuItem)
\r
4252 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
\r
4255 // Message map and handlers
\r
4256 BEGIN_MSG_MAP(CTabViewImpl)
\r
4257 MESSAGE_HANDLER(WM_CREATE, OnCreate)
\r
4258 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
\r
4259 MESSAGE_HANDLER(WM_SIZE, OnSize)
\r
4260 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
\r
4261 NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
\r
4262 NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
\r
4263 #ifndef _WIN32_WCE
\r
4264 NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
\r
4265 #endif // !_WIN32_WCE
\r
4266 FORWARD_NOTIFICATIONS()
\r
4267 ALT_MSG_MAP(1) // tab control
\r
4268 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
\r
4269 MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
\r
4270 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
\r
4271 MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
\r
4272 MESSAGE_HANDLER(WM_RBUTTONUP, OnTabRButtonUp)
\r
4273 MESSAGE_HANDLER(WM_SYSKEYDOWN, OnTabSysKeyDown)
\r
4276 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
4278 T* pT = static_cast<T*>(this);
\r
4279 pT->CreateTabControl();
\r
4284 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
4288 if(m_bDestroyImageList)
\r
4290 CImageList il = m_tab.SetImageList(NULL);
\r
4291 if(il.m_hImageList != NULL)
\r
4298 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
4300 T* pT = static_cast<T*>(this);
\r
4301 pT->UpdateLayout();
\r
4305 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
\r
4307 if(m_nActivePage != -1)
\r
4308 ::SetFocus(GetPageHWND(m_nActivePage));
\r
4312 LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
\r
4314 SetActivePage(m_tab.GetCurSel());
\r
4315 T* pT = static_cast<T*>(this);
\r
4316 pT->OnPageActivated(m_nActivePage);
\r
4321 LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
\r
4323 // nothing to do - this just blocks all tab control
\r
4324 // notifications from being propagated further
\r
4328 #ifndef _WIN32_WCE
\r
4329 LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
\r
4331 LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
\r
4332 if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
\r
4334 T* pT = static_cast<T*>(this);
\r
4335 pT->UpdateTooltipText(pTTDI);
\r
4344 #endif // !_WIN32_WCE
\r
4346 // Tab control message handlers
\r
4347 LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
\r
4349 if(m_tab.GetItemCount() > 1)
\r
4351 m_bTabCapture = true;
\r
4352 m_tab.SetCapture();
\r
4354 m_ptStartDrag.x = GET_X_LPARAM(lParam);
\r
4355 m_ptStartDrag.y = GET_Y_LPARAM(lParam);
\r
4362 LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
\r
4368 TCHITTESTINFO hti = { 0 };
\r
4369 hti.pt.x = GET_X_LPARAM(lParam);
\r
4370 hti.pt.y = GET_Y_LPARAM(lParam);
\r
4371 int nItem = m_tab.HitTest(&hti);
\r
4373 MovePage(m_nActivePage, nItem);
\r
4376 ::ReleaseCapture();
\r
4383 LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
4387 m_bTabCapture = false;
\r
4391 m_bTabDrag = false;
\r
4392 T* pT = static_cast<T*>(this);
\r
4393 pT->DrawMoveMark(-1);
\r
4395 #ifndef _WIN32_WCE
\r
4396 m_ilDrag.DragLeave(GetDesktopWindow());
\r
4397 #endif // !_WIN32_WCE
\r
4398 m_ilDrag.EndDrag();
\r
4400 m_ilDrag.Destroy();
\r
4401 m_ilDrag.m_hImageList = NULL;
\r
4409 LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
\r
4415 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
\r
4419 #ifndef _WIN32_WCE
\r
4420 if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) ||
\r
4421 abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG))
\r
4422 #else // CE specific
\r
4423 if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= 4 ||
\r
4424 abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= 4)
\r
4425 #endif // _WIN32_WCE
\r
4427 T* pT = static_cast<T*>(this);
\r
4428 pT->GenerateDragImage(m_nActivePage);
\r
4430 int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
\r
4431 int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
\r
4432 m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
\r
4433 #ifndef _WIN32_WCE
\r
4434 POINT ptEnter = m_ptStartDrag;
\r
4435 m_tab.ClientToScreen(&ptEnter);
\r
4436 m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
\r
4437 #endif // !_WIN32_WCE
\r
4439 m_bTabDrag = true;
\r
4445 TCHITTESTINFO hti = { 0 };
\r
4447 int nItem = m_tab.HitTest(&hti);
\r
4449 T* pT = static_cast<T*>(this);
\r
4450 pT->SetMoveCursor(nItem != -1);
\r
4452 if(m_nInsertItem != nItem)
\r
4453 pT->DrawMoveMark(nItem);
\r
4455 m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
\r
4456 m_tab.ClientToScreen(&pt);
\r
4457 m_ilDrag.DragMove(pt);
\r
4466 LRESULT OnTabRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
\r
4468 TCHITTESTINFO hti = { 0 };
\r
4469 hti.pt.x = GET_X_LPARAM(lParam);
\r
4470 hti.pt.y = GET_Y_LPARAM(lParam);
\r
4471 int nItem = m_tab.HitTest(&hti);
\r
4474 T* pT = static_cast<T*>(this);
\r
4475 pT->OnContextMenu(nItem, hti.pt);
\r
4481 LRESULT OnTabSysKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
\r
4483 bool bShift = (::GetKeyState(VK_SHIFT) < 0);
\r
4484 if(wParam == VK_F10 && bShift)
\r
4486 if(m_nActivePage != -1)
\r
4488 RECT rect = { 0 };
\r
4489 m_tab.GetItemRect(m_nActivePage, &rect);
\r
4490 POINT pt = { rect.left, rect.bottom };
\r
4491 T* pT = static_cast<T*>(this);
\r
4492 pT->OnContextMenu(m_nActivePage, pt);
\r
4503 // Implementation helpers
\r
4504 bool IsValidPageIndex(int nPage) const
\r
4506 return (nPage >= 0 && nPage < GetPageCount());
\r
4509 bool MovePage(int nMovePage, int nInsertBeforePage)
\r
4511 ATLASSERT(IsValidPageIndex(nMovePage));
\r
4512 ATLASSERT(IsValidPageIndex(nInsertBeforePage));
\r
4514 if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
\r
4517 if(nMovePage == nInsertBeforePage)
\r
4518 return true; // nothing to do
\r
4520 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
4521 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
\r
4522 if(lpstrTabText == NULL)
\r
4524 TCITEMEXTRA tcix = { 0 };
\r
4525 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
\r
4526 tcix.tciheader.pszText = lpstrTabText;
\r
4527 tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
\r
4528 BOOL bRet = m_tab.GetItem(nMovePage, tcix);
\r
4529 ATLASSERT(bRet != FALSE);
\r
4533 int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
\r
4534 int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
\r
4535 ATLASSERT(nNewItem == nInsertItem);
\r
4536 if(nNewItem != nInsertItem)
\r
4538 ATLVERIFY(m_tab.DeleteItem(nNewItem));
\r
4542 if(nMovePage > nInsertBeforePage)
\r
4543 ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
\r
4544 else if(nMovePage < nInsertBeforePage)
\r
4545 ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
\r
4547 SetActivePage(nInsertBeforePage);
\r
4548 T* pT = static_cast<T*>(this);
\r
4549 pT->OnPageActivated(m_nActivePage);
\r
4554 // Implementation overrideables
\r
4555 bool CreateTabControl()
\r
4557 #ifndef _WIN32_WCE
\r
4558 m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
\r
4559 #else // CE specific
\r
4560 m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, m_nTabID);
\r
4561 #endif // _WIN32_WCE
\r
4562 ATLASSERT(m_tab.m_hWnd != NULL);
\r
4563 if(m_tab.m_hWnd == NULL)
\r
4566 m_tab.SetFont(AtlGetDefaultGuiFont());
\r
4568 m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
\r
4570 T* pT = static_cast<T*>(this);
\r
4571 m_cyTabHeight = pT->CalcTabHeight();
\r
4576 int CalcTabHeight()
\r
4578 int nCount = m_tab.GetItemCount();
\r
4579 TCITEMEXTRA tcix = { 0 };
\r
4580 tcix.tciheader.mask = TCIF_TEXT;
\r
4581 tcix.tciheader.pszText = _T("NS");
\r
4582 int nIndex = m_tab.InsertItem(nCount, tcix);
\r
4584 RECT rect = { 0, 0, 1000, 1000 };
\r
4585 m_tab.AdjustRect(FALSE, &rect);
\r
4587 RECT rcWnd = { 0, 0, 1000, rect.top };
\r
4588 ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
\r
4590 int nHeight = rcWnd.bottom - rcWnd.top;
\r
4592 m_tab.DeleteItem(nIndex);
\r
4597 void ShowTabControl(bool bShow)
\r
4599 m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
\r
4602 void UpdateLayout()
\r
4605 GetClientRect(&rect);
\r
4607 if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
\r
4608 m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
\r
4610 if(m_nActivePage != -1)
\r
4611 ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, m_cyTabHeight, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
\r
4616 if(m_menu.m_hMenu != NULL)
\r
4617 BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
\r
4620 void UpdateTitleBar()
\r
4622 if(!m_wndTitleBar.IsWindow() || m_lpstrTitleBarBase == NULL)
\r
4623 return; // nothing to do
\r
4625 if(m_nActivePage != -1)
\r
4627 T* pT = static_cast<T*>(this);
\r
4628 LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
\r
4629 LPCTSTR lpstrDivider = pT->GetTitleDividerText();
\r
4630 int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
\r
4631 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
4632 LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
\r
4633 ATLASSERT(lpstrPageTitle != NULL);
\r
4634 if(lpstrPageTitle != NULL)
\r
4636 pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
\r
4637 SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, lpstrDivider);
\r
4638 SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
\r
4642 lpstrPageTitle = m_lpstrTitleBarBase;
\r
4645 m_wndTitleBar.SetWindowText(lpstrPageTitle);
\r
4649 m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
\r
4653 void DrawMoveMark(int nItem)
\r
4655 T* pT = static_cast<T*>(this);
\r
4657 if(m_nInsertItem != -1)
\r
4659 RECT rect = { 0 };
\r
4660 pT->GetMoveMarkRect(rect);
\r
4661 m_tab.InvalidateRect(&rect);
\r
4664 m_nInsertItem = nItem;
\r
4666 if(m_nInsertItem != -1)
\r
4668 CClientDC dc(m_tab.m_hWnd);
\r
4670 RECT rect = { 0 };
\r
4671 pT->GetMoveMarkRect(rect);
\r
4674 pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
\r
4676 brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
\r
4678 HPEN hPenOld = dc.SelectPen(pen);
\r
4679 HBRUSH hBrushOld = dc.SelectBrush(brush);
\r
4681 int x = rect.left;
\r
4683 POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
\r
4684 dc.Polygon(ptsTop, 3);
\r
4686 y = rect.bottom - 1;
\r
4687 POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
\r
4688 dc.Polygon(ptsBottom, 3);
\r
4690 dc.SelectPen(hPenOld);
\r
4691 dc.SelectBrush(hBrushOld);
\r
4695 void GetMoveMarkRect(RECT& rect) const
\r
4697 m_tab.GetClientRect(&rect);
\r
4699 RECT rcItem = { 0 };
\r
4700 m_tab.GetItemRect(m_nInsertItem, &rcItem);
\r
4702 if(m_nInsertItem <= m_nActivePage)
\r
4704 rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
\r
4705 rect.right = rcItem.left + m_cxMoveMark / 2;
\r
4709 rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
\r
4710 rect.right = rcItem.right + m_cxMoveMark / 2;
\r
4714 void SetMoveCursor(bool bCanMove)
\r
4716 ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
\r
4719 void GenerateDragImage(int nItem)
\r
4721 ATLASSERT(IsValidPageIndex(nItem));
\r
4723 #ifndef _WIN32_WCE
\r
4724 RECT rcItem = { 0 };
\r
4725 m_tab.GetItemRect(nItem, &rcItem);
\r
4726 ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
\r
4727 #else // CE specific
\r
4728 nItem; // avoid level 4 warning
\r
4729 RECT rcItem = { 0, 0, 40, 20 };
\r
4730 #endif // _WIN32_WCE
\r
4732 ATLASSERT(m_ilDrag.m_hImageList == NULL);
\r
4733 m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
\r
4735 CClientDC dc(m_hWnd);
\r
4737 dcMem.CreateCompatibleDC(dc);
\r
4738 ATLASSERT(dcMem.m_hDC != NULL);
\r
4739 dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
\r
4742 bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
\r
4743 ATLASSERT(bmp.m_hBitmap != NULL);
\r
4745 HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
\r
4746 #ifndef _WIN32_WCE
\r
4747 m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
\r
4748 #else // CE specific
\r
4749 dcMem.Rectangle(&rcItem);
\r
4750 #endif // _WIN32_WCE
\r
4751 dcMem.SelectBitmap(hBmpOld);
\r
4753 ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
\r
4756 void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
\r
4758 if(lstrlen(lpstrTitle) >= cchShortTitle)
\r
4760 LPCTSTR lpstrEllipsis = _T("...");
\r
4761 int cchEllipsis = lstrlen(lpstrEllipsis);
\r
4762 SecureHelper::strncpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
\r
4763 SecureHelper::strcat_x(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
\r
4767 SecureHelper::strcpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle);
\r
4771 #ifndef _WIN32_WCE
\r
4772 void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
\r
4774 ATLASSERT(pTTDI != NULL);
\r
4775 pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
\r
4777 #endif // !_WIN32_WCE
\r
4779 // Text for menu items and title bar - override to provide different strings
\r
4780 static LPCTSTR GetEmptyListText()
\r
4782 return _T("(Empty)");
\r
4785 static LPCTSTR GetWindowsMenuItemText()
\r
4787 return _T("&Windows...");
\r
4790 static LPCTSTR GetTitleDividerText()
\r
4795 // Notifications - override to provide different behavior
\r
4796 void OnPageActivated(int nPage)
\r
4798 NMHDR nmhdr = { 0 };
\r
4799 nmhdr.hwndFrom = m_hWnd;
\r
4800 nmhdr.idFrom = nPage;
\r
4801 nmhdr.code = TBVN_PAGEACTIVATED;
\r
4802 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
\r
4805 void OnContextMenu(int nPage, POINT pt)
\r
4807 m_tab.ClientToScreen(&pt);
\r
4809 TBVCONTEXTMENUINFO cmi = { 0 };
\r
4810 cmi.hdr.hwndFrom = m_hWnd;
\r
4811 cmi.hdr.idFrom = nPage;
\r
4812 cmi.hdr.code = TBVN_CONTEXTMENU;
\r
4814 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&cmi);
\r
4819 class CTabView : public CTabViewImpl<CTabView>
\r
4822 DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
\r
4825 }; // namespace WTL
\r
4827 #endif // __ATLCTRLX_H__
\r