1974 lines
50 KiB
C++
1974 lines
50 KiB
C++
// WTL Version 3.1
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This file is a part of Windows Template Library.
|
|
// The code and information is provided "as-is" without
|
|
// warranty of any kind, either expressed or implied.
|
|
|
|
#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
|
|
|
|
|
|
namespace WTL
|
|
{
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Forward declarations
|
|
|
|
template <class T, class TBase = CButton, class TWinTraits = CControlWinTraits> class CBitmapButtonImpl;
|
|
class CBitmapButton;
|
|
template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits> class CCheckListViewCtrlImpl;
|
|
class CCheckListViewCtrl;
|
|
template <class T, class TBase = CWindow, class TWinTraits = CControlWinTraits> class CHyperLinkImpl;
|
|
class CHyperLink;
|
|
class CWaitCursor;
|
|
template <class T, class TBase = CStatusBarCtrl> class CMultiPaneStatusBarCtrlImpl;
|
|
class CMultiPaneStatusBarCtrl;
|
|
template <class T, class TBase = CWindow, class TWinTraits = CControlWinTraits> class CPaneContainerImpl;
|
|
class CPaneContainer;
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CBitmapButton - bitmap button implementation
|
|
|
|
// 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 = CControlWinTraits>
|
|
class ATL_NO_VTABLE CBitmapButtonImpl : public 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)
|
|
{
|
|
BOOL bRet = CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
|
|
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;
|
|
return (lstrcpyn(lpstrText, m_lpstrToolTipText, min(nLength, lstrlen(m_lpstrToolTipText) + 1)) != NULL);
|
|
}
|
|
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;
|
|
}
|
|
ATLTRY(m_lpstrToolTipText = new TCHAR[lstrlen(lpstrText) + 1]);
|
|
if(m_lpstrToolTipText == NULL)
|
|
return false;
|
|
bool bRet = (lstrcpy(m_lpstrToolTipText, lpstrText) != NULL);
|
|
if(bRet && m_tip.IsWindow())
|
|
{
|
|
m_tip.Activate(TRUE);
|
|
m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
// 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(m_fPressed == 1)
|
|
nImage = m_nImage[_nImagePushed];
|
|
else if((!bHover && m_fFocus == 1) || (bHover && m_fMouseOver == 1))
|
|
nImage = m_nImage[_nImageFocusOrHover];
|
|
else if(!IsWindowEnabled())
|
|
nImage = m_nImage[_nImageDisabled];
|
|
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
|
|
typedef CBitmapButtonImpl< T, TBase, TWinTraits > thisClass;
|
|
BEGIN_MSG_MAP(thisClass)
|
|
MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
|
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)
|
|
END_MSG_MAP()
|
|
|
|
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
{
|
|
Init();
|
|
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;
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
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)
|
|
{ }
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// 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 CWindowImpl<T, TBase, TWinTraits>
|
|
{
|
|
public:
|
|
DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
|
|
|
|
// Attributes
|
|
static DWORD GetExtendedLVStyle()
|
|
{
|
|
return TWinTraits::GetExtendedLVStyle();
|
|
}
|
|
|
|
// Operations
|
|
BOOL SubclassWindow(HWND hWnd)
|
|
{
|
|
BOOL bRet = CWindowImplBaseT< TBase, TWinTraits>::SubclassWindow(hWnd);
|
|
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;
|
|
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
|
|
typedef CCheckListViewCtrlImpl< T, TBase, TWinTraits > thisClass;
|
|
BEGIN_MSG_MAP(thisClass)
|
|
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;
|
|
lvh.pt = ptMsg;
|
|
if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0)
|
|
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)
|
|
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)
|
|
__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)
|
|
|
|
|
|
template <class T, class TBase = CWindow, class TWinTraits = CControlWinTraits>
|
|
class ATL_NO_VTABLE CHyperLinkImpl : public CWindowImpl< T, TBase, TWinTraits >
|
|
{
|
|
public:
|
|
LPTSTR m_lpstrLabel;
|
|
LPTSTR m_lpstrHyperLink;
|
|
HCURSOR m_hCursor;
|
|
HFONT m_hFont;
|
|
RECT m_rcLink;
|
|
bool m_bPaintLabel;
|
|
CToolTipCtrl m_tip;
|
|
|
|
bool m_bVisited;
|
|
COLORREF m_clrLink;
|
|
COLORREF m_clrVisited;
|
|
|
|
|
|
// Constructor/Destructor
|
|
CHyperLinkImpl() : m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
|
|
m_hCursor(NULL), m_hFont(NULL), m_bPaintLabel(true), m_bVisited(false),
|
|
m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128))
|
|
{
|
|
::SetRectEmpty(&m_rcLink);
|
|
}
|
|
|
|
~CHyperLinkImpl()
|
|
{
|
|
free(m_lpstrLabel);
|
|
free(m_lpstrHyperLink);
|
|
if(m_hFont != NULL)
|
|
::DeleteObject(m_hFont);
|
|
#if (WINVER < 0x0500)
|
|
// It was created, not loaded, so we have to destroy it
|
|
if(m_hCursor != NULL)
|
|
::DestroyCursor(m_hCursor);
|
|
#endif //(WINVER < 0x0500)
|
|
}
|
|
|
|
// Attributes
|
|
bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
|
|
{
|
|
if(m_lpstrLabel == NULL)
|
|
return false;
|
|
ATLASSERT(lpstrBuffer != NULL);
|
|
if(nLength > lstrlen(m_lpstrLabel) + 1)
|
|
{
|
|
lstrcpy(lpstrBuffer, m_lpstrLabel);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SetLabel(LPCTSTR lpstrLabel)
|
|
{
|
|
free(m_lpstrLabel);
|
|
m_lpstrLabel = NULL;
|
|
ATLTRY(m_lpstrLabel = (LPTSTR)malloc((lstrlen(lpstrLabel) + 1) * sizeof(TCHAR)));
|
|
if(m_lpstrLabel == NULL)
|
|
return false;
|
|
lstrcpy(m_lpstrLabel, lpstrLabel);
|
|
CalcLabelRect();
|
|
return true;
|
|
}
|
|
|
|
bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
|
|
{
|
|
if(m_lpstrHyperLink == NULL)
|
|
return false;
|
|
ATLASSERT(lpstrBuffer != NULL);
|
|
if(nLength > lstrlen(m_lpstrHyperLink) + 1)
|
|
{
|
|
lstrcpy(lpstrBuffer, m_lpstrHyperLink);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SetHyperLink(LPCTSTR lpstrLink)
|
|
{
|
|
free(m_lpstrHyperLink);
|
|
m_lpstrHyperLink = NULL;
|
|
ATLTRY(m_lpstrHyperLink = (LPTSTR)malloc((lstrlen(lpstrLink) + 1) * sizeof(TCHAR)));
|
|
if(m_lpstrHyperLink == NULL)
|
|
return false;
|
|
lstrcpy(m_lpstrHyperLink, lpstrLink);
|
|
if(m_lpstrLabel == NULL)
|
|
CalcLabelRect();
|
|
return true;
|
|
}
|
|
|
|
// Operations
|
|
BOOL SubclassWindow(HWND hWnd)
|
|
{
|
|
ATLASSERT(m_hWnd == NULL);
|
|
ATLASSERT(::IsWindow(hWnd));
|
|
BOOL bRet = CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
|
|
if(bRet)
|
|
Init();
|
|
return bRet;
|
|
}
|
|
|
|
bool Navigate()
|
|
{
|
|
ATLASSERT(::IsWindow(m_hWnd));
|
|
ATLASSERT(m_lpstrHyperLink != NULL);
|
|
DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
|
|
if(dwRet > 32)
|
|
{
|
|
m_bVisited = true;
|
|
Invalidate();
|
|
}
|
|
return (dwRet > 32);
|
|
}
|
|
|
|
// Message map and handlers
|
|
BEGIN_MSG_MAP(CHyperLinkImpl)
|
|
MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
|
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_MOUSEMOVE, OnMouseMove)
|
|
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)
|
|
END_MSG_MAP()
|
|
|
|
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
{
|
|
Init();
|
|
return 0;
|
|
}
|
|
|
|
LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
{
|
|
if(m_bPaintLabel)
|
|
{
|
|
HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, wParam, (LPARAM)m_hWnd);
|
|
if(hBrush != NULL)
|
|
{
|
|
CDCHandle dc = (HDC)wParam;
|
|
RECT rect;
|
|
GetClientRect(&rect);
|
|
dc.FillRect(&rect, hBrush);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bHandled = FALSE;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
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->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)
|
|
{
|
|
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 && ::PtInRect(&m_rcLink, pt))
|
|
::SetCursor(m_hCursor);
|
|
else
|
|
bHandled = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
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))
|
|
Navigate();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
{
|
|
POINT pt;
|
|
GetCursorPos(&pt);
|
|
ScreenToClient(&pt);
|
|
if(m_lpstrHyperLink != NULL && ::PtInRect(&m_rcLink, pt))
|
|
{
|
|
return TRUE;
|
|
}
|
|
bHandled = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
{
|
|
if(wParam == VK_RETURN || wParam == VK_SPACE)
|
|
Navigate();
|
|
return 0;
|
|
}
|
|
|
|
LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
{
|
|
return DLGC_WANTCHARS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Implementation
|
|
void Init()
|
|
{
|
|
ATLASSERT(::IsWindow(m_hWnd));
|
|
|
|
// Check if we should paint a label
|
|
TCHAR lpszBuffer[8];
|
|
if(::GetClassName(m_hWnd, lpszBuffer, 8))
|
|
{
|
|
if(lstrcmpi(lpszBuffer, _T("static")) == 0)
|
|
{
|
|
ModifyStyle(0, SS_NOTIFY); // we need this
|
|
DWORD dwStyle = GetStyle() & 0x000000FF;
|
|
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)
|
|
m_bPaintLabel = false;
|
|
}
|
|
}
|
|
|
|
// create or load a cursor
|
|
#if (WINVER >= 0x0500)
|
|
m_hCursor = ::LoadCursor(NULL, IDC_HAND);
|
|
#else
|
|
m_hCursor = ::CreateCursor(_Module.GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane);
|
|
#endif //!(WINVER >= 0x0500)
|
|
ATLASSERT(m_hCursor != NULL);
|
|
|
|
// set font
|
|
if(m_bPaintLabel)
|
|
{
|
|
CWindow wnd = GetParent();
|
|
CFontHandle font = wnd.GetFont();
|
|
if(font.m_hFont != NULL)
|
|
{
|
|
LOGFONT lf;
|
|
font.GetLogFont(&lf);
|
|
lf.lfUnderline = TRUE;
|
|
m_hFont = ::CreateFontIndirect(&lf);
|
|
}
|
|
}
|
|
|
|
// set label (defaults to window text)
|
|
if(m_lpstrLabel == NULL)
|
|
{
|
|
int nLen = GetWindowTextLength();
|
|
if(nLen > 0)
|
|
{
|
|
LPTSTR lpszText = (LPTSTR)_alloca((nLen+1)*sizeof(TCHAR));
|
|
if(GetWindowText(lpszText, nLen+1))
|
|
SetLabel(lpszText);
|
|
}
|
|
}
|
|
|
|
// set hyperlink (defaults to label)
|
|
if(m_lpstrHyperLink == NULL && m_lpstrLabel != NULL)
|
|
SetHyperLink(m_lpstrLabel);
|
|
|
|
CalcLabelRect();
|
|
|
|
// create a tool tip
|
|
m_tip.Create(m_hWnd);
|
|
ATLASSERT(m_tip.IsWindow());
|
|
m_tip.Activate(TRUE);
|
|
m_tip.AddTool(m_hWnd, m_lpstrHyperLink);
|
|
|
|
// set link colors
|
|
if(m_bPaintLabel)
|
|
{
|
|
CRegKey rk;
|
|
LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
|
|
if(lRet == 0)
|
|
{
|
|
TCHAR szBuff[12];
|
|
DWORD dwCount = 12 * sizeof(TCHAR);
|
|
lRet = rk.QueryValue(szBuff, _T("Anchor Color"), &dwCount);
|
|
if(lRet == 0)
|
|
{
|
|
COLORREF clr = _ParseColorString(szBuff);
|
|
ATLASSERT(clr != CLR_INVALID);
|
|
if(clr != CLR_INVALID)
|
|
m_clrLink = clr;
|
|
}
|
|
|
|
dwCount = 12 * sizeof(TCHAR);
|
|
lRet = rk.QueryValue(szBuff, _T("Anchor Color Visited"), &dwCount);
|
|
if(lRet == 0)
|
|
{
|
|
COLORREF clr = _ParseColorString(szBuff);
|
|
ATLASSERT(clr != CLR_INVALID);
|
|
if(clr != CLR_INVALID)
|
|
m_clrVisited = clr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static COLORREF _ParseColorString(LPTSTR lpstr)
|
|
{
|
|
int c[3] = { -1, -1, -1 };
|
|
LPTSTR p;
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
|
|
{
|
|
if(*p == _T(','))
|
|
{
|
|
*p = _T('\0');
|
|
c[i] = _ttoi(lpstr);
|
|
lpstr = &p[1];
|
|
break;
|
|
}
|
|
}
|
|
if(c[i] == -1)
|
|
return CLR_INVALID;
|
|
}
|
|
if(*lpstr == _T('\0'))
|
|
return CLR_INVALID;
|
|
c[2] = _ttoi(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 rect;
|
|
GetClientRect(&rect);
|
|
m_rcLink = rect;
|
|
if(m_bPaintLabel)
|
|
{
|
|
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);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DoPaint(CDCHandle dc)
|
|
{
|
|
dc.SetBkMode(TRANSPARENT);
|
|
dc.SetTextColor(m_bVisited ? m_clrVisited : m_clrLink);
|
|
if(m_hFont != NULL)
|
|
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);
|
|
if(GetFocus() == m_hWnd)
|
|
dc.DrawFocusRect(&m_rcLink);
|
|
}
|
|
};
|
|
|
|
|
|
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 : _Module.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;
|
|
}
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CMultiPaneStatusBarCtrl - Status Bar with multiple panes
|
|
|
|
template <class T, class TBase = CStatusBarCtrl>
|
|
class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public 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)
|
|
{
|
|
return CWindowImpl< T, TBase >::Create(hWndParent, rcDefault, lpstrText, dwStyle, 0, nID);
|
|
}
|
|
|
|
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)
|
|
{
|
|
TCHAR szText[128]; // max text lentgth is 127 for status bars
|
|
szText[0] = 0;
|
|
::LoadString(_Module.GetResourceInstance(), nTextID, szText, 127);
|
|
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;
|
|
memcpy(m_pPane, pPanes, nPanes * sizeof(int));
|
|
|
|
int* pPanesPos = NULL;
|
|
ATLTRY(pPanesPos = (int*)_alloca(nPanes * sizeof(int)));
|
|
ATLASSERT(pPanesPos != NULL);
|
|
|
|
// get status bar DC and set font
|
|
CClientDC dc(m_hWnd);
|
|
HFONT hOldFont = dc.SelectFont(GetFont());
|
|
|
|
// get status bar borders
|
|
int arrBorders[3];
|
|
GetBorders(arrBorders);
|
|
|
|
TCHAR szBuff[256];
|
|
SIZE size;
|
|
int cxLeft = arrBorders[0];
|
|
|
|
// calculate right edge of each part
|
|
for(int i = 0; i < nPanes; i++)
|
|
{
|
|
if(pPanes[i] == ID_DEFAULT_PANE)
|
|
{
|
|
// will be resized later
|
|
pPanesPos[i] = 100 + cxLeft + arrBorders[2];
|
|
}
|
|
else
|
|
{
|
|
::LoadString(_Module.GetResourceInstance(), pPanes[i], szBuff, sizeof(szBuff) / sizeof(TCHAR));
|
|
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(_Module.GetResourceInstance(), pPanes[i], szBuff, sizeof(szBuff) / sizeof(TCHAR));
|
|
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
|
|
int* pPanesPos = NULL;
|
|
ATLTRY(pPanesPos = (int*)_alloca(m_nPanes * sizeof(int)));
|
|
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)
|
|
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;
|
|
}
|
|
|
|
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)
|
|
|
|
// 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
|
|
int* pPanesPos = NULL;
|
|
ATLTRY(pPanesPos = (int*)_alloca(m_nPanes * sizeof(int)));
|
|
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;
|
|
GetClientRect(&rcClient);
|
|
int cxOff = rcClient.right - (pPanesPos[m_nPanes - 1] + ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE));
|
|
// 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
|
|
|
|
template <class T, class TBase = CWindow, class TWinTraits = CControlWinTraits>
|
|
class ATL_NO_VTABLE CPaneContainerImpl : public 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;
|
|
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) && ((dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
|
|
{
|
|
pT->CreateCloseButton();
|
|
bUpdate = true;
|
|
}
|
|
else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
|
|
{
|
|
pT->DestroyCloseButton();
|
|
bUpdate = true;
|
|
}
|
|
|
|
if((dwPrevStyle & PANECNT_VERTICAL) != (dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
|
|
{
|
|
CalcSize();
|
|
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);
|
|
return (lstrcpyn(lpstrTitle, m_szTitle, cchLength) != NULL);
|
|
}
|
|
|
|
BOOL SetTitle(LPCTSTR lpstrTitle)
|
|
{
|
|
ATLASSERT(lpstrTitle != NULL);
|
|
BOOL bRet = (lstrcpyn(m_szTitle, lpstrTitle, m_cchTitle) != NULL);
|
|
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)
|
|
lstrcpyn(m_szTitle, lpstrTitle, m_cchTitle);
|
|
return CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
|
|
}
|
|
|
|
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(_Module.GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
|
|
return CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
|
|
}
|
|
|
|
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;
|
|
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)
|
|
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*/)
|
|
{
|
|
CPaintDC dc(m_hWnd);
|
|
|
|
T* pT = static_cast<T*>(this);
|
|
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);
|
|
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);
|
|
// 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);
|
|
// 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, (HBRUSH)LongToPtr(COLOR_3DFACE + 1));
|
|
|
|
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;
|
|
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;
|
|
|
|
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;
|
|
memset(&tbbtn, 0, sizeof(tbbtn));
|
|
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;
|
|
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();
|
|
}
|
|
|
|
BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void DrawPaneTitle(CDCHandle dc)
|
|
{
|
|
RECT rect;
|
|
GetClientRect(&rect);
|
|
|
|
if(IsVertical())
|
|
{
|
|
rect.right = rect.left + m_cxyHeader;
|
|
dc.DrawEdge(&rect, EDGE_ETCHED, BF_LEFT | BF_TOP | BF_BOTTOM | BF_ADJUST);
|
|
dc.FillRect(&rect, (HBRUSH)LongToPtr(COLOR_3DFACE + 1));
|
|
}
|
|
else
|
|
{
|
|
rect.bottom = rect.top + m_cxyHeader;
|
|
dc.DrawEdge(&rect, EDGE_ETCHED, BF_LEFT | BF_TOP | BF_RIGHT | BF_ADJUST);
|
|
dc.FillRect(&rect, (HBRUSH)LongToPtr(COLOR_3DFACE + 1));
|
|
// 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;;
|
|
dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
|
|
dc.SelectFont(hFontOld);
|
|
}
|
|
}
|
|
|
|
// called only if pane is empty
|
|
void DrawPane(CDCHandle dc)
|
|
{
|
|
RECT rect;
|
|
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, (HBRUSH)LongToPtr(COLOR_APPWORKSPACE + 1));
|
|
}
|
|
|
|
// drawing helper - draws "x" button image
|
|
void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
|
|
{
|
|
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);
|
|
}
|
|
|
|
bool IsVertical() const
|
|
{
|
|
return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
|
|
}
|
|
};
|
|
|
|
class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
|
|
{
|
|
public:
|
|
DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
|
|
};
|
|
|
|
}; //namespace WTL
|
|
|
|
#endif // __ATLCTRLX_H__
|