Click here to Skip to main content
15,884,039 members
Articles / Desktop Programming / MFC

ClassLib, A C++ class library

Rate me:
Please Sign up or sign in to vote.
4.80/5 (32 votes)
25 May 2005CPOL8 min read 399.4K   11.5K   141  
C++ class library.
//
// infocontrol.cpp
//
// (C) Copyright 2000 Jan van den Baard.
//     All Rights Reserved.
//

#include "infocontrol.h"
#include "../../gdi/paintdc.h"
#include "../../gdi/bufferdc.h"
#include "../../gdi/getdc.h"
#include "../../gdi/selector.h"
#include "../../coords/rect.h"
#include "../../strings/string.h"

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

// Alignment flags.
#define TF_CENTER		(1<<0)	// Center text.
#define TF_RIGHT		(1<<1)	// Right align text.

// Font flags.
#define TF_BOLD			(1<<2)	// Add bold style.
#define TF_ITALIC		(1<<3)	// Add italic style.
#define TF_UNDERLINE		(1<<4)	// Add underline style.
#define TF_STYLEMASK		( TF_BOLD | TF_ITALIC | TF_UNDERLINE )

// Misc. flags.
#define TF_NOCOLOR		(1<<5)	// Do not change colors.

// Constructor. Defaults the data.
ClsInfoControl::ClsInfoControl()
{
	// Setup default values.
	m_bVertCenter = TRUE;

	// Setup default background color.
	m_crBkColor = ::GetSysColor( COLOR_BTNFACE );
}

// Destructor. Cleans up...
ClsInfoControl::~ClsInfoControl()
{
	// Destroy fonts in the array.
	for ( int i = 0; i < m_aFonts.GetSize(); i++ )
	{
		// Valid?
		if ( m_aFonts[ i ].m_hFont )
			// Destroy it.
			::DeleteObject( m_aFonts[ i ].m_hFont );
	}
}

// Compute the total height of the text.
int ClsInfoControl::TotalHeight( ClsDC *pDC, LPCTSTR pszText )
{
	_ASSERT_VALID( GetSafeHWND() ); // Must be valid.

	int		nNumLines = 1;
	ClsSize		size;
	ClsString	str;

	// If there is no input we use the control text.
	if ( pszText == NULL )
	{
		str = m_hWnd;
		pszText = str;
	}

	// Setup the font.
	ClsFont font;
	GetFont( font );
	ClsSelector sel( pDC, font );

	// Count the number of lines in the text.
	while ( *pszText )
	{
		if ( *pszText == _T( '\n' ))
			nNumLines++;
		pszText = _tcsinc( pszText );
	}

	// Compute the height of  a single line.
	size = pDC->GetTextExtent( _T( " " ), 1 );
	
	// Return the total height.
	return ( int )( nNumLines * size.CY()) + 4;
}

// Compute the total width of the text.
int ClsInfoControl::TotalWidth( ClsDC *pDC, LPCTSTR pszText )
{
	_ASSERT_VALID( GetSafeHWND() ); // Must be valid.

	int		nWidth = 0, nLineWidth = 0;
	DWORD		dwFlags = 0;
	ClsString	str;

	// Use the control text if no text
	// was given.
	if ( pszText == NULL )
	{
		str = m_hWnd;
		pszText = str;
	}

	// Snapshot the DC.
	int nSDC = pDC->SaveDC();

	// Get the font to use.
	GetFont( m_cfUseFont );
	m_hInitFont = m_cfUseFont;

	// Valid font?
	if ( m_hInitFont == NULL )
	{
		// Use the system GUI font.
		m_hInitFont = ( HFONT )::GetStockObject( DEFAULT_GUI_FONT );
		m_cfUseFont.Attach( m_hInitFont, FALSE );
	}

	// Setup the font.
	HFONT hOrigFont = ( HFONT )pDC->SelectObject( m_cfUseFont );

	// Iterate text.
	while ( *pszText )
	{
		// Evaluate character.
		switch ( *pszText )
		{
			case	_T( '\33' ):
				// Command sequence.
				pszText = _tcsinc( pszText );
				pszText = ParseCommandSequence( pDC, pszText, dwFlags );
				break;

			default:
				// Reset counter.
				int i = 0;
				LPCTSTR pszTmp = pszText;

				// Count the printable characters.
				while ( *pszTmp && ( *pszTmp != _T( '\33' )) && ( *pszTmp != _T( '\n' ))) { pszTmp = _tcsinc( pszTmp ); i++; }

				// Any printable characters?
				if ( i )
				{
					// Determine their width.
					ClsSize size = pDC->GetTextExtent( pszText, i );
					nLineWidth += size.CX();
				}

				// Newline or end of text?
				if ( *pszTmp == _T( '\n' ) || pszTmp == _T( '\0' ))
				{
					// Is it wider than the previous ones?
					if ( nLineWidth > nWidth )
						nWidth = nLineWidth;

					// Continue if this is a newline.
					if ( *pszTmp == _T( '\n' ))
						pszTmp = _tcsinc( pszTmp );
					
					// Reset line width.
					nLineWidth = 0;
				}

				// Adjust pointer.
				pszText = pszTmp;
				break;
		}
	}

	// Make sure we got the last line aswell.
	if ( nLineWidth > nWidth )
		nWidth = nLineWidth;
	
	// Restore the original font.
	pDC->SelectObject( hOrigFont );

	// Detach the used font.
	m_cfUseFont.Detach();

	// Restore the device context.
	pDC->RestoreDC( nSDC );

	// Return the width.
	return nWidth + 4;
}

