www.pudn.com > gridctrl_demo225.rar > TitleTip.cpp


//////////////////////////////////////////////////////////////////////////// 
// TitleTip.cpp : implementation file 
// 
// Based on code by Zafir Anjum 
// 
// Adapted by Chris Maunder  
// Copyright (c) 1998-2002. All Rights Reserved. 
// 
// This code may be used in compiled form in any way you desire. This 
// file may be redistributed unmodified by any means PROVIDING it is  
// not sold for profit without the authors written consent, and  
// providing that this notice and the authors name and all copyright  
// notices remains intact.  
// 
// An email letting me know how you are using it would be nice as well.  
// 
// This file is provided "as is" with no expressed or implied warranty. 
// The author accepts no liability for any damage/loss of business that 
// this product may cause. 
// 
// For use with CGridCtrl v2.20+ 
// 
// History 
//         10 Apr 1999  Now accepts a LOGFONT pointer and  
//                      a tracking rect in Show(...)  (Chris Maunder) 
//         18 Apr 1999  Resource leak in Show fixed by Daniel Gehriger 
//          8 Mar 2000  Added double-click fix found on codeguru 
//                      web site but forgot / can't find who contributed it 
//         28 Mar 2000  Aqiruse (marked with //FNA) 
//                      Titletips now use cell color 
//         18 Jun 2000  Delayed window creation added 
// 
///////////////////////////////////////////////////////////////////////////// 
  
#include "stdafx.h" 
#include "gridctrl.h" 
 
#ifndef GRIDCONTROL_NO_TITLETIPS 
 
#include "TitleTip.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// CTitleTip 
 
CTitleTip::CTitleTip() 
{ 
	// Register the window class if it has not already been registered. 
	WNDCLASS wndcls; 
	HINSTANCE hInst = AfxGetInstanceHandle(); 
	if(!(::GetClassInfo(hInst, TITLETIP_CLASSNAME, &wndcls))) 
	{ 
		// otherwise we need to register a new class 
		wndcls.style			= CS_SAVEBITS; 
		wndcls.lpfnWndProc		= ::DefWindowProc; 
		wndcls.cbClsExtra		= wndcls.cbWndExtra = 0; 
		wndcls.hInstance		= hInst; 
		wndcls.hIcon			= NULL; 
		wndcls.hCursor			= LoadCursor( hInst, IDC_ARROW ); 
		wndcls.hbrBackground	= (HBRUSH)(COLOR_INFOBK +1); 
		wndcls.lpszMenuName		= NULL; 
		wndcls.lpszClassName	= TITLETIP_CLASSNAME; 
 
		if (!AfxRegisterClass(&wndcls)) 
			AfxThrowResourceException(); 
	} 
 
    m_dwLastLButtonDown = ULONG_MAX; 
    m_dwDblClickMsecs   = GetDoubleClickTime(); 
    m_bCreated          = FALSE; 
    m_pParentWnd        = NULL; 
} 
 
CTitleTip::~CTitleTip() 
{ 
} 
 
 
BEGIN_MESSAGE_MAP(CTitleTip, CWnd) 
	//{{AFX_MSG_MAP(CTitleTip) 
	ON_WM_MOUSEMOVE() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
 
///////////////////////////////////////////////////////////////////////////// 
// CTitleTip message handlers 
 
BOOL CTitleTip::Create(CWnd * pParentWnd) 
{ 
	ASSERT_VALID(pParentWnd); 
 
    // Already created? 
    if (m_bCreated) 
        return TRUE; 
 
	DWORD dwStyle = WS_BORDER | WS_POPUP;  
	DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST; 
	m_pParentWnd = pParentWnd; 
 
	m_bCreated = CreateEx(dwExStyle, TITLETIP_CLASSNAME, NULL, dwStyle,  
                          CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,  
		                  NULL, NULL, NULL ); 
 
    return m_bCreated; 
} 
 
BOOL CTitleTip::DestroyWindow()  
{ 
    m_bCreated = FALSE; 
	 
	return CWnd::DestroyWindow(); 
} 
 
