Click here to Skip to main content
15,896,606 members
Articles / Desktop Programming / MFC

A Tree List Control

Rate me:
Please Sign up or sign in to vote.
4.87/5 (83 votes)
19 Sep 2002 1.2M   23.8K   173  
A Tree List Control
// TreeListTipCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "TreeListHeaderCtrl.h"
#include "TreeListTipCtrl.h"
#include "TreeListStaticCtrl.h"
#include "TreeListEditCtrl.h"
#include "TreeListComboCtrl.h"
#include "TreeListCtrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

static lpfnUpdateLayeredWindow g_lpfnUpdateLayeredWindow = NULL;
static lpfnSetLayeredWindowAttributes g_lpfnSetLayeredWindowAttributes = NULL;

BOOL InitLayeredWindows()
{
	if( g_lpfnUpdateLayeredWindow == NULL || g_lpfnSetLayeredWindowAttributes == NULL )
	{
		HMODULE hUser32 = GetModuleHandle(_T("USER32.DLL"));

		g_lpfnUpdateLayeredWindow =	(lpfnUpdateLayeredWindow)GetProcAddress( hUser32, _T("UpdateLayeredWindow") );
		g_lpfnSetLayeredWindowAttributes = (lpfnSetLayeredWindowAttributes)GetProcAddress( hUser32, _T("SetLayeredWindowAttributes") );

		if( g_lpfnUpdateLayeredWindow == NULL || g_lpfnSetLayeredWindowAttributes == NULL )
			return FALSE;
	}

	return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CTreeListTipCtrl

CTreeListTipCtrl::CTreeListTipCtrl() : 
	m_dwFadeStep( 10L ), 
	m_dwFadeInTime( 500L ), 
	m_dwFadeOutTime( 200L ),
	m_bLayeredWindows( FALSE ),
	m_nAlpha( 0 ),
	m_rcItem( 0, 0, 0, 0 ),
	m_rcTip( 0, 0, 0, 0 ),
	m_uFormat( 0 ),
	m_pTreeListCtrl( NULL )
{
	// construction
	WNDCLASS wndcls;
	HINSTANCE hInst = AfxGetInstanceHandle();

	if( ::GetClassInfo( hInst, TREELISTTIPCTRL_CLASSNAME, &wndcls ) == FALSE )
	{
		wndcls.style =  CS_DBLCLKS ;
		wndcls.lpfnWndProc		= ::DefWindowProc;
		wndcls.cbClsExtra		= 0;
		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	= TREELISTTIPCTRL_CLASSNAME;

		if( !AfxRegisterClass(&wndcls) )
			AfxThrowResourceException();
	}

	// for layer windows
	m_bLayeredWindows = InitLayeredWindows();
}

CTreeListTipCtrl::~CTreeListTipCtrl()
{
	// deconstruction
	m_Font.DeleteObject();
}

BEGIN_MESSAGE_MAP(CTreeListTipCtrl, CWnd)
	//{{AFX_MSG_MAP(CTreeListTipCtrl)
	ON_WM_PAINT()
	ON_WM_TIMER()
	ON_WM_MOUSEACTIVATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTreeListTipCtrl message handlers
BOOL CTreeListTipCtrl::Create( CTreeListCtrl* pTreeListCtrl )
{
	// create tip
	ASSERT_VALID( pTreeListCtrl );

	m_pTreeListCtrl = pTreeListCtrl;

	DWORD dwExStyle = WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT;
	DWORD dwStyle	= WS_BORDER | WS_POPUP;
	CRect rcRect ( 0, 0, 0, 0 );

	if( m_bLayeredWindows )
		dwExStyle |= WS_EX_LAYERED;

	return CreateEx( dwExStyle, TREELISTTIPCTRL_CLASSNAME, NULL, dwStyle, rcRect, pTreeListCtrl, 0, NULL ); 
}

BOOL CTreeListTipCtrl::Show( CRect rcItem, LPCTSTR lpszText, UINT uFormat )
{
	// show tip
	BOOL bResult = FALSE;

	ASSERT(::IsWindow(GetSafeHwnd()));

	if(	rcItem.IsRectEmpty() )// || GetFocus() == NULL ) 
	{
		Hide();
		return FALSE;
	}

	if( m_rcItem != rcItem )// || !IsWindowVisible() )
	{
		m_rcItem	= rcItem;

		CClientDC dc(this);
		CFont *pOldFont = dc.SelectObject( &m_Font );
		CSize size = dc.GetTextExtent(lpszText, _tcslen(lpszText));
		dc.SelectObject(pOldFont);

		if( size.cx + 5 > rcItem.Width() )
		{
			m_strText = lpszText;
			m_uFormat = uFormat;

			m_rcTip		= rcItem;
			m_pTreeListCtrl->ClientToScreen(m_rcTip);
				
			m_rcTip.left -= 1;
			m_rcTip.top -= 1;
			if( uFormat&DT_RIGHT )
			{
				m_rcTip.left = m_rcTip.right - size.cx - 6;
			}
			else if( uFormat&DT_CENTER )
			{
				int nFix = ( size.cx - m_rcItem.Width() ) / 2;
				m_rcTip.left = m_rcTip.left - nFix - 3;
				m_rcTip.right = m_rcTip.right + nFix + 2;
			}
			else // if( uFormat&DT_LEFT )
			{
				m_rcTip.right = m_rcTip.left + size.cx + 5;
			}

			Invalidate();
			UpdateWindow();

			KillTimer( 2 );
			SetWindowPos( &wndTop, m_rcTip.left, m_rcTip.top, m_rcTip.Width(), m_rcTip.Height(),
				SWP_NOOWNERZORDER|SWP_SHOWWINDOW|SWP_NOACTIVATE	);

			if(m_bLayeredWindows)
			{
				if(m_nAlpha < 255)
				{
					KillTimer( 2 );
					SetTimer( 1, m_dwFadeInTime/(255/m_dwFadeStep), NULL );
				}
				g_lpfnSetLayeredWindowAttributes( GetSafeHwnd(), RGB(0xFF, 0, 0xFF), m_nAlpha, ULW_ALPHA );
			}

			bResult = TRUE;
		}
		else
		{
			Hide();
			bResult = FALSE;
		}
	}
	else
	{
		bResult = TRUE;
	}

	return bResult;

}

void CTreeListTipCtrl::Hide()
{
	// hide tip
	ASSERT(::IsWindow(GetSafeHwnd()));

	if ( IsWindowVisible() )
	{
		if( m_bLayeredWindows )
		{
			KillTimer( 1 );
			SetTimer( 2, m_dwFadeOutTime/(255/m_dwFadeStep), NULL );
		}
		else
		{
			ShowWindow( SW_HIDE );
		}
	}
}

BOOL CTreeListTipCtrl::PreTranslateMessage(MSG* pMsg) 
{
	// post message of mouse and key to parent window
	DWORD	dwTick = 0;
	BOOL	bDoubleClick = FALSE;

/*
	CWnd*	pWnd;
	int		nHitTest;
	switch( pMsg->message )
	{
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_LBUTTONDBLCLK:

	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_RBUTTONDBLCLK:

	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_MBUTTONDBLCLK:

	case WM_MOUSEMOVE:

	case WM_NCLBUTTONDOWN:
	case WM_NCLBUTTONUP:
	case WM_NCLBUTTONDBLCLK:

	case WM_NCRBUTTONDOWN:
	case WM_NCRBUTTONUP:
	case WM_NCRBUTTONDBLCLK:

	case WM_NCMBUTTONDOWN:
	case WM_NCMBUTTONUP:
	case WM_NCMBUTTONDBLCLK:

	case WM_NCMOUSEMOVE:

		POINTS points = MAKEPOINTS( pMsg->lParam );
		POINT pt;
		pt.x = points.x;
		pt.y = points.y;

		ClientToScreen( &pt );
		pWnd = WindowFromPoint( pt );

		if( pWnd == this )
			pWnd = m_pTreeListCtrl;

		if( pWnd != NULL )
		{
			nHitTest = (int)pWnd->SendMessage( WM_NCHITTEST, 0, MAKELONG( pt.x, pt.y ) );

			if( nHitTest == HTCLIENT )
			{
				switch( pMsg->message )
				{
				case WM_NCLBUTTONDOWN:		pMsg->message = WM_LBUTTONDOWN;		break;
				case WM_NCLBUTTONUP:		pMsg->message = WM_LBUTTONUP;		break;
				case WM_NCLBUTTONDBLCLK:	pMsg->message = WM_LBUTTONDBLCLK;	break;

				case WM_NCRBUTTONDOWN:		pMsg->message = WM_RBUTTONDOWN;		break;
				case WM_NCRBUTTONUP:		pMsg->message = WM_RBUTTONUP;		break;
				case WM_NCRBUTTONDBLCLK:	pMsg->message = WM_RBUTTONDBLCLK;	break;

				case WM_NCMBUTTONDOWN:		pMsg->message = WM_MBUTTONDOWN;		break;
				case WM_NCMBUTTONUP:		pMsg->message = WM_MBUTTONUP;		break;
				case WM_NCMBUTTONDBLCLK:	pMsg->message = WM_MBUTTONDBLCLK;	break;

				case WM_NCMOUSEMOVE:		pMsg->message = WM_MOUSEMOVE;		break;

				default:	break;
				}
			}
			else
			{
				switch( pMsg->message )
				{
				case WM_LBUTTONDOWN:		pMsg->message = WM_NCLBUTTONDOWN;	break;
				case WM_LBUTTONUP:			pMsg->message = WM_NCLBUTTONUP;		break;
				case WM_LBUTTONDBLCLK:		pMsg->message = WM_NCLBUTTONDBLCLK;	break;

				case WM_RBUTTONDOWN:		pMsg->message = WM_NCRBUTTONDOWN;	break;
				case WM_RBUTTONUP:			pMsg->message = WM_NCRBUTTONUP;		break;
				case WM_RBUTTONDBLCLK:		pMsg->message = WM_NCRBUTTONDBLCLK;	break;

				case WM_MBUTTONDOWN:		pMsg->message = WM_NCMBUTTONDOWN;	break;
				case WM_MBUTTONUP:			pMsg->message = WM_NCMBUTTONUP;		break;
				case WM_MBUTTONDBLCLK:		pMsg->message = WM_NCMBUTTONDBLCLK;	break;
	
				case WM_MOUSEMOVE:			pMsg->message = WM_NCMOUSEMOVE;		break;
	
				default:	break;
				}
			}

			pWnd->ScreenToClient( &pt );
			pMsg->lParam = MAKELONG( pt.x, pt.y );
			pWnd->SendMessage( pMsg->message, pMsg->wParam, pMsg->lParam );
			return TRUE;
		}
		break;

	default:
		;
	}
*/
	return CWnd::PreTranslateMessage(pMsg);
}

//////////////////////////////////////////////////////////////////////////////////////////////////
void CTreeListTipCtrl::OnPaint() 
{
	// paint the control
	CPaintDC dc(this); // device context for painting

	CRect rcText;
	rcText.SetRect( 0, 0, m_rcTip.Width(), m_rcTip.Height() );
	dc.FillSolidRect( rcText, 0xF0FFFF );

	CFont* pOldFont = dc.SelectObject( &m_Font );
	int nBkMode = dc.SetBkMode( TRANSPARENT );
	rcText.DeflateRect( 2, 0, 3, 0 );
	dc.DrawText( m_strText, rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE );
	dc.SetBkMode( nBkMode );
	dc.SelectObject( pOldFont );
}

void CTreeListTipCtrl::OnTimer(UINT nIDEvent) 
{
	// fade in/out timer
	switch( nIDEvent )
	{
	case 1:	// fade in
		ASSERT( m_bLayeredWindows == TRUE );

		if( m_nAlpha < 255 )
		{
			Invalidate();
			UpdateWindow();
			m_nAlpha += m_dwFadeStep;
			if( m_nAlpha >= 255 )
			{
				m_nAlpha = 255;
				KillTimer( 1 );
			}
			g_lpfnSetLayeredWindowAttributes( GetSafeHwnd(), 0xFF00FF, m_nAlpha, ULW_ALPHA );
		}
		else
		{
			m_nAlpha = 255;
			KillTimer( 1 );
		}

		break;
	
	case 2: // fade out
		ASSERT( m_bLayeredWindows == TRUE );

		if( m_nAlpha > 0 )
		{
			Invalidate();
			UpdateWindow();
			m_nAlpha -= m_dwFadeStep;
			if( m_nAlpha <= 0 )
			{
				m_nAlpha = 0;
				KillTimer( 2 );
				if( IsWindowVisible() )
					ShowWindow( SW_HIDE );
			}
			g_lpfnSetLayeredWindowAttributes( GetSafeHwnd(), 0xFF00FF, m_nAlpha, ULW_ALPHA );
		}
		else 
		{
			m_nAlpha = 0;
			KillTimer( 2 );
			if( IsWindowVisible() )
				ShowWindow( SW_HIDE );
			g_lpfnSetLayeredWindowAttributes( GetSafeHwnd(), 0xFF00FF, m_nAlpha, ULW_ALPHA );
		}

		break;

	default:
		break;
	}

	CWnd::OnTimer(nIDEvent);
}

int CTreeListTipCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message) 
{
	// active a window with mouse click
	// * do NOT write into PreTranslateMessage
	// * because MFC call it first.
	return MA_NOACTIVATE;
}

void CTreeListTipCtrl::SetFont()
{
	// set font with font of parent window
	if( m_pTreeListCtrl == NULL )
		return;

	CFont* pFont = m_pTreeListCtrl->GetFont();
	if( pFont == NULL )
		return;

	LOGFONT lf;
	pFont->GetLogFont(&lf);
	m_Font.DeleteObject();
	m_Font.CreateFontIndirect(&lf);
}

CFont* CTreeListTipCtrl::GetFont()
{
	// get font
	return &m_Font;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
Chief Technology Officer
China China
He is a Visual C++ developer, MCSE
He has been programming in C/C++ for 7 years, Visual C++ with MFC for 5 years and RDBMS: Oracle, MS SQL for 5 years

Comments and Discussions