Add ICU message format support
[chromium-blink-merge.git] / third_party / wtl / include / atlctrlx.h
blobe1ce59ed2642cf7bd9068fcb94670aa196d926d1
1 // Windows Template Library - WTL version 8.0
2 // Copyright (C) Microsoft Corporation. All rights reserved.
3 //
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Permissive License (Ms-PL) which can be found in the file
7 // Ms-PL.txt at the root of this distribution.
9 #ifndef __ATLCTRLX_H__
10 #define __ATLCTRLX_H__
12 #pragma once
14 #ifndef __cplusplus
15 #error ATL requires C++ compilation (use a .cpp suffix)
16 #endif
18 #ifndef __ATLAPP_H__
19 #error atlctrlx.h requires atlapp.h to be included first
20 #endif
22 #ifndef __ATLCTRLS_H__
23 #error atlctrlx.h requires atlctrls.h to be included first
24 #endif
26 #ifndef WM_UPDATEUISTATE
27 #define WM_UPDATEUISTATE 0x0128
28 #endif // !WM_UPDATEUISTATE
31 ///////////////////////////////////////////////////////////////////////////////
32 // Classes in this file:
34 // CBitmapButtonImpl<T, TBase, TWinTraits>
35 // CBitmapButton
36 // CCheckListViewCtrlImpl<T, TBase, TWinTraits>
37 // CCheckListViewCtrl
38 // CHyperLinkImpl<T, TBase, TWinTraits>
39 // CHyperLink
40 // CWaitCursor
41 // CCustomWaitCursor
42 // CMultiPaneStatusBarCtrlImpl<T, TBase>
43 // CMultiPaneStatusBarCtrl
44 // CPaneContainerImpl<T, TBase, TWinTraits>
45 // CPaneContainer
46 // CSortListViewImpl<T>
47 // CSortListViewCtrlImpl<T, TBase, TWinTraits>
48 // CSortListViewCtrl
49 // CTabViewImpl<T, TBase, TWinTraits>
50 // CTabView
52 namespace WTL
55 ///////////////////////////////////////////////////////////////////////////////
56 // CBitmapButton - bitmap button implementation
58 #ifndef _WIN32_WCE
60 // bitmap button extended styles
61 #define BMPBTN_HOVER 0x00000001
62 #define BMPBTN_AUTO3D_SINGLE 0x00000002
63 #define BMPBTN_AUTO3D_DOUBLE 0x00000004
64 #define BMPBTN_AUTOSIZE 0x00000008
65 #define BMPBTN_SHAREIMAGELISTS 0x00000010
66 #define BMPBTN_AUTOFIRE 0x00000020
68 template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
69 class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits>
71 public:
72 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
74 enum
76 _nImageNormal = 0,
77 _nImagePushed,
78 _nImageFocusOrHover,
79 _nImageDisabled,
81 _nImageCount = 4,
84 enum
86 ID_TIMER_FIRST = 1000,
87 ID_TIMER_REPEAT = 1001
90 // Bitmap button specific extended styles
91 DWORD m_dwExtendedStyle;
93 CImageList m_ImageList;
94 int m_nImage[_nImageCount];
96 CToolTipCtrl m_tip;
97 LPTSTR m_lpstrToolTipText;
99 // Internal states
100 unsigned m_fMouseOver:1;
101 unsigned m_fFocus:1;
102 unsigned m_fPressed:1;
105 // Constructor/Destructor
106 CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
107 m_ImageList(hImageList), m_dwExtendedStyle(dwExtendedStyle),
108 m_lpstrToolTipText(NULL),
109 m_fMouseOver(0), m_fFocus(0), m_fPressed(0)
111 m_nImage[_nImageNormal] = -1;
112 m_nImage[_nImagePushed] = -1;
113 m_nImage[_nImageFocusOrHover] = -1;
114 m_nImage[_nImageDisabled] = -1;
117 ~CBitmapButtonImpl()
119 if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
120 m_ImageList.Destroy();
121 delete [] m_lpstrToolTipText;
124 // overridden to provide proper initialization
125 BOOL SubclassWindow(HWND hWnd)
127 #if (_MSC_VER >= 1300)
128 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
129 #else // !(_MSC_VER >= 1300)
130 typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
131 BOOL bRet = _baseClass::SubclassWindow(hWnd);
132 #endif // !(_MSC_VER >= 1300)
133 if(bRet)
134 Init();
135 return bRet;
138 // Attributes
139 DWORD GetBitmapButtonExtendedStyle() const
141 return m_dwExtendedStyle;
144 DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
146 DWORD dwPrevStyle = m_dwExtendedStyle;
147 if(dwMask == 0)
148 m_dwExtendedStyle = dwExtendedStyle;
149 else
150 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
151 return dwPrevStyle;
154 HIMAGELIST GetImageList() const
156 return m_ImageList;
159 HIMAGELIST SetImageList(HIMAGELIST hImageList)
161 HIMAGELIST hImageListPrev = m_ImageList;
162 m_ImageList = hImageList;
163 if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd))
164 SizeToImage();
165 return hImageListPrev;
168 int GetToolTipTextLength() const
170 return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
173 bool GetToolTipText(LPTSTR lpstrText, int nLength) const
175 ATLASSERT(lpstrText != NULL);
176 if(m_lpstrToolTipText == NULL)
177 return false;
179 errno_t nRet = SecureHelper::strncpy_x(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
181 return (nRet == 0 || nRet == STRUNCATE);
184 bool SetToolTipText(LPCTSTR lpstrText)
186 if(m_lpstrToolTipText != NULL)
188 delete [] m_lpstrToolTipText;
189 m_lpstrToolTipText = NULL;
192 if(lpstrText == NULL)
194 if(m_tip.IsWindow())
195 m_tip.Activate(FALSE);
196 return true;
199 int cchLen = lstrlen(lpstrText) + 1;
200 ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
201 if(m_lpstrToolTipText == NULL)
202 return false;
204 SecureHelper::strcpy_x(m_lpstrToolTipText, cchLen, lpstrText);
205 if(m_tip.IsWindow())
207 m_tip.Activate(TRUE);
208 m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
211 return true;
214 // Operations
215 void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
217 if(nNormal != -1)
218 m_nImage[_nImageNormal] = nNormal;
219 if(nPushed != -1)
220 m_nImage[_nImagePushed] = nPushed;
221 if(nFocusOrHover != -1)
222 m_nImage[_nImageFocusOrHover] = nFocusOrHover;
223 if(nDisabled != -1)
224 m_nImage[_nImageDisabled] = nDisabled;
227 BOOL SizeToImage()
229 ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL);
230 int cx = 0;
231 int cy = 0;
232 if(!m_ImageList.GetIconSize(cx, cy))
233 return FALSE;
234 return ResizeClient(cx, cy);
237 // Overrideables
238 void DoPaint(CDCHandle dc)
240 ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set
241 ATLASSERT(m_nImage[0] != -1); // main bitmap must be set
243 // set bitmap according to the current button state
244 int nImage = -1;
245 bool bHover = IsHoverMode();
246 if(!IsWindowEnabled())
247 nImage = m_nImage[_nImageDisabled];
248 else if(m_fPressed == 1)
249 nImage = m_nImage[_nImagePushed];
250 else if((!bHover && m_fFocus == 1) || (bHover && m_fMouseOver == 1))
251 nImage = m_nImage[_nImageFocusOrHover];
252 if(nImage == -1) // not there, use default one
253 nImage = m_nImage[_nImageNormal];
255 // draw the button image
256 int xyPos = 0;
257 if((m_fPressed == 1) && ((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0) && (m_nImage[_nImagePushed] == -1))
258 xyPos = 1;
259 m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
261 // draw 3D border if required
262 if((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0)
264 RECT rect;
265 GetClientRect(&rect);
267 if(m_fPressed == 1)
268 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
269 else if(!bHover || m_fMouseOver == 1)
270 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
272 if(!bHover && m_fFocus == 1)
274 ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
275 dc.DrawFocusRect(&rect);
280 // Message map and handlers
281 BEGIN_MSG_MAP(CBitmapButtonImpl)
282 MESSAGE_HANDLER(WM_CREATE, OnCreate)
283 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
284 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
285 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
286 MESSAGE_HANDLER(WM_PAINT, OnPaint)
287 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
288 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
289 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
290 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
291 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
292 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
293 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
294 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
295 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
296 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
297 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
298 MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
299 MESSAGE_HANDLER(WM_TIMER, OnTimer)
300 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
301 END_MSG_MAP()
303 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
305 Init();
306 bHandled = FALSE;
307 return 1;
310 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
312 if(m_tip.IsWindow())
314 m_tip.DestroyWindow();
315 m_tip.m_hWnd = NULL;
317 bHandled = FALSE;
318 return 1;
321 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
323 MSG msg = { m_hWnd, uMsg, wParam, lParam };
324 if(m_tip.IsWindow())
325 m_tip.RelayEvent(&msg);
326 bHandled = FALSE;
327 return 1;
330 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
332 return 1; // no background needed
335 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
337 T* pT = static_cast<T*>(this);
338 if(wParam != NULL)
340 pT->DoPaint((HDC)wParam);
342 else
344 CPaintDC dc(m_hWnd);
345 pT->DoPaint(dc.m_hDC);
347 return 0;
350 LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
352 m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
353 Invalidate();
354 UpdateWindow();
355 bHandled = FALSE;
356 return 1;
359 LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
361 LRESULT lRet = 0;
362 if(IsHoverMode())
363 SetCapture();
364 else
365 lRet = DefWindowProc(uMsg, wParam, lParam);
366 if(::GetCapture() == m_hWnd)
368 m_fPressed = 1;
369 Invalidate();
370 UpdateWindow();
372 if((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0)
374 int nElapse = 250;
375 int nDelay = 0;
376 if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
377 nElapse += nDelay * 250; // all milli-seconds
378 SetTimer(ID_TIMER_FIRST, nElapse);
380 return lRet;
383 LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
385 LRESULT lRet = 0;
386 if(!IsHoverMode())
387 lRet = DefWindowProc(uMsg, wParam, lParam);
388 if(::GetCapture() != m_hWnd)
389 SetCapture();
390 if(m_fPressed == 0)
392 m_fPressed = 1;
393 Invalidate();
394 UpdateWindow();
396 return lRet;
399 LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
401 LRESULT lRet = 0;
402 bool bHover = IsHoverMode();
403 if(!bHover)
404 lRet = DefWindowProc(uMsg, wParam, lParam);
405 if(::GetCapture() == m_hWnd)
407 if(bHover && m_fPressed == 1)
408 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
409 ::ReleaseCapture();
411 return lRet;
414 LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
416 if(m_fPressed == 1)
418 m_fPressed = 0;
419 Invalidate();
420 UpdateWindow();
422 bHandled = FALSE;
423 return 1;
426 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
428 Invalidate();
429 UpdateWindow();
430 bHandled = FALSE;
431 return 1;
434 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
436 if(::GetCapture() == m_hWnd)
438 POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
439 ClientToScreen(&ptCursor);
440 RECT rect = { 0 };
441 GetWindowRect(&rect);
442 unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
443 if(m_fPressed != uPressed)
445 m_fPressed = uPressed;
446 Invalidate();
447 UpdateWindow();
450 else if(IsHoverMode() && m_fMouseOver == 0)
452 m_fMouseOver = 1;
453 Invalidate();
454 UpdateWindow();
455 StartTrackMouseLeave();
457 bHandled = FALSE;
458 return 1;
461 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
463 if(m_fMouseOver == 1)
465 m_fMouseOver = 0;
466 Invalidate();
467 UpdateWindow();
469 return 0;
472 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
474 if(wParam == VK_SPACE && IsHoverMode())
475 return 0; // ignore if in hover mode
476 if(wParam == VK_SPACE && m_fPressed == 0)
478 m_fPressed = 1;
479 Invalidate();
480 UpdateWindow();
482 bHandled = FALSE;
483 return 1;
486 LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
488 if(wParam == VK_SPACE && IsHoverMode())
489 return 0; // ignore if in hover mode
490 if(wParam == VK_SPACE && m_fPressed == 1)
492 m_fPressed = 0;
493 Invalidate();
494 UpdateWindow();
496 bHandled = FALSE;
497 return 1;
500 LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
502 ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
503 switch(wParam) // timer ID
505 case ID_TIMER_FIRST:
506 KillTimer(ID_TIMER_FIRST);
507 if(m_fPressed == 1)
509 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
510 int nElapse = 250;
511 int nRepeat = 40;
512 if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
513 nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated
514 SetTimer(ID_TIMER_REPEAT, nElapse);
516 break;
517 case ID_TIMER_REPEAT:
518 if(m_fPressed == 1)
519 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
520 else if(::GetCapture() != m_hWnd)
521 KillTimer(ID_TIMER_REPEAT);
522 break;
523 default: // not our timer
524 break;
526 return 0;
529 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
531 // If the control is subclassed or superclassed, this message can cause
532 // repainting without WM_PAINT. We don't use this state, so just do nothing.
533 return 0;
536 // Implementation
537 void Init()
539 // We need this style to prevent Windows from painting the button
540 ModifyStyle(0, BS_OWNERDRAW);
542 // create a tool tip
543 m_tip.Create(m_hWnd);
544 ATLASSERT(m_tip.IsWindow());
545 if(m_tip.IsWindow() && m_lpstrToolTipText != NULL)
547 m_tip.Activate(TRUE);
548 m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
551 if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0)
552 SizeToImage();
555 BOOL StartTrackMouseLeave()
557 TRACKMOUSEEVENT tme = { 0 };
558 tme.cbSize = sizeof(tme);
559 tme.dwFlags = TME_LEAVE;
560 tme.hwndTrack = m_hWnd;
561 return _TrackMouseEvent(&tme);
564 bool IsHoverMode() const
566 return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
571 class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
573 public:
574 DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
576 CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
577 CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
581 #endif // !_WIN32_WCE
584 ///////////////////////////////////////////////////////////////////////////////
585 // CCheckListCtrlView - list view control with check boxes
587 template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
588 class CCheckListViewCtrlImplTraits
590 public:
591 static DWORD GetWndStyle(DWORD dwStyle)
593 return (dwStyle == 0) ? t_dwStyle : dwStyle;
596 static DWORD GetWndExStyle(DWORD dwExStyle)
598 return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
601 static DWORD GetExtendedLVStyle()
603 return t_dwExListViewStyle;
607 typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT> CCheckListViewCtrlTraits;
609 template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
610 class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
612 public:
613 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
615 // Attributes
616 static DWORD GetExtendedLVStyle()
618 return TWinTraits::GetExtendedLVStyle();
621 // Operations
622 BOOL SubclassWindow(HWND hWnd)
624 #if (_MSC_VER >= 1300)
625 BOOL bRet = ATL::CWindowImplBaseT< TBase, TWinTraits>::SubclassWindow(hWnd);
626 #else // !(_MSC_VER >= 1300)
627 typedef ATL::CWindowImplBaseT< TBase, TWinTraits> _baseClass;
628 BOOL bRet = _baseClass::SubclassWindow(hWnd);
629 #endif // !(_MSC_VER >= 1300)
630 if(bRet)
632 T* pT = static_cast<T*>(this);
634 ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
635 SetExtendedListViewStyle(pT->GetExtendedLVStyle());
637 return bRet;
640 void CheckSelectedItems(int nCurrItem)
642 // first check if this item is selected
643 LVITEM lvi = { 0 };
644 lvi.iItem = nCurrItem;
645 lvi.iSubItem = 0;
646 lvi.mask = LVIF_STATE;
647 lvi.stateMask = LVIS_SELECTED;
648 GetItem(&lvi);
649 // if item is not selected, don't do anything
650 if(!(lvi.state & LVIS_SELECTED))
651 return;
652 // new check state will be reverse of the current state,
653 BOOL bCheck = !GetCheckState(nCurrItem);
654 int nItem = -1;
655 int nOldItem = -1;
656 while((nItem = GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
658 if(nItem != nCurrItem)
659 SetCheckState(nItem, bCheck);
660 nOldItem = nItem;
664 // Implementation
665 BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
666 MESSAGE_HANDLER(WM_CREATE, OnCreate)
667 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
668 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
669 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
670 END_MSG_MAP()
672 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
674 // first let list view control initialize everything
675 LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
676 T* pT = static_cast<T*>(this);
678 ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
679 SetExtendedListViewStyle(pT->GetExtendedLVStyle());
680 return lRet;
683 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
685 POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
686 LVHITTESTINFO lvh = { 0 };
687 lvh.pt = ptMsg;
688 if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0)
690 T* pT = static_cast<T*>(this);
691 pT->CheckSelectedItems(lvh.iItem);
693 bHandled = FALSE;
694 return 1;
697 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
699 if(wParam == VK_SPACE)
701 int nCurrItem = GetNextItem(-1, LVNI_FOCUSED);
702 if(nCurrItem != -1 && ::GetKeyState(VK_CONTROL) >= 0)
704 T* pT = static_cast<T*>(this);
705 pT->CheckSelectedItems(nCurrItem);
708 bHandled = FALSE;
709 return 1;
713 class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
715 public:
716 DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
720 ///////////////////////////////////////////////////////////////////////////////
721 // CHyperLink - hyper link control implementation
723 #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
724 __declspec(selectany) struct
726 enum { cxWidth = 32, cyHeight = 32 };
727 int xHotSpot;
728 int yHotSpot;
729 unsigned char arrANDPlane[cxWidth * cyHeight / 8];
730 unsigned char arrXORPlane[cxWidth * cyHeight / 8];
731 } _AtlHyperLink_CursorData =
733 5, 0,
735 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF,
736 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF,
737 0xF0, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF,
738 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF,
739 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF,
740 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
741 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
742 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
745 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
746 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x06, 0xD8, 0x00, 0x00,
747 0x06, 0xDA, 0x00, 0x00, 0x06, 0xDB, 0x00, 0x00, 0x67, 0xFB, 0x00, 0x00, 0x77, 0xFF, 0x00, 0x00,
748 0x37, 0xFF, 0x00, 0x00, 0x17, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00,
749 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00,
750 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
751 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
752 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
755 #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
757 #define HLINK_UNDERLINED 0x00000000
758 #define HLINK_NOTUNDERLINED 0x00000001
759 #define HLINK_UNDERLINEHOVER 0x00000002
760 #define HLINK_COMMANDBUTTON 0x00000004
761 #define HLINK_NOTIFYBUTTON 0x0000000C
762 #define HLINK_USETAGS 0x00000010
763 #define HLINK_USETAGSBOLD 0x00000030
764 #define HLINK_NOTOOLTIP 0x00000040
766 // Notes:
767 // - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
768 // - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
770 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
771 class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
773 public:
774 LPTSTR m_lpstrLabel;
775 LPTSTR m_lpstrHyperLink;
777 HCURSOR m_hCursor;
778 HFONT m_hFont;
779 HFONT m_hFontNormal;
781 RECT m_rcLink;
782 #ifndef _WIN32_WCE
783 CToolTipCtrl m_tip;
784 #endif // !_WIN32_WCE
786 COLORREF m_clrLink;
787 COLORREF m_clrVisited;
789 DWORD m_dwExtendedStyle; // Hyper Link specific extended styles
791 bool m_bPaintLabel:1;
792 bool m_bVisited:1;
793 bool m_bHover:1;
794 bool m_bInternalLinkFont:1;
797 // Constructor/Destructor
798 CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) :
799 m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
800 m_hCursor(NULL), m_hFont(NULL), m_hFontNormal(NULL),
801 m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
802 m_dwExtendedStyle(dwExtendedStyle),
803 m_bPaintLabel(true), m_bVisited(false),
804 m_bHover(false), m_bInternalLinkFont(false)
806 ::SetRectEmpty(&m_rcLink);
809 ~CHyperLinkImpl()
811 delete [] m_lpstrLabel;
812 delete [] m_lpstrHyperLink;
813 if(m_bInternalLinkFont && m_hFont != NULL)
814 ::DeleteObject(m_hFont);
815 #if (WINVER < 0x0500) && !defined(_WIN32_WCE)
816 // It was created, not loaded, so we have to destroy it
817 if(m_hCursor != NULL)
818 ::DestroyCursor(m_hCursor);
819 #endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
822 // Attributes
823 DWORD GetHyperLinkExtendedStyle() const
825 return m_dwExtendedStyle;
828 DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
830 DWORD dwPrevStyle = m_dwExtendedStyle;
831 if(dwMask == 0)
832 m_dwExtendedStyle = dwExtendedStyle;
833 else
834 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
835 return dwPrevStyle;
838 bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
840 if(m_lpstrLabel == NULL)
841 return false;
842 ATLASSERT(lpstrBuffer != NULL);
843 if(nLength <= lstrlen(m_lpstrLabel))
844 return false;
846 SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrLabel);
848 return true;
851 bool SetLabel(LPCTSTR lpstrLabel)
853 delete [] m_lpstrLabel;
854 m_lpstrLabel = NULL;
855 int cchLen = lstrlen(lpstrLabel) + 1;
856 ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
857 if(m_lpstrLabel == NULL)
858 return false;
860 SecureHelper::strcpy_x(m_lpstrLabel, cchLen, lpstrLabel);
861 T* pT = static_cast<T*>(this);
862 pT->CalcLabelRect();
864 if(m_hWnd != NULL)
865 SetWindowText(lpstrLabel); // Set this for accessibility
867 return true;
870 bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
872 if(m_lpstrHyperLink == NULL)
873 return false;
874 ATLASSERT(lpstrBuffer != NULL);
875 if(nLength <= lstrlen(m_lpstrHyperLink))
876 return false;
878 SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrHyperLink);
880 return true;
883 bool SetHyperLink(LPCTSTR lpstrLink)
885 delete [] m_lpstrHyperLink;
886 m_lpstrHyperLink = NULL;
887 int cchLen = lstrlen(lpstrLink) + 1;
888 ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
889 if(m_lpstrHyperLink == NULL)
890 return false;
892 SecureHelper::strcpy_x(m_lpstrHyperLink, cchLen, lpstrLink);
893 if(m_lpstrLabel == NULL)
895 T* pT = static_cast<T*>(this);
896 pT->CalcLabelRect();
898 #ifndef _WIN32_WCE
899 if(m_tip.IsWindow())
901 m_tip.Activate(TRUE);
902 m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
904 #endif // !_WIN32_WCE
905 return true;
908 HFONT GetLinkFont() const
910 return m_hFont;
913 void SetLinkFont(HFONT hFont)
915 if(m_bInternalLinkFont && m_hFont != NULL)
917 ::DeleteObject(m_hFont);
918 m_bInternalLinkFont = false;
920 m_hFont = hFont;
923 int GetIdealHeight() const
925 ATLASSERT(::IsWindow(m_hWnd));
926 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
927 return -1;
928 if(!m_bPaintLabel)
929 return -1;
931 CClientDC dc(m_hWnd);
932 RECT rect = { 0 };
933 GetClientRect(&rect);
934 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
935 RECT rcText = rect;
936 dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
937 dc.SelectFont(m_hFont);
938 RECT rcLink = rect;
939 dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
940 dc.SelectFont(hFontOld);
941 return __max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
944 bool GetIdealSize(SIZE& size) const
946 int cx = 0, cy = 0;
947 bool bRet = GetIdealSize(cx, cy);
948 if(bRet)
950 size.cx = cx;
951 size.cy = cy;
953 return bRet;
956 bool GetIdealSize(int& cx, int& cy) const
958 ATLASSERT(::IsWindow(m_hWnd));
959 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
960 return false;
961 if(!m_bPaintLabel)
962 return false;
964 CClientDC dc(m_hWnd);
965 RECT rcClient = { 0 };
966 GetClientRect(&rcClient);
967 RECT rcAll = rcClient;
969 if(IsUsingTags())
971 // find tags and label parts
972 LPTSTR lpstrLeft = NULL;
973 int cchLeft = 0;
974 LPTSTR lpstrLink = NULL;
975 int cchLink = 0;
976 LPTSTR lpstrRight = NULL;
977 int cchRight = 0;
979 const T* pT = static_cast<const T*>(this);
980 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
982 // get label part rects
983 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
984 RECT rcLeft = rcClient;
985 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
987 dc.SelectFont(m_hFont);
988 RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
989 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
991 dc.SelectFont(m_hFontNormal);
992 RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
993 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
995 dc.SelectFont(hFontOld);
997 int cyMax = __max(rcLeft.bottom, max(rcLink.bottom, rcRight.bottom));
998 ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
1000 else
1002 HFONT hOldFont = NULL;
1003 if(m_hFont != NULL)
1004 hOldFont = dc.SelectFont(m_hFont);
1005 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1006 DWORD dwStyle = GetStyle();
1007 int nDrawStyle = DT_LEFT;
1008 if (dwStyle & SS_CENTER)
1009 nDrawStyle = DT_CENTER;
1010 else if (dwStyle & SS_RIGHT)
1011 nDrawStyle = DT_RIGHT;
1012 dc.DrawText(lpstrText, -1, &rcAll, nDrawStyle | DT_WORDBREAK | DT_CALCRECT);
1013 if(m_hFont != NULL)
1014 dc.SelectFont(hOldFont);
1015 if (dwStyle & SS_CENTER)
1017 int dx = (rcClient.right - rcAll.right) / 2;
1018 ::OffsetRect(&rcAll, dx, 0);
1020 else if (dwStyle & SS_RIGHT)
1022 int dx = rcClient.right - rcAll.right;
1023 ::OffsetRect(&rcAll, dx, 0);
1027 cx = rcAll.right - rcAll.left;
1028 cy = rcAll.bottom - rcAll.top;
1030 return true;
1033 // for command buttons only
1034 bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
1036 ATLASSERT(IsCommandButton());
1037 return GetHyperLink(lpstrBuffer, nLength);
1040 bool SetToolTipText(LPCTSTR lpstrToolTipText)
1042 ATLASSERT(IsCommandButton());
1043 return SetHyperLink(lpstrToolTipText);
1046 // Operations
1047 BOOL SubclassWindow(HWND hWnd)
1049 ATLASSERT(m_hWnd == NULL);
1050 ATLASSERT(::IsWindow(hWnd));
1051 #if (_MSC_VER >= 1300)
1052 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
1053 #else // !(_MSC_VER >= 1300)
1054 typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
1055 BOOL bRet = _baseClass::SubclassWindow(hWnd);
1056 #endif // !(_MSC_VER >= 1300)
1057 if(bRet)
1059 T* pT = static_cast<T*>(this);
1060 pT->Init();
1062 return bRet;
1065 bool Navigate()
1067 ATLASSERT(::IsWindow(m_hWnd));
1068 bool bRet = true;
1069 if(IsNotifyButton())
1071 NMHDR nmhdr = { m_hWnd, GetDlgCtrlID(), NM_CLICK };
1072 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
1074 else if(IsCommandButton())
1076 ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
1078 else
1080 ATLASSERT(m_lpstrHyperLink != NULL);
1081 #ifndef _WIN32_WCE
1082 DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
1083 bRet = (dwRet > 32);
1084 #else // CE specific
1085 SHELLEXECUTEINFO shExeInfo = { sizeof(SHELLEXECUTEINFO), 0, 0, L"open", m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL, 0, 0, 0, 0, 0, 0, 0 };
1086 ::ShellExecuteEx(&shExeInfo);
1087 DWORD_PTR dwRet = (DWORD_PTR)shExeInfo.hInstApp;
1088 bRet = (dwRet == 0) || (dwRet > 32);
1089 #endif // _WIN32_WCE
1090 ATLASSERT(bRet);
1091 if(bRet)
1093 m_bVisited = true;
1094 Invalidate();
1097 return bRet;
1100 // Message map and handlers
1101 BEGIN_MSG_MAP(CHyperLinkImpl)
1102 MESSAGE_HANDLER(WM_CREATE, OnCreate)
1103 #ifndef _WIN32_WCE
1104 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
1105 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
1106 #endif // !_WIN32_WCE
1107 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1108 MESSAGE_HANDLER(WM_PAINT, OnPaint)
1109 #ifndef _WIN32_WCE
1110 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
1111 #endif // !_WIN32_WCE
1112 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
1113 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
1114 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
1115 #ifndef _WIN32_WCE
1116 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
1117 #endif // !_WIN32_WCE
1118 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
1119 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
1120 MESSAGE_HANDLER(WM_CHAR, OnChar)
1121 MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
1122 MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
1123 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
1124 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
1125 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
1126 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
1127 MESSAGE_HANDLER(WM_SIZE, OnSize)
1128 END_MSG_MAP()
1130 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1132 T* pT = static_cast<T*>(this);
1133 pT->Init();
1134 return 0;
1137 #ifndef _WIN32_WCE
1138 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1140 if(m_tip.IsWindow())
1142 m_tip.DestroyWindow();
1143 m_tip.m_hWnd = NULL;
1145 bHandled = FALSE;
1146 return 1;
1149 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1151 MSG msg = { m_hWnd, uMsg, wParam, lParam };
1152 if(m_tip.IsWindow() && IsUsingToolTip())
1153 m_tip.RelayEvent(&msg);
1154 bHandled = FALSE;
1155 return 1;
1157 #endif // !_WIN32_WCE
1159 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1161 return 1; // no background painting needed (we do it all during WM_PAINT)
1164 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
1166 if(!m_bPaintLabel)
1168 bHandled = FALSE;
1169 return 1;
1172 T* pT = static_cast<T*>(this);
1173 if(wParam != NULL)
1175 pT->DoEraseBackground((HDC)wParam);
1176 pT->DoPaint((HDC)wParam);
1178 else
1180 CPaintDC dc(m_hWnd);
1181 pT->DoEraseBackground(dc.m_hDC);
1182 pT->DoPaint(dc.m_hDC);
1185 return 0;
1188 LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1190 if(m_bPaintLabel)
1191 Invalidate();
1192 else
1193 bHandled = FALSE;
1194 return 0;
1197 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1199 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1200 if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
1202 ::SetCursor(m_hCursor);
1203 if(IsUnderlineHover())
1205 if(!m_bHover)
1207 m_bHover = true;
1208 InvalidateRect(&m_rcLink);
1209 UpdateWindow();
1210 #ifndef _WIN32_WCE
1211 StartTrackMouseLeave();
1212 #endif // !_WIN32_WCE
1216 else
1218 if(IsUnderlineHover())
1220 if(m_bHover)
1222 m_bHover = false;
1223 InvalidateRect(&m_rcLink);
1224 UpdateWindow();
1227 bHandled = FALSE;
1229 return 0;
1232 #ifndef _WIN32_WCE
1233 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1235 if(IsUnderlineHover() && m_bHover)
1237 m_bHover = false;
1238 InvalidateRect(&m_rcLink);
1239 UpdateWindow();
1241 return 0;
1243 #endif // !_WIN32_WCE
1245 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
1247 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1248 if(::PtInRect(&m_rcLink, pt))
1250 SetFocus();
1251 SetCapture();
1253 return 0;
1256 LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
1258 if(GetCapture() == m_hWnd)
1260 ReleaseCapture();
1261 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1262 if(::PtInRect(&m_rcLink, pt))
1264 T* pT = static_cast<T*>(this);
1265 pT->Navigate();
1268 return 0;
1271 LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1273 if(wParam == VK_RETURN || wParam == VK_SPACE)
1275 T* pT = static_cast<T*>(this);
1276 pT->Navigate();
1278 return 0;
1281 LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1283 return DLGC_WANTCHARS;
1286 LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1288 POINT pt = { 0, 0 };
1289 GetCursorPos(&pt);
1290 ScreenToClient(&pt);
1291 if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
1293 return TRUE;
1295 bHandled = FALSE;
1296 return FALSE;
1299 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1301 Invalidate();
1302 UpdateWindow();
1303 return 0;
1306 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1308 return (LRESULT)m_hFontNormal;
1311 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
1313 m_hFontNormal = (HFONT)wParam;
1314 if((BOOL)lParam)
1316 Invalidate();
1317 UpdateWindow();
1319 return 0;
1322 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1324 // If the control is subclassed or superclassed, this message can cause
1325 // repainting without WM_PAINT. We don't use this state, so just do nothing.
1326 return 0;
1329 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1331 T* pT = static_cast<T*>(this);
1332 pT->CalcLabelRect();
1333 pT->Invalidate();
1334 return 0;
1337 // Implementation
1338 void Init()
1340 ATLASSERT(::IsWindow(m_hWnd));
1342 // Check if we should paint a label
1343 const int cchBuff = 8;
1344 TCHAR szBuffer[cchBuff] = { 0 };
1345 if(::GetClassName(m_hWnd, szBuffer, cchBuff))
1347 if(lstrcmpi(szBuffer, _T("static")) == 0)
1349 ModifyStyle(0, SS_NOTIFY); // we need this
1350 DWORD dwStyle = GetStyle() & 0x000000FF;
1351 #ifndef _WIN32_WCE
1352 if(dwStyle == SS_ICON || dwStyle == SS_BLACKRECT || dwStyle == SS_GRAYRECT ||
1353 dwStyle == SS_WHITERECT || dwStyle == SS_BLACKFRAME || dwStyle == SS_GRAYFRAME ||
1354 dwStyle == SS_WHITEFRAME || dwStyle == SS_OWNERDRAW ||
1355 dwStyle == SS_BITMAP || dwStyle == SS_ENHMETAFILE)
1356 #else // CE specific
1357 if(dwStyle == SS_ICON || dwStyle == SS_BITMAP)
1358 #endif // _WIN32_WCE
1359 m_bPaintLabel = false;
1363 // create or load a cursor
1364 #if (WINVER >= 0x0500) || defined(_WIN32_WCE)
1365 m_hCursor = ::LoadCursor(NULL, IDC_HAND);
1366 #else
1367 m_hCursor = ::CreateCursor(ModuleHelper::GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane);
1368 #endif
1369 ATLASSERT(m_hCursor != NULL);
1371 // set font
1372 if(m_bPaintLabel)
1374 ATL::CWindow wnd = GetParent();
1375 m_hFontNormal = wnd.GetFont();
1376 if(m_hFontNormal == NULL)
1377 m_hFontNormal = (HFONT)::GetStockObject(SYSTEM_FONT);
1378 if(m_hFontNormal != NULL && m_hFont == NULL)
1380 LOGFONT lf = { 0 };
1381 CFontHandle font = m_hFontNormal;
1382 font.GetLogFont(&lf);
1383 if(IsUsingTagsBold())
1384 lf.lfWeight = FW_BOLD;
1385 else if(!IsNotUnderlined())
1386 lf.lfUnderline = TRUE;
1387 m_hFont = ::CreateFontIndirect(&lf);
1388 m_bInternalLinkFont = true;
1389 ATLASSERT(m_hFont != NULL);
1393 #ifndef _WIN32_WCE
1394 // create a tool tip
1395 m_tip.Create(m_hWnd);
1396 ATLASSERT(m_tip.IsWindow());
1397 #endif // !_WIN32_WCE
1399 // set label (defaults to window text)
1400 if(m_lpstrLabel == NULL)
1402 int nLen = GetWindowTextLength();
1403 if(nLen > 0)
1405 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
1406 LPTSTR lpstrText = buff.Allocate(nLen + 1);
1407 ATLASSERT(lpstrText != NULL);
1408 if((lpstrText != NULL) && (GetWindowText(lpstrText, nLen + 1) > 0))
1409 SetLabel(lpstrText);
1413 T* pT = static_cast<T*>(this);
1414 pT->CalcLabelRect();
1416 // set hyperlink (defaults to label), or just activate tool tip if already set
1417 if(m_lpstrHyperLink == NULL && !IsCommandButton())
1419 if(m_lpstrLabel != NULL)
1420 SetHyperLink(m_lpstrLabel);
1422 #ifndef _WIN32_WCE
1423 else
1425 m_tip.Activate(TRUE);
1426 m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
1428 #endif // !_WIN32_WCE
1430 // set link colors
1431 if(m_bPaintLabel)
1433 ATL::CRegKey rk;
1434 LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
1435 if(lRet == 0)
1437 const int cchValue = 12;
1438 TCHAR szValue[cchValue] = { 0 };
1439 #if (_ATL_VER >= 0x0700)
1440 ULONG ulCount = cchValue;
1441 lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
1442 #else
1443 DWORD dwCount = cchValue * sizeof(TCHAR);
1444 lRet = rk.QueryValue(szValue, _T("Anchor Color"), &dwCount);
1445 #endif
1446 if(lRet == 0)
1448 COLORREF clr = pT->_ParseColorString(szValue);
1449 ATLASSERT(clr != CLR_INVALID);
1450 if(clr != CLR_INVALID)
1451 m_clrLink = clr;
1454 #if (_ATL_VER >= 0x0700)
1455 ulCount = cchValue;
1456 lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
1457 #else
1458 dwCount = cchValue * sizeof(TCHAR);
1459 lRet = rk.QueryValue(szValue, _T("Anchor Color Visited"), &dwCount);
1460 #endif
1461 if(lRet == 0)
1463 COLORREF clr = pT->_ParseColorString(szValue);
1464 ATLASSERT(clr != CLR_INVALID);
1465 if(clr != CLR_INVALID)
1466 m_clrVisited = clr;
1472 static COLORREF _ParseColorString(LPTSTR lpstr)
1474 int c[3] = { -1, -1, -1 };
1475 LPTSTR p = NULL;
1476 for(int i = 0; i < 2; i++)
1478 for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
1480 if(*p == _T(','))
1482 *p = _T('\0');
1483 c[i] = T::_xttoi(lpstr);
1484 lpstr = &p[1];
1485 break;
1488 if(c[i] == -1)
1489 return CLR_INVALID;
1491 if(*lpstr == _T('\0'))
1492 return CLR_INVALID;
1493 c[2] = T::_xttoi(lpstr);
1495 return RGB(c[0], c[1], c[2]);
1498 bool CalcLabelRect()
1500 if(!::IsWindow(m_hWnd))
1501 return false;
1502 if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
1503 return false;
1505 CClientDC dc(m_hWnd);
1506 RECT rcClient = { 0 };
1507 GetClientRect(&rcClient);
1508 m_rcLink = rcClient;
1509 if(!m_bPaintLabel)
1510 return true;
1512 if(IsUsingTags())
1514 // find tags and label parts
1515 LPTSTR lpstrLeft = NULL;
1516 int cchLeft = 0;
1517 LPTSTR lpstrLink = NULL;
1518 int cchLink = 0;
1519 LPTSTR lpstrRight = NULL;
1520 int cchRight = 0;
1522 T* pT = static_cast<T*>(this);
1523 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
1524 ATLASSERT(lpstrLink != NULL);
1525 ATLASSERT(cchLink > 0);
1527 // get label part rects
1528 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1530 RECT rcLeft = rcClient;
1531 if(lpstrLeft != NULL)
1532 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
1534 dc.SelectFont(m_hFont);
1535 RECT rcLink = rcClient;
1536 if(lpstrLeft != NULL)
1537 rcLink.left = rcLeft.right;
1538 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
1540 dc.SelectFont(hFontOld);
1542 m_rcLink = rcLink;
1544 else
1546 HFONT hOldFont = NULL;
1547 if(m_hFont != NULL)
1548 hOldFont = dc.SelectFont(m_hFont);
1549 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1550 DWORD dwStyle = GetStyle();
1551 int nDrawStyle = DT_LEFT;
1552 if (dwStyle & SS_CENTER)
1553 nDrawStyle = DT_CENTER;
1554 else if (dwStyle & SS_RIGHT)
1555 nDrawStyle = DT_RIGHT;
1556 dc.DrawText(lpstrText, -1, &m_rcLink, nDrawStyle | DT_WORDBREAK | DT_CALCRECT);
1557 if(m_hFont != NULL)
1558 dc.SelectFont(hOldFont);
1559 if (dwStyle & SS_CENTER)
1561 int dx = (rcClient.right - m_rcLink.right) / 2;
1562 ::OffsetRect(&m_rcLink, dx, 0);
1564 else if (dwStyle & SS_RIGHT)
1566 int dx = rcClient.right - m_rcLink.right;
1567 ::OffsetRect(&m_rcLink, dx, 0);
1571 return true;
1574 void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
1576 lpstrLeft = NULL;
1577 cchLeft = 0;
1578 lpstrLink = NULL;
1579 cchLink = 0;
1580 lpstrRight = NULL;
1581 cchRight = 0;
1583 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1584 int cchText = lstrlen(lpstrText);
1585 bool bOutsideLink = true;
1586 for(int i = 0; i < cchText; i++)
1588 if(lpstrText[i] != _T('<'))
1589 continue;
1591 if(bOutsideLink)
1593 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
1595 if(i > 0)
1597 lpstrLeft = lpstrText;
1598 cchLeft = i;
1600 lpstrLink = &lpstrText[i + 3];
1601 bOutsideLink = false;
1604 else
1606 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
1608 cchLink = i - 3 - cchLeft;
1609 if(lpstrText[i + 4] != 0)
1611 lpstrRight = &lpstrText[i + 4];
1612 cchRight = cchText - (i + 4);
1613 break;
1621 void DoEraseBackground(CDCHandle dc)
1623 HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd);
1624 if(hBrush != NULL)
1626 RECT rect = { 0 };
1627 GetClientRect(&rect);
1628 dc.FillRect(&rect, hBrush);
1632 void DoPaint(CDCHandle dc)
1634 if(IsUsingTags())
1636 // find tags and label parts
1637 LPTSTR lpstrLeft = NULL;
1638 int cchLeft = 0;
1639 LPTSTR lpstrLink = NULL;
1640 int cchLink = 0;
1641 LPTSTR lpstrRight = NULL;
1642 int cchRight = 0;
1644 T* pT = static_cast<T*>(this);
1645 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
1647 // get label part rects
1648 RECT rcClient = { 0 };
1649 GetClientRect(&rcClient);
1651 dc.SetBkMode(TRANSPARENT);
1652 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1654 if(lpstrLeft != NULL)
1655 dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | DT_WORDBREAK);
1657 COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
1658 if(m_hFont != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
1659 dc.SelectFont(m_hFont);
1660 else
1661 dc.SelectFont(m_hFontNormal);
1663 dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | DT_WORDBREAK);
1665 dc.SetTextColor(clrOld);
1666 dc.SelectFont(m_hFontNormal);
1667 if(lpstrRight != NULL)
1669 RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
1670 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | DT_WORDBREAK);
1673 if(GetFocus() == m_hWnd)
1674 dc.DrawFocusRect(&m_rcLink);
1676 dc.SelectFont(hFontOld);
1678 else
1680 dc.SetBkMode(TRANSPARENT);
1681 COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
1683 HFONT hFontOld = NULL;
1684 if(m_hFont != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
1685 hFontOld = dc.SelectFont(m_hFont);
1686 else
1687 hFontOld = dc.SelectFont(m_hFontNormal);
1689 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1691 DWORD dwStyle = GetStyle();
1692 int nDrawStyle = DT_LEFT;
1693 if (dwStyle & SS_CENTER)
1694 nDrawStyle = DT_CENTER;
1695 else if (dwStyle & SS_RIGHT)
1696 nDrawStyle = DT_RIGHT;
1698 dc.DrawText(lpstrText, -1, &m_rcLink, nDrawStyle | DT_WORDBREAK);
1700 if(GetFocus() == m_hWnd)
1701 dc.DrawFocusRect(&m_rcLink);
1703 dc.SetTextColor(clrOld);
1704 dc.SelectFont(hFontOld);
1708 #ifndef _WIN32_WCE
1709 BOOL StartTrackMouseLeave()
1711 TRACKMOUSEEVENT tme = { 0 };
1712 tme.cbSize = sizeof(tme);
1713 tme.dwFlags = TME_LEAVE;
1714 tme.hwndTrack = m_hWnd;
1715 return _TrackMouseEvent(&tme);
1717 #endif // !_WIN32_WCE
1719 // Implementation helpers
1720 bool IsUnderlined() const
1722 return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
1725 bool IsNotUnderlined() const
1727 return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
1730 bool IsUnderlineHover() const
1732 return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
1735 bool IsCommandButton() const
1737 return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
1740 bool IsNotifyButton() const
1742 return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
1745 bool IsUsingTags() const
1747 return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
1750 bool IsUsingTagsBold() const
1752 return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
1755 bool IsUsingToolTip() const
1757 return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
1760 static int _xttoi(const TCHAR* nptr)
1762 #ifndef _ATL_MIN_CRT
1763 return _ttoi(nptr);
1764 #else // _ATL_MIN_CRT
1765 while(*nptr == _T(' ')) // skip spaces
1766 ++nptr;
1768 int c = (int)(_TUCHAR)*nptr++;
1769 int sign = c; // save sign indication
1770 if (c == _T('-') || c == _T('+'))
1771 c = (int)(_TUCHAR)*nptr++; // skip sign
1773 int total = 0;
1774 while((TCHAR)c >= _T('0') && (TCHAR)c <= _T('9'))
1776 total = 10 * total + ((TCHAR)c - _T('0')); // accumulate digit
1777 c = (int)(_TUCHAR)*nptr++; // get next char
1780 // return result, negated if necessary
1781 return ((TCHAR)sign != _T('-')) ? total : -total;
1782 #endif // _ATL_MIN_CRT
1787 class CHyperLink : public CHyperLinkImpl<CHyperLink>
1789 public:
1790 DECLARE_WND_CLASS(_T("WTL_HyperLink"))
1794 ///////////////////////////////////////////////////////////////////////////////
1795 // CWaitCursor - displays a wait cursor
1797 class CWaitCursor
1799 public:
1800 // Data
1801 HCURSOR m_hWaitCursor;
1802 HCURSOR m_hOldCursor;
1803 bool m_bInUse;
1805 // Constructor/destructor
1806 CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
1808 HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
1809 m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
1810 ATLASSERT(m_hWaitCursor != NULL);
1812 if(bSet)
1813 Set();
1816 ~CWaitCursor()
1818 Restore();
1821 // Methods
1822 bool Set()
1824 if(m_bInUse)
1825 return false;
1826 m_hOldCursor = ::SetCursor(m_hWaitCursor);
1827 m_bInUse = true;
1828 return true;
1831 bool Restore()
1833 if(!m_bInUse)
1834 return false;
1835 ::SetCursor(m_hOldCursor);
1836 m_bInUse = false;
1837 return true;
1842 ///////////////////////////////////////////////////////////////////////////////
1843 // CCustomWaitCursor - for custom and animated cursors
1845 class CCustomWaitCursor : public CWaitCursor
1847 public:
1848 // Constructor/destructor
1849 CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) :
1850 CWaitCursor(false, IDC_WAIT, true)
1852 if(hInstance == NULL)
1853 hInstance = ModuleHelper::GetResourceInstance();
1854 m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
1856 if(bSet)
1857 Set();
1860 ~CCustomWaitCursor()
1862 Restore();
1863 #if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
1864 ::DestroyCursor(m_hWaitCursor);
1865 #endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
1870 ///////////////////////////////////////////////////////////////////////////////
1871 // CMultiPaneStatusBarCtrl - Status Bar with multiple panes
1873 template <class T, class TBase = CStatusBarCtrl>
1874 class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase >
1876 public:
1877 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
1879 // Data
1880 enum { m_cxPaneMargin = 3 };
1882 int m_nPanes;
1883 int* m_pPane;
1885 // Constructor/destructor
1886 CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL)
1889 ~CMultiPaneStatusBarCtrlImpl()
1891 delete [] m_pPane;
1894 // Methods
1895 HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
1897 #if (_MSC_VER >= 1300)
1898 return ATL::CWindowImpl< T, TBase >::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
1899 #else // !(_MSC_VER >= 1300)
1900 typedef ATL::CWindowImpl< T, TBase > _baseClass;
1901 return _baseClass::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
1902 #endif // !(_MSC_VER >= 1300)
1905 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)
1907 const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
1908 TCHAR szText[cchMax];
1909 szText[0] = 0;
1910 ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
1911 return Create(hWndParent, szText, dwStyle, nID);
1914 BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true)
1916 ATLASSERT(::IsWindow(m_hWnd));
1917 ATLASSERT(nPanes > 0);
1919 m_nPanes = nPanes;
1920 delete [] m_pPane;
1921 m_pPane = NULL;
1923 ATLTRY(m_pPane = new int[nPanes]);
1924 ATLASSERT(m_pPane != NULL);
1925 if(m_pPane == NULL)
1926 return FALSE;
1928 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
1929 int* pPanesPos = buff.Allocate(nPanes);
1930 ATLASSERT(pPanesPos != NULL);
1931 if(pPanesPos == NULL)
1932 return FALSE;
1934 SecureHelper::memcpy_x(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int));
1936 // get status bar DC and set font
1937 CClientDC dc(m_hWnd);
1938 HFONT hOldFont = dc.SelectFont(GetFont());
1940 // get status bar borders
1941 int arrBorders[3] = { 0 };
1942 GetBorders(arrBorders);
1944 const int cchBuff = 128;
1945 TCHAR szBuff[cchBuff] = { 0 };
1946 SIZE size = { 0, 0 };
1947 int cxLeft = arrBorders[0];
1949 // calculate right edge of each part
1950 for(int i = 0; i < nPanes; i++)
1952 if(pPanes[i] == ID_DEFAULT_PANE)
1954 // make very large, will be resized later
1955 pPanesPos[i] = INT_MAX / 2;
1957 else
1959 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
1960 dc.GetTextExtent(szBuff, lstrlen(szBuff), &size);
1961 T* pT = static_cast<T*>(this);
1963 pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin;
1965 cxLeft = pPanesPos[i];
1968 BOOL bRet = SetParts(nPanes, pPanesPos);
1970 if(bRet && bSetText)
1972 for(int i = 0; i < nPanes; i++)
1974 if(pPanes[i] != ID_DEFAULT_PANE)
1976 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
1977 SetPaneText(m_pPane[i], szBuff);
1982 dc.SelectFont(hOldFont);
1983 return bRet;
1986 bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const
1988 ATLASSERT(::IsWindow(m_hWnd));
1989 int nIndex = GetPaneIndexFromID(nPaneID);
1990 if(nIndex == -1)
1991 return false;
1993 int nLength = GetTextLength(nIndex, pnType);
1994 if(pcchLength != NULL)
1995 *pcchLength = nLength;
1997 return true;
2000 BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const
2002 ATLASSERT(::IsWindow(m_hWnd));
2003 int nIndex = GetPaneIndexFromID(nPaneID);
2004 if(nIndex == -1)
2005 return FALSE;
2007 int nLength = GetText(nIndex, lpstrText, pnType);
2008 if(pcchLength != NULL)
2009 *pcchLength = nLength;
2011 return TRUE;
2014 BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0)
2016 ATLASSERT(::IsWindow(m_hWnd));
2017 int nIndex = GetPaneIndexFromID(nPaneID);
2018 if(nIndex == -1)
2019 return FALSE;
2021 return SetText(nIndex, lpstrText, nType);
2024 BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const
2026 ATLASSERT(::IsWindow(m_hWnd));
2027 int nIndex = GetPaneIndexFromID(nPaneID);
2028 if(nIndex == -1)
2029 return FALSE;
2031 return GetRect(nIndex, lpRect);
2034 BOOL SetPaneWidth(int nPaneID, int cxWidth)
2036 ATLASSERT(::IsWindow(m_hWnd));
2037 ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one
2038 int nIndex = GetPaneIndexFromID(nPaneID);
2039 if(nIndex == -1)
2040 return FALSE;
2042 // get pane positions
2043 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
2044 int* pPanesPos = buff.Allocate(m_nPanes);
2045 if(pPanesPos == NULL)
2046 return FALSE;
2047 GetParts(m_nPanes, pPanesPos);
2048 // calculate offset
2049 int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]);
2050 int cxOff = cxWidth - cxPaneWidth;
2051 // find variable width pane
2052 int nDef = m_nPanes;
2053 for(int i = 0; i < m_nPanes; i++)
2055 if(m_pPane[i] == ID_DEFAULT_PANE)
2057 nDef = i;
2058 break;
2061 // resize
2062 if(nIndex < nDef) // before default pane
2064 for(int i = nIndex; i < nDef; i++)
2065 pPanesPos[i] += cxOff;
2068 else // after default one
2070 for(int i = nDef; i < nIndex; i++)
2071 pPanesPos[i] -= cxOff;
2073 // set pane postions
2074 return SetParts(m_nPanes, pPanesPos);
2077 #if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
2078 BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const
2080 ATLASSERT(::IsWindow(m_hWnd));
2081 int nIndex = GetPaneIndexFromID(nPaneID);
2082 if(nIndex == -1)
2083 return FALSE;
2085 GetTipText(nIndex, lpstrText, nSize);
2086 return TRUE;
2089 BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText)
2091 ATLASSERT(::IsWindow(m_hWnd));
2092 int nIndex = GetPaneIndexFromID(nPaneID);
2093 if(nIndex == -1)
2094 return FALSE;
2096 SetTipText(nIndex, lpstrText);
2097 return TRUE;
2099 #endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
2101 #if ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
2102 BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const
2104 ATLASSERT(::IsWindow(m_hWnd));
2105 int nIndex = GetPaneIndexFromID(nPaneID);
2106 if(nIndex == -1)
2107 return FALSE;
2109 hIcon = GetIcon(nIndex);
2110 return TRUE;
2113 BOOL SetPaneIcon(int nPaneID, HICON hIcon)
2115 ATLASSERT(::IsWindow(m_hWnd));
2116 int nIndex = GetPaneIndexFromID(nPaneID);
2117 if(nIndex == -1)
2118 return FALSE;
2120 return SetIcon(nIndex, hIcon);
2122 #endif // ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
2124 // Message map and handlers
2125 BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >)
2126 MESSAGE_HANDLER(WM_SIZE, OnSize)
2127 END_MSG_MAP()
2129 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
2131 LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
2132 if(wParam != SIZE_MINIMIZED && m_nPanes > 0)
2134 T* pT = static_cast<T*>(this);
2135 pT->UpdatePanesLayout();
2137 return lRet;
2140 // Implementation
2141 BOOL UpdatePanesLayout()
2143 // get pane positions
2144 CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
2145 int* pPanesPos = buff.Allocate(m_nPanes);
2146 ATLASSERT(pPanesPos != NULL);
2147 if(pPanesPos == NULL)
2148 return FALSE;
2149 int nRet = GetParts(m_nPanes, pPanesPos);
2150 ATLASSERT(nRet == m_nPanes);
2151 if(nRet != m_nPanes)
2152 return FALSE;
2153 // calculate offset
2154 RECT rcClient = { 0 };
2155 GetClientRect(&rcClient);
2156 int cxOff = rcClient.right - pPanesPos[m_nPanes - 1];
2157 #ifndef _WIN32_WCE
2158 // Move panes left if size grip box is present
2159 if((GetStyle() & SBARS_SIZEGRIP) != 0)
2160 cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE);
2161 #endif // !_WIN32_WCE
2162 // find variable width pane
2163 int i;
2164 for(i = 0; i < m_nPanes; i++)
2166 if(m_pPane[i] == ID_DEFAULT_PANE)
2167 break;
2169 // resize all panes from the variable one to the right
2170 if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1]))
2172 for(; i < m_nPanes; i++)
2173 pPanesPos[i] += cxOff;
2175 // set pane postions
2176 return SetParts(m_nPanes, pPanesPos);
2179 int GetPaneIndexFromID(int nPaneID) const
2181 for(int i = 0; i < m_nPanes; i++)
2183 if(m_pPane[i] == nPaneID)
2184 return i;
2187 return -1; // not found
2191 class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl<CMultiPaneStatusBarCtrl>
2193 public:
2194 DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName())
2198 ///////////////////////////////////////////////////////////////////////////////
2199 // CPaneContainer - provides header with title and close button for panes
2201 // pane container extended styles
2202 #define PANECNT_NOCLOSEBUTTON 0x00000001
2203 #define PANECNT_VERTICAL 0x00000002
2204 #define PANECNT_FLATBORDER 0x00000004
2205 #define PANECNT_NOBORDER 0x00000008
2207 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
2208 class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
2210 public:
2211 DECLARE_WND_CLASS_EX(NULL, 0, -1)
2213 // Constants
2214 enum
2216 m_cxyBorder = 2,
2217 m_cxyTextOffset = 4,
2218 m_cxyBtnOffset = 1,
2220 m_cchTitle = 80,
2222 m_cxImageTB = 13,
2223 m_cyImageTB = 11,
2224 m_cxyBtnAddTB = 7,
2226 m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset,
2228 m_xBtnImageLeft = 6,
2229 m_yBtnImageTop = 5,
2230 m_xBtnImageRight = 12,
2231 m_yBtnImageBottom = 11,
2233 m_nCloseBtnID = ID_PANE_CLOSE
2236 // Data members
2237 CToolBarCtrl m_tb;
2238 ATL::CWindow m_wndClient;
2239 int m_cxyHeader;
2240 TCHAR m_szTitle[m_cchTitle];
2241 DWORD m_dwExtendedStyle; // Pane container specific extended styles
2244 // Constructor
2245 CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0)
2247 m_szTitle[0] = 0;
2250 // Attributes
2251 DWORD GetPaneContainerExtendedStyle() const
2253 return m_dwExtendedStyle;
2256 DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
2258 DWORD dwPrevStyle = m_dwExtendedStyle;
2259 if(dwMask == 0)
2260 m_dwExtendedStyle = dwExtendedStyle;
2261 else
2262 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
2263 if(m_hWnd != NULL)
2265 T* pT = static_cast<T*>(this);
2266 bool bUpdate = false;
2268 if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
2270 pT->CreateCloseButton();
2271 bUpdate = true;
2273 else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
2275 pT->DestroyCloseButton();
2276 bUpdate = true;
2279 if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
2281 pT->CalcSize();
2282 bUpdate = true;
2285 if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) !=
2286 (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border
2288 bUpdate = true;
2291 if(bUpdate)
2292 pT->UpdateLayout();
2294 return dwPrevStyle;
2297 HWND GetClient() const
2299 return m_wndClient;
2302 HWND SetClient(HWND hWndClient)
2304 HWND hWndOldClient = m_wndClient;
2305 m_wndClient = hWndClient;
2306 if(m_hWnd != NULL)
2308 T* pT = static_cast<T*>(this);
2309 pT->UpdateLayout();
2311 return hWndOldClient;
2314 BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const
2316 ATLASSERT(lpstrTitle != NULL);
2318 errno_t nRet = SecureHelper::strncpy_x(lpstrTitle, cchLength, m_szTitle, _TRUNCATE);
2320 return (nRet == 0 || nRet == STRUNCATE);
2323 BOOL SetTitle(LPCTSTR lpstrTitle)
2325 ATLASSERT(lpstrTitle != NULL);
2327 errno_t nRet = SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
2328 bool bRet = (nRet == 0 || nRet == STRUNCATE);
2329 if(bRet && m_hWnd != NULL)
2331 T* pT = static_cast<T*>(this);
2332 pT->UpdateLayout();
2335 return bRet;
2338 int GetTitleLength() const
2340 return lstrlen(m_szTitle);
2343 // Methods
2344 HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
2345 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
2347 if(lpstrTitle != NULL)
2348 SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
2349 #if (_MSC_VER >= 1300)
2350 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2351 #else // !(_MSC_VER >= 1300)
2352 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
2353 return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2354 #endif // !(_MSC_VER >= 1300)
2357 HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
2358 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
2360 if(uTitleID != 0U)
2361 ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
2362 #if (_MSC_VER >= 1300)
2363 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2364 #else // !(_MSC_VER >= 1300)
2365 typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
2366 return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
2367 #endif // !(_MSC_VER >= 1300)
2370 BOOL EnableCloseButton(BOOL bEnable)
2372 ATLASSERT(::IsWindow(m_hWnd));
2373 T* pT = static_cast<T*>(this);
2374 pT; // avoid level 4 warning
2375 return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE;
2378 void UpdateLayout()
2380 RECT rcClient = { 0 };
2381 GetClientRect(&rcClient);
2382 T* pT = static_cast<T*>(this);
2383 pT->UpdateLayout(rcClient.right, rcClient.bottom);
2386 // Message map and handlers
2387 BEGIN_MSG_MAP(CPaneContainerImpl)
2388 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2389 MESSAGE_HANDLER(WM_SIZE, OnSize)
2390 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
2391 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2392 MESSAGE_HANDLER(WM_PAINT, OnPaint)
2393 #ifndef _WIN32_WCE
2394 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
2395 #endif // !_WIN32_WCE
2396 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2397 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2398 FORWARD_NOTIFICATIONS()
2399 END_MSG_MAP()
2401 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2403 T* pT = static_cast<T*>(this);
2404 pT->CalcSize();
2406 if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)
2407 pT->CreateCloseButton();
2409 return 0;
2412 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
2414 T* pT = static_cast<T*>(this);
2415 pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
2416 return 0;
2419 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2421 if(m_wndClient.m_hWnd != NULL)
2422 m_wndClient.SetFocus();
2423 return 0;
2426 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2428 return 1; // no background needed
2431 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
2433 T* pT = static_cast<T*>(this);
2434 if(wParam != NULL)
2436 pT->DrawPaneTitle((HDC)wParam);
2438 if(m_wndClient.m_hWnd == NULL) // no client window
2439 pT->DrawPane((HDC)wParam);
2441 else
2443 CPaintDC dc(m_hWnd);
2444 pT->DrawPaneTitle(dc.m_hDC);
2446 if(m_wndClient.m_hWnd == NULL) // no client window
2447 pT->DrawPane(dc.m_hDC);
2450 return 0;
2453 LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
2455 if(m_tb.m_hWnd == NULL)
2457 bHandled = FALSE;
2458 return 1;
2461 T* pT = static_cast<T*>(this);
2463 LPNMHDR lpnmh = (LPNMHDR)lParam;
2464 LRESULT lRet = 0;
2466 // pass toolbar custom draw notifications to the base class
2467 if(lpnmh->code == NM_CUSTOMDRAW && lpnmh->hwndFrom == m_tb.m_hWnd)
2468 lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
2469 #ifndef _WIN32_WCE
2470 // tooltip notifications come with the tooltip window handle and button ID,
2471 // pass them to the parent if we don't handle them
2472 else if(lpnmh->code == TTN_GETDISPINFO && lpnmh->idFrom == pT->m_nCloseBtnID)
2473 bHandled = pT->GetToolTipText(lpnmh);
2474 #endif // !_WIN32_WCE
2475 // only let notifications not from the toolbar go to the parent
2476 else if(lpnmh->hwndFrom != m_tb.m_hWnd && lpnmh->idFrom != pT->m_nCloseBtnID)
2477 bHandled = FALSE;
2479 return lRet;
2482 LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2484 // if command comes from the close button, substitute HWND of the pane container instead
2485 if(m_tb.m_hWnd != NULL && (HWND)lParam == m_tb.m_hWnd)
2486 return ::SendMessage(GetParent(), WM_COMMAND, wParam, (LPARAM)m_hWnd);
2488 bHandled = FALSE;
2489 return 1;
2492 // Custom draw overrides
2493 DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
2495 return CDRF_NOTIFYITEMDRAW; // we need per-item notifications
2498 DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
2500 CDCHandle dc = lpNMCustomDraw->hdc;
2501 #if (_WIN32_IE >= 0x0400)
2502 RECT& rc = lpNMCustomDraw->rc;
2503 #else // !(_WIN32_IE >= 0x0400)
2504 RECT rc;
2505 m_tb.GetItemRect(0, &rc);
2506 #endif // !(_WIN32_IE >= 0x0400)
2508 dc.FillRect(&rc, COLOR_3DFACE);
2510 return CDRF_NOTIFYPOSTPAINT;
2513 DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
2515 CDCHandle dc = lpNMCustomDraw->hdc;
2516 #if (_WIN32_IE >= 0x0400)
2517 RECT& rc = lpNMCustomDraw->rc;
2518 #else // !(_WIN32_IE >= 0x0400)
2519 RECT rc = { 0 };
2520 m_tb.GetItemRect(0, &rc);
2521 #endif // !(_WIN32_IE >= 0x0400)
2523 RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 };
2524 ::OffsetRect(&rcImage, rc.left, rc.top);
2525 T* pT = static_cast<T*>(this);
2527 if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0)
2529 RECT rcShadow = rcImage;
2530 ::OffsetRect(&rcShadow, 1, 1);
2531 CPen pen1;
2532 pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT));
2533 pT->DrawButtonImage(dc, rcShadow, pen1);
2534 CPen pen2;
2535 pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW));
2536 pT->DrawButtonImage(dc, rcImage, pen2);
2538 else
2540 if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0)
2541 ::OffsetRect(&rcImage, 1, 1);
2542 CPen pen;
2543 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
2544 pT->DrawButtonImage(dc, rcImage, pen);
2547 return CDRF_DODEFAULT; // continue with the default item painting
2550 // Implementation - overrideable methods
2551 void UpdateLayout(int cxWidth, int cyHeight)
2553 ATLASSERT(::IsWindow(m_hWnd));
2554 RECT rect = { 0 };
2556 if(IsVertical())
2558 ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight);
2559 if(m_tb.m_hWnd != NULL)
2560 m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
2562 if(m_wndClient.m_hWnd != NULL)
2563 m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER);
2564 else
2565 rect.right = cxWidth;
2567 else
2569 ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader);
2570 if(m_tb.m_hWnd != NULL)
2571 m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
2573 if(m_wndClient.m_hWnd != NULL)
2574 m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER);
2575 else
2576 rect.bottom = cyHeight;
2579 InvalidateRect(&rect);
2582 void CreateCloseButton()
2584 ATLASSERT(m_tb.m_hWnd == NULL);
2585 // create toolbar for the "x" button
2586 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);
2587 ATLASSERT(m_tb.IsWindow());
2589 if(m_tb.m_hWnd != NULL)
2591 T* pT = static_cast<T*>(this);
2592 pT; // avoid level 4 warning
2594 m_tb.SetButtonStructSize();
2596 TBBUTTON tbbtn = { 0 };
2597 tbbtn.idCommand = pT->m_nCloseBtnID;
2598 tbbtn.fsState = TBSTATE_ENABLED;
2599 tbbtn.fsStyle = TBSTYLE_BUTTON;
2600 m_tb.AddButtons(1, &tbbtn);
2602 m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB);
2603 m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB);
2605 if(IsVertical())
2606 m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOACTIVATE);
2607 else
2608 m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
2612 void DestroyCloseButton()
2614 if(m_tb.m_hWnd != NULL)
2615 m_tb.DestroyWindow();
2618 void CalcSize()
2620 T* pT = static_cast<T*>(this);
2621 CFontHandle font = pT->GetTitleFont();
2622 LOGFONT lf = { 0 };
2623 font.GetLogFont(lf);
2624 if(IsVertical())
2626 m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder;
2628 else
2630 int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset;
2631 int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset;
2632 m_cxyHeader = __max(cyFont, cyBtn);
2636 HFONT GetTitleFont() const
2638 return AtlGetDefaultGuiFont();
2641 #ifndef _WIN32_WCE
2642 BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
2644 return FALSE;
2646 #endif // !_WIN32_WCE
2648 void DrawPaneTitle(CDCHandle dc)
2650 RECT rect = { 0 };
2651 GetClientRect(&rect);
2653 UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST;
2654 if(IsVertical())
2656 rect.right = rect.left + m_cxyHeader;
2657 uBorder |= BF_BOTTOM;
2659 else
2661 rect.bottom = rect.top + m_cxyHeader;
2662 uBorder |= BF_RIGHT;
2665 if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0)
2667 if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0)
2668 uBorder |= BF_FLAT;
2669 dc.DrawEdge(&rect, EDGE_ETCHED, uBorder);
2671 dc.FillRect(&rect, COLOR_3DFACE);
2673 if(!IsVertical()) // draw title only for horizontal pane container
2675 dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
2676 dc.SetBkMode(TRANSPARENT);
2677 T* pT = static_cast<T*>(this);
2678 HFONT hFontOld = dc.SelectFont(pT->GetTitleFont());
2679 rect.left += m_cxyTextOffset;
2680 rect.right -= m_cxyTextOffset;
2681 if(m_tb.m_hWnd != NULL)
2682 rect.right -= m_cxToolBar;;
2683 #ifndef _WIN32_WCE
2684 dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
2685 #else // CE specific
2686 dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER);
2687 #endif // _WIN32_WCE
2688 dc.SelectFont(hFontOld);
2692 // called only if pane is empty
2693 void DrawPane(CDCHandle dc)
2695 RECT rect = { 0 };
2696 GetClientRect(&rect);
2697 if(IsVertical())
2698 rect.left += m_cxyHeader;
2699 else
2700 rect.top += m_cxyHeader;
2701 if((GetExStyle() & WS_EX_CLIENTEDGE) == 0)
2702 dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
2703 dc.FillRect(&rect, COLOR_APPWORKSPACE);
2706 // drawing helper - draws "x" button image
2707 void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
2709 #if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
2710 HPEN hPenOld = dc.SelectPen(hPen);
2712 dc.MoveTo(rcImage.left, rcImage.top);
2713 dc.LineTo(rcImage.right, rcImage.bottom);
2714 dc.MoveTo(rcImage.left + 1, rcImage.top);
2715 dc.LineTo(rcImage.right + 1, rcImage.bottom);
2717 dc.MoveTo(rcImage.left, rcImage.bottom - 1);
2718 dc.LineTo(rcImage.right, rcImage.top - 1);
2719 dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
2720 dc.LineTo(rcImage.right + 1, rcImage.top - 1);
2722 dc.SelectPen(hPenOld);
2723 #else // (_WIN32_WCE < 400)
2724 rcImage;
2725 hPen;
2726 // no support for the "x" button image
2727 #endif // (_WIN32_WCE < 400)
2730 bool IsVertical() const
2732 return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
2736 class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
2738 public:
2739 DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
2743 ///////////////////////////////////////////////////////////////////////////////
2744 // CSortListViewCtrl - implements sorting for a listview control
2746 // sort listview extended styles
2747 #define SORTLV_USESHELLBITMAPS 0x00000001
2749 // Notification sent to parent when sort column is changed by user clicking header.
2750 #define SLVN_SORTCHANGED LVN_LAST
2752 // A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification
2753 typedef struct tagNMSORTLISTVIEW
2755 NMHDR hdr;
2756 int iNewSortColumn;
2757 int iOldSortColumn;
2758 } NMSORTLISTVIEW, *LPNMSORTLISTVIEW;
2760 // Column sort types. Can be set on a per-column basis with the SetColumnSortType method.
2761 enum
2763 LVCOLSORT_NONE,
2764 LVCOLSORT_TEXT, // default
2765 LVCOLSORT_TEXTNOCASE,
2766 LVCOLSORT_LONG,
2767 LVCOLSORT_DOUBLE,
2768 LVCOLSORT_DECIMAL,
2769 LVCOLSORT_DATETIME,
2770 LVCOLSORT_DATE,
2771 LVCOLSORT_TIME,
2772 LVCOLSORT_CUSTOM,
2773 LVCOLSORT_LAST = LVCOLSORT_CUSTOM
2777 template <class T>
2778 class CSortListViewImpl
2780 public:
2781 enum
2783 m_cchCmpTextMax = 32, // overrideable
2784 m_cxSortImage = 16,
2785 m_cySortImage = 15,
2786 m_cxSortArrow = 11,
2787 m_cySortArrow = 6,
2788 m_iSortUp = 0, // index of sort bitmaps
2789 m_iSortDown = 1,
2790 m_nShellSortUpID = 133
2793 // passed to LVCompare functions as lParam1 and lParam2
2794 struct LVCompareParam
2796 int iItem;
2797 DWORD_PTR dwItemData;
2798 union
2800 long lValue;
2801 double dblValue;
2802 DECIMAL decValue;
2803 LPCTSTR pszValue;
2807 // passed to LVCompare functions as the lParamSort parameter
2808 struct LVSortInfo
2810 T* pT;
2811 int iSortCol;
2812 bool bDescending;
2815 bool m_bSortDescending;
2816 bool m_bCommCtrl6;
2817 int m_iSortColumn;
2818 CBitmap m_bmSort[2];
2819 int m_fmtOldSortCol;
2820 HBITMAP m_hbmOldSortCol;
2821 DWORD m_dwSortLVExtendedStyle;
2822 ATL::CSimpleArray<WORD> m_arrColSortType;
2823 bool m_bUseWaitCursor;
2825 CSortListViewImpl() :
2826 m_bSortDescending(false),
2827 m_bCommCtrl6(false),
2828 m_iSortColumn(-1),
2829 m_fmtOldSortCol(0),
2830 m_hbmOldSortCol(NULL),
2831 m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS),
2832 m_bUseWaitCursor(true)
2834 #ifndef _WIN32_WCE
2835 DWORD dwMajor = 0;
2836 DWORD dwMinor = 0;
2837 HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
2838 m_bCommCtrl6 = SUCCEEDED(hRet) && dwMajor >= 6;
2839 #endif // !_WIN32_WCE
2842 // Attributes
2843 void SetSortColumn(int iCol)
2845 T* pT = static_cast<T*>(this);
2846 ATLASSERT(::IsWindow(pT->m_hWnd));
2847 CHeaderCtrl header = pT->GetHeader();
2848 ATLASSERT(header.m_hWnd != NULL);
2849 ATLASSERT(iCol >= -1 && iCol < m_arrColSortType.GetSize());
2851 int iOldSortCol = m_iSortColumn;
2852 m_iSortColumn = iCol;
2853 if(m_bCommCtrl6)
2855 #ifndef HDF_SORTUP
2856 const int HDF_SORTUP = 0x0400;
2857 #endif // HDF_SORTUP
2858 #ifndef HDF_SORTDOWN
2859 const int HDF_SORTDOWN = 0x0200;
2860 #endif // HDF_SORTDOWN
2861 const int nMask = HDF_SORTUP | HDF_SORTDOWN;
2862 HDITEM hditem = { HDI_FORMAT };
2863 if(iOldSortCol != iCol && iOldSortCol >= 0 && header.GetItem(iOldSortCol, &hditem))
2865 hditem.fmt &= ~nMask;
2866 header.SetItem(iOldSortCol, &hditem);
2868 if(iCol >= 0 && header.GetItem(iCol, &hditem))
2870 hditem.fmt &= ~nMask;
2871 hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP;
2872 header.SetItem(iCol, &hditem);
2874 return;
2877 if(m_bmSort[m_iSortUp].IsNull())
2878 pT->CreateSortBitmaps();
2880 // restore previous sort column's bitmap, if any, and format
2881 HDITEM hditem = { HDI_BITMAP | HDI_FORMAT };
2882 if(iOldSortCol != iCol && iOldSortCol >= 0)
2884 hditem.hbm = m_hbmOldSortCol;
2885 hditem.fmt = m_fmtOldSortCol;
2886 header.SetItem(iOldSortCol, &hditem);
2889 // save new sort column's bitmap and format, and add our sort bitmap
2890 if(iCol >= 0 && header.GetItem(iCol, &hditem))
2892 if(iOldSortCol != iCol)
2894 m_fmtOldSortCol = hditem.fmt;
2895 m_hbmOldSortCol = hditem.hbm;
2897 hditem.fmt &= ~HDF_IMAGE;
2898 hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
2899 int i = m_bSortDescending ? m_iSortDown : m_iSortUp;
2900 hditem.hbm = m_bmSort[i];
2901 header.SetItem(iCol, &hditem);
2905 int GetSortColumn() const
2907 return m_iSortColumn;
2910 void SetColumnSortType(int iCol, WORD wType)
2912 ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
2913 ATLASSERT(wType >= LVCOLSORT_NONE && wType <= LVCOLSORT_LAST);
2914 m_arrColSortType[iCol] = wType;
2917 WORD GetColumnSortType(int iCol) const
2919 ATLASSERT((iCol >= 0) && iCol < m_arrColSortType.GetSize());
2920 return m_arrColSortType[iCol];
2923 int GetColumnCount() const
2925 const T* pT = static_cast<const T*>(this);
2926 ATLASSERT(::IsWindow(pT->m_hWnd));
2927 CHeaderCtrl header = pT->GetHeader();
2928 return header.m_hWnd != NULL ? header.GetItemCount() : 0;
2931 bool IsSortDescending() const
2933 return m_bSortDescending;
2936 DWORD GetSortListViewExtendedStyle() const
2938 return m_dwSortLVExtendedStyle;
2941 DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
2943 DWORD dwPrevStyle = m_dwSortLVExtendedStyle;
2944 if(dwMask == 0)
2945 m_dwSortLVExtendedStyle = dwExtendedStyle;
2946 else
2947 m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
2948 return dwPrevStyle;
2951 // Operations
2952 bool DoSortItems(int iCol, bool bDescending = false)
2954 T* pT = static_cast<T*>(this);
2955 ATLASSERT(::IsWindow(pT->m_hWnd));
2956 ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
2958 WORD wType = m_arrColSortType[iCol];
2959 if(wType == LVCOLSORT_NONE)
2960 return false;
2962 int nCount = pT->GetItemCount();
2963 if(nCount < 2)
2965 m_bSortDescending = bDescending;
2966 SetSortColumn(iCol);
2967 return true;
2970 CWaitCursor waitCursor(false);
2971 if(m_bUseWaitCursor)
2972 waitCursor.Set();
2974 LVCompareParam* pParam = NULL;
2975 ATLTRY(pParam = new LVCompareParam[nCount]);
2976 PFNLVCOMPARE pFunc = NULL;
2977 TCHAR pszTemp[pT->m_cchCmpTextMax];
2978 bool bStrValue = false;
2980 switch(wType)
2982 case LVCOLSORT_TEXT:
2983 pFunc = (PFNLVCOMPARE)pT->LVCompareText;
2984 case LVCOLSORT_TEXTNOCASE:
2985 if(pFunc == NULL)
2986 pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase;
2987 case LVCOLSORT_CUSTOM:
2989 if(pFunc == NULL)
2990 pFunc = (PFNLVCOMPARE)pT->LVCompareCustom;
2992 for(int i = 0; i < nCount; i++)
2994 pParam[i].iItem = i;
2995 pParam[i].dwItemData = pT->GetItemData(i);
2996 pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax];
2997 pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax);
2998 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3000 bStrValue = true;
3002 break;
3003 case LVCOLSORT_LONG:
3005 pFunc = (PFNLVCOMPARE)pT->LVCompareLong;
3006 for(int i = 0; i < nCount; i++)
3008 pParam[i].iItem = i;
3009 pParam[i].dwItemData = pT->GetItemData(i);
3010 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3011 pParam[i].lValue = pT->StrToLong(pszTemp);
3012 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3015 break;
3016 case LVCOLSORT_DOUBLE:
3018 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
3019 for(int i = 0; i < nCount; i++)
3021 pParam[i].iItem = i;
3022 pParam[i].dwItemData = pT->GetItemData(i);
3023 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3024 pParam[i].dblValue = pT->StrToDouble(pszTemp);
3025 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3028 break;
3029 case LVCOLSORT_DECIMAL:
3031 pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal;
3032 for(int i = 0; i < nCount; i++)
3034 pParam[i].iItem = i;
3035 pParam[i].dwItemData = pT->GetItemData(i);
3036 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3037 pT->StrToDecimal(pszTemp, &pParam[i].decValue);
3038 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3041 break;
3042 case LVCOLSORT_DATETIME:
3043 case LVCOLSORT_DATE:
3044 case LVCOLSORT_TIME:
3046 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
3047 DWORD dwFlags = LOCALE_NOUSEROVERRIDE;
3048 if(wType == LVCOLSORT_DATE)
3049 dwFlags |= VAR_DATEVALUEONLY;
3050 else if(wType == LVCOLSORT_TIME)
3051 dwFlags |= VAR_TIMEVALUEONLY;
3052 for(int i = 0; i < nCount; i++)
3054 pParam[i].iItem = i;
3055 pParam[i].dwItemData = pT->GetItemData(i);
3056 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
3057 pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags);
3058 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
3061 break;
3062 default:
3063 ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n"));
3064 break;
3065 } // switch(wType)
3067 ATLASSERT(pFunc != NULL);
3068 LVSortInfo lvsi = { pT, iCol, bDescending };
3069 bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE);
3070 for(int i = 0; i < nCount; i++)
3072 DWORD_PTR dwItemData = pT->GetItemData(i);
3073 LVCompareParam* p = (LVCompareParam*)dwItemData;
3074 ATLASSERT(p != NULL);
3075 if(bStrValue)
3076 delete [] (TCHAR*)p->pszValue;
3077 pT->SetItemData(i, p->dwItemData);
3079 delete [] pParam;
3081 if(bRet)
3083 m_bSortDescending = bDescending;
3084 SetSortColumn(iCol);
3087 if(m_bUseWaitCursor)
3088 waitCursor.Restore();
3090 return bRet;
3093 void CreateSortBitmaps()
3095 if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0)
3097 bool bFree = false;
3098 LPCTSTR pszModule = _T("shell32.dll");
3099 HINSTANCE hShell = ::GetModuleHandle(pszModule);
3101 if (hShell == NULL)
3103 hShell = ::LoadLibrary(pszModule);
3104 bFree = true;
3107 if (hShell != NULL)
3109 bool bSuccess = true;
3110 for(int i = m_iSortUp; i <= m_iSortDown; i++)
3112 if(!m_bmSort[i].IsNull())
3113 m_bmSort[i].DeleteObject();
3114 m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i),
3115 #ifndef _WIN32_WCE
3116 IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
3117 #else // CE specific
3118 IMAGE_BITMAP, 0, 0, 0);
3119 #endif // _WIN32_WCE
3120 if(m_bmSort[i].IsNull())
3122 bSuccess = false;
3123 break;
3126 if(bFree)
3127 ::FreeLibrary(hShell);
3128 if(bSuccess)
3129 return;
3133 T* pT = static_cast<T*>(this);
3134 for(int i = m_iSortUp; i <= m_iSortDown; i++)
3136 if(!m_bmSort[i].IsNull())
3137 m_bmSort[i].DeleteObject();
3139 CDC dcMem;
3140 CClientDC dc(::GetDesktopWindow());
3141 dcMem.CreateCompatibleDC(dc.m_hDC);
3142 m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage);
3143 HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]);
3144 RECT rc = {0,0,m_cxSortImage, m_cySortImage};
3145 pT->DrawSortBitmap(dcMem.m_hDC, i, &rc);
3146 dcMem.SelectBitmap(hbmOld);
3147 dcMem.DeleteDC();
3151 void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol)
3153 T* pT = static_cast<T*>(this);
3154 int nID = pT->GetDlgCtrlID();
3155 NMSORTLISTVIEW nm = { { pT->m_hWnd, nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol };
3156 ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm);
3159 // Overrideables
3160 int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/)
3162 // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members.
3163 // If item1 > item2 return 1, if item1 < item2 return -1, else return 0.
3164 return 0;
3167 void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc)
3169 dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE));
3170 HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
3171 CPen pen;
3172 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW));
3173 HPEN hpenOld = dc.SelectPen(pen);
3174 POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 };
3175 if(iBitmap == m_iSortUp)
3177 POINT pts[3] =
3179 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y },
3180 { ptOrg.x, ptOrg.y + m_cySortArrow - 1 },
3181 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 }
3183 dc.Polygon(pts, 3);
3185 else
3187 POINT pts[3] =
3189 { ptOrg.x, ptOrg.y },
3190 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 },
3191 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y }
3193 dc.Polygon(pts, 3);
3195 dc.SelectBrush(hbrOld);
3196 dc.SelectPen(hpenOld);
3199 double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags)
3201 ATLASSERT(lpstr != NULL);
3202 if(lpstr == NULL || lpstr[0] == _T('\0'))
3203 return 0;
3205 USES_CONVERSION;
3206 HRESULT hRet = E_FAIL;
3207 DATE dRet = 0;
3208 if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet)))
3210 ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet);
3211 dRet = 0;
3213 return dRet;
3216 long StrToLong(LPCTSTR lpstr)
3218 ATLASSERT(lpstr != NULL);
3219 if(lpstr == NULL || lpstr[0] == _T('\0'))
3220 return 0;
3222 USES_CONVERSION;
3223 HRESULT hRet = E_FAIL;
3224 long lRet = 0;
3225 if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet)))
3227 ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet);
3228 lRet = 0;
3230 return lRet;
3233 double StrToDouble(LPCTSTR lpstr)
3235 ATLASSERT(lpstr != NULL);
3236 if(lpstr == NULL || lpstr[0] == _T('\0'))
3237 return 0;
3239 USES_CONVERSION;
3240 HRESULT hRet = E_FAIL;
3241 double dblRet = 0;
3242 if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet)))
3244 ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet);
3245 dblRet = 0;
3247 return dblRet;
3250 bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal)
3252 ATLASSERT(lpstr != NULL);
3253 ATLASSERT(pDecimal != NULL);
3254 if(lpstr == NULL || pDecimal == NULL)
3255 return false;
3257 USES_CONVERSION;
3258 HRESULT hRet = E_FAIL;
3259 if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal)))
3261 ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet);
3262 pDecimal->Lo64 = 0;
3263 pDecimal->Hi32 = 0;
3264 pDecimal->signscale = 0;
3265 return false;
3267 return true;
3270 // Overrideable PFNLVCOMPARE functions
3271 static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3273 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3275 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3276 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3277 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3279 int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue);
3280 return pInfo->bDescending ? -nRet : nRet;
3283 static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3285 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3287 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3288 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3289 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3291 int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue);
3292 return pInfo->bDescending ? -nRet : nRet;
3295 static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3297 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3299 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3300 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3301 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3303 int nRet = 0;
3304 if(pParam1->lValue > pParam2->lValue)
3305 nRet = 1;
3306 else if(pParam1->lValue < pParam2->lValue)
3307 nRet = -1;
3308 return pInfo->bDescending ? -nRet : nRet;
3311 static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3313 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3315 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3316 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3317 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3319 int nRet = 0;
3320 if(pParam1->dblValue > pParam2->dblValue)
3321 nRet = 1;
3322 else if(pParam1->dblValue < pParam2->dblValue)
3323 nRet = -1;
3324 return pInfo->bDescending ? -nRet : nRet;
3327 static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3329 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3331 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3332 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3333 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3335 int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol);
3336 return pInfo->bDescending ? -nRet : nRet;
3339 #ifndef _WIN32_WCE
3340 static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3342 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3344 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3345 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3346 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3348 int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue);
3349 nRet--;
3350 return pInfo->bDescending ? -nRet : nRet;
3352 #else
3353 // Compare mantissas, ignore sign and scale
3354 static int CompareMantissas(const DECIMAL& decLeft, const DECIMAL& decRight)
3356 if (decLeft.Hi32 < decRight.Hi32)
3358 return -1;
3360 if (decLeft.Hi32 > decRight.Hi32)
3362 return 1;
3364 // Here, decLeft.Hi32 == decRight.Hi32
3365 if (decLeft.Lo64 < decRight.Lo64)
3367 return -1;
3369 if (decLeft.Lo64 > decRight.Lo64)
3371 return 1;
3373 return 0;
3376 // return values: VARCMP_LT, VARCMP_EQ, VARCMP_GT, VARCMP_NULL
3377 static HRESULT VarDecCmp(const DECIMAL* pdecLeft, const DECIMAL* pdecRight)
3379 static const ULONG powersOfTen[] =
3381 10ul,
3382 100ul,
3383 1000ul,
3384 10000ul,
3385 100000ul,
3386 1000000ul,
3387 10000000ul,
3388 100000000ul,
3389 1000000000ul
3391 static const int largestPower = sizeof(powersOfTen) / sizeof(powersOfTen[0]);
3392 if (!pdecLeft || !pdecRight)
3394 return VARCMP_NULL;
3397 // Degenerate case - at least one comparand is of the form
3398 // [+-]0*10^N (denormalized zero)
3399 bool bLeftZero = (!pdecLeft->Lo64 && !pdecLeft->Hi32);
3400 bool bRightZero = (!pdecRight->Lo64 && !pdecRight->Hi32);
3401 if (bLeftZero && bRightZero)
3403 return VARCMP_EQ;
3405 bool bLeftNeg = ((pdecLeft->sign & DECIMAL_NEG) != 0);
3406 bool bRightNeg = ((pdecRight->sign & DECIMAL_NEG) != 0);
3407 if (bLeftZero)
3409 return (bRightNeg ? VARCMP_GT : VARCMP_LT);
3411 // This also covers the case where the comparands have different signs
3412 if (bRightZero || bLeftNeg != bRightNeg)
3414 return (bLeftNeg ? VARCMP_LT : VARCMP_GT);
3417 // Here both comparands have the same sign and need to be compared
3418 // on mantissa and scale. The result is obvious when
3419 // 1. Scales are equal (then compare mantissas)
3420 // 2. A number with smaller scale is also the one with larger mantissa
3421 // (then this number is obviously larger)
3422 // In the remaining case, we would multiply the number with smaller
3423 // scale by 10 and simultaneously increment its scale (which amounts to
3424 // adding trailing zeros after decimal point), until the numbers fall under
3425 // one of the two cases above
3426 DECIMAL temp;
3427 bool bInvert = bLeftNeg; // the final result needs to be inverted
3428 if (pdecLeft->scale < pdecRight->scale)
3430 temp = *pdecLeft;
3432 else
3434 temp = *pdecRight;
3435 pdecRight = pdecLeft;
3436 bInvert = !bInvert;
3439 // Now temp is the number with smaller (or equal) scale, and
3440 // we can modify it freely without touching original parameters
3441 int comp;
3442 while ((comp = CompareMantissas(temp, *pdecRight)) < 0 &&
3443 temp.scale < pdecRight->scale)
3445 // Multiply by an appropriate power of 10
3446 int scaleDiff = pdecRight->scale - temp.scale;
3447 if (scaleDiff > largestPower)
3449 // Keep the multiplier representable in 32bit
3450 scaleDiff = largestPower;
3452 DWORDLONG power = powersOfTen[scaleDiff - 1];
3453 // Multiply temp's mantissa by power
3454 DWORDLONG product = temp.Lo32 * power;
3455 ULONG carry = static_cast<ULONG>(product >> 32);
3456 temp.Lo32 = static_cast<ULONG>(product);
3457 product = temp.Mid32 * power + carry;
3458 carry = static_cast<ULONG>(product >> 32);
3459 temp.Mid32 = static_cast<ULONG>(product);
3460 product = temp.Hi32 * power + carry;
3461 if (static_cast<ULONG>(product >> 32))
3463 // Multiplication overflowed - pdecLeft is clearly larger
3464 break;
3466 temp.Hi32 = static_cast<ULONG>(product);
3467 temp.scale = (BYTE)(temp.scale + scaleDiff);
3469 if (temp.scale < pdecRight->scale)
3471 comp = 1;
3473 if (bInvert)
3475 comp = -comp;
3477 return (comp > 0 ? VARCMP_GT : comp < 0 ? VARCMP_LT : VARCMP_EQ);
3480 static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
3482 ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
3484 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
3485 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
3486 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
3488 int nRet = (int)VarDecCmp(&pParam1->decValue, &pParam2->decValue);
3489 nRet--;
3490 return pInfo->bDescending ? -nRet : nRet;
3492 #endif // !_WIN32_WCE
3494 BEGIN_MSG_MAP(CSortListViewImpl)
3495 MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn)
3496 MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn)
3497 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick)
3498 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick)
3499 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
3500 END_MSG_MAP()
3502 LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
3504 T* pT = static_cast<T*>(this);
3505 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
3506 if(lRet == -1)
3507 return -1;
3509 WORD wType = 0;
3510 m_arrColSortType.Add(wType);
3511 int nCount = m_arrColSortType.GetSize();
3512 ATLASSERT(nCount == GetColumnCount());
3514 for(int i = nCount - 1; i > lRet; i--)
3515 m_arrColSortType[i] = m_arrColSortType[i - 1];
3516 m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT;
3518 if(lRet <= m_iSortColumn)
3519 m_iSortColumn++;
3521 return lRet;
3524 LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
3526 T* pT = static_cast<T*>(this);
3527 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
3528 if(lRet == 0)
3529 return 0;
3531 int iCol = (int)wParam;
3532 if(m_iSortColumn == iCol)
3533 m_iSortColumn = -1;
3534 else if(m_iSortColumn > iCol)
3535 m_iSortColumn--;
3536 m_arrColSortType.RemoveAt(iCol);
3538 return lRet;
3541 LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
3543 LPNMHEADER p = (LPNMHEADER)pnmh;
3544 if(p->iButton == 0)
3546 int iOld = m_iSortColumn;
3547 bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false;
3548 if(DoSortItems(p->iItem, bDescending))
3549 NotifyParentSortChanged(p->iItem, iOld);
3551 bHandled = FALSE;
3552 return 0;
3555 LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
3557 #ifndef _WIN32_WCE
3558 if(wParam == SPI_SETNONCLIENTMETRICS)
3559 GetSystemSettings();
3560 #else // CE specific
3561 wParam; // avoid level 4 warning
3562 GetSystemSettings();
3563 #endif // _WIN32_WCE
3564 bHandled = FALSE;
3565 return 0;
3568 void GetSystemSettings()
3570 if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull())
3572 T* pT = static_cast<T*>(this);
3573 pT->CreateSortBitmaps();
3574 if(m_iSortColumn != -1)
3575 SetSortColumn(m_iSortColumn);
3582 typedef ATL::CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SHOWSELALWAYS , WS_EX_CLIENTEDGE> CSortListViewCtrlTraits;
3584 template <class T, class TBase = CListViewCtrl, class TWinTraits = CSortListViewCtrlTraits>
3585 class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl<T, TBase, TWinTraits>, public CSortListViewImpl<T>
3587 public:
3588 DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
3590 bool SortItems(int iCol, bool bDescending = false)
3592 return DoSortItems(iCol, bDescending);
3595 BEGIN_MSG_MAP(CSortListViewCtrlImpl)
3596 MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl<T>::OnInsertColumn)
3597 MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl<T>::OnDeleteColumn)
3598 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl<T>::OnHeaderItemClick)
3599 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl<T>::OnHeaderItemClick)
3600 MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl<T>::OnSettingChange)
3601 END_MSG_MAP()
3604 class CSortListViewCtrl : public CSortListViewCtrlImpl<CSortListViewCtrl>
3606 public:
3607 DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName())
3611 ///////////////////////////////////////////////////////////////////////////////
3612 // CTabView - implements tab view window
3614 // TabView Notifications
3615 #define TBVN_PAGEACTIVATED (0U-741)
3616 #define TBVN_CONTEXTMENU (0U-742)
3618 // Notification data for TBVN_CONTEXTMENU
3619 struct TBVCONTEXTMENUINFO
3621 NMHDR hdr;
3622 POINT pt;
3625 typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
3628 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
3629 class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
3631 public:
3632 DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE)
3634 // Declarations and enums
3635 struct TABVIEWPAGE
3637 HWND hWnd;
3638 LPTSTR lpstrTitle;
3639 LPVOID pData;
3642 struct TCITEMEXTRA
3644 TCITEMHEADER tciheader;
3645 TABVIEWPAGE tvpage;
3647 operator LPTCITEM() { return (LPTCITEM)this; }
3650 enum
3652 m_nTabID = 1313,
3653 m_cxMoveMark = 6,
3654 m_cyMoveMark = 3,
3655 m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
3658 // Data members
3659 ATL::CContainedWindowT<CTabCtrl> m_tab;
3660 int m_cyTabHeight;
3662 int m_nActivePage;
3664 int m_nInsertItem;
3665 POINT m_ptStartDrag;
3667 CMenuHandle m_menu;
3669 int m_cchTabTextLength;
3671 int m_nMenuItemsCount;
3673 ATL::CWindow m_wndTitleBar;
3674 LPTSTR m_lpstrTitleBarBase;
3675 int m_cchTitleBarLength;
3677 CImageList m_ilDrag;
3679 bool m_bDestroyPageOnRemove:1;
3680 bool m_bDestroyImageList:1;
3681 bool m_bActivePageMenuItem:1;
3682 bool m_bActiveAsDefaultMenuItem:1;
3683 bool m_bEmptyMenuItem:1;
3684 bool m_bWindowsMenuItem:1;
3685 // internal
3686 bool m_bTabCapture:1;
3687 bool m_bTabDrag:1;
3689 // Constructor/destructor
3690 CTabViewImpl() :
3691 m_nActivePage(-1),
3692 m_cyTabHeight(0),
3693 m_tab(this, 1),
3694 m_nInsertItem(-1),
3695 m_cchTabTextLength(30),
3696 m_nMenuItemsCount(10),
3697 m_lpstrTitleBarBase(NULL),
3698 m_cchTitleBarLength(100),
3699 m_bDestroyPageOnRemove(true),
3700 m_bDestroyImageList(true),
3701 m_bActivePageMenuItem(true),
3702 m_bActiveAsDefaultMenuItem(false),
3703 m_bEmptyMenuItem(false),
3704 m_bWindowsMenuItem(false),
3705 m_bTabCapture(false),
3706 m_bTabDrag(false)
3708 m_ptStartDrag.x = 0;
3709 m_ptStartDrag.y = 0;
3712 ~CTabViewImpl()
3714 delete [] m_lpstrTitleBarBase;
3717 // Message filter function - to be called from PreTranslateMessage of the main window
3718 BOOL PreTranslateMessage(MSG* pMsg)
3720 if(IsWindow() == FALSE)
3721 return FALSE;
3723 BOOL bRet = FALSE;
3725 // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
3726 int nCount = GetPageCount();
3727 if(nCount > 0)
3729 bool bControl = (::GetKeyState(VK_CONTROL) < 0);
3730 if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
3732 if(nCount > 1)
3734 int nPage = m_nActivePage;
3735 bool bShift = (::GetKeyState(VK_SHIFT) < 0);
3736 if(bShift)
3737 nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
3738 else
3739 nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
3741 SetActivePage(nPage);
3742 T* pT = static_cast<T*>(this);
3743 pT->OnPageActivated(m_nActivePage);
3746 bRet = TRUE;
3750 // If we are doing drag-drop, check for Escape key that cancels it
3751 if(bRet == FALSE)
3753 if(m_bTabCapture && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
3755 ::ReleaseCapture();
3756 bRet = TRUE;
3760 // Pass the message to the active page
3761 if(bRet == FALSE)
3763 if(m_nActivePage != -1)
3764 bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
3767 return bRet;
3770 // Attributes
3771 int GetPageCount() const
3773 ATLASSERT(::IsWindow(m_hWnd));
3774 return m_tab.GetItemCount();
3777 int GetActivePage() const
3779 return m_nActivePage;
3782 void SetActivePage(int nPage)
3784 ATLASSERT(::IsWindow(m_hWnd));
3785 ATLASSERT(IsValidPageIndex(nPage));
3787 T* pT = static_cast<T*>(this);
3789 SetRedraw(FALSE);
3791 if(m_nActivePage != -1)
3792 ::ShowWindow(GetPageHWND(m_nActivePage), FALSE);
3793 m_nActivePage = nPage;
3794 m_tab.SetCurSel(m_nActivePage);
3795 ::ShowWindow(GetPageHWND(m_nActivePage), TRUE);
3797 pT->UpdateLayout();
3799 SetRedraw(TRUE);
3800 RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
3802 if(::GetFocus() != m_tab.m_hWnd)
3803 ::SetFocus(GetPageHWND(m_nActivePage));
3805 pT->UpdateTitleBar();
3806 pT->UpdateMenu();
3809 HIMAGELIST GetImageList() const
3811 ATLASSERT(::IsWindow(m_hWnd));
3812 return m_tab.GetImageList();
3815 HIMAGELIST SetImageList(HIMAGELIST hImageList)
3817 ATLASSERT(::IsWindow(m_hWnd));
3818 return m_tab.SetImageList(hImageList);
3821 void SetWindowMenu(HMENU hMenu)
3823 ATLASSERT(::IsWindow(m_hWnd));
3825 m_menu = hMenu;
3827 T* pT = static_cast<T*>(this);
3828 pT->UpdateMenu();
3831 void SetTitleBarWindow(HWND hWnd)
3833 ATLASSERT(::IsWindow(m_hWnd));
3835 delete [] m_lpstrTitleBarBase;
3836 m_lpstrTitleBarBase = NULL;
3838 m_wndTitleBar = hWnd;
3839 if(hWnd == NULL)
3840 return;
3842 int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
3843 ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
3844 if(m_lpstrTitleBarBase != NULL)
3846 m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
3847 T* pT = static_cast<T*>(this);
3848 pT->UpdateTitleBar();
3852 // Page attributes
3853 HWND GetPageHWND(int nPage) const
3855 ATLASSERT(::IsWindow(m_hWnd));
3856 ATLASSERT(IsValidPageIndex(nPage));
3858 TCITEMEXTRA tcix = { 0 };
3859 tcix.tciheader.mask = TCIF_PARAM;
3860 m_tab.GetItem(nPage, tcix);
3862 return tcix.tvpage.hWnd;
3865 LPCTSTR GetPageTitle(int nPage) const
3867 ATLASSERT(::IsWindow(m_hWnd));
3868 ATLASSERT(IsValidPageIndex(nPage));
3870 TCITEMEXTRA tcix = { 0 };
3871 tcix.tciheader.mask = TCIF_PARAM;
3872 if(m_tab.GetItem(nPage, tcix) == FALSE)
3873 return NULL;
3875 return tcix.tvpage.lpstrTitle;
3878 bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
3880 ATLASSERT(::IsWindow(m_hWnd));
3881 ATLASSERT(IsValidPageIndex(nPage));
3883 T* pT = static_cast<T*>(this);
3885 int cchBuff = lstrlen(lpstrTitle) + 1;
3886 LPTSTR lpstrBuff = NULL;
3887 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
3888 if(lpstrBuff == NULL)
3889 return false;
3891 SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
3892 TCITEMEXTRA tcix = { 0 };
3893 tcix.tciheader.mask = TCIF_PARAM;
3894 if(m_tab.GetItem(nPage, tcix) == FALSE)
3895 return false;
3897 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
3898 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
3899 if(lpstrTabText == NULL)
3900 return false;
3902 delete [] tcix.tvpage.lpstrTitle;
3904 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
3906 tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
3907 tcix.tciheader.pszText = lpstrTabText;
3908 tcix.tvpage.lpstrTitle = lpstrBuff;
3909 if(m_tab.SetItem(nPage, tcix) == FALSE)
3910 return false;
3912 pT->UpdateTitleBar();
3913 pT->UpdateMenu();
3915 return true;
3918 LPVOID GetPageData(int nPage) const
3920 ATLASSERT(::IsWindow(m_hWnd));
3921 ATLASSERT(IsValidPageIndex(nPage));
3923 TCITEMEXTRA tcix = { 0 };
3924 tcix.tciheader.mask = TCIF_PARAM;
3925 m_tab.GetItem(nPage, tcix);
3927 return tcix.tvpage.pData;
3930 LPVOID SetPageData(int nPage, LPVOID pData)
3932 ATLASSERT(::IsWindow(m_hWnd));
3933 ATLASSERT(IsValidPageIndex(nPage));
3935 TCITEMEXTRA tcix = { 0 };
3936 tcix.tciheader.mask = TCIF_PARAM;
3937 m_tab.GetItem(nPage, tcix);
3938 LPVOID pDataOld = tcix.tvpage.pData;
3940 tcix.tvpage.pData = pData;
3941 m_tab.SetItem(nPage, tcix);
3943 return pDataOld;
3946 int GetPageImage(int nPage) const
3948 ATLASSERT(::IsWindow(m_hWnd));
3949 ATLASSERT(IsValidPageIndex(nPage));
3951 TCITEMEXTRA tcix = { 0 };
3952 tcix.tciheader.mask = TCIF_IMAGE;
3953 m_tab.GetItem(nPage, tcix);
3955 return tcix.tciheader.iImage;
3958 int SetPageImage(int nPage, int nImage)
3960 ATLASSERT(::IsWindow(m_hWnd));
3961 ATLASSERT(IsValidPageIndex(nPage));
3963 TCITEMEXTRA tcix = { 0 };
3964 tcix.tciheader.mask = TCIF_IMAGE;
3965 m_tab.GetItem(nPage, tcix);
3966 int nImageOld = tcix.tciheader.iImage;
3968 tcix.tciheader.iImage = nImage;
3969 m_tab.SetItem(nPage, tcix);
3971 return nImageOld;
3974 // Operations
3975 bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
3977 return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
3980 bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
3982 ATLASSERT(::IsWindow(m_hWnd));
3983 ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage));
3985 T* pT = static_cast<T*>(this);
3987 int cchBuff = lstrlen(lpstrTitle) + 1;
3988 LPTSTR lpstrBuff = NULL;
3989 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
3990 if(lpstrBuff == NULL)
3991 return false;
3993 SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
3995 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
3996 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
3997 if(lpstrTabText == NULL)
3998 return false;
4000 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
4002 SetRedraw(FALSE);
4004 TCITEMEXTRA tcix = { 0 };
4005 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
4006 tcix.tciheader.pszText = lpstrTabText;
4007 tcix.tciheader.iImage = nImage;
4008 tcix.tvpage.hWnd = hWndView;
4009 tcix.tvpage.lpstrTitle = lpstrBuff;
4010 tcix.tvpage.pData = pData;
4011 int nItem = m_tab.InsertItem(nPage, tcix);
4012 if(nItem == -1)
4014 delete [] lpstrBuff;
4015 SetRedraw(TRUE);
4016 return false;
4019 SetActivePage(nItem);
4020 pT->OnPageActivated(m_nActivePage);
4022 if(GetPageCount() == 1)
4023 pT->ShowTabControl(true);
4025 pT->UpdateLayout();
4027 SetRedraw(TRUE);
4028 RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
4030 return true;
4033 void RemovePage(int nPage)
4035 ATLASSERT(::IsWindow(m_hWnd));
4036 ATLASSERT(IsValidPageIndex(nPage));
4038 T* pT = static_cast<T*>(this);
4040 SetRedraw(FALSE);
4042 if(GetPageCount() == 1)
4043 pT->ShowTabControl(false);
4045 if(m_bDestroyPageOnRemove)
4046 ::DestroyWindow(GetPageHWND(nPage));
4047 else
4048 ::ShowWindow(GetPageHWND(nPage), FALSE);
4049 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
4050 delete [] lpstrTitle;
4052 ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
4054 if(m_nActivePage == nPage)
4056 m_nActivePage = -1;
4058 if(nPage > 0)
4060 SetActivePage(nPage - 1);
4062 else if(GetPageCount() > 0)
4064 SetActivePage(nPage);
4066 else
4068 SetRedraw(TRUE);
4069 Invalidate();
4070 UpdateWindow();
4071 pT->UpdateTitleBar();
4072 pT->UpdateMenu();
4075 else
4077 nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
4078 m_nActivePage = -1;
4079 SetActivePage(nPage);
4082 pT->OnPageActivated(m_nActivePage);
4085 void RemoveAllPages()
4087 ATLASSERT(::IsWindow(m_hWnd));
4089 if(GetPageCount() == 0)
4090 return;
4092 T* pT = static_cast<T*>(this);
4094 SetRedraw(FALSE);
4096 pT->ShowTabControl(false);
4098 for(int i = 0; i < GetPageCount(); i++)
4100 if(m_bDestroyPageOnRemove)
4101 ::DestroyWindow(GetPageHWND(i));
4102 else
4103 ::ShowWindow(GetPageHWND(i), FALSE);
4104 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
4105 delete [] lpstrTitle;
4107 m_tab.DeleteAllItems();
4109 m_nActivePage = -1;
4110 pT->OnPageActivated(m_nActivePage);
4112 SetRedraw(TRUE);
4113 Invalidate();
4114 UpdateWindow();
4116 pT->UpdateTitleBar();
4117 pT->UpdateMenu();
4120 int PageIndexFromHwnd(HWND hWnd) const
4122 int nIndex = -1;
4124 for(int i = 0; i < GetPageCount(); i++)
4126 if(GetPageHWND(i) == hWnd)
4128 nIndex = i;
4129 break;
4133 return nIndex;
4136 void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
4138 ATLASSERT(::IsWindow(m_hWnd));
4140 CMenuHandle menu = hMenu;
4141 T* pT = static_cast<T*>(this);
4142 pT; // avoid level 4 warning
4143 int nFirstPos = 0;
4145 // Find first menu item in our range
4146 #ifndef _WIN32_WCE
4147 for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
4149 UINT nID = menu.GetMenuItemID(nFirstPos);
4150 if((nID >= ID_WINDOW_TABFIRST && nID <= ID_WINDOW_TABLAST) || nID == ID_WINDOW_SHOWTABLIST)
4151 break;
4153 #else // CE specific
4154 for(nFirstPos = 0; ; nFirstPos++)
4156 CMenuItemInfo mii;
4157 mii.fMask = MIIM_ID;
4158 BOOL bRet = menu.GetMenuItemInfo(nFirstPos, TRUE, &mii);
4159 if(bRet == FALSE)
4160 break;
4161 if((mii.wID >= ID_WINDOW_TABFIRST && mii.wID <= ID_WINDOW_TABLAST) || mii.wID == ID_WINDOW_SHOWTABLIST)
4162 break;
4164 #endif // _WIN32_WCE
4166 // Remove all menu items for tab pages
4167 BOOL bRet = TRUE;
4168 while(bRet != FALSE)
4169 bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
4171 // Add separator if it's not already there
4172 int nPageCount = GetPageCount();
4173 if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
4175 CMenuItemInfo mii;
4176 mii.fMask = MIIM_TYPE;
4177 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
4178 if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0))
4180 menu.AppendMenu(MF_SEPARATOR);
4181 nFirstPos++;
4185 // Add menu items for all pages
4186 if(nPageCount > 0)
4188 // Append menu items for all pages
4189 const int cchPrefix = 3; // 2 digits + space
4190 nMenuItemsCount = __min(min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
4191 ATLASSERT(nMenuItemsCount < 100); // 2 digits only
4192 if(nMenuItemsCount >= 100)
4193 nMenuItemsCount = 99;
4195 for(int i = 0; i < nMenuItemsCount; i++)
4197 LPCTSTR lpstrTitle = GetPageTitle(i);
4198 int nLen = lstrlen(lpstrTitle);
4199 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4200 LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
4201 ATLASSERT(lpstrText != NULL);
4202 if(lpstrText != NULL)
4204 LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
4205 SecureHelper::wsprintf_x(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
4206 menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
4210 // Mark active page
4211 if(bActivePageMenuItem && (m_nActivePage != -1))
4213 #ifndef _WIN32_WCE
4214 if(bActiveAsDefaultMenuItem)
4216 menu.SetMenuDefaultItem((UINT)-1, TRUE);
4217 menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
4219 else
4220 #else // CE specific
4221 bActiveAsDefaultMenuItem; // avoid level 4 warning
4222 #endif // _WIN32_WCE
4224 menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
4228 else
4230 if(bEmptyMenuItem)
4232 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
4233 menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
4236 // Remove separator if nothing else is there
4237 if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
4239 CMenuItemInfo mii;
4240 mii.fMask = MIIM_TYPE;
4241 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
4242 if((mii.fType & MFT_SEPARATOR) != 0)
4243 menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
4247 // Add "Windows..." menu item
4248 if(bWindowsMenuItem)
4249 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
4252 // Message map and handlers
4253 BEGIN_MSG_MAP(CTabViewImpl)
4254 MESSAGE_HANDLER(WM_CREATE, OnCreate)
4255 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
4256 MESSAGE_HANDLER(WM_SIZE, OnSize)
4257 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
4258 NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
4259 NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
4260 #ifndef _WIN32_WCE
4261 NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
4262 #endif // !_WIN32_WCE
4263 FORWARD_NOTIFICATIONS()
4264 ALT_MSG_MAP(1) // tab control
4265 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
4266 MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
4267 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
4268 MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
4269 MESSAGE_HANDLER(WM_RBUTTONUP, OnTabRButtonUp)
4270 MESSAGE_HANDLER(WM_SYSKEYDOWN, OnTabSysKeyDown)
4271 END_MSG_MAP()
4273 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4275 T* pT = static_cast<T*>(this);
4276 pT->CreateTabControl();
4278 return 0;
4281 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4283 RemoveAllPages();
4285 if(m_bDestroyImageList)
4287 CImageList il = m_tab.SetImageList(NULL);
4288 if(il.m_hImageList != NULL)
4289 il.Destroy();
4292 return 0;
4295 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4297 T* pT = static_cast<T*>(this);
4298 pT->UpdateLayout();
4299 return 0;
4302 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
4304 if(m_nActivePage != -1)
4305 ::SetFocus(GetPageHWND(m_nActivePage));
4306 return 0;
4309 LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
4311 SetActivePage(m_tab.GetCurSel());
4312 T* pT = static_cast<T*>(this);
4313 pT->OnPageActivated(m_nActivePage);
4315 return 0;
4318 LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
4320 // nothing to do - this just blocks all tab control
4321 // notifications from being propagated further
4322 return 0;
4325 #ifndef _WIN32_WCE
4326 LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
4328 LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
4329 if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
4331 T* pT = static_cast<T*>(this);
4332 pT->UpdateTooltipText(pTTDI);
4334 else
4336 bHandled = FALSE;
4339 return 0;
4341 #endif // !_WIN32_WCE
4343 // Tab control message handlers
4344 LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
4346 if(m_tab.GetItemCount() > 1)
4348 m_bTabCapture = true;
4349 m_tab.SetCapture();
4351 m_ptStartDrag.x = GET_X_LPARAM(lParam);
4352 m_ptStartDrag.y = GET_Y_LPARAM(lParam);
4355 bHandled = FALSE;
4356 return 0;
4359 LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
4361 if(m_bTabCapture)
4363 if(m_bTabDrag)
4365 TCHITTESTINFO hti = { 0 };
4366 hti.pt.x = GET_X_LPARAM(lParam);
4367 hti.pt.y = GET_Y_LPARAM(lParam);
4368 int nItem = m_tab.HitTest(&hti);
4369 if(nItem != -1)
4370 MovePage(m_nActivePage, nItem);
4373 ::ReleaseCapture();
4376 bHandled = FALSE;
4377 return 0;
4380 LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
4382 if(m_bTabCapture)
4384 m_bTabCapture = false;
4386 if(m_bTabDrag)
4388 m_bTabDrag = false;
4389 T* pT = static_cast<T*>(this);
4390 pT->DrawMoveMark(-1);
4392 #ifndef _WIN32_WCE
4393 m_ilDrag.DragLeave(GetDesktopWindow());
4394 #endif // !_WIN32_WCE
4395 m_ilDrag.EndDrag();
4397 m_ilDrag.Destroy();
4398 m_ilDrag.m_hImageList = NULL;
4402 bHandled = FALSE;
4403 return 0;
4406 LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
4408 bHandled = FALSE;
4410 if(m_bTabCapture)
4412 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
4414 if(!m_bTabDrag)
4416 #ifndef _WIN32_WCE
4417 if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) ||
4418 abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG))
4419 #else // CE specific
4420 if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= 4 ||
4421 abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= 4)
4422 #endif // _WIN32_WCE
4424 T* pT = static_cast<T*>(this);
4425 pT->GenerateDragImage(m_nActivePage);
4427 int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
4428 int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
4429 m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
4430 #ifndef _WIN32_WCE
4431 POINT ptEnter = m_ptStartDrag;
4432 m_tab.ClientToScreen(&ptEnter);
4433 m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
4434 #endif // !_WIN32_WCE
4436 m_bTabDrag = true;
4440 if(m_bTabDrag)
4442 TCHITTESTINFO hti = { 0 };
4443 hti.pt = pt;
4444 int nItem = m_tab.HitTest(&hti);
4446 T* pT = static_cast<T*>(this);
4447 pT->SetMoveCursor(nItem != -1);
4449 if(m_nInsertItem != nItem)
4450 pT->DrawMoveMark(nItem);
4452 m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
4453 m_tab.ClientToScreen(&pt);
4454 m_ilDrag.DragMove(pt);
4456 bHandled = TRUE;
4460 return 0;
4463 LRESULT OnTabRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
4465 TCHITTESTINFO hti = { 0 };
4466 hti.pt.x = GET_X_LPARAM(lParam);
4467 hti.pt.y = GET_Y_LPARAM(lParam);
4468 int nItem = m_tab.HitTest(&hti);
4469 if(nItem != -1)
4471 T* pT = static_cast<T*>(this);
4472 pT->OnContextMenu(nItem, hti.pt);
4475 return 0;
4478 LRESULT OnTabSysKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
4480 bool bShift = (::GetKeyState(VK_SHIFT) < 0);
4481 if(wParam == VK_F10 && bShift)
4483 if(m_nActivePage != -1)
4485 RECT rect = { 0 };
4486 m_tab.GetItemRect(m_nActivePage, &rect);
4487 POINT pt = { rect.left, rect.bottom };
4488 T* pT = static_cast<T*>(this);
4489 pT->OnContextMenu(m_nActivePage, pt);
4492 else
4494 bHandled = FALSE;
4497 return 0;
4500 // Implementation helpers
4501 bool IsValidPageIndex(int nPage) const
4503 return (nPage >= 0 && nPage < GetPageCount());
4506 bool MovePage(int nMovePage, int nInsertBeforePage)
4508 ATLASSERT(IsValidPageIndex(nMovePage));
4509 ATLASSERT(IsValidPageIndex(nInsertBeforePage));
4511 if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
4512 return false;
4514 if(nMovePage == nInsertBeforePage)
4515 return true; // nothing to do
4517 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4518 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
4519 if(lpstrTabText == NULL)
4520 return false;
4521 TCITEMEXTRA tcix = { 0 };
4522 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
4523 tcix.tciheader.pszText = lpstrTabText;
4524 tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
4525 BOOL bRet = m_tab.GetItem(nMovePage, tcix);
4526 ATLASSERT(bRet != FALSE);
4527 if(bRet == FALSE)
4528 return false;
4530 int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
4531 int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
4532 ATLASSERT(nNewItem == nInsertItem);
4533 if(nNewItem != nInsertItem)
4535 ATLVERIFY(m_tab.DeleteItem(nNewItem));
4536 return false;
4539 if(nMovePage > nInsertBeforePage)
4540 ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
4541 else if(nMovePage < nInsertBeforePage)
4542 ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
4544 SetActivePage(nInsertBeforePage);
4545 T* pT = static_cast<T*>(this);
4546 pT->OnPageActivated(m_nActivePage);
4548 return true;
4551 // Implementation overrideables
4552 bool CreateTabControl()
4554 #ifndef _WIN32_WCE
4555 m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
4556 #else // CE specific
4557 m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, m_nTabID);
4558 #endif // _WIN32_WCE
4559 ATLASSERT(m_tab.m_hWnd != NULL);
4560 if(m_tab.m_hWnd == NULL)
4561 return false;
4563 m_tab.SetFont(AtlGetDefaultGuiFont());
4565 m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
4567 T* pT = static_cast<T*>(this);
4568 m_cyTabHeight = pT->CalcTabHeight();
4570 return true;
4573 int CalcTabHeight()
4575 int nCount = m_tab.GetItemCount();
4576 TCITEMEXTRA tcix = { 0 };
4577 tcix.tciheader.mask = TCIF_TEXT;
4578 tcix.tciheader.pszText = _T("NS");
4579 int nIndex = m_tab.InsertItem(nCount, tcix);
4581 RECT rect = { 0, 0, 1000, 1000 };
4582 m_tab.AdjustRect(FALSE, &rect);
4584 RECT rcWnd = { 0, 0, 1000, rect.top };
4585 ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
4587 int nHeight = rcWnd.bottom - rcWnd.top;
4589 m_tab.DeleteItem(nIndex);
4591 return nHeight;
4594 void ShowTabControl(bool bShow)
4596 m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
4599 void UpdateLayout()
4601 RECT rect;
4602 GetClientRect(&rect);
4604 if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
4605 m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
4607 if(m_nActivePage != -1)
4608 ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, m_cyTabHeight, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
4611 void UpdateMenu()
4613 if(m_menu.m_hMenu != NULL)
4614 BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
4617 void UpdateTitleBar()
4619 if(!m_wndTitleBar.IsWindow() || m_lpstrTitleBarBase == NULL)
4620 return; // nothing to do
4622 if(m_nActivePage != -1)
4624 T* pT = static_cast<T*>(this);
4625 LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
4626 LPCTSTR lpstrDivider = pT->GetTitleDividerText();
4627 int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
4628 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
4629 LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
4630 ATLASSERT(lpstrPageTitle != NULL);
4631 if(lpstrPageTitle != NULL)
4633 pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
4634 SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, lpstrDivider);
4635 SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
4637 else
4639 lpstrPageTitle = m_lpstrTitleBarBase;
4642 m_wndTitleBar.SetWindowText(lpstrPageTitle);
4644 else
4646 m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
4650 void DrawMoveMark(int nItem)
4652 T* pT = static_cast<T*>(this);
4654 if(m_nInsertItem != -1)
4656 RECT rect = { 0 };
4657 pT->GetMoveMarkRect(rect);
4658 m_tab.InvalidateRect(&rect);
4661 m_nInsertItem = nItem;
4663 if(m_nInsertItem != -1)
4665 CClientDC dc(m_tab.m_hWnd);
4667 RECT rect = { 0 };
4668 pT->GetMoveMarkRect(rect);
4670 CPen pen;
4671 pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
4672 CBrush brush;
4673 brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
4675 HPEN hPenOld = dc.SelectPen(pen);
4676 HBRUSH hBrushOld = dc.SelectBrush(brush);
4678 int x = rect.left;
4679 int y = rect.top;
4680 POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
4681 dc.Polygon(ptsTop, 3);
4683 y = rect.bottom - 1;
4684 POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
4685 dc.Polygon(ptsBottom, 3);
4687 dc.SelectPen(hPenOld);
4688 dc.SelectBrush(hBrushOld);
4692 void GetMoveMarkRect(RECT& rect) const
4694 m_tab.GetClientRect(&rect);
4696 RECT rcItem = { 0 };
4697 m_tab.GetItemRect(m_nInsertItem, &rcItem);
4699 if(m_nInsertItem <= m_nActivePage)
4701 rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
4702 rect.right = rcItem.left + m_cxMoveMark / 2;
4704 else
4706 rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
4707 rect.right = rcItem.right + m_cxMoveMark / 2;
4711 void SetMoveCursor(bool bCanMove)
4713 ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
4716 void GenerateDragImage(int nItem)
4718 ATLASSERT(IsValidPageIndex(nItem));
4720 #ifndef _WIN32_WCE
4721 RECT rcItem = { 0 };
4722 m_tab.GetItemRect(nItem, &rcItem);
4723 ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
4724 #else // CE specific
4725 nItem; // avoid level 4 warning
4726 RECT rcItem = { 0, 0, 40, 20 };
4727 #endif // _WIN32_WCE
4729 ATLASSERT(m_ilDrag.m_hImageList == NULL);
4730 m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
4732 CClientDC dc(m_hWnd);
4733 CDC dcMem;
4734 dcMem.CreateCompatibleDC(dc);
4735 ATLASSERT(dcMem.m_hDC != NULL);
4736 dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
4738 CBitmap bmp;
4739 bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
4740 ATLASSERT(bmp.m_hBitmap != NULL);
4742 HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
4743 #ifndef _WIN32_WCE
4744 m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
4745 #else // CE specific
4746 dcMem.Rectangle(&rcItem);
4747 #endif // _WIN32_WCE
4748 dcMem.SelectBitmap(hBmpOld);
4750 ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
4753 void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
4755 if(lstrlen(lpstrTitle) >= cchShortTitle)
4757 LPCTSTR lpstrEllipsis = _T("...");
4758 int cchEllipsis = lstrlen(lpstrEllipsis);
4759 SecureHelper::strncpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
4760 SecureHelper::strcat_x(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
4762 else
4764 SecureHelper::strcpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle);
4768 #ifndef _WIN32_WCE
4769 void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
4771 ATLASSERT(pTTDI != NULL);
4772 pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
4774 #endif // !_WIN32_WCE
4776 // Text for menu items and title bar - override to provide different strings
4777 static LPCTSTR GetEmptyListText()
4779 return _T("(Empty)");
4782 static LPCTSTR GetWindowsMenuItemText()
4784 return _T("&Windows...");
4787 static LPCTSTR GetTitleDividerText()
4789 return _T(" - ");
4792 // Notifications - override to provide different behavior
4793 void OnPageActivated(int nPage)
4795 NMHDR nmhdr = { 0 };
4796 nmhdr.hwndFrom = m_hWnd;
4797 nmhdr.idFrom = nPage;
4798 nmhdr.code = TBVN_PAGEACTIVATED;
4799 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
4802 void OnContextMenu(int nPage, POINT pt)
4804 m_tab.ClientToScreen(&pt);
4806 TBVCONTEXTMENUINFO cmi = { 0 };
4807 cmi.hdr.hwndFrom = m_hWnd;
4808 cmi.hdr.idFrom = nPage;
4809 cmi.hdr.code = TBVN_CONTEXTMENU;
4810 cmi.pt = pt;
4811 ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&cmi);
4816 class CTabView : public CTabViewImpl<CTabView>
4818 public:
4819 DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
4822 }; // namespace WTL
4824 #endif // __ATLCTRLX_H__