// Show 		 - Show the titletip if needed 
// rectTitle	 - The rectangle within which the original  
//				    title is constrained - in client coordinates 
// lpszTitleText - The text to be displayed 
// xoffset		 - Number of pixel that the text is offset from 
//				   left border of the cell 
void CTitleTip::Show(CRect rectTitle, LPCTSTR lpszTitleText, int xoffset /*=0*/, 
                     LPRECT lpHoverRect /*=NULL*/, 
                     const LOGFONT* lpLogFont /*=NULL*/, 
                     COLORREF crTextClr /* CLR_DEFAULT */, 
                     COLORREF crBackClr /* CLR_DEFAULT */) 
{ 
    if (!IsWindow(m_hWnd)) 
        Create(m_pParentWnd); 
 
	ASSERT( ::IsWindow( GetSafeHwnd() ) ); 
 
    if (rectTitle.IsRectEmpty()) 
        return; 
 
	// If titletip is already displayed, don't do anything. 
	if( IsWindowVisible() )  
		return; 
 
    m_rectHover = (lpHoverRect != NULL)? lpHoverRect : rectTitle; 
    m_rectHover.right++; m_rectHover.bottom++; 
 
	m_pParentWnd->ClientToScreen( m_rectHover ); 
    ScreenToClient( m_rectHover ); 
 
	// Do not display the titletip is app does not have focus 
	if( GetFocus() == NULL ) 
		return; 
 
	// Define the rectangle outside which the titletip will be hidden. 
	// We add a buffer of one pixel around the rectangle 
	m_rectTitle.top    = -1; 
	m_rectTitle.left   = -xoffset-1; 
	m_rectTitle.right  = rectTitle.Width()-xoffset; 
	m_rectTitle.bottom = rectTitle.Height()+1; 
 
	// Determine the width of the text 
	m_pParentWnd->ClientToScreen( rectTitle ); 
 
	CClientDC dc(this); 
	CString strTitle = _T(""); 
    strTitle += _T(" "); 
    strTitle += lpszTitleText;  
    strTitle += _T(" "); 
 
	CFont font, *pOldFont = NULL; 
    if (lpLogFont) 
    { 
        font.CreateFontIndirect(lpLogFont); 
	    pOldFont = dc.SelectObject( &font ); 
    } 
    else 
    { 
        // use same font as ctrl 
	    pOldFont = dc.SelectObject( m_pParentWnd->GetFont() ); 
    } 
 
	CSize size = dc.GetTextExtent( strTitle ); 
 
    TEXTMETRIC tm; 
    dc.GetTextMetrics(&tm); 
    size.cx += tm.tmOverhang; 
 
	CRect rectDisplay = rectTitle; 
	rectDisplay.left += xoffset; 
	rectDisplay.right = rectDisplay.left + size.cx + xoffset; 
     
    // Do not display if the text fits within available space 
    if ( rectDisplay.right > rectTitle.right-xoffset ) 
    { 
		// Show the titletip 
        SetWindowPos( &wndTop, rectDisplay.left, rectDisplay.top,  
            rectDisplay.Width(), rectDisplay.Height(),  
            SWP_SHOWWINDOW|SWP_NOACTIVATE ); 
         
        // FNA - handle colors correctly 
        if (crBackClr != CLR_DEFAULT) 
        { 
		    CBrush backBrush(crBackClr); 
		    CBrush* pOldBrush = dc.SelectObject(&backBrush); 
		    CRect rect; 
		    dc.GetClipBox(&rect);     // Erase the area needed  
 
		    dc.PatBlt(rect.left, rect.top, rect.Width(), rect.Height(),  PATCOPY); 
		    dc.SelectObject(pOldBrush); 
	    } 
        // Set color 
        if (crTextClr != CLR_DEFAULT)//FNA 
            dc.SetTextColor(crTextClr);//FA 
 
        dc.SetBkMode( TRANSPARENT ); 
        dc.TextOut( 0, 0, strTitle ); 
        SetCapture(); 
    } 
     
    dc.SelectObject( pOldFont ); 
} 
 
void CTitleTip::Hide() 
{ 
  	if (!::IsWindow(GetSafeHwnd())) 
        return; 
 
    if (GetCapture()->GetSafeHwnd() == GetSafeHwnd()) 
        ReleaseCapture(); 
 
	ShowWindow( SW_HIDE ); 
} 
 