// Get the digits from the text.
LPCTSTR ClsInfoControl::GetDigits( LPCTSTR pszText, int& nNum )
{
	TCHAR		tcNumber[ 4 ];
	int		i = 0;

	// End of the string?
	if ( *pszText == _T( '\0' ))
	{
		// Set the number to 0.
		nNum = 0;
		return pszText;
	}

	// Scan for digits.
	while ( _istdigit( *pszText ) && i < 3 )
	{
		// Store the number in the conversion string.
		tcNumber[ i ] = *pszText;
		pszText = _tcsinc( pszText );
		i++;
	}

	// 0-terminate the number string.
	tcNumber[ i ] = '\0';

	// Convert the string to an integer.
	nNum = ( int )_ttoi( tcNumber );

	// Return the current string position.
	return pszText;
}

// Change the current font style.
BOOL ClsInfoControl::ChangeFontStyle( ClsDC *pDC, DWORD dwFlags )
{
	// No styles? If there are no styles specified
	// we use the original font.
	if (( dwFlags & TF_STYLEMASK ) == 0 )
	{
		// Setup input font if necessary.
		if ( m_cfUseFont != m_hInitFont )
		{
			// Set it up.
			m_cfUseFont.Attach( m_hInitFont, FALSE );
			pDC->SelectObject( m_hInitFont );
		}
		return TRUE;
	}

	// First we look if we have created such a font
	// already.
	for ( int i = 0; i < m_aFonts.GetSize(); i++ )
	{
		// Is this the one?
		if ( m_aFonts[ i ].m_dwStyles == ( dwFlags & TF_STYLEMASK ))
		{
			// Did it really change?
			if ( m_cfUseFont != m_aFonts[ i ].m_hFont )
			{
				// Set it up.
				m_cfUseFont.Attach( m_aFonts[ i ].m_hFont, FALSE );
				pDC->SelectObject( m_aFonts[ i ].m_hFont );
			}
			return TRUE;
		}
	}

	// Get the current font.
	ClsFont *pFont = pDC->GetCurrentFont();

	// Valid?
	if ( pFont )
	{
		// Got font information.
		LOGFONT		lf;
		if ( pFont->GetLogFont( &lf ))
		{
			// Change the style accoording to the flags.
			lf.lfItalic	    = ( BYTE )( dwFlags & TF_ITALIC    ? TRUE : FALSE );
			lf.lfUnderline	    = ( BYTE )( dwFlags & TF_UNDERLINE ? TRUE : FALSE );
			lf.lfWeight	    = dwFlags & TF_BOLD ? FW_BOLD : FW_NORMAL;

			// Setup data structure.
			FontData fd;
			fd.m_dwStyles = ( dwFlags & TF_STYLEMASK );
			fd.m_hFont    = ::CreateFontIndirect( &lf );

			// Font created OK?
			if ( fd.m_hFont )
			{
				// Add it to the array.
				m_aFonts.Add( fd );

				// Setup new font.
				m_cfUseFont.Attach( fd.m_hFont, FALSE );
				pDC->SelectObject( fd.m_hFont );
				return TRUE;
			}
		}
	}
	return FALSE;
}

