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__ |