void CTitleTip::OnMouseMove(UINT nFlags, CPoint point)  
{ 
    if (!m_rectHover.PtInRect(point))  
    { 
        Hide(); 
         
        // Forward the message 
        ClientToScreen( &point ); 
        CWnd *pWnd = WindowFromPoint( point ); 
        if ( pWnd == this )  
            pWnd = m_pParentWnd; 
         
        int hittest = (int)pWnd->SendMessage(WM_NCHITTEST,0,MAKELONG(point.x,point.y)); 
         
        if (hittest == HTCLIENT) { 
            pWnd->ScreenToClient( &point ); 
            pWnd->PostMessage( WM_MOUSEMOVE, nFlags, MAKELONG(point.x,point.y) ); 
        } else { 
            pWnd->PostMessage( WM_NCMOUSEMOVE, hittest, MAKELONG(point.x,point.y) ); 
        } 
    } 
} 
 
BOOL CTitleTip::PreTranslateMessage(MSG* pMsg)  
{ 
    // Used to qualify WM_LBUTTONDOWN messages as double-clicks 
    DWORD dwTick=0; 
    BOOL bDoubleClick=FALSE; 
 
    CWnd *pWnd; 
	int hittest; 
	switch (pMsg->message) 
	{ 
	case WM_LBUTTONDOWN: 
       // Get tick count since last LButtonDown 
        dwTick = GetTickCount(); 
        bDoubleClick = ((dwTick - m_dwLastLButtonDown) <= m_dwDblClickMsecs); 
        m_dwLastLButtonDown = dwTick; 
        // NOTE: DO NOT ADD break; STATEMENT HERE! Let code fall through 
 
	case WM_RBUTTONDOWN: 
	case WM_MBUTTONDOWN: 
		{ 
		POINTS pts = MAKEPOINTS( pMsg->lParam ); 
		POINT  point; 
		point.x = pts.x; 
		point.y = pts.y; 
 
		ClientToScreen( &point ); 
        Hide(); 
 
		pWnd = WindowFromPoint( point ); 
		if (!pWnd) 
			return CWnd::PreTranslateMessage(pMsg); 
 
		if( pWnd->GetSafeHwnd() == GetSafeHwnd())  
			pWnd = m_pParentWnd; 
 
		hittest = (int)pWnd->SendMessage(WM_NCHITTEST,0,MAKELONG(point.x,point.y)); 
 
		if (hittest == HTCLIENT)  
		{ 
			pWnd->ScreenToClient( &point ); 
			pMsg->lParam = MAKELONG(point.x,point.y); 
		} 
		else  
		{ 
			switch (pMsg->message) { 
			case WM_LBUTTONDOWN:  
				pMsg->message = WM_NCLBUTTONDOWN; 
				break; 
			case WM_RBUTTONDOWN:  
				pMsg->message = WM_NCRBUTTONDOWN; 
				break; 
			case WM_MBUTTONDOWN:  
				pMsg->message = WM_NCMBUTTONDOWN; 
				break; 
			} 
			pMsg->wParam = hittest; 
			pMsg->lParam = MAKELONG(point.x,point.y); 
		} 
 
 
        // If this is the 2nd WM_LBUTTONDOWN in x milliseconds, 
        // post a WM_LBUTTONDBLCLK message instead of a single click. 
        pWnd->PostMessage(  bDoubleClick ? WM_LBUTTONDBLCLK : pMsg->message, 
                            pMsg->wParam, 
                            pMsg->lParam); 
		return TRUE; 
		} 
		 
	case WM_KEYDOWN: 
	case WM_SYSKEYDOWN: 
        Hide(); 
		m_pParentWnd->PostMessage( pMsg->message, pMsg->wParam, pMsg->lParam ); 
		return TRUE; 
	} 
 
	if( GetFocus() == NULL ) 
	{ 
        Hide(); 
		return TRUE; 
	} 
 
	return CWnd::PreTranslateMessage(pMsg); 
} 
 
#endif // GRIDCONTROL_NO_TITLETIPS