// Parse a command sequence.
LPCTSTR ClsInfoControl::ParseCommandSequence( ClsDC *pDC, LPCTSTR pszText, DWORD& dwFlags )
{
	int	r = 0, g = 0, b = 0;
	DWORD	dwOldFlags = dwFlags & ( TF_BOLD | TF_ITALIC | TF_UNDERLINE );

	// Check command character.
	switch ( *pszText )
	{
		case	_T( '\0' ):
			// End of string...
			break;

		case	_T( 'c' ):
			// Center
			dwFlags &= ~TF_RIGHT;
			dwFlags |= TF_CENTER;
			pszText = _tcsinc( pszText );
			break;

		case	_T( 'r' ):
			// Right
			dwFlags &= ~TF_CENTER;
			dwFlags |= TF_RIGHT;
			pszText = _tcsinc( pszText );
			break;
		
		case	_T( 'l' ):
			// Left
			dwFlags &= ~( TF_RIGHT | TF_CENTER );
			pszText = _tcsinc( pszText );
			break;

		case	_T( 't' ):
			// Text color RGB sequence.
			pszText = _tcsinc( pszText );
			pszText = GetDigits( pszText, r );
			if ( *pszText == _T( '\0' )) break;
			pszText = _tcsinc( pszText );
			pszText = GetDigits( pszText, g );
			if ( *pszText == _T( '\0' )) break;
			pszText = _tcsinc( pszText );
			pszText = GetDigits( pszText, b );

			// Setup the color.
			if ( ! ( dwFlags & TF_NOCOLOR ))
				pDC->SetTextColor( RGB( r, g, b ));
			r = g = b = 0;
			break;


		case	_T( 'b' ):
			// Background color RGB sequence.
			pszText = _tcsinc( pszText );
			pszText = GetDigits( pszText, r );
			if ( *pszText == _T( '\0' )) break;
			pszText = _tcsinc( pszText );
			pszText = GetDigits( pszText, g );
			if ( *pszText == _T( '\0' )) break;
			pszText = _tcsinc( pszText );
			pszText = GetDigits( pszText, b );

			// Setup the color.
			if ( ! ( dwFlags & TF_NOCOLOR ))
			{
				pDC->SetBkMode( OPAQUE );
				pDC->SetBkColor( RGB( r, g, b ));
			}
			r = g = b = 0;
			break;

		case	_T( 's' ):
			// Transparent mode.
			pszText = _tcsinc( pszText );
			if ( ! ( dwFlags & TF_NOCOLOR ))
				pDC->SetBkMode( TRANSPARENT );
			break;

		case	_T( 'n' ):
			// Reset styles.
			pszText = _tcsinc( pszText );
			dwFlags &= ~( TF_BOLD | TF_ITALIC | TF_UNDERLINE );
			break;

		case	_T( '+' ):
			pszText = _tcsinc( pszText );
			// Add which mode?
			switch ( *pszText )
			{
				case	_T( '\0' ):
					break;

				case	_T( 'b' ):
					// Bold
					pszText = _tcsinc( pszText );
					dwFlags |= TF_BOLD;
					break;

				case	_T( 'i' ):
					// Italic
					pszText = _tcsinc( pszText );
					dwFlags |= TF_ITALIC;
					break;

				case	_T( 'u' ):
					// Underline
					pszText = _tcsinc( pszText );
					dwFlags |= TF_UNDERLINE;
					break;
			}
			break;

		case	_T( '-' ):
			pszText = _tcsinc( pszText );
			// Add which mode?
			switch ( *pszText )
			{
				case	_T( '\0' ):
					break;

				case	_T( 'b' ):
					// Bold
					pszText = _tcsinc( pszText );
					dwFlags &= ~TF_BOLD;
					break;

				case	_T( 'i' ):
					// Italic
					pszText = _tcsinc( pszText );
					dwFlags &= ~TF_ITALIC;
					break;

				case	_T( 'u' ):
					// Underline
					pszText = _tcsinc( pszText );
					dwFlags &= ~TF_UNDERLINE;
					break;
			}
			break;

		case	_T( 'g' ):
			pszText = _tcsinc( pszText );
			switch ( *pszText )
			{
				case	_T( '\0' ):
					break;

				case	_T( 't' ):
					pszText = _tcsinc( pszText );
					pszText = GetDigits( pszText, r );
		                        if ( ! ( dwFlags & TF_NOCOLOR ))
						pDC->SetTextColor( ::GetSysColor( r ));
                                        r = 0;
                                        break;

				case    _T( 'b' ):
					pszText = _tcsinc( pszText );
					pszText = GetDigits( pszText, r );
					if ( ! ( dwFlags & TF_NOCOLOR ))
					{
						pDC->SetBkMode( OPAQUE );
						pDC->SetBkColor( ::GetSysColor( r ));
					}
                                        r = 0;
                                        break;
                        }
                        break;
	}

	// Change font if necessary.
	if (( dwFlags & ( TF_BOLD | TF_ITALIC | TF_UNDERLINE )) != dwOldFlags )
		ChangeFontStyle( pDC, dwFlags );

	// Return text pointer.
	return pszText;
}

