/*
* Kenny Liu
* http://www.codeproject.com/Members/yonken
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
#include "StdAfx.h"
#include "CustomDrawCommon.h"
#include "CustomDrawControl.h"
#include <afxmt.h> // CMutex
#include <algorithm> // find
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
#include "afxtoolbarbutton.h"
#include "afxdrawmanager.h"
#include "afxvisualmanager.h"
#include "afxribbonbutton.h"
#include "afxribbonbar.h"
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
/*----------------------------------------------------------------------------*/
/* CCustomDrawHeaderCtrl
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CCustomDrawHeaderCtrl, CCustomDrawHeaderCtrlBase)
CCustomDrawHeaderCtrl::CCustomDrawHeaderCtrl()
: m_hFont(NULL)
, m_bIsMousePressed(FALSE)
, m_nHighlightedItem(-1)
, m_bTracked(FALSE)
, m_pTextDrawer(NULL)
{
}
CCustomDrawHeaderCtrl::~CCustomDrawHeaderCtrl()
{
}
BEGIN_MESSAGE_MAP(CCustomDrawHeaderCtrl, CCustomDrawHeaderCtrlBase)
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_CANCELMODE()
ON_WM_CREATE()
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_SETFONT, OnSetFont)
END_MESSAGE_MAP()
void CCustomDrawHeaderCtrl::PreSubclassWindow()
{
CCustomDrawHeaderCtrlBase::PreSubclassWindow();
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
if (pThreadState->m_pWndInit == NULL)
{
OnInitHeader();
}
}
int CCustomDrawHeaderCtrl::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
if (CCustomDrawHeaderCtrlBase::OnCreate(lpCreateStruct) == -1)
return -1;
OnInitHeader();
return 0;
}
BOOL CCustomDrawHeaderCtrl::IsDragDropEnabled() const
{
return (HDS_DRAGDROP & GetStyle());
}
void CCustomDrawHeaderCtrl::EnableDragDrop( BOOL bEnable /*= TRUE*/ )
{
DWORD dwStyle = (DWORD)::GetWindowLong(m_hWnd, GWL_STYLE);
if (bEnable)
dwStyle |= HDS_DRAGDROP;
else
dwStyle &= ~HDS_DRAGDROP;
::SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
}
BOOL CCustomDrawHeaderCtrl::OnEraseBkgnd( CDC* pDC )
{
return TRUE; // flicker free
}
void CCustomDrawHeaderCtrl::OnDrawItem( CDC* pDC, int iItem, CRect rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
ASSERT_VALID(this);
ASSERT_VALID(pDC);
OnFillBackground(pDC, iItem, rect, bIsPressed, bIsHighlighted);
// Draw border:
OnDrawBorder(pDC, iItem, rect, bIsPressed, bIsHighlighted);
if (iItem < 0)
{
return;
}
HD_ITEM hdItem;
memset(&hdItem, 0, sizeof(hdItem));
hdItem.mask = HDI_FORMAT | HDI_BITMAP | HDI_TEXT | HDI_IMAGE;
TCHAR szText [256];
hdItem.pszText = szText;
hdItem.cchTextMax = 255;
if (!GetItem(iItem, &hdItem))
{
return;
}
// Draw bitmap and image:
OnDrawImage(pDC, hdItem, rect, bIsPressed, bIsHighlighted);
OnDrawBitmap(pDC, hdItem, rect, bIsPressed, bIsHighlighted);
// Draw text:
OnDrawText(pDC, hdItem, rect, bIsPressed, bIsHighlighted);
}
void CCustomDrawHeaderCtrl::OnDrawImage( CDC* pDC, HD_ITEM& hdItem, CRect& rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
if ((hdItem.fmt & HDF_IMAGE) && hdItem.iImage >= 0)
{
// The column has a image from imagelist:
CImageList* pImageList = GetImageList();
if (pImageList != NULL)
{
int cx = 0;
int cy = 0;
VERIFY(::ImageList_GetIconSize(*pImageList, &cx, &cy));
CPoint pt = rect.TopLeft();
pt.x ++;
pt.y = (rect.top + rect.bottom - cy) / 2;
VERIFY(pImageList->Draw(pDC, hdItem.iImage, pt, ILD_NORMAL));
rect.left += cx;
}
}
}
void CCustomDrawHeaderCtrl::OnDrawBitmap( CDC* pDC, HD_ITEM& hdItem, CRect& rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
if ((hdItem.fmt &(HDF_BITMAP | HDF_BITMAP_ON_RIGHT)) && hdItem.hbm != NULL)
{
CBitmap* pBmp = CBitmap::FromHandle(hdItem.hbm);
ASSERT_VALID(pBmp);
BITMAP bmp;
pBmp->GetBitmap(&bmp);
CRect rectBitmap = rect;
if (hdItem.fmt & HDF_BITMAP_ON_RIGHT)
{
rectBitmap.right--;
rect.right = rectBitmap.left = rectBitmap.right - bmp.bmWidth;
}
else
{
rectBitmap.left++;
rect.left = rectBitmap.right = rectBitmap.left + bmp.bmWidth;
}
rectBitmap.top += max(0, (rectBitmap.Height() - bmp.bmHeight) / 2);
rectBitmap.bottom = rectBitmap.top + bmp.bmHeight;
pDC->DrawState(rectBitmap.TopLeft(), rectBitmap.Size(), pBmp, DSS_NORMAL);
}
}
void CCustomDrawHeaderCtrl::OnDrawText( CDC* pDC, HD_ITEM& hdItem, CRect& rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
if ((hdItem.fmt & HDF_STRING) && hdItem.pszText != NULL)
{
CRect rectLabel = rect;
const int nTextMargin = 5;
rectLabel.DeflateRect(nTextMargin, 0);
if (bIsPressed)
{
rectLabel.OffsetRect(1, 1); // push-like
}
UINT uiTextFlags = DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX;
if (hdItem.fmt & HDF_CENTER)
{
uiTextFlags |= DT_CENTER;
}
else if (hdItem.fmt & HDF_RIGHT)
{
uiTextFlags |= DT_RIGHT;
}
CString strLabel = hdItem.pszText;
if ( GetTextDrawer() )
GetTextDrawer()->Draw(pDC, strLabel, rectLabel, uiTextFlags);
else
pDC->DrawText(strLabel, rectLabel, uiTextFlags);
}
}
void CCustomDrawHeaderCtrl::OnFillBackground( CDC* pDC )
{
ASSERT_VALID(this);
ASSERT_VALID(pDC);
CRect rectClient;
GetClientRect(rectClient);
#ifdef CUSTOMDRAW_GRADIENT
pDC->FillSolidRect(rectClient, ::GetSysColor(COLOR_WINDOW));
#else
pDC->FillSolidRect(rectClient, ::GetSysColor(COLOR_BTNFACE));
#endif // CUSTOMDRAW_GRADIENT
}
void CCustomDrawHeaderCtrl::OnFillBackground( CDC* pDC, int iItem, CRect& rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
ASSERT_VALID(this);
ASSERT_VALID(pDC);
#ifdef CUSTOMDRAW_GRADIENT
CCustomDrawHelper drawer;
drawer.m_bWndEnabled = !!IsWindowEnabled();
drawer.m_bWndHasFocus = ::GetFocus() == GetSafeHwnd();
drawer.m_bFocusItem = false;
drawer.m_bIsHotItem = bIsHighlighted ? true : false;
drawer.m_bIsPressed = bIsPressed ? true : false;
drawer.m_hWnd = m_hWnd;
drawer.DrawThemeBackground(pDC, rect, CDTBT_HEADER);
#endif // CUSTOMDRAW_GRADIENT
}
void CCustomDrawHeaderCtrl::OnDrawBorder( CDC* pDC, int iItem, CRect& rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
ASSERT_VALID(pDC);
#ifdef CUSTOMDRAW_GRADIENT
COLORREF clrBorder;
if (bIsPressed || bIsHighlighted)
{
if (bIsPressed)
{
clrBorder = RGB(112,185,223);
}
else
{
clrBorder = RGB(147,201,227);
}
}
else
{
clrBorder = RGB(213,213,213);
}
CBrush brBorder(clrBorder);
pDC->FrameRect(rect, &brBorder);
#else
COLORREF clrHilite = GetSysColor(COLOR_3DHILIGHT);
COLORREF clrShadow = GetSysColor(COLOR_3DSHADOW);
if (bIsPressed)
{
pDC->Draw3dRect(rect, clrShadow, clrShadow);
rect.left++;
rect.top++;
}
else
{
pDC->Draw3dRect(rect, clrHilite, clrShadow);
}
#endif // CUSTOMDRAW_GRADIENT
}
void CCustomDrawHeaderCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
CSimpleMemDC memDC(dc, this);
CDC* pDC = &memDC.GetDC();
CRect rectClip;
dc.GetClipBox(rectClip);
CRect rectClient;
GetClientRect(rectClient);
OnFillBackground(pDC);
CFont* pOldFont = SelectFont(pDC);
ASSERT_VALID(pOldFont);
pDC->SetTextColor(RGB(0x4C,0x4C,0x4C));
pDC->SetBkMode(TRANSPARENT);
CRect rect;
GetClientRect(rect);
CRect rectItem;
int nCount = GetItemCount();
int xMax = 0;
for (int i = 0; i < nCount; i++)
{
// Is item pressed?
CPoint ptCursor;
::GetCursorPos(&ptCursor);
ScreenToClient(&ptCursor);
HDHITTESTINFO hdHitTestInfo;
hdHitTestInfo.pt = ptCursor;
int iHit = (int) SendMessage(HDM_HITTEST, 0, (LPARAM) &hdHitTestInfo);
BOOL bIsHighlighted = iHit == i &&(hdHitTestInfo.flags & HHT_ONHEADER);
BOOL bIsPressed = m_bIsMousePressed && bIsHighlighted;
GetItemRect(i, rectItem);
CRgn rgnClip;
rgnClip.CreateRectRgnIndirect(&rectItem);
pDC->SelectClipRgn(&rgnClip);
// Draw item:
OnDrawItem(pDC, i, rectItem, bIsPressed, m_nHighlightedItem == i);
pDC->SelectClipRgn(NULL);
xMax = max(xMax, rectItem.right);
}
// Draw "tail border":
if (nCount == 0)
{
rectItem = rect;
rectItem.right++;
}
else
{
rectItem.left = xMax;
rectItem.right = rect.right + 1;
}
OnDrawItem(pDC, -1, rectItem, FALSE, FALSE);
pDC->SelectObject(pOldFont);
}
void CCustomDrawHeaderCtrl::OnLButtonDown( UINT nFlags, CPoint point )
{
m_bIsMousePressed = TRUE;
CCustomDrawHeaderCtrlBase::OnLButtonDown(nFlags, point);
}
void CCustomDrawHeaderCtrl::OnLButtonUp( UINT nFlags, CPoint point )
{
m_bIsMousePressed = FALSE;
CCustomDrawHeaderCtrlBase::OnLButtonUp(nFlags, point);
}
void CCustomDrawHeaderCtrl::OnMouseMove( UINT nFlags, CPoint point )
{
if ((nFlags & MK_LBUTTON) == 0)
{
HDHITTESTINFO hdHitTestInfo;
hdHitTestInfo.pt = point;
int nPrevHighlightedItem = m_nHighlightedItem;
m_nHighlightedItem = (int) SendMessage(HDM_HITTEST, 0, (LPARAM) &hdHitTestInfo);
if ((hdHitTestInfo.flags & HHT_ONHEADER) == 0)
{
m_nHighlightedItem = -1;
}
if (!m_bTracked)
{
m_bTracked = TRUE;
TRACKMOUSEEVENT trackmouseevent;
trackmouseevent.cbSize = sizeof(trackmouseevent);
trackmouseevent.dwFlags = TME_LEAVE;
trackmouseevent.hwndTrack = GetSafeHwnd();
trackmouseevent.dwHoverTime = HOVER_DEFAULT;
//::AFXTrackMouse(&trackmouseevent);
_TrackMouseEvent(&trackmouseevent);
}
if (nPrevHighlightedItem != m_nHighlightedItem)
{
RedrawWindow();
}
}
CCustomDrawHeaderCtrlBase::OnMouseMove(nFlags, point);
}
void CCustomDrawHeaderCtrl::OnCancelMode()
{
CCustomDrawHeaderCtrlBase::OnCancelMode();
if (m_nHighlightedItem >= 0)
{
m_nHighlightedItem = -1;
Invalidate(FALSE);
}
}
LRESULT CCustomDrawHeaderCtrl::OnMouseLeave( WPARAM,LPARAM )
{
m_bTracked = FALSE;
if (m_nHighlightedItem >= 0)
{
m_nHighlightedItem = -1;
Invalidate(FALSE);
}
return 0;
}
LRESULT CCustomDrawHeaderCtrl::OnSetFont( WPARAM wParam, LPARAM lParam )
{
BOOL bRedraw = (BOOL) LOWORD(lParam);
m_hFont = (HFONT) wParam;
if (bRedraw)
{
RedrawWindow();
UpdateWindow();
}
return 0;
}
CFont* CCustomDrawHeaderCtrl::SelectFont( CDC *pDC )
{
ASSERT_VALID(this);
ASSERT_VALID(pDC);
CFont* pOldFont = NULL;
if (m_hFont != NULL)
{
pOldFont = pDC->SelectObject(CFont::FromHandle(m_hFont));
}
else
{
pOldFont = (CFont*) pDC->SelectStockObject(DEFAULT_GUI_FONT);
}
return pOldFont;
}
/*----------------------------------------------------------------------------*/
/* CSortHeaderCtrl
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CSortHeaderCtrl, CSortHeaderCtrlBase)
CSortHeaderCtrl::CSortHeaderCtrl()
: m_nSortColumn( -1 )
, m_bSortable(true)
{
}
CSortHeaderCtrl::~CSortHeaderCtrl()
{
}
BEGIN_MESSAGE_MAP(CSortHeaderCtrl, CSortHeaderCtrlBase)
END_MESSAGE_MAP()
BOOL CSortHeaderCtrl::Init( HWND hWndToSubClass )
{
const BOOL bStatus = CSortHeaderCtrlBase::SubclassWindow( hWndToSubClass );
return bStatus;
}
void CSortHeaderCtrl::SwitchSortItem( int nSortItem )
{
if ( m_nSortColumn == nSortItem )
{
m_bAscending = !m_bAscending;
}
else
{
m_nSortColumn = nSortItem;
m_bAscending = DEFAULT_SORT_ASCENDING;
}
}
void CSortHeaderCtrl::OnFillBackground( CDC* pDC, int iItem, CRect& rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
CSortHeaderCtrlBase::OnFillBackground(pDC, iItem, rect, bIsPressed, bIsHighlighted);
if ( m_bSortable && iItem >= 0 && iItem == m_nSortColumn )
{
if ( !bIsPressed && !bIsHighlighted )
{
#ifdef CUSTOMDRAW_GRADIENT
Fill4ColorsGradient(pDC->GetSafeHdc(), rect,
RGB_HEADER_SORT_FILL_COLOR1, RGB_HEADER_SORT_FILL_COLOR2, RGB_HEADER_SORT_FILL_COLOR3, RGB_HEADER_SORT_FILL_COLOR4,
TRUE, HEADER_FIRST_HALF_PERCENTAGE);
#else
pDC->FillSolidRect(rect, RGB(216,236,246));
#endif // CUSTOMDRAW_GRADIENT
}
CPoint ptCenter = rect.CenterPoint();
CRect rectArrow;
rectArrow.left = ptCenter.x - 3;
rectArrow.right = ptCenter.x + 3;
rectArrow.top = rect.top + 2;
rectArrow.bottom = rectArrow.top + 3;
DrawSortArrow(pDC, rectArrow, m_bAscending);
}
}
void CSortHeaderCtrl::OnDrawBorder( CDC* pDC, int iItem, CRect& rect, BOOL bIsPressed, BOOL bIsHighlighted )
{
if ( m_bSortable && iItem >= 0 && iItem == m_nSortColumn )
{
COLORREF clrBorder = RGB(150,217,249);
CBrush brBorder(clrBorder);
pDC->FrameRect(rect, &brBorder);
}
else
CSortHeaderCtrlBase::OnDrawBorder(pDC, iItem, rect, bIsPressed, bIsHighlighted);
}
void CSortHeaderCtrl::SetSortable( bool bSortable /*= true*/, bool bRedraw /*= true*/ )
{
m_bSortable = bSortable;
if (bRedraw)
{
Invalidate(FALSE);
}
}
/*----------------------------------------------------------------------------*/
/* CCustomDrawToolTipCtrl
/*----------------------------------------------------------------------------*/
#ifndef TTS_BALLOON
#define TTS_BALLOON 0x40
#endif
#ifndef TTM_SETTITLE
#define TTM_SETTITLE (WM_USER + 32)
#endif
IMPLEMENT_DYNCREATE(CCustomDrawToolTipCtrl, CCDToolTipCtrlBase)
CCustomDrawToolTipCtrl::CCustomDrawToolTipCtrl(CCustomDrawToolTipInfo* pParams /*= NULL*/)
: m_DisplayIDWParam(-1)
, m_DisplayIDLParam(-1)
#ifndef MSVC_NEW_VER
, m_hFont(NULL)
#endif // MSVC_NEW_VER
, m_pTextDrawer(NULL)
{
SetParams(pParams);
#ifndef DERIVE_FROM_MFCTOOLTIPCTRL
m_ptMargin = CPoint(0, 0);
m_sizeImage = CSize(0, 0);
m_ptMargin = CPoint(0, 0);
m_ptLocation = CPoint(-1, -1);
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
m_sizeDescrImage = CSize(0, 0);
m_sizeSupplementalImage = CSize(0, 0);
m_nTextTabSize = 4;
m_nLabelHeight = 0;
m_hLabelIcon = NULL;
m_hDescrIcon = NULL;
m_hSupplementalBmp = NULL;
}
CCustomDrawToolTipCtrl::~CCustomDrawToolTipCtrl()
{
}
BEGIN_MESSAGE_MAP(CCustomDrawToolTipCtrl, CCDToolTipCtrlBase)
#ifndef DERIVE_FROM_MFCTOOLTIPCTRL
//ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomdraw)
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_CREATE()
ON_NOTIFY_REFLECT(TTN_SHOW, OnShow)
ON_NOTIFY_REFLECT(TTN_POP, OnPop)
#ifndef MSVC_NEW_VER
ON_MESSAGE(WM_SETFONT, OnSetFont)
#endif // MSVC_NEW_VER
END_MESSAGE_MAP()
#define TIPTEXTBUFFER_SIZE 80
void CCustomDrawToolTipCtrl::SetText( NMHDR* pNMHDR, LPCTSTR lpcszText )
{
// the szText can not exceed 80 characters.
// See http://msdn.microsoft.com/en-us/library/bb760252(VS.85).aspx#tooltip_sample_multiline
// handle both ANSI and UNICODE versions of the message
LPTOOLTIPTEXTA pTTTA = reinterpret_cast<LPTOOLTIPTEXTA>(pNMHDR);
LPTOOLTIPTEXTW pTTTW = reinterpret_cast<LPTOOLTIPTEXTW>(pNMHDR);
size_t nStrLen = _tcslen(lpcszText);
if ( nStrLen >= TIPTEXTBUFFER_SIZE )
{
// the
#ifdef _UNICODE
// Unicode environment
if (pNMHDR->code == TTN_NEEDTEXTA)
{
m_astrLongTipText = toastring(lpcszText);
pTTTA->lpszText = (PSTR)m_astrLongTipText.c_str();
}
else if(pNMHDR->code == TTN_NEEDTEXTW)
{
m_wstrLongTipText = lpcszText;
pTTTW->lpszText = (PWSTR)m_wstrLongTipText.c_str();
}
#else
// Ansi environment
if (pNMHDR->code == TTN_NEEDTEXTA)
{
m_astrLongTipText = lpcszText;
pTTTA->lpszText = (PSTR)m_astrLongTipText.c_str();
}
else if(pNMHDR->code == TTN_NEEDTEXTW)
{
m_wstrLongTipText = towstring(lpcszText);
pTTTW->lpszText = (PWSTR)m_wstrLongTipText.c_str();
}
#endif
}
else
{
// otherwise, just do the conversion & copy
#ifdef _UNICODE
// Unicode environment
if (pNMHDR->code == TTN_NEEDTEXTA)
_wcstombsz(pTTTA->szText, lpcszText, TIPTEXTBUFFER_SIZE);
else
lstrcpyn(pTTTW->szText, lpcszText, TIPTEXTBUFFER_SIZE);
#else
// Ansi environment
if (pNMHDR->code == TTN_NEEDTEXTA)
lstrcpyn(pTTTA->szText, lpcszText, TIPTEXTBUFFER_SIZE);
else if(pNMHDR->code == TTN_NEEDTEXTW)
{
#ifdef MSVC_NEW_VER
mbstowcs_s(NULL, pTTTW->szText, TIPTEXTBUFFER_SIZE, lpcszText, _TRUNCATE);
#else
mbstowcs(pTTTW->szText, lpcszText, TIPTEXTBUFFER_SIZE);
#endif // MSVC_NEW_VER
}
#endif
}
}
void CCustomDrawToolTipCtrl::SetParams( CCustomDrawToolTipInfo* pParams )
{
ASSERT_VALID(this);
if (pParams == NULL)
{
m_Params = CCustomDrawToolTipInfo();
}
else
{
m_Params = *pParams;
}
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CCDToolTipCtrlBase::m_Params = m_Params;
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
BOOL CCustomDrawToolTipCtrl::CheckDisplayToolTip( WPARAM wParam, LPARAM lParam /*= 0*/ )
{
if ( m_DisplayIDWParam != wParam || m_DisplayIDLParam != lParam )
{
m_DisplayIDWParam = wParam;
m_DisplayIDLParam = lParam;
return TRUE;
}
return FALSE;
}
void CCustomDrawToolTipCtrl::SetFixedWidth( int nWidthRegular, int nWidthLargeImage )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CCDToolTipCtrlBase::SetFixedWidth(nWidthRegular, nWidthLargeImage);
#else
m_nFixedWidthRegular = nWidthRegular;
m_nFixedWidthWithImage = nWidthLargeImage;
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
int CCustomDrawToolTipCtrl::GetFixedWidth()
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
return CCDToolTipCtrlBase::GetFixedWidth();
#else
ASSERT_VALID(this);
#ifdef MSVC_NEW_VER
if (m_sizeImage.cx <= (int)(afxGlobalData.GetRibbonImageScale() * 32))
{
return m_nFixedWidthRegular;
}
else
{
return m_nFixedWidthWithImage;
}
#else
return 0;
#endif // MSVC_NEW_VER
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
int CCustomDrawToolTipCtrl::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
return CCDToolTipCtrlBase::OnCreate(lpCreateStruct);
#else
if (CCDToolTipCtrlBase::OnCreate(lpCreateStruct) == -1)
return -1;
ModifyStyle(WS_BORDER, 0);
if (m_Params.m_bBalloonTooltip)
{
ModifyStyle(0, TTS_BALLOON);
}
return 0;
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
BOOL CCustomDrawToolTipCtrl::OnEraseBkgnd( CDC* pDC )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
return CCDToolTipCtrlBase::OnEraseBkgnd(pDC);
#else
CRect rect;
GetClientRect (rect);
COLORREF clrDummy;
OnFillBackground(pDC, rect, clrDummy, clrDummy);
return TRUE;
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
void CCustomDrawToolTipCtrl::OnShow( NMHDR* pNMHDR, LRESULT* pResult )
{
*pResult = 0;
CWnd* pOwnerWnd = GetOwner();
if ( pOwnerWnd )
{
pOwnerWnd->SendMessage(WM_CDTOOLTIPCTRL_NOTIFY, CDTOOLTIP_ONBEFORE_SHOW);
}
if (m_Params.m_bVislManagerTheme)
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CMFCVisualManager::GetInstance()->GetToolTipInfo(m_Params);
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
m_Params.m_bVislManagerTheme = TRUE;
}
if (m_Params.m_bBalloonTooltip)
{
return;
}
CPoint ptCursor;
::GetCursorPos(&ptCursor);
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
GetHotButton();
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
m_sizeImage = m_Params.m_bDrawIcon ? GetIconSize() : CSize(0, 0);
m_ptMargin = m_Params.m_bRoundedCorners ? CPoint(6, 4) : CPoint(4, 2);
m_sizeDescrImage = m_Params.m_bDrawDescrIcon ? GetDescrIconSize() : CSize(0, 0);
m_sizeSupplementalImage = m_Params.m_bDrawSupplementalImage ? GetSupplementalImageSize() : CSize(0, 0);
CRect rectMargin;
GetMargin(rectMargin);
CRect rectDraw;
GetClientRect(rectDraw);
CRect rectText(rectDraw);
CClientDC dc(this);
CSize sizeText = OnDrawLabel(&dc, rectText, TRUE);
int cx = sizeText.cx;
int cy = sizeText.cy;
CSize sizeDescr(0, 0);
m_nLabelHeight = cy;
if (m_sizeImage.cy > 0 && m_Params.m_bDrawIcon)
{
m_nLabelHeight = max(cy, m_sizeImage.cy);
}
cy = m_nLabelHeight;
if ( m_Params.m_bDrawDescription && !m_strDescription.IsEmpty() )
{
sizeDescr = OnDrawDescription(&dc, rectText, TRUE);
if (m_sizeDescrImage != CSize(0, 0) && m_Params.m_bDrawDescrIcon )
{
sizeDescr.cx += m_sizeDescrImage.cx + m_ptMargin.x;
sizeDescr.cy = max(sizeDescr.cy, m_sizeDescrImage.cy);
}
if ( sizeDescr.cy <= 0)
cy += 2 * m_ptMargin.y;
else
{
cy += sizeDescr.cy + 4 * m_ptMargin.y;
cx = max(cx, sizeDescr.cx);
}
}
if (m_sizeImage.cx > 0 && m_Params.m_bDrawIcon)
{
cx += m_sizeImage.cx + m_ptMargin.x;
}
CSize sizeBody = OnDrawBody(&dc, rectDraw, TRUE);
if (sizeBody != CSize(0, 0))
{
cx = max(cx, sizeBody.cx + 2 * m_ptMargin.x);
cy += sizeBody.cy + m_ptMargin.y;
}
cx += 2 * m_ptMargin.x;
cy += 2 * m_ptMargin.y;
const int nFixedWidth = GetFixedWidth();
if (nFixedWidth > 0 && sizeDescr != CSize(0, 0))
{
cx = max(cx, nFixedWidth);
}
CRect rectWindow;
GetWindowRect(rectWindow);
int x = rectWindow.left;
int y = rectWindow.top;
if (m_ptLocation != CPoint(-1, -1))
{
x = m_ptLocation.x;
y = m_ptLocation.y;
*pResult = 1;
}
CRect rectScreen;
MONITORINFO mi;
mi.cbSize = sizeof(MONITORINFO);
if (GetMonitorInfo(MonitorFromPoint(rectWindow.TopLeft(), MONITOR_DEFAULTTONEAREST), &mi))
{
rectScreen = mi.rcWork;
}
else
{
::SystemParametersInfo(SPI_GETWORKAREA, 0, &rectScreen, 0);
}
int nBottom = max(ptCursor.y + cy + ::GetSystemMetrics(SM_CYCURSOR), y + cy + 2);
if (nBottom > rectScreen.bottom)
{
y = ptCursor.y - cy - 1;
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
if (m_pRibbonButton != NULL && m_ptLocation != CPoint(-1, -1))
{
ASSERT_VALID(m_pRibbonButton);
CMFCRibbonBar* pRibbon = m_pRibbonButton->GetTopLevelRibbonBar();
if (pRibbon->GetSafeHwnd() != NULL)
{
CRect rectRibbon;
pRibbon->GetWindowRect(rectRibbon);
y = rectRibbon.top - cy;
}
}
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
*pResult = 1;
}
if (x + cx + 2 > rectScreen.right)
{
if ((*pResult) == 1) // Y has been changed
{
x = ptCursor.x - cx - 1;
}
else
{
x = rectScreen.right - cx - 1;
*pResult = 1;
}
}
// const CWnd* pWndInsertAfter = NULL;
// UINT nFlags = SWP_NOZORDER;
const CWnd* pWndInsertAfter = &wndTopMost;
UINT nFlags = 0;
if ((*pResult) == 1)
{
SetWindowPos(pWndInsertAfter, x, y, cx, cy, nFlags | SWP_NOACTIVATE);
}
else
{
SetWindowPos(pWndInsertAfter, -1, -1, cx, cy, nFlags | SWP_NOMOVE | SWP_NOACTIVATE);
}
if (m_Params.m_bRoundedCorners)
{
CRgn rgn;
rgn.CreateRoundRectRgn(0, 0, cx + 1, cy + 1, 4, 4);
SetWindowRgn(rgn, FALSE);
}
}
void CCustomDrawToolTipCtrl::OnPop( NMHDR* pNMHDR, LRESULT* pResult )
{
CWnd* pOwnerWnd = GetOwner();
if ( pOwnerWnd )
{
pOwnerWnd->SendMessage(WM_CDTOOLTIPCTRL_NOTIFY, CDTOOLTIP_ONBEFORE_POP);
}
if ( m_hLabelIcon )
{
::DestroyIcon(m_hLabelIcon);
m_hLabelIcon = NULL;
}
if ( m_hDescrIcon )
{
::DestroyIcon(m_hDescrIcon);
m_hDescrIcon = NULL;
}
if ( m_hSupplementalBmp )
{
::DeleteObject(m_hSupplementalBmp);
m_hSupplementalBmp = NULL;
}
m_strLabel.Empty();
m_strSupplementalDescription.Empty();
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CCDToolTipCtrlBase::OnPop(pNMHDR, pResult);
#else
m_strDescription.Empty();
m_ptLocation = CPoint(-1, -1);
*pResult = 0;
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
void CCustomDrawToolTipCtrl::OnPaint()
{
if (m_Params.m_bBalloonTooltip)
{
CToolTipCtrl::OnPaint();
return;
}
CPaintDC dcPaint(this); // device context for painting
CSimpleMemDC memDC(dcPaint, this);
CDC* pDC = &memDC.GetDC();
CRect rect;
GetClientRect(rect);
CRect rectMargin;
GetMargin(rectMargin);
CRect rectDraw(rect);
rectDraw.DeflateRect(rectMargin);
rectDraw.DeflateRect(m_ptMargin.x, m_ptMargin.y);
CRect rectText(rectDraw);
COLORREF clrLine = m_Params.m_clrBorder == (COLORREF)-1 ? ::GetSysColor(COLOR_INFOTEXT) : m_Params.m_clrBorder;
COLORREF clrText = m_Params.m_clrText == (COLORREF)-1 ? ::GetSysColor(COLOR_INFOTEXT) : m_Params.m_clrText;
// Fill background:
OnFillBackground(pDC, rect, clrText, clrLine);
CPen penLine(PS_SOLID, 1, clrLine);
CPen* pOldPen = pDC->SelectObject(&penLine);
// Draw border:
OnDrawBorder(pDC, rect, clrLine);
int nIconHeight = 0;
// Draw icon:
if (m_sizeImage != CSize(0, 0) && m_Params.m_bDrawIcon)
{
CRect rectImage = rectText;
rectImage.right = rectImage.left + m_sizeImage.cx;
rectImage.bottom = rectImage.top + m_sizeImage.cy;
OnDrawIcon(pDC, rectImage);
rectText.left += m_sizeImage.cx + m_ptMargin.x;
nIconHeight = m_sizeImage.cy;
}
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(clrText);
// Draw label:
int nLabelHeight = OnDrawLabel(pDC, rectText, FALSE).cy;
// Draw body:
rectDraw.top += max(nLabelHeight, nIconHeight);
CSize sizeBody = OnDrawBody(pDC, rectDraw, FALSE);
// Draw separator + description:
if (!m_strDescription.IsEmpty() && m_Params.m_bDrawDescription)
{
CRect rectDescr = rectDraw;
if (sizeBody != CSize(0, 0))
{
rectDescr.top += m_ptMargin.y;
}
rectDescr.top += sizeBody.cy + 3 * m_ptMargin.y / 2;
if (m_Params.m_bDrawSeparator)
{
OnDrawSeparator(pDC, rectDescr.left, rectDescr.right, rectDescr.top - m_ptMargin.y / 2);
}
rectDescr.top += m_ptMargin.y;
if (m_sizeDescrImage != CSize(0, 0) && m_Params.m_bDrawDescrIcon )
{
CRect rectImage = rectDescr;
rectImage.right = rectImage.left + m_sizeDescrImage.cx;
rectImage.bottom = rectImage.top + m_sizeDescrImage.cy;
OnDrawDescriptionIcon(pDC, rectImage);
rectDescr.left += m_sizeDescrImage.cx + m_ptMargin.x;
}
OnDrawDescription(pDC, rectDescr, FALSE);
}
pDC->SelectObject(pOldPen);
}
void CCustomDrawToolTipCtrl::OnFillBackground( CDC* pDC, CRect rect, COLORREF& clrText, COLORREF& clrLine )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CCDToolTipCtrlBase::OnFillBackground(pDC, rect, clrText, clrLine);
#else
if (m_Params.m_clrFill == (COLORREF)-1)
{
::FillRect(pDC->GetSafeHdc(), rect, ::GetSysColorBrush(COLOR_INFOBK));
}
else
{
if (m_Params.m_clrFillGradient == (COLORREF)-1)
{
CBrush br(m_Params.m_clrFill);
pDC->FillRect(rect, &br);
}
else
{
#ifdef MSVC_NEW_VER
CDrawingManager dm(*pDC);
dm.FillGradient2(rect, m_Params.m_clrFillGradient, m_Params.m_clrFill, m_Params.m_nGradientAngle == -1 ? 90 : m_Params.m_nGradientAngle);
#else
#ifdef CUSTOMDRAW_GRADIENT
//int nGradientAngle = m_Params.m_nGradientAngle == -1 ? 90 : m_Params.m_nGradientAngle;
FillGradient(pDC->GetSafeHdc(), rect, m_Params.m_clrFill, m_Params.m_clrFillGradient);
#else
pDC->FillSolidRect(rect, m_Params.m_clrFillGradient);
#endif // CUSTOMDRAW_GRADIENT
#endif // MSVC_NEW_VER
}
}
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
CSize CCustomDrawToolTipCtrl::DrawText( CDC *pDC,
const CString& strText,
CRect rect,
BOOL bCalcOnly,
BOOL bBold /*= FALSE*/,
BOOL bVertCenter /*= FALSE*/,
BOOL bWordBreak /*= FALSE*/
)
{
ASSERT_VALID(pDC);
CSize sizeText(0, 0);
DRAWTEXTPARAMS dtp = {sizeof(DRAWTEXTPARAMS)};
dtp.iTabLength = m_nTextTabSize;
CFont* pOldFont = SelectFont(pDC, bBold);
UINT nFormat = DT_NOCLIP | DT_NOPREFIX | DT_TABSTOP | DT_EXPANDTABS;
if (bWordBreak)
{
nFormat |= DT_WORDBREAK;
}
else if (strText.Find(_T('\n')) < 0) // signle line text
{
nFormat = DT_SINGLELINE;
if (bVertCenter)
{
nFormat |= DT_VCENTER;
}
}
if (bCalcOnly)
{
nFormat |= DT_CALCRECT;
}
if ( GetTextDrawer() )
sizeText.cy = GetTextDrawer()->Draw(pDC, strText, rect, nFormat);
else
sizeText.cy = DrawTextEx(pDC->GetSafeHdc(), (LPTSTR)(LPCTSTR)strText, strText.GetLength(), rect, nFormat, &dtp);
sizeText.cx = rect.Width();
pDC->SelectObject(pOldFont);
return sizeText;
}
CSize CCustomDrawToolTipCtrl::OnDrawLabel( CDC* pDC, CRect rect, BOOL bCalcOnly )
{
ASSERT_VALID(pDC);
CString strText(m_strLabel);
if (strText.IsEmpty())
{
GetWindowText(strText);
}
BOOL bDrawDescr = m_Params.m_bDrawDescription && !m_strDescription.IsEmpty();
BOOL bDrawSupplementalDescr = m_Params.m_bDrawSupplementalDescription && !m_strSupplementalDescription.IsEmpty();
BOOL bDrawSupplementalImage = m_Params.m_bDrawSupplementalImage && m_sizeSupplementalImage != CSize(0, 0);
BOOL bBoldText = m_Params.m_bBoldLabel && (bDrawDescr || bDrawSupplementalDescr || bDrawSupplementalImage);
if (!bCalcOnly)
{
rect.bottom = rect.top + m_nLabelHeight;
}
return DrawText(pDC, strText, rect, bCalcOnly, bBoldText, TRUE);
}
void CCustomDrawToolTipCtrl::OnDrawBorder( CDC* pDC, CRect rect, COLORREF clrLine )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CCDToolTipCtrlBase::OnDrawBorder(pDC, rect, clrLine);
#else
if (m_Params.m_bRoundedCorners)
{
DrawSimpleRoundRectBorder(pDC, rect, clrLine);
}
else
{
pDC->Draw3dRect(rect, clrLine, clrLine);
}
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
CSize CCustomDrawToolTipCtrl::OnDrawBody( CDC* pDC, CRect rect, BOOL bCalcOnly )
{
CRect rectDraw(rect);
rectDraw.DeflateRect(m_ptMargin.x, m_ptMargin.y);
CSize sizeBody(0, 0);
if (m_sizeSupplementalImage != CSize(0, 0) && m_Params.m_bDrawSupplementalImage)
{
if (!bCalcOnly)
{
CRect rectImage = rectDraw;
rectImage.right = rectImage.left + m_sizeSupplementalImage.cx;
rectImage.bottom = rectImage.top + m_sizeSupplementalImage.cy;
OnDrawSupplementalImage(pDC, rectImage);
}
sizeBody = m_sizeSupplementalImage;
}
// Draw separator + supplemental description:
if (!m_strSupplementalDescription.IsEmpty() && m_Params.m_bDrawSupplementalDescription)
{
CRect rectDescr = rectDraw;
int cx = 0;
if (sizeBody.cx > 0)
{
cx = m_ptMargin.x;
rectDescr.left += sizeBody.cx + cx;
}
CSize sizeDescr = OnDrawSupplementalDescription(pDC, rectDescr, bCalcOnly);
sizeBody.cx += sizeDescr.cx + cx;
sizeBody.cy = max(sizeBody.cy, sizeDescr.cy);
}
return sizeBody;
}
CSize CCustomDrawToolTipCtrl::GetDescrIconSize()
{
if (m_hDescrIcon)
{
CSize sizeIcon = ::GetIconSize(m_hDescrIcon);
return sizeIcon;
}
return CSize(0, 0);
}
CSize CCustomDrawToolTipCtrl::GetSupplementalImageSize()
{
if (m_hSupplementalBmp)
{
CSize sizeIcon = ::GetBmpSize(m_hSupplementalBmp);
return sizeIcon;
}
return CSize(0, 0);
}
BOOL CCustomDrawToolTipCtrl::OnDrawSupplementalImage( CDC* pDC, CRect rect )
{
if (m_hSupplementalBmp)
{
CBitmap* pBmp = CBitmap::FromHandle(m_hSupplementalBmp);
if ( pBmp )
{
CMemBitmap bmpDrawer(pDC, pBmp);
bmpDrawer.BeginDraw(m_sizeSupplementalImage.cx, m_sizeSupplementalImage.cy, FALSE);
pDC->BitBlt(rect.left, rect.top, m_sizeSupplementalImage.cx, m_sizeSupplementalImage.cy, &bmpDrawer.GetDC(), 0, 0, SRCCOPY);
return TRUE;
}
}
return FALSE;
}
CSize CCustomDrawToolTipCtrl::OnDrawSupplementalDescription( CDC* pDC, CRect rect, BOOL bCalcOnly )
{
ASSERT_VALID(pDC);
BOOL bSingleLineText = m_strSupplementalDescription.Find(_T('\n')) < 0;
if ( bSingleLineText )
{
rect.right = rect.left + m_Params.m_nMaxDescrWidth;
}
return DrawText(pDC, m_strSupplementalDescription, rect, bCalcOnly, FALSE, FALSE, bSingleLineText);
}
void CCustomDrawToolTipCtrl::SetLabel( const CString strLabel )
{
ASSERT_VALID(this);
m_strLabel = strLabel;
}
void CCustomDrawToolTipCtrl::SetDescription( const CString strDescription )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CCDToolTipCtrlBase::SetDescription(strDescription);
#else
ASSERT_VALID(this);
m_strDescription = strDescription;
//m_strDescription.Replace(_T("\t"), _T(" "));
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
void CCustomDrawToolTipCtrl::SetSupplementalDescription( const CString strDescription )
{
ASSERT_VALID(this);
m_strSupplementalDescription = strDescription;
}
CSize CCustomDrawToolTipCtrl::GetIconSize()
{
if (m_hLabelIcon)
{
CSize sizeIcon = ::GetIconSize(m_hLabelIcon);
return sizeIcon;
}
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
return CCDToolTipCtrlBase::GetIconSize();
#else
return CSize(0, 0);
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
void CCustomDrawToolTipCtrl::SetLocation( CPoint pt )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
CCDToolTipCtrlBase::SetLocation( pt );
#else
ASSERT_VALID(this);
m_ptLocation = pt;
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
void CCustomDrawToolTipCtrl::SetTextTabSize( int nTabSize /*= 4*/ )
{
ASSERT_VALID(this);
m_nTextTabSize = nTabSize;
}
BOOL CCustomDrawToolTipCtrl::OnDrawIcon( CDC* pDC, CRect rectImage )
{
if (m_hLabelIcon)
{
return ::DrawIconEx(pDC->GetSafeHdc(), rectImage.left, rectImage.top, m_hLabelIcon, m_sizeImage.cx, m_sizeImage.cy, 0, NULL, DI_NORMAL);
}
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
return CCDToolTipCtrlBase::OnDrawIcon( pDC, rectImage );
#else
return FALSE;
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
BOOL CCustomDrawToolTipCtrl::OnDrawDescriptionIcon( CDC* pDC, CRect rect )
{
if (m_hDescrIcon)
{
return ::DrawIconEx(pDC->GetSafeHdc(), rect.left, rect.top, m_hDescrIcon, m_sizeDescrImage.cx, m_sizeDescrImage.cy, 0, NULL, DI_NORMAL);
}
return FALSE;
}
CSize CCustomDrawToolTipCtrl::OnDrawDescription( CDC* pDC, CRect rect, BOOL bCalcOnly )
{
#ifdef DERIVE_FROM_MFCTOOLTIPCTRL
return CCDToolTipCtrlBase::OnDrawDescription( pDC, rect, bCalcOnly );
#else
ASSERT_VALID(pDC);
if (!m_Params.m_bDrawDescription)
{
return CSize(0, 0);
}
int nFixedWidth = GetFixedWidth();
if (nFixedWidth > 0 && m_sizeImage.cx <= 32)
{
rect.right = rect.left + nFixedWidth;
if (m_sizeImage.cx > 0 && m_Params.m_bDrawIcon)
{
rect.right -= m_sizeImage.cx + m_ptMargin.x;
}
}
else if ( m_Params.m_nMaxDescrWidth > 0)
{
rect.right = rect.left + m_Params.m_nMaxDescrWidth;
}
return DrawText(pDC, m_strDescription, rect, bCalcOnly, m_Params.m_bBoldDescription, TRUE, TRUE);
#endif // DERIVE_FROM_MFCTOOLTIPCTRL
}
void CCustomDrawToolTipCtrl::OnDrawSeparator( CDC* pDC, int x1, int x2, int y )
{
COLORREF clrSeparator = m_Params.m_clrSeparator == (COLORREF)-1 ? RGB(158,187,221) : m_Params.m_clrBorder;;
CPen penDark(PS_SOLID, 1, clrSeparator);
CPen* pOldPen = pDC->SelectObject(&penDark);
ASSERT_VALID(pDC);
pDC->MoveTo(x1, y);
pDC->LineTo(x2, y);
CPen penLight(PS_SOLID, 1, RGB(255,255,255));
pDC->SelectObject(&penLight);
pDC->MoveTo(x1, y+1);
pDC->LineTo(x2, y+1);
pDC->SelectObject(pOldPen);
}
#ifndef MSVC_NEW_VER
LRESULT CCustomDrawToolTipCtrl::OnSetFont( WPARAM wParam, LPARAM lParam )
{
BOOL bRedraw = (BOOL) LOWORD(lParam);
m_hFont = (HFONT) wParam;
if (m_fontBold.GetSafeHandle())
m_fontBold.DeleteObject();
if ( !m_fontBold.GetSafeHandle() )
{
LOGFONT lf = {0};
GetObject(m_hFont, sizeof(LOGFONT), &lf);
lf.lfWeight = FW_BOLD;
m_fontBold.CreateFontIndirect(&lf);
}
if (bRedraw)
{
RedrawWindow();
UpdateWindow();
}
return 0;
}
#endif // MSVC_NEW_VER
CFont* CCustomDrawToolTipCtrl::SelectFont( CDC *pDC, BOOL bBold /*= FALSE*/ )
{
#ifdef MSVC_NEW_VER
CFont* pOldFont = (CFont*) pDC->SelectObject( bBold ? &afxGlobalData.fontBold : &afxGlobalData.fontTooltip);
#else
ASSERT_VALID(this);
ASSERT_VALID(pDC);
CFont* pOldFont = NULL;
if (m_hFont != NULL)
{
if (bBold)
{
ASSERT(m_fontBold.GetSafeHandle());
pOldFont = pDC->SelectObject(&m_fontBold);
}
else
{
pOldFont = pDC->SelectObject(CFont::FromHandle(m_hFont));
}
}
else
{
if (bBold)
{
if ( !m_fontBold.GetSafeHandle() )
{
HFONT hDefFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
LOGFONT lf = {0};
GetObject(hDefFont, sizeof(LOGFONT), &lf);
lf.lfWeight = FW_BOLD;
m_fontBold.CreateFontIndirect(&lf);
}
pOldFont = pDC->SelectObject(&m_fontBold);
}
else
{
pOldFont = (CFont*) pDC->SelectStockObject(DEFAULT_GUI_FONT);
}
}
#endif // MSVC_NEW_VER
return pOldFont;
}
void CCustomDrawToolTipCtrl::SetLabelIcon( HICON hIcon )
{
ASSERT_VALID(this);
m_hLabelIcon = hIcon;
}
void CCustomDrawToolTipCtrl::SetDescriptionIcon( HICON hIcon )
{
ASSERT_VALID(this);
m_hDescrIcon = hIcon;
}
void CCustomDrawToolTipCtrl::SetSupplementalImage( HBITMAP hBmp )
{
ASSERT_VALID(this);
m_hSupplementalBmp = hBmp;
}
/*----------------------------------------------------------------------------*/
/* CCustomDrawListCtrl
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CCustomDrawListCtrl, CCustomDrawListCtrlBase)
CCustomDrawListCtrl::CCustomDrawListCtrl()
: m_bEnableCustomDraw(TRUE)
, m_nHotItem(-1)
, m_bIsOwnerDraw(FALSE)
, m_bDrawHotItem(TRUE)
, m_bMouseEventsTracked(FALSE)
, m_bExplorerVisualStyle(FALSE)
, m_nLockDrawCount(0)
, m_pTextDrawer(NULL)
{
m_bMarkSortedColumn = TRUE;
m_clrSortedColumn = RGB(225,242,249);
}
CCustomDrawListCtrl::~CCustomDrawListCtrl()
{
}
BEGIN_MESSAGE_MAP(CCustomDrawListCtrl, CCustomDrawListCtrlBase)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnHeaderClicked )
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
ON_WM_SIZE()
#ifdef FIX_LISTCTRL_HILIGHT_UNSELECTED_ITEM_ISSUE
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
#endif // FIX_LISTCTRL_HILIGHT_UNSELECTED_ITEM_ISSUE
ON_MESSAGE(WM_CDTOOLTIPCTRL_NOTIFY, OnCustomToolTipNotify)
END_MESSAGE_MAP()
void CCustomDrawListCtrl::OnInitList()
{
#ifndef DERIVE_FROM_MFCLISTCTRL
InitHeader();
#endif // DERIVE_FROM_MFCLISTCTRL
SetDoubleBuffered(TRUE);
// Disable the CToolTipCtrl of CListCtrl so it won't disturb our own tooltip-ctrl
CToolTipCtrl* pToolTipCtrl = GetToolTips();
if (pToolTipCtrl)
{
pToolTipCtrl->Activate(FALSE);
}
DWORD dwStyle = GetStyle();
m_bIsOwnerDraw = (dwStyle & LVS_OWNERDRAWFIXED) == LVS_OWNERDRAWFIXED;
// Enable our own tooltip-ctrl and make it show tooltip even if not having focus
VERIFY( GetCustomDrawToolTips().Create(this, TTS_ALWAYSTIP) );
GetCustomDrawToolTips().Activate(TRUE);
EnableExplorerVisualStyles();
}
void CCustomDrawListCtrl::InitHeader()
{
if (CListCtrl::GetHeaderCtrl()->GetSafeHwnd())
{
GetSortHeaderCtrl().Init( CListCtrl::GetHeaderCtrl()->GetSafeHwnd() );
GetSortHeaderCtrl().SetSortable( IsSortable() );
}
}
int CCustomDrawListCtrl::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
if (CCustomDrawListCtrlBase::OnCreate(lpCreateStruct) == -1)
return -1;
OnInitList();
return 0;
}
void CCustomDrawListCtrl::PreSubclassWindow()
{
CCustomDrawListCtrlBase::PreSubclassWindow();
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
if (pThreadState->m_pWndInit == NULL)
{
OnInitList();
}
}
void CCustomDrawListCtrl::EnableCustomDraw( BOOL bEnable /*= TRUE*/, BOOL bRedraw /*= FALSE*/ )
{
m_bEnableCustomDraw = bEnable;
if ( bRedraw )
RedrawWindow();
}
BOOL CCustomDrawListCtrl::IsDoubleBuffered()
{
return (GetExtendedStyle() & LVS_EX_DOUBLEBUFFER);
}
void CCustomDrawListCtrl::SetDoubleBuffered( BOOL bSet /*= TRUE*/ )
{
DWORD dwExtStyle = GetExtendedStyle();
if ( bSet )
dwExtStyle |= LVS_EX_DOUBLEBUFFER;
else
dwExtStyle &= ~LVS_EX_DOUBLEBUFFER;
SetExtendedStyle(dwExtStyle);
}
BOOL CCustomDrawListCtrl::IsFullRowSelect()
{
return (GetExtendedStyle() & LVS_EX_FULLROWSELECT);
}
void CCustomDrawListCtrl::SetFullRowSelect(BOOL bSet /*= TRUE*/)
{
DWORD dwExtStyle = GetExtendedStyle();
if ( bSet )
dwExtStyle |= LVS_EX_FULLROWSELECT;
else
dwExtStyle &= ~LVS_EX_FULLROWSELECT;
SetExtendedStyle(dwExtStyle);
}
BOOL CCustomDrawListCtrl::IsHeaderDragDrop()
{
return (GetExtendedStyle() & LVS_EX_HEADERDRAGDROP);
}
void CCustomDrawListCtrl::SetHeaderDragDrop( BOOL bSet /*= TRUE*/ )
{
DWORD dwExtStyle = GetExtendedStyle();
if ( bSet )
dwExtStyle |= LVS_EX_HEADERDRAGDROP;
else
dwExtStyle &= ~LVS_EX_HEADERDRAGDROP;
SetExtendedStyle(dwExtStyle);
}
BOOL CCustomDrawListCtrl::IsSingleSel()
{
return (GetStyle() & LVS_SINGLESEL);
}
void CCustomDrawListCtrl::SetSignleSel( BOOL bSet /*= TRUE*/ )
{
DWORD dwStyle = GetStyle();
if ( bSet )
dwStyle |= LVS_SINGLESEL;
else
dwStyle &= ~LVS_SINGLESEL;
SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);
}
LRESULT CCustomDrawListCtrl::EnableExplorerVisualStyles( bool bValue /*= true*/ )
{
if (!IsThemeEnabled())
{
m_bExplorerVisualStyle = false;
return S_FALSE;
}
if (!CheckOSVersion(0x0600))
{
m_bExplorerVisualStyle = false;
return S_FALSE;
}
LRESULT rc = S_FALSE;
if (bValue)
rc = EnableWindowTheme(GetSafeHwnd(), L"ListView", L"Explorer", NULL);
else
rc = EnableWindowTheme(GetSafeHwnd(), L"", L"", NULL);
if (bValue && rc==S_OK)
{
// OBS! Focus retangle is not painted properly without double-buffering
m_bExplorerVisualStyle = true;
//#if (_WIN32_WINNT >= 0x501)
if (CheckOSVersion(0x501))
SetDoubleBuffered(TRUE);
//#endif
}
else
{
m_bExplorerVisualStyle = false;
}
return rc;
}
#ifndef MSVC_NEW_VER
CToolTipCtrl* CCustomDrawListCtrl::GetToolTips() const
{
ASSERT(::IsWindow(m_hWnd));
return (CToolTipCtrl*)CWnd::FromHandle((HWND)::SendMessage(m_hWnd, LVM_GETTOOLTIPS, 0, 0L));
}
CToolTipCtrl* CCustomDrawListCtrl::SetToolTips( CToolTipCtrl* pWndTip )
{
ASSERT(::IsWindow(m_hWnd));
return (CToolTipCtrl*)CWnd::FromHandle((HWND)::SendMessage(m_hWnd, LVM_SETTOOLTIPS, 0, (LPARAM) pWndTip->GetSafeHwnd()));
}
#endif // MSVC_NEW_VER
#ifndef DERIVE_FROM_MFCLISTCTRL
void CCustomDrawListCtrl::EnableMarkSortedColumn( BOOL bMark /*= TRUE*/, BOOL bRedraw /*= TRUE*/ )
{
m_bMarkSortedColumn = bMark;
if (GetSafeHwnd() != NULL && bRedraw)
{
RedrawWindow();
}
}
#endif // DERIVE_FROM_MFCLISTCTRL
#define DEBUG_CUSTOMDRAW
#ifdef DEBUG_CUSTOMDRAW
#define DBGCD_TRACE TRACE
#else
#define DBGCD_TRACE
#endif // DEBUG_CUSTOMDRAW
#ifdef _DEBUG
CString getDrawState(UINT uDrawItemState)
{
CString strState;
if ( uDrawItemState & CDIS_SELECTED )
{
strState += " selected";
}
if ( uDrawItemState & CDIS_GRAYED )
{
strState += " grayed";
}
if ( uDrawItemState & CDIS_DISABLED )
{
strState += " disabled";
}
if ( uDrawItemState & CDIS_CHECKED )
{
strState += " checked";
}
if ( uDrawItemState & CDIS_FOCUS )
{
strState += " focus";
}
if ( uDrawItemState & CDIS_DEFAULT )
{
strState += " default";
}
if ( uDrawItemState & CDIS_HOT )
{
strState += " hot";
}
if ( uDrawItemState & CDIS_MARKED )
{
strState += " marked";
}
if ( uDrawItemState & CDIS_INDETERMINATE )
{
strState += " indeterminate";
}
if ( uDrawItemState == 0 )
{
strState += " nothing";
}
return strState;
}
CString getLVItemState(UINT uItemState)
{
CString strState;
if ( uItemState & LVIS_FOCUSED )
{
strState += " focused";
}
if ( uItemState & LVIS_SELECTED )
{
strState += " selected";
}
if ( uItemState & LVIS_CUT )
{
strState += " cut";
}
if ( uItemState & LVIS_DROPHILITED )
{
strState += " drophilited";
}
if ( uItemState & LVIS_ACTIVATING )
{
strState += " activating";
}
if ( uItemState == 0 )
{
strState += " nothing";
}
return strState;
}
#endif // _DEBUG
void CCustomDrawListCtrl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
// DrawItem() is not thread safe
// Reference:
// http://www.codeproject.com/KB/list/selectentirerow.aspx#Readers Comments
CMutex mutex(FALSE, _T("CCustomDrawListCtrl::DrawItem()"));
CSingleLock lock(&mutex);
lock.Lock(); // Wait until signalled.
// Unlock takes place in destructor when function exit
int nItem = static_cast<int>( lpDrawItemStruct->itemID );
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
ASSERT(pDC);
OnDrawItem(pDC, nItem);
}
void CCustomDrawListCtrl::OnCustomDraw( NMHDR* pNMHDR, LRESULT* pResult )
{
LPNMLVCUSTOMDRAW pNMLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
if ( !pNMLVCD )
{
ASSERT(0); // can't fail!
return;
}
DWORD& dwDrawStage = pNMLVCD->nmcd.dwDrawStage;
switch (dwDrawStage)
{
case CDDS_PREPAINT:
if ( IsCustomDrawEnabled() )
*pResult = CDRF_NOTIFYITEMDRAW;
else
*pResult = CDRF_DODEFAULT;
break;
case CDDS_ITEMPREPAINT:
{
int nItem = static_cast<int>( pNMLVCD->nmcd.dwItemSpec );
CDC* pDC = CDC::FromHandle(pNMLVCD->nmcd.hdc);
ASSERT(pDC);
OnDrawItem(pDC, nItem);
*pResult = CDRF_SKIPDEFAULT;
}
break;
default:
TRACE("===== CCustomDrawListCtrl::OnCustomdraw() unhandled draw stage!\n");
*pResult = CDRF_DODEFAULT;
break;
}
}
CRect CCustomDrawListCtrl::GetSubItemDrawRect( int nItem, int nSubItem )
{
CRect rcItem;
int nArea = IsFullRowSelect() ? LVIR_BOUNDS : LVIR_LABEL; // if it is full-row select, we need to invalidate thw whole row
VERIFY( GetSubItemRect(nItem, nSubItem, nArea, rcItem) );
if ( nItem+1 < GetItemCount() )
{
// this is not the last item
UINT nState = GetItemState(nItem+1, LVIS_SELECTED|LVIS_FOCUSED);
if ( 0 == nState )
--rcItem.bottom; // avoid bottom border gets covered by the grid lines
}
return rcItem;
}
#define TRY_DRAW_LISTCTRL_WITH_MEMDC
void CCustomDrawListCtrl::OnDrawItem( CDC* pDC, int nItem )
{
CRect rcBounds;
GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
#ifdef TRY_DRAW_LISTCTRL_WITH_MEMDC
CSimpleMemDC memDC(*pDC, rcBounds);
CDC* pDrawDC = &memDC.GetDC();
CFont* pFont = pDC->GetCurrentFont();
CFont* pOldFont = pDrawDC->SelectObject(pFont);
#else
CDC* pDrawDC = pDC;
#endif // TRY_DRAW_LISTCTRL_WITH_MEMDC
// Erase the background first
OnEraseBackground(pDrawDC, rcBounds, nItem);
int nColCount = GetSortHeaderCtrl().GetItemCount();
LVITEM lvitem = {0};
lvitem.mask = LVIF_IMAGE|LVIF_STATE;
lvitem.iItem = nItem;
lvitem.stateMask = (UINT)-1;
VERIFY( GetItem(&lvitem) );
//DBGCD_TRACE("====> %d, Draw State is [%s] at tick %X\n", nItem, getLVItemState(lvitem.state), GetTickCount());
for (int nCol = 0; nCol < nColCount; ++nCol)
{
OnDrawCell(pDrawDC, nItem, nCol, &lvitem);
}
#ifdef TRY_DRAW_LISTCTRL_WITH_MEMDC
pDrawDC->SelectObject(pOldFont);
#endif // TRY_DRAW_LISTCTRL_WITH_MEMDC
}
void CCustomDrawListCtrl::OnEraseBackground( CDC* pDC, CRect rect, int nItem )
{
// you can also copy the background bitmap from the client window if you wish
COLORREF clrBk = ::GetSysColor( IsWindowEnabled() ? COLOR_WINDOW : COLOR_BTNFACE );
pDC->FillSolidRect(rect, clrBk);
}
void CCustomDrawListCtrl::OnDrawCell( CDC* pDC, int nItem, int nSubItem, LPLVITEM lpLVItem )
{
CRect rcItem = GetSubItemDrawRect(nItem, nSubItem);
// Fill the background first
if ( IsWindowEnabled() )
OnFillBackground(pDC, rcItem, nItem, nSubItem, lpLVItem);
// Draw the icon
CRect rectIcon;
VERIFY( GetSubItemRect(nItem, nSubItem, LVIR_ICON, rectIcon) );
BOOL bHasIcon = OnDrawIcon(pDC, rectIcon, nItem, nSubItem, lpLVItem);
// Draw the text
CRect rectLabel;
VERIFY( GetSubItemRect(nItem, nSubItem, LVIR_LABEL, rectLabel) );
rectLabel.left += 2;
if ( bHasIcon )
{
rectLabel.left = rectIcon.right + 5;
//rectLabel.top += 1;
}
OnDrawCellText(pDC, rectLabel, nItem, nSubItem, lpLVItem);
}
CSize CCustomDrawListCtrl::GetIconSize( int nItem, int nSubItem )
{
CSize sizeIcon(0,0);
CImageList* pImageList = GetImageList(LVSIL_SMALL);
if (pImageList)
{
int cx, cy;
ImageList_GetIconSize(pImageList->GetSafeHandle(), &cx, &cy);
sizeIcon.cx = cx;
sizeIcon.cy = cy;
}
return sizeIcon;
}
BOOL CCustomDrawListCtrl::OnDrawIcon( CDC* pDC, CRect rect, int nItem, int nSubItem, LPLVITEM lpLVItem )
{
if ( 0 == nSubItem )
{
CImageList* pImageList = GetImageList(LVSIL_SMALL);
if ( pImageList && lpLVItem->iImage >= 0 )
{
CSize sizeImage = GetIconSize(nItem, nSubItem);
int ny = rect.top + (rect.Height() - sizeImage.cy) / 2;
// Unfortunately CImageList::Draw(..ILD_TRANSPARENT) can't take care of transparent icon in
// expected way, I see black outline on the bitmap, so here I use DrawIconEx instead
// CPoint ptImage(rect.left, ny);
// pImageList->Draw(pDC, lpLVItem->iImage, ptImage, ILD_TRANSPARENT);
HICON hIcon = pImageList->ExtractIcon(lpLVItem->iImage);
::DrawIconEx(pDC->GetSafeHdc(), rect.left, ny, hIcon, sizeImage.cx, sizeImage.cy, 0, NULL, DI_NORMAL);
::DestroyIcon(hIcon);
return TRUE;
}
}
return FALSE;
}
void CCustomDrawListCtrl::OnFillBackground( CDC* pDC, CRect rect, int nItem, int nSubItem, LPLVITEM lpLVItem )
{
UINT& nItemState = lpLVItem->state;
CCustomDrawHelper itemDrawer;
itemDrawer.m_bWndHasFocus = ::GetFocus() == GetSafeHwnd();
itemDrawer.m_bFocusItem = ((nItemState & LVIS_FOCUSED) ? true : false);
itemDrawer.m_bSelected = ((nItemState & LVIS_SELECTED) ? true : false);
itemDrawer.m_bIsHotItem = m_bDrawHotItem && m_nHotItem == nItem;
BOOL bFilledWithSortClr = FALSE;
if ( IsSortable()
&& m_bMarkSortedColumn
&& GetSortHeaderCtrl().GetSortColumn() == nSubItem
&& !itemDrawer.m_bSelected
&& !itemDrawer.m_bIsHotItem
)
{
CRect rectColumn;
GetSubItemRect(nItem, nSubItem, LVIR_LABEL, rectColumn);
pDC->FillSolidRect(rectColumn, m_clrSortedColumn);
bFilledWithSortClr = TRUE;
}
if ( 0 == nSubItem || (bFilledWithSortClr && itemDrawer.m_bFocusItem) )
{
itemDrawer.m_bDrawBorderWhenFill = 0 == nSubItem;
itemDrawer.DrawItemBackground(pDC, rect);
}
}
void CCustomDrawListCtrl::OnDrawCellText( CDC* pDC, CRect rect, int nItem, int nSubItem, LPLVITEM lpLVItem )
{
UINT& nItemState = lpLVItem->state;
CString strLabel = GetItemText(nItem, nSubItem);
if ( !strLabel.IsEmpty() )
{
LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH;
GetColumn(nSubItem, &lvc);
int nOldBkMode = pDC->SetBkMode(TRANSPARENT);
COLORREF clrTxtColor = GetItemTextColor(nItem, nSubItem, nItemState);
COLORREF clrOldTxtColor;
if (clrTxtColor != CLR_INVALID)
clrOldTxtColor = pDC->SetTextColor(clrTxtColor);
UINT nJustify = DT_LEFT;
switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
{
case LVCFMT_RIGHT:
nJustify = DT_RIGHT;
break;
case LVCFMT_CENTER:
nJustify = DT_CENTER;
break;
default:
break;
}
UINT nDTFormat = nJustify|DT_END_ELLIPSIS|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX;
if ( GetTextDrawer() )
GetTextDrawer()->Draw(pDC, strLabel, rect, nDTFormat);
else
pDC->DrawText(strLabel, rect, nDTFormat);
if (clrTxtColor != CLR_INVALID)
pDC->SetTextColor(clrOldTxtColor);
pDC->SetBkMode(nOldBkMode);
}
}
COLORREF CCustomDrawListCtrl::GetItemTextColor( int nItem, int nSubItem, UINT nItemState )
{
#ifndef CUSTOMDRAW_GRADIENT
if ( nItemState & LVIS_SELECTED && ::GetFocus() == GetSafeHwnd() && (IsFullRowSelect() || nSubItem == 0) )
{
return ::GetSysColor(COLOR_HIGHLIGHTTEXT);
}
#endif // CUSTOMDRAW_GRADIENT
return CLR_INVALID;
}
// Taken from http://www.codeproject.com/KB/list/quicklist.aspx
BOOL CCustomDrawListCtrl::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT* pResult )
{
UINT nID = pNMHDR->idFrom;
// check if this is the automatic tooltip of the control
if (nID == 0)
return TRUE; // do not allow display of automatic tooltip,
// or our tooltip will disappear
*pResult = 0;
// get the mouse position
const MSG* pMessage = GetCurrentMessage();
ASSERT(pMessage);
CPoint pt;
pt = pMessage->pt; // get the point from the message
ScreenToClient(&pt); // convert the point's coords to be relative to this control
// see if the point falls onto a list item
LVHITTESTINFO hitinfo = {0};
hitinfo.pt = pt;
hitinfo.flags = 0;
hitinfo.iItem = hitinfo.iSubItem = -1;
if ( SubItemHitTest(&hitinfo) >= 0 && ShouldShowToolTipForCell(hitinfo.iItem, hitinfo.iSubItem) )
{
CString strToolTip = GetToolTipLabelForCell(hitinfo.iItem, hitinfo.iSubItem);
if (strToolTip.IsEmpty())
{
strToolTip = GetItemText(hitinfo.iItem, hitinfo.iSubItem);
}
if (!strToolTip.IsEmpty())
{
GetCustomDrawToolTips().SetText(pNMHDR, strToolTip);
GetCustomDrawToolTips().SetLabel(strToolTip); // otherwise the tooltip can't show correct UNICODE text under ANSI environment
return TRUE;
}
}
return FALSE; // we didn't handle the message, let the
// framework continue propagating the message
}
void CCustomDrawListCtrl::OnMouseMove( UINT nFlags, CPoint point )
{
CCustomDrawListCtrlBase::OnMouseMove(nFlags, point);
// Find the subitem
LVHITTESTINFO hitinfo = {0};
hitinfo.flags = nFlags;
hitinfo.pt = point;
SubItemHitTest(&hitinfo);
if ( m_bDrawHotItem )
{
if ( !m_bMouseEventsTracked )
{
m_bMouseEventsTracked = TRUE;
TRACKMOUSEEVENT trackmouseevent;
trackmouseevent.cbSize = sizeof(trackmouseevent);
trackmouseevent.dwFlags = TME_LEAVE;
trackmouseevent.hwndTrack = GetSafeHwnd();
trackmouseevent.dwHoverTime = HOVER_DEFAULT;
//::AFXTrackMouse(&trackmouseevent);
_TrackMouseEvent(&trackmouseevent);
}
if ( m_nHotItem != hitinfo.iItem )
{
int nOldHotItem = m_nHotItem;
m_nHotItem = hitinfo.iItem;
LockSetRedraw(FALSE);
SetHotItem(m_nHotItem);
// RedrawItems produces flicker.
// RedrawItems(m_nHotItem, m_nHotItem);
// RedrawItems(nOldHotItem, nOldHotItem);
CRect rect;
GetItemRect(m_nHotItem, rect, LVIR_BOUNDS);
InvalidateRect(rect, FALSE);
GetItemRect(nOldHotItem, rect, LVIR_BOUNDS);
InvalidateRect(rect, FALSE);
LockSetRedraw(TRUE);
}
}
if ( ShouldShowToolTipForCell(hitinfo.iItem, hitinfo.iSubItem)
&& GetCustomDrawToolTips().CheckDisplayToolTip( (WPARAM)hitinfo.iItem, (LPARAM)hitinfo.iSubItem )
)
{
// Remove the old tooltip (if available)
int nToolCount = GetCustomDrawToolTips().GetToolCount();
//TRACE("CCustomDrawListCtrl tooltip tool count: %d\n", nToolCount);
if (nToolCount > 0)
{
// Not using CToolTipCtrl::DelTool() because it redirects the messages to CListCtrl parent
// If we call DelTool(), you will see that the return value of GetToolCount() still keep increasing!
//GetCustomDrawToolTips().DelTool(this);
TOOLINFO ti = {0};
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND; // Indicate that uId is handle to a control
ti.uId = (UINT_PTR)m_hWnd; // Handle to the control
ti.hwnd = m_hWnd; // Handle to window to receive the tooltip-messages
ti.hinst = AfxGetInstanceHandle();
GetCustomDrawToolTips().SendMessage(TTM_DELTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
GetCustomDrawToolTips().Activate(FALSE);
}
// Add the new tooltip (if available)
if (hitinfo.iItem != -1 && hitinfo.iSubItem != -1)
{
// Not using CToolTipCtrl::AddTool() because it redirects the messages to CListCtrl parent
//GetCustomDrawToolTips().AddTool(this);
TOOLINFO ti = {0};
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_IDISHWND; // Indicate that uId is handle to a control
ti.uId = (UINT_PTR)m_hWnd; // Handle to the control
ti.hwnd = m_hWnd; // Handle to window to receive the tooltip-messages
ti.hinst = AfxGetInstanceHandle();
ti.lpszText = LPSTR_TEXTCALLBACK;
GetCustomDrawToolTips().SendMessage(TTM_ADDTOOL, 0, (LPARAM) (LPTOOLINFO) &ti);
PreAddToolTipForCell(hitinfo.iItem, hitinfo.iSubItem);
GetCustomDrawToolTips().Activate(TRUE);
}
}
}
LRESULT CCustomDrawListCtrl::OnMouseLeave( WPARAM wParam, LPARAM lParam )
{
LRESULT result = Default();
m_bMouseEventsTracked = FALSE;
if (m_nHotItem >= 0)
{
int nTmpHotItem = m_nHotItem;
m_nHotItem = -1;
//RedrawItems(nTmpHotItem, nTmpHotItem);
CRect rect;
GetItemRect(nTmpHotItem, rect, LVIR_BOUNDS);
InvalidateRect(rect, FALSE);
}
return result;
}
bool CCustomDrawListCtrl::IsSortable() const
{
return false;
}
BOOL CCustomDrawListCtrl::OnSort( int nCol )
{
ASSERT(IsSortable());
return TRUE;
}
void CCustomDrawListCtrl::OnHeaderClicked( NMHDR* pNMHDR, LRESULT* pResult )
{
// List view notification header
NM_LISTVIEW* pNMListView = reinterpret_cast<NM_LISTVIEW*>(pNMHDR);
if ( IsSortable() )
{
int nOldSortColumn = GetSortHeaderCtrl().GetSortColumn();
if ( OnSort(pNMListView->iSubItem) )
{
GetSortHeaderCtrl().SwitchSortItem(pNMListView->iSubItem);
}
if (m_bMarkSortedColumn)
{
LockSetRedraw(FALSE);
CRect rectClient;
GetClientRect(rectClient);
CRect rectColumn;
if (nOldSortColumn >= 0)
{
GetSubItemRect(0, nOldSortColumn, LVIR_LABEL, rectColumn);
rectColumn.top = rectClient.top;
rectColumn.bottom = rectClient.bottom;
InvalidateRect(rectColumn, FALSE);
}
int nNewSortColumn = GetSortHeaderCtrl().GetSortColumn();
if (nNewSortColumn >= 0 && nNewSortColumn != nOldSortColumn)
{
GetSubItemRect(0, nNewSortColumn, LVIR_LABEL, rectColumn);
rectColumn.top = rectClient.top;
rectColumn.bottom = rectClient.bottom;
InvalidateRect(rectColumn, FALSE);
}
LockSetRedraw(TRUE);
}
}
// Return result
*pResult = 0;
}
BOOL CCustomDrawListCtrl::PreTranslateMessage( MSG* pMsg )
{
if (GetCustomDrawToolTips().m_hWnd)
GetCustomDrawToolTips().RelayEvent(pMsg);
return CCustomDrawListCtrlBase::PreTranslateMessage(pMsg);
}
BOOL CCustomDrawListCtrl::ShouldShowToolTipForCell( int nRow, int nCol )
{
return TRUE;
}
CString CCustomDrawListCtrl::GetToolTipLabelForCell( int nRow, int nCol )
{
return _T("");
}
void CCustomDrawListCtrl::PreAddToolTipForCell( int nRow, int nCol )
{
// nothing to do
}
void CCustomDrawListCtrl::PreShowToolTipForCell( int nRow, int nCol )
{
// nothing to do
}
void CCustomDrawListCtrl::PrePopToolTip()
{
// nothing to do
}
void CCustomDrawListCtrl::OnSize( UINT nType, int cx, int cy )
{
CCustomDrawListCtrlBase::OnSize(nType, cx, cy);
if (GetSortHeaderCtrl().GetSafeHwnd() != NULL)
{
GetSortHeaderCtrl().RedrawWindow();
}
}
void CCustomDrawListCtrl::OnDestroy()
{
OnDestroyList();
CCustomDrawListCtrlBase::OnDestroy();
}
#ifdef FIX_LISTCTRL_HILIGHT_UNSELECTED_ITEM_ISSUE
void CCustomDrawListCtrl::RepaintSelectedItems()
{
RedrawWindow();
}
void CCustomDrawListCtrl::OnKillFocus( CWnd* pNewWnd )
{
CCustomDrawListCtrlBase::OnKillFocus(pNewWnd);
// check if we are losing focus to label edit box
if(pNewWnd != NULL && pNewWnd->GetParent() == this)
return;
// repaint items that should change appearance
if( m_bIsOwnerDraw && (GetStyle() & LVS_TYPEMASK) == LVS_REPORT )
RepaintSelectedItems();
}
void CCustomDrawListCtrl::OnSetFocus( CWnd* pOldWnd )
{
CCustomDrawListCtrlBase::OnSetFocus(pOldWnd);
// check if we are getting focus from label edit box
if(pOldWnd!=NULL && pOldWnd->GetParent()==this)
return;
// repaint items that should change appearance
if( m_bIsOwnerDraw && (GetStyle() & LVS_TYPEMASK) == LVS_REPORT )
RepaintSelectedItems();
}
void CCustomDrawListCtrl::OnLButtonDown( UINT nFlags, CPoint point )
{
CCustomDrawListCtrlBase::OnLButtonDown(nFlags, point);
// repaint items that should change appearance
if( m_bIsOwnerDraw
&& (GetStyle() & LVS_TYPEMASK) == LVS_REPORT
&& !IsSingleSel()
&& !SHIFT_DOWN
&& !CNTRL_DOWN
)
RepaintSelectedItems();
}
void CCustomDrawListCtrl::OnRButtonDown( UINT nFlags, CPoint point )
{
CCustomDrawListCtrlBase::OnRButtonDown(nFlags, point);
// repaint items that should change appearance
if( m_bIsOwnerDraw
&& (GetStyle() & LVS_TYPEMASK) == LVS_REPORT
&& !IsSingleSel()
&& !SHIFT_DOWN
&& !CNTRL_DOWN
)
RepaintSelectedItems();
}
#endif // FIX_LISTCTRL_HILIGHT_UNSELECTED_ITEM_ISSUE
LRESULT CCustomDrawListCtrl::OnCustomToolTipNotify( WPARAM wParam, LPARAM lParam )
{
switch (wParam)
{
case CDTOOLTIP_ONBEFORE_SHOW:
{
int nRow = (int)GetCustomDrawToolTips().GetDisplayWParam();
int nCol = (int)GetCustomDrawToolTips().GetDisplayLParam();
PreShowToolTipForCell(nRow, nCol);
}
break;
case CDTOOLTIP_ONBEFORE_POP:
PrePopToolTip();
break;
}
return 0;
}
IMPLEMENT_DYNCREATE(CXEditPrompt, CXEditPromptBase)
BEGIN_MESSAGE_MAP(CXEditPrompt, CXEditPromptBase)
ON_WM_CREATE()
ON_WM_CTLCOLOR_REFLECT()
ON_WM_KEYDOWN()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_LBUTTONDOWN()
ON_WM_MBUTTONDOWN()
ON_WM_RBUTTONDOWN()
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////////
// ctor
CXEditPrompt::CXEditPrompt()
: m_bShowCueBanner(TRUE)
, m_strPromptText(_T("<Enter text here>"))
, m_crPromptColor(RGB(119,121,118)) // RAL 9023 (Pearl dark gray)
// see http://www.highplains.net/pixelcolor.html
, m_dwCueBannerAlign(ES_CENTER)
{
m_crBkColor = GetSysColor(COLOR_WINDOW);
}
///////////////////////////////////////////////////////////////////////////////
// dtor
CXEditPrompt::~CXEditPrompt()
{
m_brush.DeleteObject();
m_robrush.DeleteObject();
}
int CXEditPrompt::OnCreate( LPCREATESTRUCT lpCreateStruct )
{
if (CXEditPromptBase::OnCreate(lpCreateStruct) == -1)
return -1;
OnInitEdit();
return 0;
}
///////////////////////////////////////////////////////////////////////////////
// PreSubclassWindow
void CXEditPrompt::PreSubclassWindow()
{
CXEditPromptBase::PreSubclassWindow();
_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
if (pThreadState->m_pWndInit == NULL)
{
OnInitEdit();
}
}
BOOL CXEditPrompt::OnInitEdit()
{
m_brush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
m_robrush.CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
EnableCueBanner();
//SetWindowText(m_strPromptText);
SetSel(-1, 0); // get rid of standard highlighting
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// Reset
void CXEditPrompt::Reset()
{
EnableCueBanner();
SetWindowText(m_strPromptText);
SetSel(m_strPromptText.GetLength(), m_strPromptText.GetLength());
RedrawWindow();
}
///////////////////////////////////////////////////////////////////////////////
// SetPromptColor
void CXEditPrompt::SetPromptColor(COLORREF crText)
{
m_crPromptColor = crText;
//if (m_bFirstTime)
// RedrawWindow();
}
///////////////////////////////////////////////////////////////////////////////
// SetPromptText
void CXEditPrompt::SetPromptText(LPCTSTR lpszPrompt)
{
m_strPromptText = lpszPrompt;
if (m_strPromptText.IsEmpty())
{
EnableCueBanner(FALSE);
}
if (m_bShowCueBanner)
SetWindowText(m_strPromptText);
}
///////////////////////////////////////////////////////////////////////////////
// OnSetFocus
void CXEditPrompt::OnSetFocus(CWnd* pOldWnd)
{
CXEditPromptBase::OnSetFocus(pOldWnd);
if (m_bShowCueBanner)
{
// get rid of standard highlighting
SetSel(m_strPromptText.GetLength(), m_strPromptText.GetLength());
}
}
///////////////////////////////////////////////////////////////////////////////
// CtlColor
HBRUSH CXEditPrompt::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
if ( !IsWindowEnabled() )
return NULL;
if (m_bShowCueBanner)
{
pDC->SetTextColor(m_crPromptColor);
}
if ( GetStyle() & ES_READONLY )
{
pDC->SetBkColor(GetSysColor(COLOR_BTNFACE));
return m_robrush;
}
pDC->SetBkColor(m_crBkColor);
return m_brush; // setting text color will have no effect unless
// we return a valid brush
}
///////////////////////////////////////////////////////////////////////////////
// OnKeyDown
void CXEditPrompt::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (m_bShowCueBanner)
{
// key down includes keys like shift and ctrl
EnableCueBanner(FALSE);
SetWindowText(_T(""));
}
CXEditPromptBase::OnKeyDown(nChar, nRepCnt, nFlags);
}
///////////////////////////////////////////////////////////////////////////////
// OnLButtonDown
void CXEditPrompt::OnLButtonDown(UINT nFlags, CPoint point)
{
if (m_bShowCueBanner)
{
EnableCueBanner(FALSE);
SetWindowText(_T(""));
}
CXEditPromptBase::OnLButtonDown(nFlags, point);
}
///////////////////////////////////////////////////////////////////////////////
// OnMButtonDown
void CXEditPrompt::OnMButtonDown(UINT nFlags, CPoint point)
{
if (m_bShowCueBanner)
{
EnableCueBanner(FALSE);
SetWindowText(_T(""));
}
CXEditPromptBase::OnMButtonDown(nFlags, point);
}
///////////////////////////////////////////////////////////////////////////////
// OnRButtonDown
void CXEditPrompt::OnRButtonDown(UINT nFlags, CPoint point)
{
if (m_bShowCueBanner)
{
EnableCueBanner(FALSE);
SetWindowText(_T(""));
}
CXEditPromptBase::OnRButtonDown(nFlags, point);
}
///////////////////////////////////////////////////////////////////////////////
// GetWindowText
int CXEditPrompt::GetWindowText(LPTSTR lpszStringBuf, int nMaxCount) const
{
if (m_bShowCueBanner)
{
if (lpszStringBuf && (nMaxCount > 0))
lpszStringBuf[0] = _T('\0');
return 0;
}
return CXEditPromptBase::GetWindowText(lpszStringBuf, nMaxCount);
}
///////////////////////////////////////////////////////////////////////////////
// GetWindowText
void CXEditPrompt::GetWindowText(CString& rString) const
{
if (m_bShowCueBanner)
rString = _T("");
else
CXEditPromptBase::GetWindowText(rString);
}
///////////////////////////////////////////////////////////////////////////////
// SetWindowText
void CXEditPrompt::SetWindowText(LPCTSTR lpszString)
{
// the control is being initialized, just ignore
if (m_bShowCueBanner && (lpszString[0] == _T('\0')))
return;
// if this is not prompt string, then no need to prompt
if (lpszString &&
(m_strPromptText.Compare(lpszString) != 0))
EnableCueBanner(FALSE);
CXEditPromptBase::SetWindowText(lpszString);
}
///////////////////////////////////////////////////////////////////////////////
// DefWindowProc
LRESULT CXEditPrompt::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_SETTEXT)
{
TCHAR *cp = (TCHAR *) lParam;
if (!cp)
return TRUE;
// the control is being initialized, just ignore
if (m_bShowCueBanner && (cp[0] == _T('\0')))
return TRUE;
// if this is not prompt string, then no need to prompt
if (m_strPromptText.Compare(cp) != 0)
EnableCueBanner(FALSE);
}
else if (message == WM_GETTEXT)
{
if (m_bShowCueBanner)
{
TCHAR *cp = (TCHAR *) lParam;
if (cp && (wParam != 0))
*cp = _T('\0');
return 0;
}
}
else if ( WM_GETTEXTLENGTH == message )
{
if ( m_bShowCueBanner )
return 0;
}
return CXEditPromptBase::DefWindowProc(message, wParam, lParam);
}
void CXEditPrompt::SetBKColor( COLORREF crBK )
{
m_crBkColor = crBK;
RedrawWindow();
}
void CXEditPrompt::OnKillFocus(CWnd* pOldWnd)
{
CXEditPromptBase::OnKillFocus(pOldWnd);
if (!m_bShowCueBanner)
{
CString strWndText;
GetWindowText(strWndText);
if (strWndText.IsEmpty())
{
EnableCueBanner();
SetWindowText(m_strPromptText);
}
}
}
int CXEditPrompt::GetWindowTextLength() const
{
if (m_bShowCueBanner)
return 0;
return CXEditPromptBase::GetWindowTextLength();
}
void CXEditPrompt::SetCueBannerAlign( DWORD val )
{
m_dwCueBannerAlign = val;
if ( m_bShowCueBanner )
UpdateStyleForCueBanner();
}
/*----------------------------------------------------------------------------*/
/* CFilterEdit
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CFilterEdit, CXEditPrompt)
CFilterEdit::CFilterEdit(CWnd* pBuddyWnd /*= NULL*/)
:m_pBuddyWnd(pBuddyWnd)
{
}
CFilterEdit::~CFilterEdit()
{
}
BOOL CFilterEdit::PreTranslateMessage( MSG* pMsg )
{
if ( pMsg->message == WM_KEYDOWN )
{
switch ( pMsg->wParam )
{
case VK_DOWN:
case VK_UP:
case VK_PRIOR:
case VK_NEXT:
{
CWnd* pWnd = GetFocus();
if ( pWnd && pWnd == this && m_pBuddyWnd )
{
// CDialog* pDlg = (CDialog*)GetParent();
// ASSERT(m_pBuddyWnd);
// SetDialogFocus(pDlg->GetSafeHwnd(), m_pBuddyWnd->GetSafeHwnd());
m_pBuddyWnd->SetFocus();
m_pBuddyWnd->SendMessage(WM_KEYDOWN, pMsg->wParam, pMsg->lParam);
return TRUE;
}
}
break;
default:
break;
}
}
return CXEditPrompt::PreTranslateMessage(pMsg);
}
void CFilterEdit::SetBuddyWindow( CWnd* pBuddyWnd )
{
m_pBuddyWnd = pBuddyWnd;
}