Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(266)

Unified Diff: third_party/wtl/include/atlctrlx.h

Issue 703753005: More Windows build fixes. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: remove generated files Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/wtl/include/atlctrlw.h ('k') | third_party/wtl/include/atlddx.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/wtl/include/atlctrlx.h
diff --git a/third_party/wtl/include/atlctrlx.h b/third_party/wtl/include/atlctrlx.h
new file mode 100644
index 0000000000000000000000000000000000000000..e1ce59ed2642cf7bd9068fcb94670aa196d926d1
--- /dev/null
+++ b/third_party/wtl/include/atlctrlx.h
@@ -0,0 +1,4824 @@
+// Windows Template Library - WTL version 8.0
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//
+// This file is a part of the Windows Template Library.
+// The use and distribution terms for this software are covered by the
+// Microsoft Permissive License (Ms-PL) which can be found in the file
+// Ms-PL.txt at the root of this distribution.
+
+#ifndef __ATLCTRLX_H__
+#define __ATLCTRLX_H__
+
+#pragma once
+
+#ifndef __cplusplus
+ #error ATL requires C++ compilation (use a .cpp suffix)
+#endif
+
+#ifndef __ATLAPP_H__
+ #error atlctrlx.h requires atlapp.h to be included first
+#endif
+
+#ifndef __ATLCTRLS_H__
+ #error atlctrlx.h requires atlctrls.h to be included first
+#endif
+
+#ifndef WM_UPDATEUISTATE
+ #define WM_UPDATEUISTATE 0x0128
+#endif // !WM_UPDATEUISTATE
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes in this file:
+//
+// CBitmapButtonImpl<T, TBase, TWinTraits>
+// CBitmapButton
+// CCheckListViewCtrlImpl<T, TBase, TWinTraits>
+// CCheckListViewCtrl
+// CHyperLinkImpl<T, TBase, TWinTraits>
+// CHyperLink
+// CWaitCursor
+// CCustomWaitCursor
+// CMultiPaneStatusBarCtrlImpl<T, TBase>
+// CMultiPaneStatusBarCtrl
+// CPaneContainerImpl<T, TBase, TWinTraits>
+// CPaneContainer
+// CSortListViewImpl<T>
+// CSortListViewCtrlImpl<T, TBase, TWinTraits>
+// CSortListViewCtrl
+// CTabViewImpl<T, TBase, TWinTraits>
+// CTabView
+
+namespace WTL
+{
+
+///////////////////////////////////////////////////////////////////////////////
+// CBitmapButton - bitmap button implementation
+
+#ifndef _WIN32_WCE
+
+// bitmap button extended styles
+#define BMPBTN_HOVER 0x00000001
+#define BMPBTN_AUTO3D_SINGLE 0x00000002
+#define BMPBTN_AUTO3D_DOUBLE 0x00000004
+#define BMPBTN_AUTOSIZE 0x00000008
+#define BMPBTN_SHAREIMAGELISTS 0x00000010
+#define BMPBTN_AUTOFIRE 0x00000020
+
+template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
+class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits>
+{
+public:
+ DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
+
+ enum
+ {
+ _nImageNormal = 0,
+ _nImagePushed,
+ _nImageFocusOrHover,
+ _nImageDisabled,
+
+ _nImageCount = 4,
+ };
+
+ enum
+ {
+ ID_TIMER_FIRST = 1000,
+ ID_TIMER_REPEAT = 1001
+ };
+
+ // Bitmap button specific extended styles
+ DWORD m_dwExtendedStyle;
+
+ CImageList m_ImageList;
+ int m_nImage[_nImageCount];
+
+ CToolTipCtrl m_tip;
+ LPTSTR m_lpstrToolTipText;
+
+ // Internal states
+ unsigned m_fMouseOver:1;
+ unsigned m_fFocus:1;
+ unsigned m_fPressed:1;
+
+
+// Constructor/Destructor
+ CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
+ m_ImageList(hImageList), m_dwExtendedStyle(dwExtendedStyle),
+ m_lpstrToolTipText(NULL),
+ m_fMouseOver(0), m_fFocus(0), m_fPressed(0)
+ {
+ m_nImage[_nImageNormal] = -1;
+ m_nImage[_nImagePushed] = -1;
+ m_nImage[_nImageFocusOrHover] = -1;
+ m_nImage[_nImageDisabled] = -1;
+ }
+
+ ~CBitmapButtonImpl()
+ {
+ if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
+ m_ImageList.Destroy();
+ delete [] m_lpstrToolTipText;
+ }
+
+ // overridden to provide proper initialization
+ BOOL SubclassWindow(HWND hWnd)
+ {
+#if (_MSC_VER >= 1300)
+ BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
+#else // !(_MSC_VER >= 1300)
+ typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
+ BOOL bRet = _baseClass::SubclassWindow(hWnd);
+#endif // !(_MSC_VER >= 1300)
+ if(bRet)
+ Init();
+ return bRet;
+ }
+
+// Attributes
+ DWORD GetBitmapButtonExtendedStyle() const
+ {
+ return m_dwExtendedStyle;
+ }
+
+ DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
+ {
+ DWORD dwPrevStyle = m_dwExtendedStyle;
+ if(dwMask == 0)
+ m_dwExtendedStyle = dwExtendedStyle;
+ else
+ m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
+ return dwPrevStyle;
+ }
+
+ HIMAGELIST GetImageList() const
+ {
+ return m_ImageList;
+ }
+
+ HIMAGELIST SetImageList(HIMAGELIST hImageList)
+ {
+ HIMAGELIST hImageListPrev = m_ImageList;
+ m_ImageList = hImageList;
+ if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd))
+ SizeToImage();
+ return hImageListPrev;
+ }
+
+ int GetToolTipTextLength() const
+ {
+ return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
+ }
+
+ bool GetToolTipText(LPTSTR lpstrText, int nLength) const
+ {
+ ATLASSERT(lpstrText != NULL);
+ if(m_lpstrToolTipText == NULL)
+ return false;
+
+ errno_t nRet = SecureHelper::strncpy_x(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
+
+ return (nRet == 0 || nRet == STRUNCATE);
+ }
+
+ bool SetToolTipText(LPCTSTR lpstrText)
+ {
+ if(m_lpstrToolTipText != NULL)
+ {
+ delete [] m_lpstrToolTipText;
+ m_lpstrToolTipText = NULL;
+ }
+
+ if(lpstrText == NULL)
+ {
+ if(m_tip.IsWindow())
+ m_tip.Activate(FALSE);
+ return true;
+ }
+
+ int cchLen = lstrlen(lpstrText) + 1;
+ ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
+ if(m_lpstrToolTipText == NULL)
+ return false;
+
+ SecureHelper::strcpy_x(m_lpstrToolTipText, cchLen, lpstrText);
+ if(m_tip.IsWindow())
+ {
+ m_tip.Activate(TRUE);
+ m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
+ }
+
+ return true;
+ }
+
+// Operations
+ void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
+ {
+ if(nNormal != -1)
+ m_nImage[_nImageNormal] = nNormal;
+ if(nPushed != -1)
+ m_nImage[_nImagePushed] = nPushed;
+ if(nFocusOrHover != -1)
+ m_nImage[_nImageFocusOrHover] = nFocusOrHover;
+ if(nDisabled != -1)
+ m_nImage[_nImageDisabled] = nDisabled;
+ }
+
+ BOOL SizeToImage()
+ {
+ ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL);
+ int cx = 0;
+ int cy = 0;
+ if(!m_ImageList.GetIconSize(cx, cy))
+ return FALSE;
+ return ResizeClient(cx, cy);
+ }
+
+// Overrideables
+ void DoPaint(CDCHandle dc)
+ {
+ ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set
+ ATLASSERT(m_nImage[0] != -1); // main bitmap must be set
+
+ // set bitmap according to the current button state
+ int nImage = -1;
+ bool bHover = IsHoverMode();
+ if(!IsWindowEnabled())
+ nImage = m_nImage[_nImageDisabled];
+ else if(m_fPressed == 1)
+ nImage = m_nImage[_nImagePushed];
+ else if((!bHover && m_fFocus == 1) || (bHover && m_fMouseOver == 1))
+ nImage = m_nImage[_nImageFocusOrHover];
+ if(nImage == -1) // not there, use default one
+ nImage = m_nImage[_nImageNormal];
+
+ // draw the button image
+ int xyPos = 0;
+ if((m_fPressed == 1) && ((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0) && (m_nImage[_nImagePushed] == -1))
+ xyPos = 1;
+ m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
+
+ // draw 3D border if required
+ if((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0)
+ {
+ RECT rect;
+ GetClientRect(&rect);
+
+ if(m_fPressed == 1)
+ dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
+ else if(!bHover || m_fMouseOver == 1)
+ dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
+
+ if(!bHover && m_fFocus == 1)
+ {
+ ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
+ dc.DrawFocusRect(&rect);
+ }
+ }
+ }
+
+// Message map and handlers
+ BEGIN_MSG_MAP(CBitmapButtonImpl)
+ MESSAGE_HANDLER(WM_CREATE, OnCreate)
+ MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+ MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
+ MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+ MESSAGE_HANDLER(WM_PAINT, OnPaint)
+ MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
+ MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
+ MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
+ MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
+ MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
+ MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
+ MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
+ MESSAGE_HANDLER(WM_ENABLE, OnEnable)
+ MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
+ MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
+ MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
+ MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
+ MESSAGE_HANDLER(WM_TIMER, OnTimer)
+ MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
+ END_MSG_MAP()
+
+ LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ Init();
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(m_tip.IsWindow())
+ {
+ m_tip.DestroyWindow();
+ m_tip.m_hWnd = NULL;
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+ {
+ MSG msg = { m_hWnd, uMsg, wParam, lParam };
+ if(m_tip.IsWindow())
+ m_tip.RelayEvent(&msg);
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ return 1; // no background needed
+ }
+
+ LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ if(wParam != NULL)
+ {
+ pT->DoPaint((HDC)wParam);
+ }
+ else
+ {
+ CPaintDC dc(m_hWnd);
+ pT->DoPaint(dc.m_hDC);
+ }
+ return 0;
+ }
+
+ LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
+ Invalidate();
+ UpdateWindow();
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ LRESULT lRet = 0;
+ if(IsHoverMode())
+ SetCapture();
+ else
+ lRet = DefWindowProc(uMsg, wParam, lParam);
+ if(::GetCapture() == m_hWnd)
+ {
+ m_fPressed = 1;
+ Invalidate();
+ UpdateWindow();
+ }
+ if((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0)
+ {
+ int nElapse = 250;
+ int nDelay = 0;
+ if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
+ nElapse += nDelay * 250; // all milli-seconds
+ SetTimer(ID_TIMER_FIRST, nElapse);
+ }
+ return lRet;
+ }
+
+ LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ LRESULT lRet = 0;
+ if(!IsHoverMode())
+ lRet = DefWindowProc(uMsg, wParam, lParam);
+ if(::GetCapture() != m_hWnd)
+ SetCapture();
+ if(m_fPressed == 0)
+ {
+ m_fPressed = 1;
+ Invalidate();
+ UpdateWindow();
+ }
+ return lRet;
+ }
+
+ LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ LRESULT lRet = 0;
+ bool bHover = IsHoverMode();
+ if(!bHover)
+ lRet = DefWindowProc(uMsg, wParam, lParam);
+ if(::GetCapture() == m_hWnd)
+ {
+ if(bHover && m_fPressed == 1)
+ ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
+ ::ReleaseCapture();
+ }
+ return lRet;
+ }
+
+ LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(m_fPressed == 1)
+ {
+ m_fPressed = 0;
+ Invalidate();
+ UpdateWindow();
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ Invalidate();
+ UpdateWindow();
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+ {
+ if(::GetCapture() == m_hWnd)
+ {
+ POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ ClientToScreen(&ptCursor);
+ RECT rect = { 0 };
+ GetWindowRect(&rect);
+ unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
+ if(m_fPressed != uPressed)
+ {
+ m_fPressed = uPressed;
+ Invalidate();
+ UpdateWindow();
+ }
+ }
+ else if(IsHoverMode() && m_fMouseOver == 0)
+ {
+ m_fMouseOver = 1;
+ Invalidate();
+ UpdateWindow();
+ StartTrackMouseLeave();
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ if(m_fMouseOver == 1)
+ {
+ m_fMouseOver = 0;
+ Invalidate();
+ UpdateWindow();
+ }
+ return 0;
+ }
+
+ LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(wParam == VK_SPACE && IsHoverMode())
+ return 0; // ignore if in hover mode
+ if(wParam == VK_SPACE && m_fPressed == 0)
+ {
+ m_fPressed = 1;
+ Invalidate();
+ UpdateWindow();
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(wParam == VK_SPACE && IsHoverMode())
+ return 0; // ignore if in hover mode
+ if(wParam == VK_SPACE && m_fPressed == 1)
+ {
+ m_fPressed = 0;
+ Invalidate();
+ UpdateWindow();
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
+ switch(wParam) // timer ID
+ {
+ case ID_TIMER_FIRST:
+ KillTimer(ID_TIMER_FIRST);
+ if(m_fPressed == 1)
+ {
+ ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
+ int nElapse = 250;
+ int nRepeat = 40;
+ if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
+ nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated
+ SetTimer(ID_TIMER_REPEAT, nElapse);
+ }
+ break;
+ case ID_TIMER_REPEAT:
+ if(m_fPressed == 1)
+ ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
+ else if(::GetCapture() != m_hWnd)
+ KillTimer(ID_TIMER_REPEAT);
+ break;
+ default: // not our timer
+ break;
+ }
+ return 0;
+ }
+
+ LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ // If the control is subclassed or superclassed, this message can cause
+ // repainting without WM_PAINT. We don't use this state, so just do nothing.
+ return 0;
+ }
+
+// Implementation
+ void Init()
+ {
+ // We need this style to prevent Windows from painting the button
+ ModifyStyle(0, BS_OWNERDRAW);
+
+ // create a tool tip
+ m_tip.Create(m_hWnd);
+ ATLASSERT(m_tip.IsWindow());
+ if(m_tip.IsWindow() && m_lpstrToolTipText != NULL)
+ {
+ m_tip.Activate(TRUE);
+ m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
+ }
+
+ if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0)
+ SizeToImage();
+ }
+
+ BOOL StartTrackMouseLeave()
+ {
+ TRACKMOUSEEVENT tme = { 0 };
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = m_hWnd;
+ return _TrackMouseEvent(&tme);
+ }
+
+ bool IsHoverMode() const
+ {
+ return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
+ }
+};
+
+
+class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
+{
+public:
+ DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
+
+ CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
+ CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
+ { }
+};
+
+#endif // !_WIN32_WCE
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CCheckListCtrlView - list view control with check boxes
+
+template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
+class CCheckListViewCtrlImplTraits
+{
+public:
+ static DWORD GetWndStyle(DWORD dwStyle)
+ {
+ return (dwStyle == 0) ? t_dwStyle : dwStyle;
+ }
+
+ static DWORD GetWndExStyle(DWORD dwExStyle)
+ {
+ return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
+ }
+
+ static DWORD GetExtendedLVStyle()
+ {
+ return t_dwExListViewStyle;
+ }
+};
+
+typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT> CCheckListViewCtrlTraits;
+
+template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
+class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
+{
+public:
+ DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
+
+// Attributes
+ static DWORD GetExtendedLVStyle()
+ {
+ return TWinTraits::GetExtendedLVStyle();
+ }
+
+// Operations
+ BOOL SubclassWindow(HWND hWnd)
+ {
+#if (_MSC_VER >= 1300)
+ BOOL bRet = ATL::CWindowImplBaseT< TBase, TWinTraits>::SubclassWindow(hWnd);
+#else // !(_MSC_VER >= 1300)
+ typedef ATL::CWindowImplBaseT< TBase, TWinTraits> _baseClass;
+ BOOL bRet = _baseClass::SubclassWindow(hWnd);
+#endif // !(_MSC_VER >= 1300)
+ if(bRet)
+ {
+ T* pT = static_cast<T*>(this);
+ pT;
+ ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
+ SetExtendedListViewStyle(pT->GetExtendedLVStyle());
+ }
+ return bRet;
+ }
+
+ void CheckSelectedItems(int nCurrItem)
+ {
+ // first check if this item is selected
+ LVITEM lvi = { 0 };
+ lvi.iItem = nCurrItem;
+ lvi.iSubItem = 0;
+ lvi.mask = LVIF_STATE;
+ lvi.stateMask = LVIS_SELECTED;
+ GetItem(&lvi);
+ // if item is not selected, don't do anything
+ if(!(lvi.state & LVIS_SELECTED))
+ return;
+ // new check state will be reverse of the current state,
+ BOOL bCheck = !GetCheckState(nCurrItem);
+ int nItem = -1;
+ int nOldItem = -1;
+ while((nItem = GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
+ {
+ if(nItem != nCurrItem)
+ SetCheckState(nItem, bCheck);
+ nOldItem = nItem;
+ }
+ }
+
+// Implementation
+ BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
+ MESSAGE_HANDLER(WM_CREATE, OnCreate)
+ MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
+ MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
+ MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
+ END_MSG_MAP()
+
+ LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ // first let list view control initialize everything
+ LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
+ T* pT = static_cast<T*>(this);
+ pT;
+ ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
+ SetExtendedListViewStyle(pT->GetExtendedLVStyle());
+ return lRet;
+ }
+
+ LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+ {
+ POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ LVHITTESTINFO lvh = { 0 };
+ lvh.pt = ptMsg;
+ if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->CheckSelectedItems(lvh.iItem);
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(wParam == VK_SPACE)
+ {
+ int nCurrItem = GetNextItem(-1, LVNI_FOCUSED);
+ if(nCurrItem != -1 && ::GetKeyState(VK_CONTROL) >= 0)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->CheckSelectedItems(nCurrItem);
+ }
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+};
+
+class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
+{
+public:
+ DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CHyperLink - hyper link control implementation
+
+#if (WINVER < 0x0500) && !defined(_WIN32_WCE)
+__declspec(selectany) struct
+{
+ enum { cxWidth = 32, cyHeight = 32 };
+ int xHotSpot;
+ int yHotSpot;
+ unsigned char arrANDPlane[cxWidth * cyHeight / 8];
+ unsigned char arrXORPlane[cxWidth * cyHeight / 8];
+} _AtlHyperLink_CursorData =
+{
+ 5, 0,
+ {
+ 0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF,
+ 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF,
+ 0xF0, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF,
+ 0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF,
+ 0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF,
+ 0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ },
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x06, 0xD8, 0x00, 0x00,
+ 0x06, 0xDA, 0x00, 0x00, 0x06, 0xDB, 0x00, 0x00, 0x67, 0xFB, 0x00, 0x00, 0x77, 0xFF, 0x00, 0x00,
+ 0x37, 0xFF, 0x00, 0x00, 0x17, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00,
+ 0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00,
+ 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ }
+};
+#endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
+
+#define HLINK_UNDERLINED 0x00000000
+#define HLINK_NOTUNDERLINED 0x00000001
+#define HLINK_UNDERLINEHOVER 0x00000002
+#define HLINK_COMMANDBUTTON 0x00000004
+#define HLINK_NOTIFYBUTTON 0x0000000C
+#define HLINK_USETAGS 0x00000010
+#define HLINK_USETAGSBOLD 0x00000030
+#define HLINK_NOTOOLTIP 0x00000040
+
+// Notes:
+// - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
+// - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
+
+template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
+class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
+{
+public:
+ LPTSTR m_lpstrLabel;
+ LPTSTR m_lpstrHyperLink;
+
+ HCURSOR m_hCursor;
+ HFONT m_hFont;
+ HFONT m_hFontNormal;
+
+ RECT m_rcLink;
+#ifndef _WIN32_WCE
+ CToolTipCtrl m_tip;
+#endif // !_WIN32_WCE
+
+ COLORREF m_clrLink;
+ COLORREF m_clrVisited;
+
+ DWORD m_dwExtendedStyle; // Hyper Link specific extended styles
+
+ bool m_bPaintLabel:1;
+ bool m_bVisited:1;
+ bool m_bHover:1;
+ bool m_bInternalLinkFont:1;
+
+
+// Constructor/Destructor
+ CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) :
+ m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
+ m_hCursor(NULL), m_hFont(NULL), m_hFontNormal(NULL),
+ m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
+ m_dwExtendedStyle(dwExtendedStyle),
+ m_bPaintLabel(true), m_bVisited(false),
+ m_bHover(false), m_bInternalLinkFont(false)
+ {
+ ::SetRectEmpty(&m_rcLink);
+ }
+
+ ~CHyperLinkImpl()
+ {
+ delete [] m_lpstrLabel;
+ delete [] m_lpstrHyperLink;
+ if(m_bInternalLinkFont && m_hFont != NULL)
+ ::DeleteObject(m_hFont);
+#if (WINVER < 0x0500) && !defined(_WIN32_WCE)
+ // It was created, not loaded, so we have to destroy it
+ if(m_hCursor != NULL)
+ ::DestroyCursor(m_hCursor);
+#endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
+ }
+
+// Attributes
+ DWORD GetHyperLinkExtendedStyle() const
+ {
+ return m_dwExtendedStyle;
+ }
+
+ DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
+ {
+ DWORD dwPrevStyle = m_dwExtendedStyle;
+ if(dwMask == 0)
+ m_dwExtendedStyle = dwExtendedStyle;
+ else
+ m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
+ return dwPrevStyle;
+ }
+
+ bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
+ {
+ if(m_lpstrLabel == NULL)
+ return false;
+ ATLASSERT(lpstrBuffer != NULL);
+ if(nLength <= lstrlen(m_lpstrLabel))
+ return false;
+
+ SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrLabel);
+
+ return true;
+ }
+
+ bool SetLabel(LPCTSTR lpstrLabel)
+ {
+ delete [] m_lpstrLabel;
+ m_lpstrLabel = NULL;
+ int cchLen = lstrlen(lpstrLabel) + 1;
+ ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
+ if(m_lpstrLabel == NULL)
+ return false;
+
+ SecureHelper::strcpy_x(m_lpstrLabel, cchLen, lpstrLabel);
+ T* pT = static_cast<T*>(this);
+ pT->CalcLabelRect();
+
+ if(m_hWnd != NULL)
+ SetWindowText(lpstrLabel); // Set this for accessibility
+
+ return true;
+ }
+
+ bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
+ {
+ if(m_lpstrHyperLink == NULL)
+ return false;
+ ATLASSERT(lpstrBuffer != NULL);
+ if(nLength <= lstrlen(m_lpstrHyperLink))
+ return false;
+
+ SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrHyperLink);
+
+ return true;
+ }
+
+ bool SetHyperLink(LPCTSTR lpstrLink)
+ {
+ delete [] m_lpstrHyperLink;
+ m_lpstrHyperLink = NULL;
+ int cchLen = lstrlen(lpstrLink) + 1;
+ ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
+ if(m_lpstrHyperLink == NULL)
+ return false;
+
+ SecureHelper::strcpy_x(m_lpstrHyperLink, cchLen, lpstrLink);
+ if(m_lpstrLabel == NULL)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->CalcLabelRect();
+ }
+#ifndef _WIN32_WCE
+ if(m_tip.IsWindow())
+ {
+ m_tip.Activate(TRUE);
+ m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
+ }
+#endif // !_WIN32_WCE
+ return true;
+ }
+
+ HFONT GetLinkFont() const
+ {
+ return m_hFont;
+ }
+
+ void SetLinkFont(HFONT hFont)
+ {
+ if(m_bInternalLinkFont && m_hFont != NULL)
+ {
+ ::DeleteObject(m_hFont);
+ m_bInternalLinkFont = false;
+ }
+ m_hFont = hFont;
+ }
+
+ int GetIdealHeight() const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
+ return -1;
+ if(!m_bPaintLabel)
+ return -1;
+
+ CClientDC dc(m_hWnd);
+ RECT rect = { 0 };
+ GetClientRect(&rect);
+ HFONT hFontOld = dc.SelectFont(m_hFontNormal);
+ RECT rcText = rect;
+ dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
+ dc.SelectFont(m_hFont);
+ RECT rcLink = rect;
+ dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
+ dc.SelectFont(hFontOld);
+ return __max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
+ }
+
+ bool GetIdealSize(SIZE& size) const
+ {
+ int cx = 0, cy = 0;
+ bool bRet = GetIdealSize(cx, cy);
+ if(bRet)
+ {
+ size.cx = cx;
+ size.cy = cy;
+ }
+ return bRet;
+ }
+
+ bool GetIdealSize(int& cx, int& cy) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
+ return false;
+ if(!m_bPaintLabel)
+ return false;
+
+ CClientDC dc(m_hWnd);
+ RECT rcClient = { 0 };
+ GetClientRect(&rcClient);
+ RECT rcAll = rcClient;
+
+ if(IsUsingTags())
+ {
+ // find tags and label parts
+ LPTSTR lpstrLeft = NULL;
+ int cchLeft = 0;
+ LPTSTR lpstrLink = NULL;
+ int cchLink = 0;
+ LPTSTR lpstrRight = NULL;
+ int cchRight = 0;
+
+ const T* pT = static_cast<const T*>(this);
+ pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
+
+ // get label part rects
+ HFONT hFontOld = dc.SelectFont(m_hFontNormal);
+ RECT rcLeft = rcClient;
+ dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
+
+ dc.SelectFont(m_hFont);
+ RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
+ dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
+
+ dc.SelectFont(m_hFontNormal);
+ RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
+ dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
+
+ dc.SelectFont(hFontOld);
+
+ int cyMax = __max(rcLeft.bottom, max(rcLink.bottom, rcRight.bottom));
+ ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
+ }
+ else
+ {
+ HFONT hOldFont = NULL;
+ if(m_hFont != NULL)
+ hOldFont = dc.SelectFont(m_hFont);
+ LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
+ DWORD dwStyle = GetStyle();
+ int nDrawStyle = DT_LEFT;
+ if (dwStyle & SS_CENTER)
+ nDrawStyle = DT_CENTER;
+ else if (dwStyle & SS_RIGHT)
+ nDrawStyle = DT_RIGHT;
+ dc.DrawText(lpstrText, -1, &rcAll, nDrawStyle | DT_WORDBREAK | DT_CALCRECT);
+ if(m_hFont != NULL)
+ dc.SelectFont(hOldFont);
+ if (dwStyle & SS_CENTER)
+ {
+ int dx = (rcClient.right - rcAll.right) / 2;
+ ::OffsetRect(&rcAll, dx, 0);
+ }
+ else if (dwStyle & SS_RIGHT)
+ {
+ int dx = rcClient.right - rcAll.right;
+ ::OffsetRect(&rcAll, dx, 0);
+ }
+ }
+
+ cx = rcAll.right - rcAll.left;
+ cy = rcAll.bottom - rcAll.top;
+
+ return true;
+ }
+
+ // for command buttons only
+ bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
+ {
+ ATLASSERT(IsCommandButton());
+ return GetHyperLink(lpstrBuffer, nLength);
+ }
+
+ bool SetToolTipText(LPCTSTR lpstrToolTipText)
+ {
+ ATLASSERT(IsCommandButton());
+ return SetHyperLink(lpstrToolTipText);
+ }
+
+// Operations
+ BOOL SubclassWindow(HWND hWnd)
+ {
+ ATLASSERT(m_hWnd == NULL);
+ ATLASSERT(::IsWindow(hWnd));
+#if (_MSC_VER >= 1300)
+ BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
+#else // !(_MSC_VER >= 1300)
+ typedef ATL::CWindowImpl< T, TBase, TWinTraits> _baseClass;
+ BOOL bRet = _baseClass::SubclassWindow(hWnd);
+#endif // !(_MSC_VER >= 1300)
+ if(bRet)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->Init();
+ }
+ return bRet;
+ }
+
+ bool Navigate()
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ bool bRet = true;
+ if(IsNotifyButton())
+ {
+ NMHDR nmhdr = { m_hWnd, GetDlgCtrlID(), NM_CLICK };
+ ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
+ }
+ else if(IsCommandButton())
+ {
+ ::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
+ }
+ else
+ {
+ ATLASSERT(m_lpstrHyperLink != NULL);
+#ifndef _WIN32_WCE
+ DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
+ bRet = (dwRet > 32);
+#else // CE specific
+ SHELLEXECUTEINFO shExeInfo = { sizeof(SHELLEXECUTEINFO), 0, 0, L"open", m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL, 0, 0, 0, 0, 0, 0, 0 };
+ ::ShellExecuteEx(&shExeInfo);
+ DWORD_PTR dwRet = (DWORD_PTR)shExeInfo.hInstApp;
+ bRet = (dwRet == 0) || (dwRet > 32);
+#endif // _WIN32_WCE
+ ATLASSERT(bRet);
+ if(bRet)
+ {
+ m_bVisited = true;
+ Invalidate();
+ }
+ }
+ return bRet;
+ }
+
+// Message map and handlers
+ BEGIN_MSG_MAP(CHyperLinkImpl)
+ MESSAGE_HANDLER(WM_CREATE, OnCreate)
+#ifndef _WIN32_WCE
+ MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+ MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
+#endif // !_WIN32_WCE
+ MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+ MESSAGE_HANDLER(WM_PAINT, OnPaint)
+#ifndef _WIN32_WCE
+ MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
+#endif // !_WIN32_WCE
+ MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
+ MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
+ MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
+#ifndef _WIN32_WCE
+ MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
+#endif // !_WIN32_WCE
+ MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
+ MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
+ MESSAGE_HANDLER(WM_CHAR, OnChar)
+ MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
+ MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
+ MESSAGE_HANDLER(WM_ENABLE, OnEnable)
+ MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
+ MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
+ MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
+ MESSAGE_HANDLER(WM_SIZE, OnSize)
+ END_MSG_MAP()
+
+ LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->Init();
+ return 0;
+ }
+
+#ifndef _WIN32_WCE
+ LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(m_tip.IsWindow())
+ {
+ m_tip.DestroyWindow();
+ m_tip.m_hWnd = NULL;
+ }
+ bHandled = FALSE;
+ return 1;
+ }
+
+ LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+ {
+ MSG msg = { m_hWnd, uMsg, wParam, lParam };
+ if(m_tip.IsWindow() && IsUsingToolTip())
+ m_tip.RelayEvent(&msg);
+ bHandled = FALSE;
+ return 1;
+ }
+#endif // !_WIN32_WCE
+
+ LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ return 1; // no background painting needed (we do it all during WM_PAINT)
+ }
+
+ LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(!m_bPaintLabel)
+ {
+ bHandled = FALSE;
+ return 1;
+ }
+
+ T* pT = static_cast<T*>(this);
+ if(wParam != NULL)
+ {
+ pT->DoEraseBackground((HDC)wParam);
+ pT->DoPaint((HDC)wParam);
+ }
+ else
+ {
+ CPaintDC dc(m_hWnd);
+ pT->DoEraseBackground(dc.m_hDC);
+ pT->DoPaint(dc.m_hDC);
+ }
+
+ return 0;
+ }
+
+ LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(m_bPaintLabel)
+ Invalidate();
+ else
+ bHandled = FALSE;
+ return 0;
+ }
+
+ LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+ {
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
+ {
+ ::SetCursor(m_hCursor);
+ if(IsUnderlineHover())
+ {
+ if(!m_bHover)
+ {
+ m_bHover = true;
+ InvalidateRect(&m_rcLink);
+ UpdateWindow();
+#ifndef _WIN32_WCE
+ StartTrackMouseLeave();
+#endif // !_WIN32_WCE
+ }
+ }
+ }
+ else
+ {
+ if(IsUnderlineHover())
+ {
+ if(m_bHover)
+ {
+ m_bHover = false;
+ InvalidateRect(&m_rcLink);
+ UpdateWindow();
+ }
+ }
+ bHandled = FALSE;
+ }
+ return 0;
+ }
+
+#ifndef _WIN32_WCE
+ LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ if(IsUnderlineHover() && m_bHover)
+ {
+ m_bHover = false;
+ InvalidateRect(&m_rcLink);
+ UpdateWindow();
+ }
+ return 0;
+ }
+#endif // !_WIN32_WCE
+
+ LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ if(::PtInRect(&m_rcLink, pt))
+ {
+ SetFocus();
+ SetCapture();
+ }
+ return 0;
+ }
+
+ LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ if(GetCapture() == m_hWnd)
+ {
+ ReleaseCapture();
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+ if(::PtInRect(&m_rcLink, pt))
+ {
+ T* pT = static_cast<T*>(this);
+ pT->Navigate();
+ }
+ }
+ return 0;
+ }
+
+ LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ if(wParam == VK_RETURN || wParam == VK_SPACE)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->Navigate();
+ }
+ return 0;
+ }
+
+ LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ return DLGC_WANTCHARS;
+ }
+
+ LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ POINT pt = { 0, 0 };
+ GetCursorPos(&pt);
+ ScreenToClient(&pt);
+ if((m_lpstrHyperLink != NULL || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
+ {
+ return TRUE;
+ }
+ bHandled = FALSE;
+ return FALSE;
+ }
+
+ LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ Invalidate();
+ UpdateWindow();
+ return 0;
+ }
+
+ LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ return (LRESULT)m_hFontNormal;
+ }
+
+ LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ m_hFontNormal = (HFONT)wParam;
+ if((BOOL)lParam)
+ {
+ Invalidate();
+ UpdateWindow();
+ }
+ return 0;
+ }
+
+ LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ // If the control is subclassed or superclassed, this message can cause
+ // repainting without WM_PAINT. We don't use this state, so just do nothing.
+ return 0;
+ }
+
+ LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->CalcLabelRect();
+ pT->Invalidate();
+ return 0;
+ }
+
+// Implementation
+ void Init()
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+
+ // Check if we should paint a label
+ const int cchBuff = 8;
+ TCHAR szBuffer[cchBuff] = { 0 };
+ if(::GetClassName(m_hWnd, szBuffer, cchBuff))
+ {
+ if(lstrcmpi(szBuffer, _T("static")) == 0)
+ {
+ ModifyStyle(0, SS_NOTIFY); // we need this
+ DWORD dwStyle = GetStyle() & 0x000000FF;
+#ifndef _WIN32_WCE
+ if(dwStyle == SS_ICON || dwStyle == SS_BLACKRECT || dwStyle == SS_GRAYRECT ||
+ dwStyle == SS_WHITERECT || dwStyle == SS_BLACKFRAME || dwStyle == SS_GRAYFRAME ||
+ dwStyle == SS_WHITEFRAME || dwStyle == SS_OWNERDRAW ||
+ dwStyle == SS_BITMAP || dwStyle == SS_ENHMETAFILE)
+#else // CE specific
+ if(dwStyle == SS_ICON || dwStyle == SS_BITMAP)
+#endif // _WIN32_WCE
+ m_bPaintLabel = false;
+ }
+ }
+
+ // create or load a cursor
+#if (WINVER >= 0x0500) || defined(_WIN32_WCE)
+ m_hCursor = ::LoadCursor(NULL, IDC_HAND);
+#else
+ m_hCursor = ::CreateCursor(ModuleHelper::GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane);
+#endif
+ ATLASSERT(m_hCursor != NULL);
+
+ // set font
+ if(m_bPaintLabel)
+ {
+ ATL::CWindow wnd = GetParent();
+ m_hFontNormal = wnd.GetFont();
+ if(m_hFontNormal == NULL)
+ m_hFontNormal = (HFONT)::GetStockObject(SYSTEM_FONT);
+ if(m_hFontNormal != NULL && m_hFont == NULL)
+ {
+ LOGFONT lf = { 0 };
+ CFontHandle font = m_hFontNormal;
+ font.GetLogFont(&lf);
+ if(IsUsingTagsBold())
+ lf.lfWeight = FW_BOLD;
+ else if(!IsNotUnderlined())
+ lf.lfUnderline = TRUE;
+ m_hFont = ::CreateFontIndirect(&lf);
+ m_bInternalLinkFont = true;
+ ATLASSERT(m_hFont != NULL);
+ }
+ }
+
+#ifndef _WIN32_WCE
+ // create a tool tip
+ m_tip.Create(m_hWnd);
+ ATLASSERT(m_tip.IsWindow());
+#endif // !_WIN32_WCE
+
+ // set label (defaults to window text)
+ if(m_lpstrLabel == NULL)
+ {
+ int nLen = GetWindowTextLength();
+ if(nLen > 0)
+ {
+ CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ LPTSTR lpstrText = buff.Allocate(nLen + 1);
+ ATLASSERT(lpstrText != NULL);
+ if((lpstrText != NULL) && (GetWindowText(lpstrText, nLen + 1) > 0))
+ SetLabel(lpstrText);
+ }
+ }
+
+ T* pT = static_cast<T*>(this);
+ pT->CalcLabelRect();
+
+ // set hyperlink (defaults to label), or just activate tool tip if already set
+ if(m_lpstrHyperLink == NULL && !IsCommandButton())
+ {
+ if(m_lpstrLabel != NULL)
+ SetHyperLink(m_lpstrLabel);
+ }
+#ifndef _WIN32_WCE
+ else
+ {
+ m_tip.Activate(TRUE);
+ m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
+ }
+#endif // !_WIN32_WCE
+
+ // set link colors
+ if(m_bPaintLabel)
+ {
+ ATL::CRegKey rk;
+ LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
+ if(lRet == 0)
+ {
+ const int cchValue = 12;
+ TCHAR szValue[cchValue] = { 0 };
+#if (_ATL_VER >= 0x0700)
+ ULONG ulCount = cchValue;
+ lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
+#else
+ DWORD dwCount = cchValue * sizeof(TCHAR);
+ lRet = rk.QueryValue(szValue, _T("Anchor Color"), &dwCount);
+#endif
+ if(lRet == 0)
+ {
+ COLORREF clr = pT->_ParseColorString(szValue);
+ ATLASSERT(clr != CLR_INVALID);
+ if(clr != CLR_INVALID)
+ m_clrLink = clr;
+ }
+
+#if (_ATL_VER >= 0x0700)
+ ulCount = cchValue;
+ lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
+#else
+ dwCount = cchValue * sizeof(TCHAR);
+ lRet = rk.QueryValue(szValue, _T("Anchor Color Visited"), &dwCount);
+#endif
+ if(lRet == 0)
+ {
+ COLORREF clr = pT->_ParseColorString(szValue);
+ ATLASSERT(clr != CLR_INVALID);
+ if(clr != CLR_INVALID)
+ m_clrVisited = clr;
+ }
+ }
+ }
+ }
+
+ static COLORREF _ParseColorString(LPTSTR lpstr)
+ {
+ int c[3] = { -1, -1, -1 };
+ LPTSTR p = NULL;
+ for(int i = 0; i < 2; i++)
+ {
+ for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
+ {
+ if(*p == _T(','))
+ {
+ *p = _T('\0');
+ c[i] = T::_xttoi(lpstr);
+ lpstr = &p[1];
+ break;
+ }
+ }
+ if(c[i] == -1)
+ return CLR_INVALID;
+ }
+ if(*lpstr == _T('\0'))
+ return CLR_INVALID;
+ c[2] = T::_xttoi(lpstr);
+
+ return RGB(c[0], c[1], c[2]);
+ }
+
+ bool CalcLabelRect()
+ {
+ if(!::IsWindow(m_hWnd))
+ return false;
+ if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
+ return false;
+
+ CClientDC dc(m_hWnd);
+ RECT rcClient = { 0 };
+ GetClientRect(&rcClient);
+ m_rcLink = rcClient;
+ if(!m_bPaintLabel)
+ return true;
+
+ if(IsUsingTags())
+ {
+ // find tags and label parts
+ LPTSTR lpstrLeft = NULL;
+ int cchLeft = 0;
+ LPTSTR lpstrLink = NULL;
+ int cchLink = 0;
+ LPTSTR lpstrRight = NULL;
+ int cchRight = 0;
+
+ T* pT = static_cast<T*>(this);
+ pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
+ ATLASSERT(lpstrLink != NULL);
+ ATLASSERT(cchLink > 0);
+
+ // get label part rects
+ HFONT hFontOld = dc.SelectFont(m_hFontNormal);
+
+ RECT rcLeft = rcClient;
+ if(lpstrLeft != NULL)
+ dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
+
+ dc.SelectFont(m_hFont);
+ RECT rcLink = rcClient;
+ if(lpstrLeft != NULL)
+ rcLink.left = rcLeft.right;
+ dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
+
+ dc.SelectFont(hFontOld);
+
+ m_rcLink = rcLink;
+ }
+ else
+ {
+ HFONT hOldFont = NULL;
+ if(m_hFont != NULL)
+ hOldFont = dc.SelectFont(m_hFont);
+ LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
+ DWORD dwStyle = GetStyle();
+ int nDrawStyle = DT_LEFT;
+ if (dwStyle & SS_CENTER)
+ nDrawStyle = DT_CENTER;
+ else if (dwStyle & SS_RIGHT)
+ nDrawStyle = DT_RIGHT;
+ dc.DrawText(lpstrText, -1, &m_rcLink, nDrawStyle | DT_WORDBREAK | DT_CALCRECT);
+ if(m_hFont != NULL)
+ dc.SelectFont(hOldFont);
+ if (dwStyle & SS_CENTER)
+ {
+ int dx = (rcClient.right - m_rcLink.right) / 2;
+ ::OffsetRect(&m_rcLink, dx, 0);
+ }
+ else if (dwStyle & SS_RIGHT)
+ {
+ int dx = rcClient.right - m_rcLink.right;
+ ::OffsetRect(&m_rcLink, dx, 0);
+ }
+ }
+
+ return true;
+ }
+
+ void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
+ {
+ lpstrLeft = NULL;
+ cchLeft = 0;
+ lpstrLink = NULL;
+ cchLink = 0;
+ lpstrRight = NULL;
+ cchRight = 0;
+
+ LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
+ int cchText = lstrlen(lpstrText);
+ bool bOutsideLink = true;
+ for(int i = 0; i < cchText; i++)
+ {
+ if(lpstrText[i] != _T('<'))
+ continue;
+
+ if(bOutsideLink)
+ {
+ if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
+ {
+ if(i > 0)
+ {
+ lpstrLeft = lpstrText;
+ cchLeft = i;
+ }
+ lpstrLink = &lpstrText[i + 3];
+ bOutsideLink = false;
+ }
+ }
+ else
+ {
+ if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
+ {
+ cchLink = i - 3 - cchLeft;
+ if(lpstrText[i + 4] != 0)
+ {
+ lpstrRight = &lpstrText[i + 4];
+ cchRight = cchText - (i + 4);
+ break;
+ }
+ }
+ }
+ }
+
+ }
+
+ void DoEraseBackground(CDCHandle dc)
+ {
+ HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd);
+ if(hBrush != NULL)
+ {
+ RECT rect = { 0 };
+ GetClientRect(&rect);
+ dc.FillRect(&rect, hBrush);
+ }
+ }
+
+ void DoPaint(CDCHandle dc)
+ {
+ if(IsUsingTags())
+ {
+ // find tags and label parts
+ LPTSTR lpstrLeft = NULL;
+ int cchLeft = 0;
+ LPTSTR lpstrLink = NULL;
+ int cchLink = 0;
+ LPTSTR lpstrRight = NULL;
+ int cchRight = 0;
+
+ T* pT = static_cast<T*>(this);
+ pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
+
+ // get label part rects
+ RECT rcClient = { 0 };
+ GetClientRect(&rcClient);
+
+ dc.SetBkMode(TRANSPARENT);
+ HFONT hFontOld = dc.SelectFont(m_hFontNormal);
+
+ if(lpstrLeft != NULL)
+ dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | DT_WORDBREAK);
+
+ COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
+ if(m_hFont != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
+ dc.SelectFont(m_hFont);
+ else
+ dc.SelectFont(m_hFontNormal);
+
+ dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | DT_WORDBREAK);
+
+ dc.SetTextColor(clrOld);
+ dc.SelectFont(m_hFontNormal);
+ if(lpstrRight != NULL)
+ {
+ RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
+ dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | DT_WORDBREAK);
+ }
+
+ if(GetFocus() == m_hWnd)
+ dc.DrawFocusRect(&m_rcLink);
+
+ dc.SelectFont(hFontOld);
+ }
+ else
+ {
+ dc.SetBkMode(TRANSPARENT);
+ COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
+
+ HFONT hFontOld = NULL;
+ if(m_hFont != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
+ hFontOld = dc.SelectFont(m_hFont);
+ else
+ hFontOld = dc.SelectFont(m_hFontNormal);
+
+ LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
+
+ DWORD dwStyle = GetStyle();
+ int nDrawStyle = DT_LEFT;
+ if (dwStyle & SS_CENTER)
+ nDrawStyle = DT_CENTER;
+ else if (dwStyle & SS_RIGHT)
+ nDrawStyle = DT_RIGHT;
+
+ dc.DrawText(lpstrText, -1, &m_rcLink, nDrawStyle | DT_WORDBREAK);
+
+ if(GetFocus() == m_hWnd)
+ dc.DrawFocusRect(&m_rcLink);
+
+ dc.SetTextColor(clrOld);
+ dc.SelectFont(hFontOld);
+ }
+ }
+
+#ifndef _WIN32_WCE
+ BOOL StartTrackMouseLeave()
+ {
+ TRACKMOUSEEVENT tme = { 0 };
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = m_hWnd;
+ return _TrackMouseEvent(&tme);
+ }
+#endif // !_WIN32_WCE
+
+// Implementation helpers
+ bool IsUnderlined() const
+ {
+ return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
+ }
+
+ bool IsNotUnderlined() const
+ {
+ return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
+ }
+
+ bool IsUnderlineHover() const
+ {
+ return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
+ }
+
+ bool IsCommandButton() const
+ {
+ return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
+ }
+
+ bool IsNotifyButton() const
+ {
+ return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
+ }
+
+ bool IsUsingTags() const
+ {
+ return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
+ }
+
+ bool IsUsingTagsBold() const
+ {
+ return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
+ }
+
+ bool IsUsingToolTip() const
+ {
+ return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
+ }
+
+ static int _xttoi(const TCHAR* nptr)
+ {
+#ifndef _ATL_MIN_CRT
+ return _ttoi(nptr);
+#else // _ATL_MIN_CRT
+ while(*nptr == _T(' ')) // skip spaces
+ ++nptr;
+
+ int c = (int)(_TUCHAR)*nptr++;
+ int sign = c; // save sign indication
+ if (c == _T('-') || c == _T('+'))
+ c = (int)(_TUCHAR)*nptr++; // skip sign
+
+ int total = 0;
+ while((TCHAR)c >= _T('0') && (TCHAR)c <= _T('9'))
+ {
+ total = 10 * total + ((TCHAR)c - _T('0')); // accumulate digit
+ c = (int)(_TUCHAR)*nptr++; // get next char
+ }
+
+ // return result, negated if necessary
+ return ((TCHAR)sign != _T('-')) ? total : -total;
+#endif // _ATL_MIN_CRT
+ }
+};
+
+
+class CHyperLink : public CHyperLinkImpl<CHyperLink>
+{
+public:
+ DECLARE_WND_CLASS(_T("WTL_HyperLink"))
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CWaitCursor - displays a wait cursor
+
+class CWaitCursor
+{
+public:
+// Data
+ HCURSOR m_hWaitCursor;
+ HCURSOR m_hOldCursor;
+ bool m_bInUse;
+
+// Constructor/destructor
+ CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
+ {
+ HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
+ m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
+ ATLASSERT(m_hWaitCursor != NULL);
+
+ if(bSet)
+ Set();
+ }
+
+ ~CWaitCursor()
+ {
+ Restore();
+ }
+
+// Methods
+ bool Set()
+ {
+ if(m_bInUse)
+ return false;
+ m_hOldCursor = ::SetCursor(m_hWaitCursor);
+ m_bInUse = true;
+ return true;
+ }
+
+ bool Restore()
+ {
+ if(!m_bInUse)
+ return false;
+ ::SetCursor(m_hOldCursor);
+ m_bInUse = false;
+ return true;
+ }
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CCustomWaitCursor - for custom and animated cursors
+
+class CCustomWaitCursor : public CWaitCursor
+{
+public:
+// Constructor/destructor
+ CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) :
+ CWaitCursor(false, IDC_WAIT, true)
+ {
+ if(hInstance == NULL)
+ hInstance = ModuleHelper::GetResourceInstance();
+ m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
+
+ if(bSet)
+ Set();
+ }
+
+ ~CCustomWaitCursor()
+ {
+ Restore();
+#if !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
+ ::DestroyCursor(m_hWaitCursor);
+#endif // !defined(_WIN32_WCE) || ((_WIN32_WCE >= 0x400) && !(defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)))
+ }
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CMultiPaneStatusBarCtrl - Status Bar with multiple panes
+
+template <class T, class TBase = CStatusBarCtrl>
+class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase >
+{
+public:
+ DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
+
+// Data
+ enum { m_cxPaneMargin = 3 };
+
+ int m_nPanes;
+ int* m_pPane;
+
+// Constructor/destructor
+ CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL)
+ { }
+
+ ~CMultiPaneStatusBarCtrlImpl()
+ {
+ delete [] m_pPane;
+ }
+
+// Methods
+ HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
+ {
+#if (_MSC_VER >= 1300)
+ return ATL::CWindowImpl< T, TBase >::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
+#else // !(_MSC_VER >= 1300)
+ typedef ATL::CWindowImpl< T, TBase > _baseClass;
+ return _baseClass::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
+#endif // !(_MSC_VER >= 1300)
+ }
+
+ 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)
+ {
+ const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
+ TCHAR szText[cchMax];
+ szText[0] = 0;
+ ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
+ return Create(hWndParent, szText, dwStyle, nID);
+ }
+
+ BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(nPanes > 0);
+
+ m_nPanes = nPanes;
+ delete [] m_pPane;
+ m_pPane = NULL;
+
+ ATLTRY(m_pPane = new int[nPanes]);
+ ATLASSERT(m_pPane != NULL);
+ if(m_pPane == NULL)
+ return FALSE;
+
+ CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ int* pPanesPos = buff.Allocate(nPanes);
+ ATLASSERT(pPanesPos != NULL);
+ if(pPanesPos == NULL)
+ return FALSE;
+
+ SecureHelper::memcpy_x(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int));
+
+ // get status bar DC and set font
+ CClientDC dc(m_hWnd);
+ HFONT hOldFont = dc.SelectFont(GetFont());
+
+ // get status bar borders
+ int arrBorders[3] = { 0 };
+ GetBorders(arrBorders);
+
+ const int cchBuff = 128;
+ TCHAR szBuff[cchBuff] = { 0 };
+ SIZE size = { 0, 0 };
+ int cxLeft = arrBorders[0];
+
+ // calculate right edge of each part
+ for(int i = 0; i < nPanes; i++)
+ {
+ if(pPanes[i] == ID_DEFAULT_PANE)
+ {
+ // make very large, will be resized later
+ pPanesPos[i] = INT_MAX / 2;
+ }
+ else
+ {
+ ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
+ dc.GetTextExtent(szBuff, lstrlen(szBuff), &size);
+ T* pT = static_cast<T*>(this);
+ pT;
+ pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin;
+ }
+ cxLeft = pPanesPos[i];
+ }
+
+ BOOL bRet = SetParts(nPanes, pPanesPos);
+
+ if(bRet && bSetText)
+ {
+ for(int i = 0; i < nPanes; i++)
+ {
+ if(pPanes[i] != ID_DEFAULT_PANE)
+ {
+ ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
+ SetPaneText(m_pPane[i], szBuff);
+ }
+ }
+ }
+
+ dc.SelectFont(hOldFont);
+ return bRet;
+ }
+
+ bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return false;
+
+ int nLength = GetTextLength(nIndex, pnType);
+ if(pcchLength != NULL)
+ *pcchLength = nLength;
+
+ return true;
+ }
+
+ BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ int nLength = GetText(nIndex, lpstrText, pnType);
+ if(pcchLength != NULL)
+ *pcchLength = nLength;
+
+ return TRUE;
+ }
+
+ BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ return SetText(nIndex, lpstrText, nType);
+ }
+
+ BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ return GetRect(nIndex, lpRect);
+ }
+
+ BOOL SetPaneWidth(int nPaneID, int cxWidth)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ // get pane positions
+ CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ int* pPanesPos = buff.Allocate(m_nPanes);
+ if(pPanesPos == NULL)
+ return FALSE;
+ GetParts(m_nPanes, pPanesPos);
+ // calculate offset
+ int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]);
+ int cxOff = cxWidth - cxPaneWidth;
+ // find variable width pane
+ int nDef = m_nPanes;
+ for(int i = 0; i < m_nPanes; i++)
+ {
+ if(m_pPane[i] == ID_DEFAULT_PANE)
+ {
+ nDef = i;
+ break;
+ }
+ }
+ // resize
+ if(nIndex < nDef) // before default pane
+ {
+ for(int i = nIndex; i < nDef; i++)
+ pPanesPos[i] += cxOff;
+
+ }
+ else // after default one
+ {
+ for(int i = nDef; i < nIndex; i++)
+ pPanesPos[i] -= cxOff;
+ }
+ // set pane postions
+ return SetParts(m_nPanes, pPanesPos);
+ }
+
+#if (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
+ BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ GetTipText(nIndex, lpstrText, nSize);
+ return TRUE;
+ }
+
+ BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ SetTipText(nIndex, lpstrText);
+ return TRUE;
+ }
+#endif // (_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)
+
+#if ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
+ BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ hIcon = GetIcon(nIndex);
+ return TRUE;
+ }
+
+ BOOL SetPaneIcon(int nPaneID, HICON hIcon)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ int nIndex = GetPaneIndexFromID(nPaneID);
+ if(nIndex == -1)
+ return FALSE;
+
+ return SetIcon(nIndex, hIcon);
+ }
+#endif // ((_WIN32_IE >= 0x0400) && !defined(_WIN32_WCE)) || (defined(_WIN32_WCE) && (_WIN32_WCE >= 0x0500))
+
+// Message map and handlers
+ BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >)
+ MESSAGE_HANDLER(WM_SIZE, OnSize)
+ END_MSG_MAP()
+
+ LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
+ if(wParam != SIZE_MINIMIZED && m_nPanes > 0)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->UpdatePanesLayout();
+ }
+ return lRet;
+ }
+
+// Implementation
+ BOOL UpdatePanesLayout()
+ {
+ // get pane positions
+ CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ int* pPanesPos = buff.Allocate(m_nPanes);
+ ATLASSERT(pPanesPos != NULL);
+ if(pPanesPos == NULL)
+ return FALSE;
+ int nRet = GetParts(m_nPanes, pPanesPos);
+ ATLASSERT(nRet == m_nPanes);
+ if(nRet != m_nPanes)
+ return FALSE;
+ // calculate offset
+ RECT rcClient = { 0 };
+ GetClientRect(&rcClient);
+ int cxOff = rcClient.right - pPanesPos[m_nPanes - 1];
+#ifndef _WIN32_WCE
+ // Move panes left if size grip box is present
+ if((GetStyle() & SBARS_SIZEGRIP) != 0)
+ cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE);
+#endif // !_WIN32_WCE
+ // find variable width pane
+ int i;
+ for(i = 0; i < m_nPanes; i++)
+ {
+ if(m_pPane[i] == ID_DEFAULT_PANE)
+ break;
+ }
+ // resize all panes from the variable one to the right
+ if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1]))
+ {
+ for(; i < m_nPanes; i++)
+ pPanesPos[i] += cxOff;
+ }
+ // set pane postions
+ return SetParts(m_nPanes, pPanesPos);
+ }
+
+ int GetPaneIndexFromID(int nPaneID) const
+ {
+ for(int i = 0; i < m_nPanes; i++)
+ {
+ if(m_pPane[i] == nPaneID)
+ return i;
+ }
+
+ return -1; // not found
+ }
+};
+
+class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl<CMultiPaneStatusBarCtrl>
+{
+public:
+ DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName())
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CPaneContainer - provides header with title and close button for panes
+
+// pane container extended styles
+#define PANECNT_NOCLOSEBUTTON 0x00000001
+#define PANECNT_VERTICAL 0x00000002
+#define PANECNT_FLATBORDER 0x00000004
+#define PANECNT_NOBORDER 0x00000008
+
+template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
+class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
+{
+public:
+ DECLARE_WND_CLASS_EX(NULL, 0, -1)
+
+// Constants
+ enum
+ {
+ m_cxyBorder = 2,
+ m_cxyTextOffset = 4,
+ m_cxyBtnOffset = 1,
+
+ m_cchTitle = 80,
+
+ m_cxImageTB = 13,
+ m_cyImageTB = 11,
+ m_cxyBtnAddTB = 7,
+
+ m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset,
+
+ m_xBtnImageLeft = 6,
+ m_yBtnImageTop = 5,
+ m_xBtnImageRight = 12,
+ m_yBtnImageBottom = 11,
+
+ m_nCloseBtnID = ID_PANE_CLOSE
+ };
+
+// Data members
+ CToolBarCtrl m_tb;
+ ATL::CWindow m_wndClient;
+ int m_cxyHeader;
+ TCHAR m_szTitle[m_cchTitle];
+ DWORD m_dwExtendedStyle; // Pane container specific extended styles
+
+
+// Constructor
+ CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0)
+ {
+ m_szTitle[0] = 0;
+ }
+
+// Attributes
+ DWORD GetPaneContainerExtendedStyle() const
+ {
+ return m_dwExtendedStyle;
+ }
+
+ DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
+ {
+ DWORD dwPrevStyle = m_dwExtendedStyle;
+ if(dwMask == 0)
+ m_dwExtendedStyle = dwExtendedStyle;
+ else
+ m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
+ if(m_hWnd != NULL)
+ {
+ T* pT = static_cast<T*>(this);
+ bool bUpdate = false;
+
+ if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
+ {
+ pT->CreateCloseButton();
+ bUpdate = true;
+ }
+ else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
+ {
+ pT->DestroyCloseButton();
+ bUpdate = true;
+ }
+
+ if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
+ {
+ pT->CalcSize();
+ bUpdate = true;
+ }
+
+ if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) !=
+ (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border
+ {
+ bUpdate = true;
+ }
+
+ if(bUpdate)
+ pT->UpdateLayout();
+ }
+ return dwPrevStyle;
+ }
+
+ HWND GetClient() const
+ {
+ return m_wndClient;
+ }
+
+ HWND SetClient(HWND hWndClient)
+ {
+ HWND hWndOldClient = m_wndClient;
+ m_wndClient = hWndClient;
+ if(m_hWnd != NULL)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->UpdateLayout();
+ }
+ return hWndOldClient;
+ }
+
+ BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const
+ {
+ ATLASSERT(lpstrTitle != NULL);
+
+ errno_t nRet = SecureHelper::strncpy_x(lpstrTitle, cchLength, m_szTitle, _TRUNCATE);
+
+ return (nRet == 0 || nRet == STRUNCATE);
+ }
+
+ BOOL SetTitle(LPCTSTR lpstrTitle)
+ {
+ ATLASSERT(lpstrTitle != NULL);
+
+ errno_t nRet = SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
+ bool bRet = (nRet == 0 || nRet == STRUNCATE);
+ if(bRet && m_hWnd != NULL)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->UpdateLayout();
+ }
+
+ return bRet;
+ }
+
+ int GetTitleLength() const
+ {
+ return lstrlen(m_szTitle);
+ }
+
+// Methods
+ HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+ DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
+ {
+ if(lpstrTitle != NULL)
+ SecureHelper::strncpy_x(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
+#if (_MSC_VER >= 1300)
+ return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
+#else // !(_MSC_VER >= 1300)
+ typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
+ return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
+#endif // !(_MSC_VER >= 1300)
+ }
+
+ HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+ DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
+ {
+ if(uTitleID != 0U)
+ ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
+#if (_MSC_VER >= 1300)
+ return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
+#else // !(_MSC_VER >= 1300)
+ typedef ATL::CWindowImpl< T, TBase, TWinTraits > _baseClass;
+ return _baseClass::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
+#endif // !(_MSC_VER >= 1300)
+ }
+
+ BOOL EnableCloseButton(BOOL bEnable)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ T* pT = static_cast<T*>(this);
+ pT; // avoid level 4 warning
+ return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE;
+ }
+
+ void UpdateLayout()
+ {
+ RECT rcClient = { 0 };
+ GetClientRect(&rcClient);
+ T* pT = static_cast<T*>(this);
+ pT->UpdateLayout(rcClient.right, rcClient.bottom);
+ }
+
+// Message map and handlers
+ BEGIN_MSG_MAP(CPaneContainerImpl)
+ MESSAGE_HANDLER(WM_CREATE, OnCreate)
+ MESSAGE_HANDLER(WM_SIZE, OnSize)
+ MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
+ MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+ MESSAGE_HANDLER(WM_PAINT, OnPaint)
+#ifndef _WIN32_WCE
+ MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
+#endif // !_WIN32_WCE
+ MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
+ MESSAGE_HANDLER(WM_COMMAND, OnCommand)
+ FORWARD_NOTIFICATIONS()
+ END_MSG_MAP()
+
+ LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->CalcSize();
+
+ if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)
+ pT->CreateCloseButton();
+
+ return 0;
+ }
+
+ LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ return 0;
+ }
+
+ LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ if(m_wndClient.m_hWnd != NULL)
+ m_wndClient.SetFocus();
+ return 0;
+ }
+
+ LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ return 1; // no background needed
+ }
+
+ LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ if(wParam != NULL)
+ {
+ pT->DrawPaneTitle((HDC)wParam);
+
+ if(m_wndClient.m_hWnd == NULL) // no client window
+ pT->DrawPane((HDC)wParam);
+ }
+ else
+ {
+ CPaintDC dc(m_hWnd);
+ pT->DrawPaneTitle(dc.m_hDC);
+
+ if(m_wndClient.m_hWnd == NULL) // no client window
+ pT->DrawPane(dc.m_hDC);
+ }
+
+ return 0;
+ }
+
+ LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+ {
+ if(m_tb.m_hWnd == NULL)
+ {
+ bHandled = FALSE;
+ return 1;
+ }
+
+ T* pT = static_cast<T*>(this);
+ pT;
+ LPNMHDR lpnmh = (LPNMHDR)lParam;
+ LRESULT lRet = 0;
+
+ // pass toolbar custom draw notifications to the base class
+ if(lpnmh->code == NM_CUSTOMDRAW && lpnmh->hwndFrom == m_tb.m_hWnd)
+ lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
+#ifndef _WIN32_WCE
+ // tooltip notifications come with the tooltip window handle and button ID,
+ // pass them to the parent if we don't handle them
+ else if(lpnmh->code == TTN_GETDISPINFO && lpnmh->idFrom == pT->m_nCloseBtnID)
+ bHandled = pT->GetToolTipText(lpnmh);
+#endif // !_WIN32_WCE
+ // only let notifications not from the toolbar go to the parent
+ else if(lpnmh->hwndFrom != m_tb.m_hWnd && lpnmh->idFrom != pT->m_nCloseBtnID)
+ bHandled = FALSE;
+
+ return lRet;
+ }
+
+ LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+ {
+ // if command comes from the close button, substitute HWND of the pane container instead
+ if(m_tb.m_hWnd != NULL && (HWND)lParam == m_tb.m_hWnd)
+ return ::SendMessage(GetParent(), WM_COMMAND, wParam, (LPARAM)m_hWnd);
+
+ bHandled = FALSE;
+ return 1;
+ }
+
+// Custom draw overrides
+ DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
+ {
+ return CDRF_NOTIFYITEMDRAW; // we need per-item notifications
+ }
+
+ DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
+ {
+ CDCHandle dc = lpNMCustomDraw->hdc;
+#if (_WIN32_IE >= 0x0400)
+ RECT& rc = lpNMCustomDraw->rc;
+#else // !(_WIN32_IE >= 0x0400)
+ RECT rc;
+ m_tb.GetItemRect(0, &rc);
+#endif // !(_WIN32_IE >= 0x0400)
+
+ dc.FillRect(&rc, COLOR_3DFACE);
+
+ return CDRF_NOTIFYPOSTPAINT;
+ }
+
+ DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
+ {
+ CDCHandle dc = lpNMCustomDraw->hdc;
+#if (_WIN32_IE >= 0x0400)
+ RECT& rc = lpNMCustomDraw->rc;
+#else // !(_WIN32_IE >= 0x0400)
+ RECT rc = { 0 };
+ m_tb.GetItemRect(0, &rc);
+#endif // !(_WIN32_IE >= 0x0400)
+
+ RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 };
+ ::OffsetRect(&rcImage, rc.left, rc.top);
+ T* pT = static_cast<T*>(this);
+
+ if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0)
+ {
+ RECT rcShadow = rcImage;
+ ::OffsetRect(&rcShadow, 1, 1);
+ CPen pen1;
+ pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT));
+ pT->DrawButtonImage(dc, rcShadow, pen1);
+ CPen pen2;
+ pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW));
+ pT->DrawButtonImage(dc, rcImage, pen2);
+ }
+ else
+ {
+ if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0)
+ ::OffsetRect(&rcImage, 1, 1);
+ CPen pen;
+ pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
+ pT->DrawButtonImage(dc, rcImage, pen);
+ }
+
+ return CDRF_DODEFAULT; // continue with the default item painting
+ }
+
+// Implementation - overrideable methods
+ void UpdateLayout(int cxWidth, int cyHeight)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ RECT rect = { 0 };
+
+ if(IsVertical())
+ {
+ ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight);
+ if(m_tb.m_hWnd != NULL)
+ m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+
+ if(m_wndClient.m_hWnd != NULL)
+ m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER);
+ else
+ rect.right = cxWidth;
+ }
+ else
+ {
+ ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader);
+ if(m_tb.m_hWnd != NULL)
+ m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+
+ if(m_wndClient.m_hWnd != NULL)
+ m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER);
+ else
+ rect.bottom = cyHeight;
+ }
+
+ InvalidateRect(&rect);
+ }
+
+ void CreateCloseButton()
+ {
+ ATLASSERT(m_tb.m_hWnd == NULL);
+ // create toolbar for the "x" button
+ 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);
+ ATLASSERT(m_tb.IsWindow());
+
+ if(m_tb.m_hWnd != NULL)
+ {
+ T* pT = static_cast<T*>(this);
+ pT; // avoid level 4 warning
+
+ m_tb.SetButtonStructSize();
+
+ TBBUTTON tbbtn = { 0 };
+ tbbtn.idCommand = pT->m_nCloseBtnID;
+ tbbtn.fsState = TBSTATE_ENABLED;
+ tbbtn.fsStyle = TBSTYLE_BUTTON;
+ m_tb.AddButtons(1, &tbbtn);
+
+ m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB);
+ m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB);
+
+ if(IsVertical())
+ m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOACTIVATE);
+ else
+ m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
+ }
+ }
+
+ void DestroyCloseButton()
+ {
+ if(m_tb.m_hWnd != NULL)
+ m_tb.DestroyWindow();
+ }
+
+ void CalcSize()
+ {
+ T* pT = static_cast<T*>(this);
+ CFontHandle font = pT->GetTitleFont();
+ LOGFONT lf = { 0 };
+ font.GetLogFont(lf);
+ if(IsVertical())
+ {
+ m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder;
+ }
+ else
+ {
+ int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset;
+ int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset;
+ m_cxyHeader = __max(cyFont, cyBtn);
+ }
+ }
+
+ HFONT GetTitleFont() const
+ {
+ return AtlGetDefaultGuiFont();
+ }
+
+#ifndef _WIN32_WCE
+ BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
+ {
+ return FALSE;
+ }
+#endif // !_WIN32_WCE
+
+ void DrawPaneTitle(CDCHandle dc)
+ {
+ RECT rect = { 0 };
+ GetClientRect(&rect);
+
+ UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST;
+ if(IsVertical())
+ {
+ rect.right = rect.left + m_cxyHeader;
+ uBorder |= BF_BOTTOM;
+ }
+ else
+ {
+ rect.bottom = rect.top + m_cxyHeader;
+ uBorder |= BF_RIGHT;
+ }
+
+ if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0)
+ {
+ if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0)
+ uBorder |= BF_FLAT;
+ dc.DrawEdge(&rect, EDGE_ETCHED, uBorder);
+ }
+ dc.FillRect(&rect, COLOR_3DFACE);
+
+ if(!IsVertical()) // draw title only for horizontal pane container
+ {
+ dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
+ dc.SetBkMode(TRANSPARENT);
+ T* pT = static_cast<T*>(this);
+ HFONT hFontOld = dc.SelectFont(pT->GetTitleFont());
+ rect.left += m_cxyTextOffset;
+ rect.right -= m_cxyTextOffset;
+ if(m_tb.m_hWnd != NULL)
+ rect.right -= m_cxToolBar;;
+#ifndef _WIN32_WCE
+ dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
+#else // CE specific
+ dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER);
+#endif // _WIN32_WCE
+ dc.SelectFont(hFontOld);
+ }
+ }
+
+ // called only if pane is empty
+ void DrawPane(CDCHandle dc)
+ {
+ RECT rect = { 0 };
+ GetClientRect(&rect);
+ if(IsVertical())
+ rect.left += m_cxyHeader;
+ else
+ rect.top += m_cxyHeader;
+ if((GetExStyle() & WS_EX_CLIENTEDGE) == 0)
+ dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ dc.FillRect(&rect, COLOR_APPWORKSPACE);
+ }
+
+ // drawing helper - draws "x" button image
+ void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
+ {
+#if !defined(_WIN32_WCE) || (_WIN32_WCE >= 400)
+ HPEN hPenOld = dc.SelectPen(hPen);
+
+ dc.MoveTo(rcImage.left, rcImage.top);
+ dc.LineTo(rcImage.right, rcImage.bottom);
+ dc.MoveTo(rcImage.left + 1, rcImage.top);
+ dc.LineTo(rcImage.right + 1, rcImage.bottom);
+
+ dc.MoveTo(rcImage.left, rcImage.bottom - 1);
+ dc.LineTo(rcImage.right, rcImage.top - 1);
+ dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
+ dc.LineTo(rcImage.right + 1, rcImage.top - 1);
+
+ dc.SelectPen(hPenOld);
+#else // (_WIN32_WCE < 400)
+ rcImage;
+ hPen;
+ // no support for the "x" button image
+#endif // (_WIN32_WCE < 400)
+ }
+
+ bool IsVertical() const
+ {
+ return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
+ }
+};
+
+class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
+{
+public:
+ DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CSortListViewCtrl - implements sorting for a listview control
+
+// sort listview extended styles
+#define SORTLV_USESHELLBITMAPS 0x00000001
+
+// Notification sent to parent when sort column is changed by user clicking header.
+#define SLVN_SORTCHANGED LVN_LAST
+
+// A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification
+typedef struct tagNMSORTLISTVIEW
+{
+ NMHDR hdr;
+ int iNewSortColumn;
+ int iOldSortColumn;
+} NMSORTLISTVIEW, *LPNMSORTLISTVIEW;
+
+// Column sort types. Can be set on a per-column basis with the SetColumnSortType method.
+enum
+{
+ LVCOLSORT_NONE,
+ LVCOLSORT_TEXT, // default
+ LVCOLSORT_TEXTNOCASE,
+ LVCOLSORT_LONG,
+ LVCOLSORT_DOUBLE,
+ LVCOLSORT_DECIMAL,
+ LVCOLSORT_DATETIME,
+ LVCOLSORT_DATE,
+ LVCOLSORT_TIME,
+ LVCOLSORT_CUSTOM,
+ LVCOLSORT_LAST = LVCOLSORT_CUSTOM
+};
+
+
+template <class T>
+class CSortListViewImpl
+{
+public:
+ enum
+ {
+ m_cchCmpTextMax = 32, // overrideable
+ m_cxSortImage = 16,
+ m_cySortImage = 15,
+ m_cxSortArrow = 11,
+ m_cySortArrow = 6,
+ m_iSortUp = 0, // index of sort bitmaps
+ m_iSortDown = 1,
+ m_nShellSortUpID = 133
+ };
+
+ // passed to LVCompare functions as lParam1 and lParam2
+ struct LVCompareParam
+ {
+ int iItem;
+ DWORD_PTR dwItemData;
+ union
+ {
+ long lValue;
+ double dblValue;
+ DECIMAL decValue;
+ LPCTSTR pszValue;
+ };
+ };
+
+ // passed to LVCompare functions as the lParamSort parameter
+ struct LVSortInfo
+ {
+ T* pT;
+ int iSortCol;
+ bool bDescending;
+ };
+
+ bool m_bSortDescending;
+ bool m_bCommCtrl6;
+ int m_iSortColumn;
+ CBitmap m_bmSort[2];
+ int m_fmtOldSortCol;
+ HBITMAP m_hbmOldSortCol;
+ DWORD m_dwSortLVExtendedStyle;
+ ATL::CSimpleArray<WORD> m_arrColSortType;
+ bool m_bUseWaitCursor;
+
+ CSortListViewImpl() :
+ m_bSortDescending(false),
+ m_bCommCtrl6(false),
+ m_iSortColumn(-1),
+ m_fmtOldSortCol(0),
+ m_hbmOldSortCol(NULL),
+ m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS),
+ m_bUseWaitCursor(true)
+ {
+#ifndef _WIN32_WCE
+ DWORD dwMajor = 0;
+ DWORD dwMinor = 0;
+ HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
+ m_bCommCtrl6 = SUCCEEDED(hRet) && dwMajor >= 6;
+#endif // !_WIN32_WCE
+ }
+
+// Attributes
+ void SetSortColumn(int iCol)
+ {
+ T* pT = static_cast<T*>(this);
+ ATLASSERT(::IsWindow(pT->m_hWnd));
+ CHeaderCtrl header = pT->GetHeader();
+ ATLASSERT(header.m_hWnd != NULL);
+ ATLASSERT(iCol >= -1 && iCol < m_arrColSortType.GetSize());
+
+ int iOldSortCol = m_iSortColumn;
+ m_iSortColumn = iCol;
+ if(m_bCommCtrl6)
+ {
+#ifndef HDF_SORTUP
+ const int HDF_SORTUP = 0x0400;
+#endif // HDF_SORTUP
+#ifndef HDF_SORTDOWN
+ const int HDF_SORTDOWN = 0x0200;
+#endif // HDF_SORTDOWN
+ const int nMask = HDF_SORTUP | HDF_SORTDOWN;
+ HDITEM hditem = { HDI_FORMAT };
+ if(iOldSortCol != iCol && iOldSortCol >= 0 && header.GetItem(iOldSortCol, &hditem))
+ {
+ hditem.fmt &= ~nMask;
+ header.SetItem(iOldSortCol, &hditem);
+ }
+ if(iCol >= 0 && header.GetItem(iCol, &hditem))
+ {
+ hditem.fmt &= ~nMask;
+ hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP;
+ header.SetItem(iCol, &hditem);
+ }
+ return;
+ }
+
+ if(m_bmSort[m_iSortUp].IsNull())
+ pT->CreateSortBitmaps();
+
+ // restore previous sort column's bitmap, if any, and format
+ HDITEM hditem = { HDI_BITMAP | HDI_FORMAT };
+ if(iOldSortCol != iCol && iOldSortCol >= 0)
+ {
+ hditem.hbm = m_hbmOldSortCol;
+ hditem.fmt = m_fmtOldSortCol;
+ header.SetItem(iOldSortCol, &hditem);
+ }
+
+ // save new sort column's bitmap and format, and add our sort bitmap
+ if(iCol >= 0 && header.GetItem(iCol, &hditem))
+ {
+ if(iOldSortCol != iCol)
+ {
+ m_fmtOldSortCol = hditem.fmt;
+ m_hbmOldSortCol = hditem.hbm;
+ }
+ hditem.fmt &= ~HDF_IMAGE;
+ hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
+ int i = m_bSortDescending ? m_iSortDown : m_iSortUp;
+ hditem.hbm = m_bmSort[i];
+ header.SetItem(iCol, &hditem);
+ }
+ }
+
+ int GetSortColumn() const
+ {
+ return m_iSortColumn;
+ }
+
+ void SetColumnSortType(int iCol, WORD wType)
+ {
+ ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
+ ATLASSERT(wType >= LVCOLSORT_NONE && wType <= LVCOLSORT_LAST);
+ m_arrColSortType[iCol] = wType;
+ }
+
+ WORD GetColumnSortType(int iCol) const
+ {
+ ATLASSERT((iCol >= 0) && iCol < m_arrColSortType.GetSize());
+ return m_arrColSortType[iCol];
+ }
+
+ int GetColumnCount() const
+ {
+ const T* pT = static_cast<const T*>(this);
+ ATLASSERT(::IsWindow(pT->m_hWnd));
+ CHeaderCtrl header = pT->GetHeader();
+ return header.m_hWnd != NULL ? header.GetItemCount() : 0;
+ }
+
+ bool IsSortDescending() const
+ {
+ return m_bSortDescending;
+ }
+
+ DWORD GetSortListViewExtendedStyle() const
+ {
+ return m_dwSortLVExtendedStyle;
+ }
+
+ DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
+ {
+ DWORD dwPrevStyle = m_dwSortLVExtendedStyle;
+ if(dwMask == 0)
+ m_dwSortLVExtendedStyle = dwExtendedStyle;
+ else
+ m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
+ return dwPrevStyle;
+ }
+
+// Operations
+ bool DoSortItems(int iCol, bool bDescending = false)
+ {
+ T* pT = static_cast<T*>(this);
+ ATLASSERT(::IsWindow(pT->m_hWnd));
+ ATLASSERT(iCol >= 0 && iCol < m_arrColSortType.GetSize());
+
+ WORD wType = m_arrColSortType[iCol];
+ if(wType == LVCOLSORT_NONE)
+ return false;
+
+ int nCount = pT->GetItemCount();
+ if(nCount < 2)
+ {
+ m_bSortDescending = bDescending;
+ SetSortColumn(iCol);
+ return true;
+ }
+
+ CWaitCursor waitCursor(false);
+ if(m_bUseWaitCursor)
+ waitCursor.Set();
+
+ LVCompareParam* pParam = NULL;
+ ATLTRY(pParam = new LVCompareParam[nCount]);
+ PFNLVCOMPARE pFunc = NULL;
+ TCHAR pszTemp[pT->m_cchCmpTextMax];
+ bool bStrValue = false;
+
+ switch(wType)
+ {
+ case LVCOLSORT_TEXT:
+ pFunc = (PFNLVCOMPARE)pT->LVCompareText;
+ case LVCOLSORT_TEXTNOCASE:
+ if(pFunc == NULL)
+ pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase;
+ case LVCOLSORT_CUSTOM:
+ {
+ if(pFunc == NULL)
+ pFunc = (PFNLVCOMPARE)pT->LVCompareCustom;
+
+ for(int i = 0; i < nCount; i++)
+ {
+ pParam[i].iItem = i;
+ pParam[i].dwItemData = pT->GetItemData(i);
+ pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax];
+ pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax);
+ pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
+ }
+ bStrValue = true;
+ }
+ break;
+ case LVCOLSORT_LONG:
+ {
+ pFunc = (PFNLVCOMPARE)pT->LVCompareLong;
+ for(int i = 0; i < nCount; i++)
+ {
+ pParam[i].iItem = i;
+ pParam[i].dwItemData = pT->GetItemData(i);
+ pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
+ pParam[i].lValue = pT->StrToLong(pszTemp);
+ pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
+ }
+ }
+ break;
+ case LVCOLSORT_DOUBLE:
+ {
+ pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
+ for(int i = 0; i < nCount; i++)
+ {
+ pParam[i].iItem = i;
+ pParam[i].dwItemData = pT->GetItemData(i);
+ pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
+ pParam[i].dblValue = pT->StrToDouble(pszTemp);
+ pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
+ }
+ }
+ break;
+ case LVCOLSORT_DECIMAL:
+ {
+ pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal;
+ for(int i = 0; i < nCount; i++)
+ {
+ pParam[i].iItem = i;
+ pParam[i].dwItemData = pT->GetItemData(i);
+ pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
+ pT->StrToDecimal(pszTemp, &pParam[i].decValue);
+ pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
+ }
+ }
+ break;
+ case LVCOLSORT_DATETIME:
+ case LVCOLSORT_DATE:
+ case LVCOLSORT_TIME:
+ {
+ pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
+ DWORD dwFlags = LOCALE_NOUSEROVERRIDE;
+ if(wType == LVCOLSORT_DATE)
+ dwFlags |= VAR_DATEVALUEONLY;
+ else if(wType == LVCOLSORT_TIME)
+ dwFlags |= VAR_TIMEVALUEONLY;
+ for(int i = 0; i < nCount; i++)
+ {
+ pParam[i].iItem = i;
+ pParam[i].dwItemData = pT->GetItemData(i);
+ pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
+ pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags);
+ pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
+ }
+ }
+ break;
+ default:
+ ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n"));
+ break;
+ } // switch(wType)
+
+ ATLASSERT(pFunc != NULL);
+ LVSortInfo lvsi = { pT, iCol, bDescending };
+ bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE);
+ for(int i = 0; i < nCount; i++)
+ {
+ DWORD_PTR dwItemData = pT->GetItemData(i);
+ LVCompareParam* p = (LVCompareParam*)dwItemData;
+ ATLASSERT(p != NULL);
+ if(bStrValue)
+ delete [] (TCHAR*)p->pszValue;
+ pT->SetItemData(i, p->dwItemData);
+ }
+ delete [] pParam;
+
+ if(bRet)
+ {
+ m_bSortDescending = bDescending;
+ SetSortColumn(iCol);
+ }
+
+ if(m_bUseWaitCursor)
+ waitCursor.Restore();
+
+ return bRet;
+ }
+
+ void CreateSortBitmaps()
+ {
+ if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0)
+ {
+ bool bFree = false;
+ LPCTSTR pszModule = _T("shell32.dll");
+ HINSTANCE hShell = ::GetModuleHandle(pszModule);
+
+ if (hShell == NULL)
+ {
+ hShell = ::LoadLibrary(pszModule);
+ bFree = true;
+ }
+
+ if (hShell != NULL)
+ {
+ bool bSuccess = true;
+ for(int i = m_iSortUp; i <= m_iSortDown; i++)
+ {
+ if(!m_bmSort[i].IsNull())
+ m_bmSort[i].DeleteObject();
+ m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i),
+#ifndef _WIN32_WCE
+ IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
+#else // CE specific
+ IMAGE_BITMAP, 0, 0, 0);
+#endif // _WIN32_WCE
+ if(m_bmSort[i].IsNull())
+ {
+ bSuccess = false;
+ break;
+ }
+ }
+ if(bFree)
+ ::FreeLibrary(hShell);
+ if(bSuccess)
+ return;
+ }
+ }
+
+ T* pT = static_cast<T*>(this);
+ for(int i = m_iSortUp; i <= m_iSortDown; i++)
+ {
+ if(!m_bmSort[i].IsNull())
+ m_bmSort[i].DeleteObject();
+
+ CDC dcMem;
+ CClientDC dc(::GetDesktopWindow());
+ dcMem.CreateCompatibleDC(dc.m_hDC);
+ m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage);
+ HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]);
+ RECT rc = {0,0,m_cxSortImage, m_cySortImage};
+ pT->DrawSortBitmap(dcMem.m_hDC, i, &rc);
+ dcMem.SelectBitmap(hbmOld);
+ dcMem.DeleteDC();
+ }
+ }
+
+ void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol)
+ {
+ T* pT = static_cast<T*>(this);
+ int nID = pT->GetDlgCtrlID();
+ NMSORTLISTVIEW nm = { { pT->m_hWnd, nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol };
+ ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm);
+ }
+
+// Overrideables
+ int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/)
+ {
+ // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members.
+ // If item1 > item2 return 1, if item1 < item2 return -1, else return 0.
+ return 0;
+ }
+
+ void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc)
+ {
+ dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE));
+ HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
+ CPen pen;
+ pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW));
+ HPEN hpenOld = dc.SelectPen(pen);
+ POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 };
+ if(iBitmap == m_iSortUp)
+ {
+ POINT pts[3] =
+ {
+ { ptOrg.x + m_cxSortArrow / 2, ptOrg.y },
+ { ptOrg.x, ptOrg.y + m_cySortArrow - 1 },
+ { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 }
+ };
+ dc.Polygon(pts, 3);
+ }
+ else
+ {
+ POINT pts[3] =
+ {
+ { ptOrg.x, ptOrg.y },
+ { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 },
+ { ptOrg.x + m_cxSortArrow - 1, ptOrg.y }
+ };
+ dc.Polygon(pts, 3);
+ }
+ dc.SelectBrush(hbrOld);
+ dc.SelectPen(hpenOld);
+ }
+
+ double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags)
+ {
+ ATLASSERT(lpstr != NULL);
+ if(lpstr == NULL || lpstr[0] == _T('\0'))
+ return 0;
+
+ USES_CONVERSION;
+ HRESULT hRet = E_FAIL;
+ DATE dRet = 0;
+ if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet)))
+ {
+ ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet);
+ dRet = 0;
+ }
+ return dRet;
+ }
+
+ long StrToLong(LPCTSTR lpstr)
+ {
+ ATLASSERT(lpstr != NULL);
+ if(lpstr == NULL || lpstr[0] == _T('\0'))
+ return 0;
+
+ USES_CONVERSION;
+ HRESULT hRet = E_FAIL;
+ long lRet = 0;
+ if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet)))
+ {
+ ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet);
+ lRet = 0;
+ }
+ return lRet;
+ }
+
+ double StrToDouble(LPCTSTR lpstr)
+ {
+ ATLASSERT(lpstr != NULL);
+ if(lpstr == NULL || lpstr[0] == _T('\0'))
+ return 0;
+
+ USES_CONVERSION;
+ HRESULT hRet = E_FAIL;
+ double dblRet = 0;
+ if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet)))
+ {
+ ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet);
+ dblRet = 0;
+ }
+ return dblRet;
+ }
+
+ bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal)
+ {
+ ATLASSERT(lpstr != NULL);
+ ATLASSERT(pDecimal != NULL);
+ if(lpstr == NULL || pDecimal == NULL)
+ return false;
+
+ USES_CONVERSION;
+ HRESULT hRet = E_FAIL;
+ if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal)))
+ {
+ ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet);
+ pDecimal->Lo64 = 0;
+ pDecimal->Hi32 = 0;
+ pDecimal->signscale = 0;
+ return false;
+ }
+ return true;
+ }
+
+// Overrideable PFNLVCOMPARE functions
+ static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+ {
+ ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
+
+ LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
+ LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
+ LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
+
+ int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue);
+ return pInfo->bDescending ? -nRet : nRet;
+ }
+
+ static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+ {
+ ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
+
+ LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
+ LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
+ LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
+
+ int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue);
+ return pInfo->bDescending ? -nRet : nRet;
+ }
+
+ static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+ {
+ ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
+
+ LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
+ LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
+ LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
+
+ int nRet = 0;
+ if(pParam1->lValue > pParam2->lValue)
+ nRet = 1;
+ else if(pParam1->lValue < pParam2->lValue)
+ nRet = -1;
+ return pInfo->bDescending ? -nRet : nRet;
+ }
+
+ static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+ {
+ ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
+
+ LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
+ LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
+ LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
+
+ int nRet = 0;
+ if(pParam1->dblValue > pParam2->dblValue)
+ nRet = 1;
+ else if(pParam1->dblValue < pParam2->dblValue)
+ nRet = -1;
+ return pInfo->bDescending ? -nRet : nRet;
+ }
+
+ static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+ {
+ ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
+
+ LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
+ LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
+ LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
+
+ int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol);
+ return pInfo->bDescending ? -nRet : nRet;
+ }
+
+#ifndef _WIN32_WCE
+ static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+ {
+ ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
+
+ LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
+ LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
+ LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
+
+ int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue);
+ nRet--;
+ return pInfo->bDescending ? -nRet : nRet;
+ }
+#else
+ // Compare mantissas, ignore sign and scale
+ static int CompareMantissas(const DECIMAL& decLeft, const DECIMAL& decRight)
+ {
+ if (decLeft.Hi32 < decRight.Hi32)
+ {
+ return -1;
+ }
+ if (decLeft.Hi32 > decRight.Hi32)
+ {
+ return 1;
+ }
+ // Here, decLeft.Hi32 == decRight.Hi32
+ if (decLeft.Lo64 < decRight.Lo64)
+ {
+ return -1;
+ }
+ if (decLeft.Lo64 > decRight.Lo64)
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ // return values: VARCMP_LT, VARCMP_EQ, VARCMP_GT, VARCMP_NULL
+ static HRESULT VarDecCmp(const DECIMAL* pdecLeft, const DECIMAL* pdecRight)
+ {
+ static const ULONG powersOfTen[] =
+ {
+ 10ul,
+ 100ul,
+ 1000ul,
+ 10000ul,
+ 100000ul,
+ 1000000ul,
+ 10000000ul,
+ 100000000ul,
+ 1000000000ul
+ };
+ static const int largestPower = sizeof(powersOfTen) / sizeof(powersOfTen[0]);
+ if (!pdecLeft || !pdecRight)
+ {
+ return VARCMP_NULL;
+ }
+
+ // Degenerate case - at least one comparand is of the form
+ // [+-]0*10^N (denormalized zero)
+ bool bLeftZero = (!pdecLeft->Lo64 && !pdecLeft->Hi32);
+ bool bRightZero = (!pdecRight->Lo64 && !pdecRight->Hi32);
+ if (bLeftZero && bRightZero)
+ {
+ return VARCMP_EQ;
+ }
+ bool bLeftNeg = ((pdecLeft->sign & DECIMAL_NEG) != 0);
+ bool bRightNeg = ((pdecRight->sign & DECIMAL_NEG) != 0);
+ if (bLeftZero)
+ {
+ return (bRightNeg ? VARCMP_GT : VARCMP_LT);
+ }
+ // This also covers the case where the comparands have different signs
+ if (bRightZero || bLeftNeg != bRightNeg)
+ {
+ return (bLeftNeg ? VARCMP_LT : VARCMP_GT);
+ }
+
+ // Here both comparands have the same sign and need to be compared
+ // on mantissa and scale. The result is obvious when
+ // 1. Scales are equal (then compare mantissas)
+ // 2. A number with smaller scale is also the one with larger mantissa
+ // (then this number is obviously larger)
+ // In the remaining case, we would multiply the number with smaller
+ // scale by 10 and simultaneously increment its scale (which amounts to
+ // adding trailing zeros after decimal point), until the numbers fall under
+ // one of the two cases above
+ DECIMAL temp;
+ bool bInvert = bLeftNeg; // the final result needs to be inverted
+ if (pdecLeft->scale < pdecRight->scale)
+ {
+ temp = *pdecLeft;
+ }
+ else
+ {
+ temp = *pdecRight;
+ pdecRight = pdecLeft;
+ bInvert = !bInvert;
+ }
+
+ // Now temp is the number with smaller (or equal) scale, and
+ // we can modify it freely without touching original parameters
+ int comp;
+ while ((comp = CompareMantissas(temp, *pdecRight)) < 0 &&
+ temp.scale < pdecRight->scale)
+ {
+ // Multiply by an appropriate power of 10
+ int scaleDiff = pdecRight->scale - temp.scale;
+ if (scaleDiff > largestPower)
+ {
+ // Keep the multiplier representable in 32bit
+ scaleDiff = largestPower;
+ }
+ DWORDLONG power = powersOfTen[scaleDiff - 1];
+ // Multiply temp's mantissa by power
+ DWORDLONG product = temp.Lo32 * power;
+ ULONG carry = static_cast<ULONG>(product >> 32);
+ temp.Lo32 = static_cast<ULONG>(product);
+ product = temp.Mid32 * power + carry;
+ carry = static_cast<ULONG>(product >> 32);
+ temp.Mid32 = static_cast<ULONG>(product);
+ product = temp.Hi32 * power + carry;
+ if (static_cast<ULONG>(product >> 32))
+ {
+ // Multiplication overflowed - pdecLeft is clearly larger
+ break;
+ }
+ temp.Hi32 = static_cast<ULONG>(product);
+ temp.scale = (BYTE)(temp.scale + scaleDiff);
+ }
+ if (temp.scale < pdecRight->scale)
+ {
+ comp = 1;
+ }
+ if (bInvert)
+ {
+ comp = -comp;
+ }
+ return (comp > 0 ? VARCMP_GT : comp < 0 ? VARCMP_LT : VARCMP_EQ);
+ }
+
+ static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+ {
+ ATLASSERT(lParam1 != NULL && lParam2 != NULL && lParamSort != NULL);
+
+ LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
+ LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
+ LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
+
+ int nRet = (int)VarDecCmp(&pParam1->decValue, &pParam2->decValue);
+ nRet--;
+ return pInfo->bDescending ? -nRet : nRet;
+ }
+#endif // !_WIN32_WCE
+
+ BEGIN_MSG_MAP(CSortListViewImpl)
+ MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn)
+ MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn)
+ NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick)
+ NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick)
+ MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
+ END_MSG_MAP()
+
+ LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
+ if(lRet == -1)
+ return -1;
+
+ WORD wType = 0;
+ m_arrColSortType.Add(wType);
+ int nCount = m_arrColSortType.GetSize();
+ ATLASSERT(nCount == GetColumnCount());
+
+ for(int i = nCount - 1; i > lRet; i--)
+ m_arrColSortType[i] = m_arrColSortType[i - 1];
+ m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT;
+
+ if(lRet <= m_iSortColumn)
+ m_iSortColumn++;
+
+ return lRet;
+ }
+
+ LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
+ if(lRet == 0)
+ return 0;
+
+ int iCol = (int)wParam;
+ if(m_iSortColumn == iCol)
+ m_iSortColumn = -1;
+ else if(m_iSortColumn > iCol)
+ m_iSortColumn--;
+ m_arrColSortType.RemoveAt(iCol);
+
+ return lRet;
+ }
+
+ LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
+ {
+ LPNMHEADER p = (LPNMHEADER)pnmh;
+ if(p->iButton == 0)
+ {
+ int iOld = m_iSortColumn;
+ bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false;
+ if(DoSortItems(p->iItem, bDescending))
+ NotifyParentSortChanged(p->iItem, iOld);
+ }
+ bHandled = FALSE;
+ return 0;
+ }
+
+ LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+#ifndef _WIN32_WCE
+ if(wParam == SPI_SETNONCLIENTMETRICS)
+ GetSystemSettings();
+#else // CE specific
+ wParam; // avoid level 4 warning
+ GetSystemSettings();
+#endif // _WIN32_WCE
+ bHandled = FALSE;
+ return 0;
+ }
+
+ void GetSystemSettings()
+ {
+ if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull())
+ {
+ T* pT = static_cast<T*>(this);
+ pT->CreateSortBitmaps();
+ if(m_iSortColumn != -1)
+ SetSortColumn(m_iSortColumn);
+ }
+ }
+
+};
+
+
+typedef ATL::CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SHOWSELALWAYS , WS_EX_CLIENTEDGE> CSortListViewCtrlTraits;
+
+template <class T, class TBase = CListViewCtrl, class TWinTraits = CSortListViewCtrlTraits>
+class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl<T, TBase, TWinTraits>, public CSortListViewImpl<T>
+{
+public:
+ DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
+
+ bool SortItems(int iCol, bool bDescending = false)
+ {
+ return DoSortItems(iCol, bDescending);
+ }
+
+ BEGIN_MSG_MAP(CSortListViewCtrlImpl)
+ MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl<T>::OnInsertColumn)
+ MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl<T>::OnDeleteColumn)
+ NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl<T>::OnHeaderItemClick)
+ NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl<T>::OnHeaderItemClick)
+ MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl<T>::OnSettingChange)
+ END_MSG_MAP()
+};
+
+class CSortListViewCtrl : public CSortListViewCtrlImpl<CSortListViewCtrl>
+{
+public:
+ DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName())
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CTabView - implements tab view window
+
+// TabView Notifications
+#define TBVN_PAGEACTIVATED (0U-741)
+#define TBVN_CONTEXTMENU (0U-742)
+
+// Notification data for TBVN_CONTEXTMENU
+struct TBVCONTEXTMENUINFO
+{
+ NMHDR hdr;
+ POINT pt;
+};
+
+typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
+
+
+template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
+class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
+{
+public:
+ DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE)
+
+// Declarations and enums
+ struct TABVIEWPAGE
+ {
+ HWND hWnd;
+ LPTSTR lpstrTitle;
+ LPVOID pData;
+ };
+
+ struct TCITEMEXTRA
+ {
+ TCITEMHEADER tciheader;
+ TABVIEWPAGE tvpage;
+
+ operator LPTCITEM() { return (LPTCITEM)this; }
+ };
+
+ enum
+ {
+ m_nTabID = 1313,
+ m_cxMoveMark = 6,
+ m_cyMoveMark = 3,
+ m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
+ };
+
+// Data members
+ ATL::CContainedWindowT<CTabCtrl> m_tab;
+ int m_cyTabHeight;
+
+ int m_nActivePage;
+
+ int m_nInsertItem;
+ POINT m_ptStartDrag;
+
+ CMenuHandle m_menu;
+
+ int m_cchTabTextLength;
+
+ int m_nMenuItemsCount;
+
+ ATL::CWindow m_wndTitleBar;
+ LPTSTR m_lpstrTitleBarBase;
+ int m_cchTitleBarLength;
+
+ CImageList m_ilDrag;
+
+ bool m_bDestroyPageOnRemove:1;
+ bool m_bDestroyImageList:1;
+ bool m_bActivePageMenuItem:1;
+ bool m_bActiveAsDefaultMenuItem:1;
+ bool m_bEmptyMenuItem:1;
+ bool m_bWindowsMenuItem:1;
+ // internal
+ bool m_bTabCapture:1;
+ bool m_bTabDrag:1;
+
+// Constructor/destructor
+ CTabViewImpl() :
+ m_nActivePage(-1),
+ m_cyTabHeight(0),
+ m_tab(this, 1),
+ m_nInsertItem(-1),
+ m_cchTabTextLength(30),
+ m_nMenuItemsCount(10),
+ m_lpstrTitleBarBase(NULL),
+ m_cchTitleBarLength(100),
+ m_bDestroyPageOnRemove(true),
+ m_bDestroyImageList(true),
+ m_bActivePageMenuItem(true),
+ m_bActiveAsDefaultMenuItem(false),
+ m_bEmptyMenuItem(false),
+ m_bWindowsMenuItem(false),
+ m_bTabCapture(false),
+ m_bTabDrag(false)
+ {
+ m_ptStartDrag.x = 0;
+ m_ptStartDrag.y = 0;
+ }
+
+ ~CTabViewImpl()
+ {
+ delete [] m_lpstrTitleBarBase;
+ }
+
+// Message filter function - to be called from PreTranslateMessage of the main window
+ BOOL PreTranslateMessage(MSG* pMsg)
+ {
+ if(IsWindow() == FALSE)
+ return FALSE;
+
+ BOOL bRet = FALSE;
+
+ // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
+ int nCount = GetPageCount();
+ if(nCount > 0)
+ {
+ bool bControl = (::GetKeyState(VK_CONTROL) < 0);
+ if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
+ {
+ if(nCount > 1)
+ {
+ int nPage = m_nActivePage;
+ bool bShift = (::GetKeyState(VK_SHIFT) < 0);
+ if(bShift)
+ nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
+ else
+ nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
+
+ SetActivePage(nPage);
+ T* pT = static_cast<T*>(this);
+ pT->OnPageActivated(m_nActivePage);
+ }
+
+ bRet = TRUE;
+ }
+ }
+
+ // If we are doing drag-drop, check for Escape key that cancels it
+ if(bRet == FALSE)
+ {
+ if(m_bTabCapture && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE)
+ {
+ ::ReleaseCapture();
+ bRet = TRUE;
+ }
+ }
+
+ // Pass the message to the active page
+ if(bRet == FALSE)
+ {
+ if(m_nActivePage != -1)
+ bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
+ }
+
+ return bRet;
+ }
+
+// Attributes
+ int GetPageCount() const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ return m_tab.GetItemCount();
+ }
+
+ int GetActivePage() const
+ {
+ return m_nActivePage;
+ }
+
+ void SetActivePage(int nPage)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ T* pT = static_cast<T*>(this);
+
+ SetRedraw(FALSE);
+
+ if(m_nActivePage != -1)
+ ::ShowWindow(GetPageHWND(m_nActivePage), FALSE);
+ m_nActivePage = nPage;
+ m_tab.SetCurSel(m_nActivePage);
+ ::ShowWindow(GetPageHWND(m_nActivePage), TRUE);
+
+ pT->UpdateLayout();
+
+ SetRedraw(TRUE);
+ RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+ if(::GetFocus() != m_tab.m_hWnd)
+ ::SetFocus(GetPageHWND(m_nActivePage));
+
+ pT->UpdateTitleBar();
+ pT->UpdateMenu();
+ }
+
+ HIMAGELIST GetImageList() const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ return m_tab.GetImageList();
+ }
+
+ HIMAGELIST SetImageList(HIMAGELIST hImageList)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ return m_tab.SetImageList(hImageList);
+ }
+
+ void SetWindowMenu(HMENU hMenu)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+
+ m_menu = hMenu;
+
+ T* pT = static_cast<T*>(this);
+ pT->UpdateMenu();
+ }
+
+ void SetTitleBarWindow(HWND hWnd)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+
+ delete [] m_lpstrTitleBarBase;
+ m_lpstrTitleBarBase = NULL;
+
+ m_wndTitleBar = hWnd;
+ if(hWnd == NULL)
+ return;
+
+ int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
+ ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
+ if(m_lpstrTitleBarBase != NULL)
+ {
+ m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
+ T* pT = static_cast<T*>(this);
+ pT->UpdateTitleBar();
+ }
+ }
+
+// Page attributes
+ HWND GetPageHWND(int nPage) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_PARAM;
+ m_tab.GetItem(nPage, tcix);
+
+ return tcix.tvpage.hWnd;
+ }
+
+ LPCTSTR GetPageTitle(int nPage) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_PARAM;
+ if(m_tab.GetItem(nPage, tcix) == FALSE)
+ return NULL;
+
+ return tcix.tvpage.lpstrTitle;
+ }
+
+ bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ T* pT = static_cast<T*>(this);
+
+ int cchBuff = lstrlen(lpstrTitle) + 1;
+ LPTSTR lpstrBuff = NULL;
+ ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
+ if(lpstrBuff == NULL)
+ return false;
+
+ SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_PARAM;
+ if(m_tab.GetItem(nPage, tcix) == FALSE)
+ return false;
+
+ CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
+ if(lpstrTabText == NULL)
+ return false;
+
+ delete [] tcix.tvpage.lpstrTitle;
+
+ pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
+
+ tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
+ tcix.tciheader.pszText = lpstrTabText;
+ tcix.tvpage.lpstrTitle = lpstrBuff;
+ if(m_tab.SetItem(nPage, tcix) == FALSE)
+ return false;
+
+ pT->UpdateTitleBar();
+ pT->UpdateMenu();
+
+ return true;
+ }
+
+ LPVOID GetPageData(int nPage) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_PARAM;
+ m_tab.GetItem(nPage, tcix);
+
+ return tcix.tvpage.pData;
+ }
+
+ LPVOID SetPageData(int nPage, LPVOID pData)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_PARAM;
+ m_tab.GetItem(nPage, tcix);
+ LPVOID pDataOld = tcix.tvpage.pData;
+
+ tcix.tvpage.pData = pData;
+ m_tab.SetItem(nPage, tcix);
+
+ return pDataOld;
+ }
+
+ int GetPageImage(int nPage) const
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_IMAGE;
+ m_tab.GetItem(nPage, tcix);
+
+ return tcix.tciheader.iImage;
+ }
+
+ int SetPageImage(int nPage, int nImage)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_IMAGE;
+ m_tab.GetItem(nPage, tcix);
+ int nImageOld = tcix.tciheader.iImage;
+
+ tcix.tciheader.iImage = nImage;
+ m_tab.SetItem(nPage, tcix);
+
+ return nImageOld;
+ }
+
+// Operations
+ bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
+ {
+ return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
+ }
+
+ bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage));
+
+ T* pT = static_cast<T*>(this);
+
+ int cchBuff = lstrlen(lpstrTitle) + 1;
+ LPTSTR lpstrBuff = NULL;
+ ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
+ if(lpstrBuff == NULL)
+ return false;
+
+ SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);
+
+ CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
+ if(lpstrTabText == NULL)
+ return false;
+
+ pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
+
+ SetRedraw(FALSE);
+
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
+ tcix.tciheader.pszText = lpstrTabText;
+ tcix.tciheader.iImage = nImage;
+ tcix.tvpage.hWnd = hWndView;
+ tcix.tvpage.lpstrTitle = lpstrBuff;
+ tcix.tvpage.pData = pData;
+ int nItem = m_tab.InsertItem(nPage, tcix);
+ if(nItem == -1)
+ {
+ delete [] lpstrBuff;
+ SetRedraw(TRUE);
+ return false;
+ }
+
+ SetActivePage(nItem);
+ pT->OnPageActivated(m_nActivePage);
+
+ if(GetPageCount() == 1)
+ pT->ShowTabControl(true);
+
+ pT->UpdateLayout();
+
+ SetRedraw(TRUE);
+ RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+ return true;
+ }
+
+ void RemovePage(int nPage)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+ ATLASSERT(IsValidPageIndex(nPage));
+
+ T* pT = static_cast<T*>(this);
+
+ SetRedraw(FALSE);
+
+ if(GetPageCount() == 1)
+ pT->ShowTabControl(false);
+
+ if(m_bDestroyPageOnRemove)
+ ::DestroyWindow(GetPageHWND(nPage));
+ else
+ ::ShowWindow(GetPageHWND(nPage), FALSE);
+ LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
+ delete [] lpstrTitle;
+
+ ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
+
+ if(m_nActivePage == nPage)
+ {
+ m_nActivePage = -1;
+
+ if(nPage > 0)
+ {
+ SetActivePage(nPage - 1);
+ }
+ else if(GetPageCount() > 0)
+ {
+ SetActivePage(nPage);
+ }
+ else
+ {
+ SetRedraw(TRUE);
+ Invalidate();
+ UpdateWindow();
+ pT->UpdateTitleBar();
+ pT->UpdateMenu();
+ }
+ }
+ else
+ {
+ nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
+ m_nActivePage = -1;
+ SetActivePage(nPage);
+ }
+
+ pT->OnPageActivated(m_nActivePage);
+ }
+
+ void RemoveAllPages()
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+
+ if(GetPageCount() == 0)
+ return;
+
+ T* pT = static_cast<T*>(this);
+
+ SetRedraw(FALSE);
+
+ pT->ShowTabControl(false);
+
+ for(int i = 0; i < GetPageCount(); i++)
+ {
+ if(m_bDestroyPageOnRemove)
+ ::DestroyWindow(GetPageHWND(i));
+ else
+ ::ShowWindow(GetPageHWND(i), FALSE);
+ LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
+ delete [] lpstrTitle;
+ }
+ m_tab.DeleteAllItems();
+
+ m_nActivePage = -1;
+ pT->OnPageActivated(m_nActivePage);
+
+ SetRedraw(TRUE);
+ Invalidate();
+ UpdateWindow();
+
+ pT->UpdateTitleBar();
+ pT->UpdateMenu();
+ }
+
+ int PageIndexFromHwnd(HWND hWnd) const
+ {
+ int nIndex = -1;
+
+ for(int i = 0; i < GetPageCount(); i++)
+ {
+ if(GetPageHWND(i) == hWnd)
+ {
+ nIndex = i;
+ break;
+ }
+ }
+
+ return nIndex;
+ }
+
+ void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
+ {
+ ATLASSERT(::IsWindow(m_hWnd));
+
+ CMenuHandle menu = hMenu;
+ T* pT = static_cast<T*>(this);
+ pT; // avoid level 4 warning
+ int nFirstPos = 0;
+
+ // Find first menu item in our range
+#ifndef _WIN32_WCE
+ for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
+ {
+ UINT nID = menu.GetMenuItemID(nFirstPos);
+ if((nID >= ID_WINDOW_TABFIRST && nID <= ID_WINDOW_TABLAST) || nID == ID_WINDOW_SHOWTABLIST)
+ break;
+ }
+#else // CE specific
+ for(nFirstPos = 0; ; nFirstPos++)
+ {
+ CMenuItemInfo mii;
+ mii.fMask = MIIM_ID;
+ BOOL bRet = menu.GetMenuItemInfo(nFirstPos, TRUE, &mii);
+ if(bRet == FALSE)
+ break;
+ if((mii.wID >= ID_WINDOW_TABFIRST && mii.wID <= ID_WINDOW_TABLAST) || mii.wID == ID_WINDOW_SHOWTABLIST)
+ break;
+ }
+#endif // _WIN32_WCE
+
+ // Remove all menu items for tab pages
+ BOOL bRet = TRUE;
+ while(bRet != FALSE)
+ bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
+
+ // Add separator if it's not already there
+ int nPageCount = GetPageCount();
+ if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
+ {
+ CMenuItemInfo mii;
+ mii.fMask = MIIM_TYPE;
+ menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
+ if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0))
+ {
+ menu.AppendMenu(MF_SEPARATOR);
+ nFirstPos++;
+ }
+ }
+
+ // Add menu items for all pages
+ if(nPageCount > 0)
+ {
+ // Append menu items for all pages
+ const int cchPrefix = 3; // 2 digits + space
+ nMenuItemsCount = __min(min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
+ ATLASSERT(nMenuItemsCount < 100); // 2 digits only
+ if(nMenuItemsCount >= 100)
+ nMenuItemsCount = 99;
+
+ for(int i = 0; i < nMenuItemsCount; i++)
+ {
+ LPCTSTR lpstrTitle = GetPageTitle(i);
+ int nLen = lstrlen(lpstrTitle);
+ CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
+ ATLASSERT(lpstrText != NULL);
+ if(lpstrText != NULL)
+ {
+ LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
+ SecureHelper::wsprintf_x(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
+ menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
+ }
+ }
+
+ // Mark active page
+ if(bActivePageMenuItem && (m_nActivePage != -1))
+ {
+#ifndef _WIN32_WCE
+ if(bActiveAsDefaultMenuItem)
+ {
+ menu.SetMenuDefaultItem((UINT)-1, TRUE);
+ menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
+ }
+ else
+#else // CE specific
+ bActiveAsDefaultMenuItem; // avoid level 4 warning
+#endif // _WIN32_WCE
+ {
+ menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
+ }
+ }
+ }
+ else
+ {
+ if(bEmptyMenuItem)
+ {
+ menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
+ menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
+ }
+
+ // Remove separator if nothing else is there
+ if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
+ {
+ CMenuItemInfo mii;
+ mii.fMask = MIIM_TYPE;
+ menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
+ if((mii.fType & MFT_SEPARATOR) != 0)
+ menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
+ }
+ }
+
+ // Add "Windows..." menu item
+ if(bWindowsMenuItem)
+ menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
+ }
+
+// Message map and handlers
+ BEGIN_MSG_MAP(CTabViewImpl)
+ MESSAGE_HANDLER(WM_CREATE, OnCreate)
+ MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+ MESSAGE_HANDLER(WM_SIZE, OnSize)
+ MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
+ NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
+ NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
+#ifndef _WIN32_WCE
+ NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
+#endif // !_WIN32_WCE
+ FORWARD_NOTIFICATIONS()
+ ALT_MSG_MAP(1) // tab control
+ MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
+ MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
+ MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
+ MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
+ MESSAGE_HANDLER(WM_RBUTTONUP, OnTabRButtonUp)
+ MESSAGE_HANDLER(WM_SYSKEYDOWN, OnTabSysKeyDown)
+ END_MSG_MAP()
+
+ LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->CreateTabControl();
+
+ return 0;
+ }
+
+ LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ RemoveAllPages();
+
+ if(m_bDestroyImageList)
+ {
+ CImageList il = m_tab.SetImageList(NULL);
+ if(il.m_hImageList != NULL)
+ il.Destroy();
+ }
+
+ return 0;
+ }
+
+ LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->UpdateLayout();
+ return 0;
+ }
+
+ LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+ {
+ if(m_nActivePage != -1)
+ ::SetFocus(GetPageHWND(m_nActivePage));
+ return 0;
+ }
+
+ LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
+ {
+ SetActivePage(m_tab.GetCurSel());
+ T* pT = static_cast<T*>(this);
+ pT->OnPageActivated(m_nActivePage);
+
+ return 0;
+ }
+
+ LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
+ {
+ // nothing to do - this just blocks all tab control
+ // notifications from being propagated further
+ return 0;
+ }
+
+#ifndef _WIN32_WCE
+ LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
+ {
+ LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
+ if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
+ {
+ T* pT = static_cast<T*>(this);
+ pT->UpdateTooltipText(pTTDI);
+ }
+ else
+ {
+ bHandled = FALSE;
+ }
+
+ return 0;
+ }
+#endif // !_WIN32_WCE
+
+// Tab control message handlers
+ LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+ {
+ if(m_tab.GetItemCount() > 1)
+ {
+ m_bTabCapture = true;
+ m_tab.SetCapture();
+
+ m_ptStartDrag.x = GET_X_LPARAM(lParam);
+ m_ptStartDrag.y = GET_Y_LPARAM(lParam);
+ }
+
+ bHandled = FALSE;
+ return 0;
+ }
+
+ LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+ {
+ if(m_bTabCapture)
+ {
+ if(m_bTabDrag)
+ {
+ TCHITTESTINFO hti = { 0 };
+ hti.pt.x = GET_X_LPARAM(lParam);
+ hti.pt.y = GET_Y_LPARAM(lParam);
+ int nItem = m_tab.HitTest(&hti);
+ if(nItem != -1)
+ MovePage(m_nActivePage, nItem);
+ }
+
+ ::ReleaseCapture();
+ }
+
+ bHandled = FALSE;
+ return 0;
+ }
+
+ LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ if(m_bTabCapture)
+ {
+ m_bTabCapture = false;
+
+ if(m_bTabDrag)
+ {
+ m_bTabDrag = false;
+ T* pT = static_cast<T*>(this);
+ pT->DrawMoveMark(-1);
+
+#ifndef _WIN32_WCE
+ m_ilDrag.DragLeave(GetDesktopWindow());
+#endif // !_WIN32_WCE
+ m_ilDrag.EndDrag();
+
+ m_ilDrag.Destroy();
+ m_ilDrag.m_hImageList = NULL;
+ }
+ }
+
+ bHandled = FALSE;
+ return 0;
+ }
+
+ LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+ {
+ bHandled = FALSE;
+
+ if(m_bTabCapture)
+ {
+ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
+
+ if(!m_bTabDrag)
+ {
+#ifndef _WIN32_WCE
+ if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) ||
+ abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG))
+#else // CE specific
+ if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= 4 ||
+ abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= 4)
+#endif // _WIN32_WCE
+ {
+ T* pT = static_cast<T*>(this);
+ pT->GenerateDragImage(m_nActivePage);
+
+ int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
+ int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
+ m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
+#ifndef _WIN32_WCE
+ POINT ptEnter = m_ptStartDrag;
+ m_tab.ClientToScreen(&ptEnter);
+ m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
+#endif // !_WIN32_WCE
+
+ m_bTabDrag = true;
+ }
+ }
+
+ if(m_bTabDrag)
+ {
+ TCHITTESTINFO hti = { 0 };
+ hti.pt = pt;
+ int nItem = m_tab.HitTest(&hti);
+
+ T* pT = static_cast<T*>(this);
+ pT->SetMoveCursor(nItem != -1);
+
+ if(m_nInsertItem != nItem)
+ pT->DrawMoveMark(nItem);
+
+ m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
+ m_tab.ClientToScreen(&pt);
+ m_ilDrag.DragMove(pt);
+
+ bHandled = TRUE;
+ }
+ }
+
+ return 0;
+ }
+
+ LRESULT OnTabRButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
+ {
+ TCHITTESTINFO hti = { 0 };
+ hti.pt.x = GET_X_LPARAM(lParam);
+ hti.pt.y = GET_Y_LPARAM(lParam);
+ int nItem = m_tab.HitTest(&hti);
+ if(nItem != -1)
+ {
+ T* pT = static_cast<T*>(this);
+ pT->OnContextMenu(nItem, hti.pt);
+ }
+
+ return 0;
+ }
+
+ LRESULT OnTabSysKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+ {
+ bool bShift = (::GetKeyState(VK_SHIFT) < 0);
+ if(wParam == VK_F10 && bShift)
+ {
+ if(m_nActivePage != -1)
+ {
+ RECT rect = { 0 };
+ m_tab.GetItemRect(m_nActivePage, &rect);
+ POINT pt = { rect.left, rect.bottom };
+ T* pT = static_cast<T*>(this);
+ pT->OnContextMenu(m_nActivePage, pt);
+ }
+ }
+ else
+ {
+ bHandled = FALSE;
+ }
+
+ return 0;
+ }
+
+// Implementation helpers
+ bool IsValidPageIndex(int nPage) const
+ {
+ return (nPage >= 0 && nPage < GetPageCount());
+ }
+
+ bool MovePage(int nMovePage, int nInsertBeforePage)
+ {
+ ATLASSERT(IsValidPageIndex(nMovePage));
+ ATLASSERT(IsValidPageIndex(nInsertBeforePage));
+
+ if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
+ return false;
+
+ if(nMovePage == nInsertBeforePage)
+ return true; // nothing to do
+
+ CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
+ if(lpstrTabText == NULL)
+ return false;
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
+ tcix.tciheader.pszText = lpstrTabText;
+ tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
+ BOOL bRet = m_tab.GetItem(nMovePage, tcix);
+ ATLASSERT(bRet != FALSE);
+ if(bRet == FALSE)
+ return false;
+
+ int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
+ int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
+ ATLASSERT(nNewItem == nInsertItem);
+ if(nNewItem != nInsertItem)
+ {
+ ATLVERIFY(m_tab.DeleteItem(nNewItem));
+ return false;
+ }
+
+ if(nMovePage > nInsertBeforePage)
+ ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
+ else if(nMovePage < nInsertBeforePage)
+ ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
+
+ SetActivePage(nInsertBeforePage);
+ T* pT = static_cast<T*>(this);
+ pT->OnPageActivated(m_nActivePage);
+
+ return true;
+ }
+
+// Implementation overrideables
+ bool CreateTabControl()
+ {
+#ifndef _WIN32_WCE
+ m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
+#else // CE specific
+ m_tab.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, m_nTabID);
+#endif // _WIN32_WCE
+ ATLASSERT(m_tab.m_hWnd != NULL);
+ if(m_tab.m_hWnd == NULL)
+ return false;
+
+ m_tab.SetFont(AtlGetDefaultGuiFont());
+
+ m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
+
+ T* pT = static_cast<T*>(this);
+ m_cyTabHeight = pT->CalcTabHeight();
+
+ return true;
+ }
+
+ int CalcTabHeight()
+ {
+ int nCount = m_tab.GetItemCount();
+ TCITEMEXTRA tcix = { 0 };
+ tcix.tciheader.mask = TCIF_TEXT;
+ tcix.tciheader.pszText = _T("NS");
+ int nIndex = m_tab.InsertItem(nCount, tcix);
+
+ RECT rect = { 0, 0, 1000, 1000 };
+ m_tab.AdjustRect(FALSE, &rect);
+
+ RECT rcWnd = { 0, 0, 1000, rect.top };
+ ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
+
+ int nHeight = rcWnd.bottom - rcWnd.top;
+
+ m_tab.DeleteItem(nIndex);
+
+ return nHeight;
+ }
+
+ void ShowTabControl(bool bShow)
+ {
+ m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
+ }
+
+ void UpdateLayout()
+ {
+ RECT rect;
+ GetClientRect(&rect);
+
+ if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
+ m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
+
+ if(m_nActivePage != -1)
+ ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, m_cyTabHeight, rect.right - rect.left, rect.bottom - rect.top - m_cyTabHeight, SWP_NOZORDER);
+ }
+
+ void UpdateMenu()
+ {
+ if(m_menu.m_hMenu != NULL)
+ BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
+ }
+
+ void UpdateTitleBar()
+ {
+ if(!m_wndTitleBar.IsWindow() || m_lpstrTitleBarBase == NULL)
+ return; // nothing to do
+
+ if(m_nActivePage != -1)
+ {
+ T* pT = static_cast<T*>(this);
+ LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
+ LPCTSTR lpstrDivider = pT->GetTitleDividerText();
+ int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
+ CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
+ LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
+ ATLASSERT(lpstrPageTitle != NULL);
+ if(lpstrPageTitle != NULL)
+ {
+ pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
+ SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, lpstrDivider);
+ SecureHelper::strcat_x(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
+ }
+ else
+ {
+ lpstrPageTitle = m_lpstrTitleBarBase;
+ }
+
+ m_wndTitleBar.SetWindowText(lpstrPageTitle);
+ }
+ else
+ {
+ m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
+ }
+ }
+
+ void DrawMoveMark(int nItem)
+ {
+ T* pT = static_cast<T*>(this);
+
+ if(m_nInsertItem != -1)
+ {
+ RECT rect = { 0 };
+ pT->GetMoveMarkRect(rect);
+ m_tab.InvalidateRect(&rect);
+ }
+
+ m_nInsertItem = nItem;
+
+ if(m_nInsertItem != -1)
+ {
+ CClientDC dc(m_tab.m_hWnd);
+
+ RECT rect = { 0 };
+ pT->GetMoveMarkRect(rect);
+
+ CPen pen;
+ pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
+ CBrush brush;
+ brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
+
+ HPEN hPenOld = dc.SelectPen(pen);
+ HBRUSH hBrushOld = dc.SelectBrush(brush);
+
+ int x = rect.left;
+ int y = rect.top;
+ POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
+ dc.Polygon(ptsTop, 3);
+
+ y = rect.bottom - 1;
+ POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
+ dc.Polygon(ptsBottom, 3);
+
+ dc.SelectPen(hPenOld);
+ dc.SelectBrush(hBrushOld);
+ }
+ }
+
+ void GetMoveMarkRect(RECT& rect) const
+ {
+ m_tab.GetClientRect(&rect);
+
+ RECT rcItem = { 0 };
+ m_tab.GetItemRect(m_nInsertItem, &rcItem);
+
+ if(m_nInsertItem <= m_nActivePage)
+ {
+ rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
+ rect.right = rcItem.left + m_cxMoveMark / 2;
+ }
+ else
+ {
+ rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
+ rect.right = rcItem.right + m_cxMoveMark / 2;
+ }
+ }
+
+ void SetMoveCursor(bool bCanMove)
+ {
+ ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
+ }
+
+ void GenerateDragImage(int nItem)
+ {
+ ATLASSERT(IsValidPageIndex(nItem));
+
+#ifndef _WIN32_WCE
+ RECT rcItem = { 0 };
+ m_tab.GetItemRect(nItem, &rcItem);
+ ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
+#else // CE specific
+ nItem; // avoid level 4 warning
+ RECT rcItem = { 0, 0, 40, 20 };
+#endif // _WIN32_WCE
+
+ ATLASSERT(m_ilDrag.m_hImageList == NULL);
+ m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
+
+ CClientDC dc(m_hWnd);
+ CDC dcMem;
+ dcMem.CreateCompatibleDC(dc);
+ ATLASSERT(dcMem.m_hDC != NULL);
+ dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
+
+ CBitmap bmp;
+ bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
+ ATLASSERT(bmp.m_hBitmap != NULL);
+
+ HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
+#ifndef _WIN32_WCE
+ m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
+#else // CE specific
+ dcMem.Rectangle(&rcItem);
+#endif // _WIN32_WCE
+ dcMem.SelectBitmap(hBmpOld);
+
+ ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
+ }
+
+ void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
+ {
+ if(lstrlen(lpstrTitle) >= cchShortTitle)
+ {
+ LPCTSTR lpstrEllipsis = _T("...");
+ int cchEllipsis = lstrlen(lpstrEllipsis);
+ SecureHelper::strncpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
+ SecureHelper::strcat_x(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
+ }
+ else
+ {
+ SecureHelper::strcpy_x(lpstrShortTitle, cchShortTitle, lpstrTitle);
+ }
+ }
+
+#ifndef _WIN32_WCE
+ void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
+ {
+ ATLASSERT(pTTDI != NULL);
+ pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
+ }
+#endif // !_WIN32_WCE
+
+// Text for menu items and title bar - override to provide different strings
+ static LPCTSTR GetEmptyListText()
+ {
+ return _T("(Empty)");
+ }
+
+ static LPCTSTR GetWindowsMenuItemText()
+ {
+ return _T("&Windows...");
+ }
+
+ static LPCTSTR GetTitleDividerText()
+ {
+ return _T(" - ");
+ }
+
+// Notifications - override to provide different behavior
+ void OnPageActivated(int nPage)
+ {
+ NMHDR nmhdr = { 0 };
+ nmhdr.hwndFrom = m_hWnd;
+ nmhdr.idFrom = nPage;
+ nmhdr.code = TBVN_PAGEACTIVATED;
+ ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
+ }
+
+ void OnContextMenu(int nPage, POINT pt)
+ {
+ m_tab.ClientToScreen(&pt);
+
+ TBVCONTEXTMENUINFO cmi = { 0 };
+ cmi.hdr.hwndFrom = m_hWnd;
+ cmi.hdr.idFrom = nPage;
+ cmi.hdr.code = TBVN_CONTEXTMENU;
+ cmi.pt = pt;
+ ::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&cmi);
+ }
+};
+
+
+class CTabView : public CTabViewImpl<CTabView>
+{
+public:
+ DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
+};
+
+}; // namespace WTL
+
+#endif // __ATLCTRLX_H__
« no previous file with comments | « third_party/wtl/include/atlctrlw.h ('k') | third_party/wtl/include/atlddx.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698