// Compute horizontal position to render at.
int ClsInfoControl::XPos( ClsDC *pDC, LPCTSTR pszText, ClsRect *pDomain, DWORD dwFlags )
{
	int		nXPos, nWidth = 0;

	// Do not change colors.
	dwFlags |= TF_NOCOLOR;

	// Save the current font style flags so we
	// reset it after the text processing is done.
	DWORD dwCurrFlags = ( dwFlags & TF_STYLEMASK );
	
	// Iterate string.
	while ( *pszText && ( *pszText != _T( '\n' )))
	{
		// Evaluate character.
		switch ( *pszText )
		{
			case	_T( '\33' ):
				pszText = _tcsinc( pszText );
				pszText = ParseCommandSequence( pDC, pszText, dwFlags );
				break;

			default:
				// Get base.
				int i = 0;
				LPCTSTR pszTmp = pszText;

				// Count printable characters.
				while ( *pszTmp && ( *pszTmp != _T( '\33' )) && ( *pszTmp != _T( '\n' ))) { pszTmp = _tcsinc( pszTmp ); i++; }

				// Any printable characters?
				if ( i )
				{
					// Get size of the printable characters.
					ClsSize size = pDC->GetTextExtent( pszText, i );
					nWidth += size.CX();

					// Adjust text pointer.
					pszText = pszTmp;
				}
				break;
		}
	}

	// Reset font styles.
	ChangeFontStyle( pDC, dwCurrFlags );

	// We can change the colors again...
	dwFlags &= ~TF_NOCOLOR;

	// Compute horizontal offset.
	if (	  dwFlags & TF_CENTER ) nXPos = pDomain->Left() + (( pDomain->Width() / 2 ) - ( nWidth / 2 ));
	else if ( dwFlags & TF_RIGHT  ) nXPos = pDomain->Right() - nWidth - 2;
	else				nXPos = pDomain->Left();

	// Make sure we do not pass the left edge.
	if ( nXPos < pDomain->Left()) nXPos = pDomain->Left();

	// Return the value.
	return nXPos;
}

// Render the info text.
void ClsInfoControl::RenderInfoText( ClsDC *pDC, LPCTSTR pszText, ClsRect *pDomain )
{
	int		nXPos, nYPos = pDomain->Top(), nNumLines;
	DWORD		dwFlags = 0;

	// Save device context.
	int nSDC = pDC->SaveDC();

	// Get the font to use.
	GetFont( m_cfUseFont );
	m_hInitFont = m_cfUseFont;

	// Valid font?
	if ( m_hInitFont == NULL )
	{
		// Use the system GUI font.
		m_hInitFont = ( HFONT )::GetStockObject( DEFAULT_GUI_FONT );
		m_cfUseFont.Attach( m_hInitFont, FALSE );
	}

	// Setup the font.
	HFONT hOrigFont = ( HFONT )pDC->SelectObject( m_cfUseFont );

	// Center vertically?
	if ( m_bVertCenter == TRUE )
	{
		// Get the height of the text.
		nNumLines = TotalHeight( pDC, pszText );

		// Is it smaller than the domain?
		if ( nNumLines < pDomain->Height())
			// Yes. Adjust the top value so that it
			// get's centered.
			nYPos = ( ::GetSystemMetrics( SM_CYEDGE ) * 2 ) + (( pDomain->Height() - nNumLines ) / 2 );
	}

	// Make sure we stay in range...
	if ( nYPos < pDomain->Top()) nYPos = pDomain->Top();

	// Get the height of a single line.
	ClsSize size = pDC->GetTextExtent( _T( " " ), 1 );

	// Any room at all?
	if (( nYPos + size.CY()) < pDomain->Bottom())
	{
		// Start rendering transparent.
		pDC->SetBkMode( TRANSPARENT );

		// Default text color.
		pDC->SetTextColor( ::GetSysColor( COLOR_BTNTEXT ));

		// Find out the starting position.
		nXPos = XPos( pDC, pszText, pDomain, dwFlags );

		// Setup break flag.
		BOOL bContinue = TRUE;

		// Iterate text.
		while ( *pszText && bContinue )
		{
			// Evaluate character.
			switch ( *pszText )
			{
				case	_T( '\33' ):
					// Command sequence
					pszText = _tcsinc( pszText );
					pszText = ParseCommandSequence( pDC, pszText, dwFlags );
					break;

				case	_T( '\n' ):
					{
					// Newline.
					pszText = _tcsinc( pszText );

					// New position.
					nXPos = XPos( pDC, pszText, pDomain, dwFlags );

					// Are we passing the bottom of the
					// rendering area?
					nYPos += size.CY();
					if ( nYPos + size.CY() > pDomain->Bottom())
					{
						// Stop parsing loop.
						bContinue = FALSE;
						continue;
					}
					}
					break;

				default:
					// Reset counter.
					int i = 0, nNumC;
					LPCTSTR pszTmp = pszText;

					// Count the printable characters.
					while ( *pszTmp && ( *pszTmp != _T( '\33' )) && ( *pszTmp != _T( '\n' ))) { pszTmp = _tcsinc( pszTmp ); i++; }

					// Any printable characters?
					if ( i )
					{
						// How many can we print?
						size = pDC->GetTextExtentExPoint( pszText, i, max( pDomain->Left(), pDomain->Width() - nXPos ), &nNumC, NULL );

						if ( nNumC )
						{
							// Output text.
							pDC->TextOut( nXPos, nYPos, pszText, nNumC );

							// Adjust x position.
							nXPos += size.CX();
						}

						// Adjust pointer.
						pszText = pszTmp;
					}
					break;
			}
		}
	}

	// Restore the original font.
	pDC->SelectObject( hOrigFont );

	// Detach the used font.
	m_cfUseFont.Detach();

	// Reset the DC.
	pDC->RestoreDC( nSDC );
}

// Window procedure override.
LRESULT ClsInfoControl::WindowProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	// The one we need?
	if ( uMsg == WM_PRINTCLIENT )
	{
		// Render what?
		if ( lParam & PRF_ERASEBKGND ) SendMessage( WM_ERASEBKGND, wParam );
		if ( lParam & PRF_CLIENT     ) SendMessage( WM_PAINT, wParam );
		return 0;
	}
	// Base class.
	return ClsStatic::WindowProc( uMsg, wParam, lParam );
}

// Paint the control.
void ClsInfoControl::PaintControl( ClsDC *pDC )
{
	// Get the painting area.	
	ClsRect	rcRect = GetClientRect();

	// Any room to render in?
	if ( ! rcRect.IsEmpty())
	{
		// Create a buffer DC with the size of the
		// client rectangle.
		ClsBufferDC dc( *pDC, rcRect );
		
		// Fill it.
		ClsBrush brush( m_crBkColor );
		dc.FillRect( rcRect, &brush );

		// Get the window text.
		ClsString wText( m_hWnd );

		// Adjust text area.
		if ( GetStyle() & SS_SUNKEN )
			rcRect.Deflate( 2, 2 );

		// Any text?
		if ( wText.GetStringLength())
			// Render...
			RenderInfoText( &dc, wText, &rcRect );
	}
}

// WM_PAINT message handler.
LRESULT ClsInfoControl::OnPaint( ClsDC *pDC )
{
	// Input DC?
	if ( pDC ) PaintControl( pDC );
	else
	{
		ClsPaintDC dc( this );
		PaintControl( &dc );
	}
	return 0;
}

// For the layout engine.
BOOL ClsInfoControl::OnGetMinSize( ClsSize& szMinSize )
{
	// Sunken edge?
	DWORD dwStyle = GetStyle();
        if ( dwStyle & SS_SUNKEN ) 
	{
		szMinSize.CX() += ::GetSystemMetrics( SM_CXEDGE ) * 4;
		szMinSize.CY() += ::GetSystemMetrics( SM_CYEDGE ) * 4;
	}

	// Any text?
	ClsString str( GetSafeHWND());
	if ( str.GetStringLength()) 
	{
		// Get a device context.
		ClsGetDC dc( this );

		// Add text dimensions.
		szMinSize.CX() += TotalWidth( &dc, str );
		szMinSize.CY() += TotalHeight( &dc, str );
	}
	return TRUE;
}

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Netherlands Netherlands
I have been programming for a hobby since 1985. I have started programming on the C= 64. After that I migrated to the C= Amiga which I traded in for a PC back in 1997 I believe. Back in 2000 I decided to lose a hobby and start developing software for a living.

Currently I am working mainly in developing software for building security and access control systems.

Comments and Discussions