Click here to Skip to main content
15,895,606 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 402.5K   11.5K   141  
C++ class library.
//
// filedirtree.cpp
//
// (C) Copyright 2000 Jan van den Baard.
//     All Rights Reserved.
//

#include "filedirtree.h"
#include "../../gdi/paintdc.h"
#include "../../gdi/bufferdc.h"
#include "../../gdi/getdc.h"
#include "../../gdi/selector.h"
#include "../../tools/findfile.h"
#include "../../tools/waitcursor.h"
#include "../../collectors/stringarray.h"
#include "../../exceptions/memoryexception.h"

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

// Constructor. Setup defaults.
ClsFileDirTree::ClsFileDirTree()
{
	// Setup defaults.
	m_dwDontShow	 = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_OFFLINE;
	m_bShowFiles	 = TRUE;
	m_bDoubleBuffer  = TRUE;
	m_bSilent	 = FALSE;
	m_strFilter	 = _T( "*.*" );
	m_strLoading	 = _T( "Loading..." );
	m_crLoadingColor = ( COLORREF )-1;
}

// Destructor. Does nothing.
ClsFileDirTree::~ClsFileDirTree()
{
}

// Show the tree.
BOOL ClsFileDirTree::SetupTree( LPCTSTR pszRoot /* = NULL */ )
{
	_ASSERT( GetSafeHWND() ); // Must be valid.

	// Make sure the tree is empty...
	if ( ! m_bSilent ) SetRedraw( FALSE );
	SelectItem( NULL );
	DeleteAllItems();
	if ( ! m_bSilent ) SetRedraw();
	
	// Do we need to set the images?
	if ( GetImageList( TVSIL_NORMAL ) != ClsGetApp()->GetSystemImageList())
		SetImageList( ClsGetApp()->GetSystemImageList(), TVSIL_NORMAL );

	// Make sure we are the LVS_EDITLABELS style is not
	// specified.
	ModifyStyle( LVS_EDITLABELS, 0 );

	// Use a specific root?
	if ( pszRoot && _tcslen( pszRoot ))
	{
		// Copy initial root.
		m_strInitial = pszRoot;

		// Make sure the backslash is present.
		ClsString str = m_strInitial;
		if ( str.GetStringLength() && str[ str.GetStringLength() - 1 ] != _T( '\\' )) str += _T( '\\' );

		// Add the root item.
		HTREEITEM hItem = AddTreeItem( TVI_ROOT, str, FALSE );
		if ( hItem == NULL )
			return FALSE;

		// Does it have any files/dirs?
		if ( HasEntries( hItem ))
		{
			// Add a single empty item.
			if ( AddTreeItem( hItem, _T( "" )) == NULL )
				return FALSE;
		}
		return TRUE;
	}

	// No specific root. Add all logical drives.
	return DisplayDrives();
}

// Turn the showing of files on or off.
void ClsFileDirTree::ShowFiles( BOOL bShow /* = TRUE */ )
{
	// We must have contents.
	if ( GetRoot() == NULL ) 
	{
		// Simply set the value.
		m_bShowFiles = bShow;
		return;
	}

	// Did the mode change?
	if ( bShow != m_bShowFiles )
	{
		// Turn of rendering.
		SetRedraw( FALSE );
		m_bSilent = TRUE;
			
		// Set new value.
		m_bShowFiles = bShow;
		
		// Get current selection.
		ClsString path;
		GetSelectedPath( path );

		// Reset the contents.
		SetupTree( m_strInitial );

		// Select the path.
		SelectPath( path );

		// Re-render.
		m_bSilent = FALSE;
		SetRedraw( TRUE );
		UpdateWindow();
	}
}

// Obtain the full path of the selected entry.
BOOL ClsFileDirTree::GetSelectedPath( ClsString& strPath ) const
{
	_ASSERT( GetSafeHWND() ); // Must be valid.

	// Get info about the selected entry.
	TVITEM tvi;
	tvi.mask = TVIF_STATE;
	tvi.hItem = GetSelection();

	if ( GetItem( tvi ))
	{
		// Obtain it's path.
		strPath = GetPath( tvi.hItem );

		// Is it a drive?
		if ( tvi.lParam )
			return TRUE;

		// Can we get it's attributes?
		DWORD dwAttr = ::GetFileAttributes( strPath );
		if ( dwAttr == 0xFFFFFFFF || ( ! ( dwAttr & FILE_ATTRIBUTE_DIRECTORY )))
		{
			// Does it have a pending backslash?
			if ( strPath[ strPath.GetStringLength() - 1 ] == _T( '\\' )) 
			{
				// Strip it and try again.
				strPath.SetStringLength( strPath.GetStringLength() - 1 );
				if (( dwAttr = ::GetFileAttributes( strPath )) == 0xFFFFFFFF )
					return FALSE;
			}
		}
		return TRUE;
	}
	return FALSE;
}

// Select and display the path.
BOOL ClsFileDirTree::SelectPath( LPCTSTR pszPath )
{
	return SelectPath( ClsString( pszPath ));
}

BOOL ClsFileDirTree::SelectPath( const ClsString& strPath )
{
	_ASSERT( GetSafeHWND() ); // Must be valid.

	// 4KChars max.
	TCHAR	szBuffer[ 4096 ];

	// Setup the structure to obtain the item
	// text.
	TVITEM	tvi;
	tvi.mask	= TVIF_TEXT | TVIF_PARAM;
	tvi.pszText	= szBuffer;

	// We start at the root.
	HTREEITEM hParent = TVI_ROOT;

	// Maximum index into the path.
	int nMax = strPath.GetStringLength() - 1;

	// Setup data.
	int nPos = 0;
	ClsString strComp;
	HTREEITEM hSelected = NULL;

	// Iterate the path to get it's components.
	for ( ;; )
	{
		// Get the index of the next backslash.
		int nPos2 = strPath.Find( _T( '\\' ), nPos );

		// Extract the path component.
		strComp = strPath.Mid( nPos, nPos2 == -1 ? nPos2 : nPos2 - nPos );

		// Obtain the first child item.
		HTREEITEM hChild = GetChild( hParent );
		if ( hChild )
		{
			// Iterate children.
			do
			{
				// Setup structure.
				tvi.hItem      = hChild;
				tvi.cchTextMax = 4096;

				// Obtain item name.
				if ( GetItem( tvi ))
				{
					// Is it a drive name?
					if ( tvi.lParam )
					{
						// Build "real" path string.
						_tcscpy( szBuffer, _T( " :" ));

						// Add drive letter.
						szBuffer[ 0 ] = ( TCHAR )( tvi.lParam );
					}

					// Is this the item we are looking for?
					if ( _tcsicmp( szBuffer, strComp ) == 0 )
					{
						// Expand the item.
						Expand( hChild, TVE_EXPAND );

						// Make this child the new parent.
						hParent = hChild;

						// Were we processing the final
						// part of the path?
						if ( nPos2 == -1 || nPos2 == nMax )
						{
							// Mark the selected item.
							hSelected = hChild;

							// Yes. Select the item.
							SelectItem( hChild );
						}
						break;
					}
					// Get the next child.
					hChild = GetNextSibling( hChild );
				}
				else
					// Failure...
					break;
			} while ( hChild );
		}

		// Did we process the last part of the path?
		if ( nPos2 == -1 || nPos2 == nMax )
		{
			// Did we select an item?
			if ( hSelected == NULL )
			{
				// No. Simply select the parent if it is
				// not the root.
				if ( hParent != TVI_ROOT )
					SelectItem( hParent );
			}
			break;
		}

		// Skip to the next part of the path.
		nPos = nPos2 + 1;
	}
	return TRUE;
}

// Determine the type of the selection
BOOL ClsFileDirTree::TypeOfSelection() const
{
	_ASSERT( GetSafeHWND() ); // Must be valid.

	// Any selection?
	if ( GetSelection())
	{
		// Get the path.
		ClsString strPath;
		if ( GetSelectedPath( strPath ))
		{
			// Determine wether it is a file or a directory.
			DWORD dwAttr = ::GetFileAttributes( strPath );
			if ( dwAttr == 0xFFFFFFFF )
			{
				// Does it have a pending backslash?
				if ( strPath[ strPath.GetStringLength() - 1 ] == _T( '\\' )) 
				{
					// Strip it and try again.
					strPath.SetStringLength( strPath.GetStringLength() - 1 );
					if (( dwAttr = ::GetFileAttributes( strPath )) == 0xFFFFFFFF )
						return STYPE_UNKNOWN;
				}
			}

			// Is it a directory?
			if ( dwAttr & FILE_ATTRIBUTE_DIRECTORY ) return STYPE_DIRECTORY;
			else					 return STYPE_FILE;
		}
	}
	return STYPE_NONE;
}

// Add a treeview item.
HTREEITEM ClsFileDirTree::AddTreeItem( HTREEITEM hParent, LPCTSTR pszPath, BOOL bParse /* = TRUE */, LPCTSTR pszDriveName /* = NULL */ )
{
	TVINSERTSTRUCT tvi;
	SHFILEINFO fi;

	// Get the index of the normal icon.
	if ( ::SHGetFileInfo( pszPath, 0, &fi, sizeof( fi ), SHGFI_ICON | SHGFI_SMALLICON ) == 0 )
		return NULL;
	
	// Store the index.
	tvi.item.iImage = fi.iIcon;
	::DestroyIcon( fi.hIcon );

	// Get the index of the open icon.
	if ( ::SHGetFileInfo( pszPath, 0, &fi, sizeof( fi ), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_OPENICON ) == 0 )
		return NULL;

	// Store the index.
	tvi.item.iSelectedImage = fi.iIcon;
	::DestroyIcon( fi.hIcon );

	// Is the path valid?
	ClsString strPath = pszPath;
	if ( bParse )
	{
		int nLen = strPath.GetStringLength();
		if ( nLen )
		{
			// If the path ends with a back-slash we remove
			// it.
			if ( strPath[ nLen - 1 ] == _T( '\\' ))
				strPath.SetStringLength( nLen - 1 );

			// Now we look for the right-most backslash. If it exists
			// we copy the string right from that back slash leaving us
			// with the last part of the path.
			//
			// If it does not exist we are left with the drive name. In this
			// case we append a backslash to it.
			int nPos = strPath.ReverseFind( _T( '\\' ));
			if ( nPos != -1 ) strPath = strPath.Mid( nPos + 1 );
			else		  strPath += _T( '\\' );
		}
	}

	// Did we get a drive name?
	if ( pszDriveName )
	{
		// Bits 24 through 31 represent the drive letter.
		tvi.item.lParam = strPath[ 0 ];

		// Simply let the drive name be the name
		// shownd to the user.
		strPath = pszDriveName;
	}
	else
		tvi.item.lParam = 0;

	// Setup the rest of the structure.
	tvi.hParent	 = hParent;
	tvi.hInsertAfter = NULL;
	tvi.item.mask	 = TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_PARAM;
	tvi.item.pszText = ( LPTSTR )strPath;

	// Insert the item.
	return InsertItem( tvi );
}

// Add the drive entries.
BOOL ClsFileDirTree::DisplayDrives()
{
	// Delete current contents.
	DeleteAllItems();

	// Get the available logical drives.
	DWORD dwDrives = ::GetLogicalDrives(), dwMask = 1;
	for ( int i = 0; i < 32; i++, dwMask <<= 1 )
	{
		// Drive available?
		if ( dwDrives & dwMask )
		{
			// Create drive string.
			ClsString drive;
			drive.Format( _T( "%c:\\" ), _T( 'A' ) + i );

			// We do not want to see error dialogs...
			DWORD dwOldMode = SetErrorMode( SEM_FAILCRITICALERRORS );

			// Obtain display name.
			SHFILEINFO sfi;
			SHGetFileInfo( drive, 0, &sfi, sizeof( sfi ), SHGFI_DISPLAYNAME );

			// Reset error mode.
			SetErrorMode( dwOldMode );

			// Add the drive to the list.
			HTREEITEM hDrive;
			if (( hDrive = AddTreeItem( TVI_ROOT, drive, TRUE, sfi.szDisplayName )) == NULL )
				return FALSE;

			// Add an empty item so we get the LVN_ITEMEXPANDED
			// notification message which we use to load or
			// remove the directory entries.
			if ( AddTreeItem( hDrive, "" ) == NULL )
				return FALSE;
		}
	}
	return TRUE;
}

// Obtain the full path of the item.
ClsString ClsFileDirTree::GetPath( HTREEITEM hItem ) const
{
	// 4KChars max.
	TCHAR	szBuffer[ 4096 ];

	// Setup the structure to obtain the item
	// text.
	TVITEM	tvi;
	tvi.mask	= TVIF_TEXT | TVIF_PARAM;
	tvi.pszText	= szBuffer;

	// Iterate the items until we reached the
	// root.
	ClsString strPath = "";
	while ( hItem )
	{
		// Set item.
		tvi.hItem	= hItem;
		tvi.cchTextMax  = 4096;

		// Get item name.
		if ( GetItem( tvi ))
		{
			// Is it a drive name?
			if ( tvi.lParam )
			{
				// Build "real" path from the drive letter.
				_tcscpy( szBuffer, _T( " :\\" ));

				// Add the drive letter.
				szBuffer[ 0 ] = ( TCHAR )( tvi.lParam );
			}
			else
				// Append the backslash if necessary.
				if ( szBuffer[ _tcslen( szBuffer ) - 1 ] != _T( '\\' ))
					_tcsncat( szBuffer, _T( "\\" ), 4096 );

			// Insert the name of the item at the start
			// of the path string.
			strPath.Insert( 0, szBuffer );

			// Get the parent item
			hItem = GetParentItem( hItem );
		}
		else
			break;
	}
	return strPath;
}

// Add the directory to the parent.
BOOL ClsFileDirTree::AddDirectory( HTREEITEM hParent )
{
	// Put up the wait cursor.
	ClsWaitCursor wait;

	// Get the full path of the directory.
	ClsString strPath = GetPath( hParent );
	
	// Add the "*.*" filter.
	if ( strPath[ strPath.GetStringLength() - 1 ] != _T( '\\' )) strPath += _T( '\\' );
	strPath += _T( "*.*" );

	// Scan the directory.
	ClsFindFile	find;
	ClsStringArray	aDirs;
	if ( find.FindFile( strPath ))
	{
		do
		{
			// We only get the directories.
			if ( ! ( find.Attributes() & m_dwDontShow ) && ! find.IsDots() && find.IsDirectory())
			{
				// Get the file name.
				ClsString strName;
				if ( find.GetFilePath( strName ))
					// Add it to the directory array.
					aDirs.Add( strName );
			}
		} while ( find.FindNextFile());
		
		// Close the handle.
		find.Close();
	}

	// Device not ready?
	if ( ::GetLastError() == ERROR_NOT_READY )
		return FALSE;

	// File string array.
	ClsStringArray	aFiles;

	// Show files?
	if ( m_bShowFiles )
	{
		// If there is no filter we use the *.* filter.
		if ( m_strFilter.GetStringLength() == 0 ) 
			m_strFilter = _T( "*.*" );

		// Get files.
		ClsString str;
		int nIndex = m_strFilter.GetDelimitedPart( _T( ';' ), 0, str );
		if ( nIndex > 0 )
		{
			// Scan using all filters.
			do
			{
				// Get the full path of the directory.
				strPath = GetPath( hParent );
				
				// Add the filter.
				if ( strPath[ strPath.GetStringLength() - 1 ] != _T( '\\' )) strPath += _T( '\\' );
				strPath += str;

				// Scan the directory.
				if ( find.FindFile( strPath ))
				{
					do
					{
						// We only show the correct files.
						if ( ! ( find.Attributes() & m_dwDontShow ) && ! find.IsDots() && ! find.IsDirectory())
						{
							// Get the file name.
							ClsString strName;
							if ( find.GetFilePath( strName ))
								// Add it to the file array.
								aFiles.Add( strName );
						}
					} while ( find.FindNextFile());
					// Close the handle.
					find.Close();
				}
			// Next filter...
			} while (( nIndex = m_strFilter.GetDelimitedPart( _T( ';' ), nIndex, str )) > 0 );
		}
	}

	// Sort both arrays.
	aDirs.Sort();
	aFiles.Sort();

	// Preset result.
	BOOL bRC = TRUE;

	// No rendering.
	if ( ! m_bSilent ) SetRedraw( FALSE );

	// Add the directories.
	for ( int i = 0; i < aDirs.GetSize(); i++ )
	{
		// Add the directory entry.
		HTREEITEM hItem = AddTreeItem( hParent, aDirs[ i ] );
		if ( hItem == NULL )
		{
			bRC = FALSE;
			break;
		}

		// Is the directory empty?
		if ( HasEntries( hItem ))
		{
			// Add a single, empty, item so we get a LVN_ITEMEXPANDED
			// notification.
			if ( AddTreeItem( hItem, _T( "" )) == NULL )
			{
				bRC = FALSE;
				break;
			}
		}
	}

	// Show files?
	if ( m_bShowFiles )
	{
		// Add the files.
		for ( i = 0; i < aFiles.GetSize(); i++ )
		{
			// Add the entry.
			if ( AddTreeItem( hParent, aFiles[ i ] ) == NULL )
			{
				bRC = FALSE;
				break;
			}
		}
	}

	// Re-render.
	if ( ! m_bSilent ) 
	{
		SetRedraw();
		UpdateWindow();
	}
	return bRC;
}

// Return TRUE if the directory has files or sub-directories
// and FALSE otherwise.
BOOL ClsFileDirTree::HasEntries( HTREEITEM hItem )
{
	// Get the full path of the directory.
	ClsString strPath = GetPath( hItem );
	
	// Add the *.* filter for a directory search.
	if ( strPath[ strPath.GetStringLength() - 1 ] != _T( '\\' )) strPath += _T( '\\' );
	strPath += _T( "*.*" );

	// We do not want to see error dialogs...
	DWORD dwOldMode = SetErrorMode( SEM_FAILCRITICALERRORS );

	// See if there are any sub directories.
	ClsFindFile find;
	if ( find.FindFile( strPath ))
	{
		do
		{
			// Get the name.
			ClsString strName;
			find.GetFileName( strName );

			// Is it a directory?.
			if ( ! ( find.Attributes() & m_dwDontShow ) && ! find.IsDots() && strName.GetStringLength() && find.IsDirectory())
				return TRUE;
		} while ( find.FindNextFile());
	}

	// Reset error mode.
	SetErrorMode( dwOldMode );

	// Device not ready?
	if ( ::GetLastError() == ERROR_NOT_READY )
		return FALSE;
	
	// Close the handle.
	find.Close();

	// Do we need to scan for files?
	if ( m_bShowFiles )
	{
		// If there is no filter we use the *.* filter.
		if ( m_strFilter.GetStringLength() == 0 ) 
			m_strFilter = _T( "*.*" );

		// Get files.
		ClsString str;
		int nIndex = m_strFilter.GetDelimitedPart( _T( ';' ), 0, str );
		if ( nIndex > 0 )
		{
			// Use all filters.
			do
			{
				// Get the full path of the directory.
				strPath = GetPath( hItem );
				
				// Add the filter.
				if ( strPath[ strPath.GetStringLength() - 1 ] != _T( '\\' )) strPath += _T( '\\' );
				strPath += str;

				// See if there are any sub entries.
				ClsFindFile find;
				if ( find.FindFile( strPath ))
				{
					do
					{
						// Get the name.
						ClsString strName;
						find.GetFileName( strName );

						// We only show the correct files.
						if ( ! ( find.Attributes() & m_dwDontShow ) && ! find.IsDots() && strName.GetStringLength())
							return TRUE;
					} while ( find.FindNextFile());
					// Close the handle.
					find.Close();
				}
			// Next filter...
			} while (( nIndex = m_strFilter.GetDelimitedPart( _T( ';' ), nIndex, str )) > 0 );
		}
	}
	return FALSE;
}

// By default nothing is done here.
void ClsFileDirTree::OnDriveEmpty( ClsString strPath )
{
}

// The default way of rendering the item displayed
// during the loading of the tree. When this overridable
// is called the device context has already been setup 
// to use the treeview font.
void ClsFileDirTree::OnRenderLoadingItem( ClsDC *pDC, ClsRect crBounds )
{
	// Setup the color to use for rendering the
	// loading item text. If the color specified equals
	// -1 we get the control it's text color. If this 
	// equals -1 aswell we use the system window text color.
	COLORREF color = ( m_crLoadingColor == ( COLORREF )-1 ? GetTextColor() : m_crLoadingColor );
	if ( color == ( COLORREF )-1 ) color = ::GetSysColor( COLOR_WINDOWTEXT );
	COLORREF crold = pDC->SetTextColor( color );

	// Obtain font height.
	ClsSize size = pDC->GetTextExtent( _T( " " ), 1 );

	// Render the text...
	pDC->TextOut( crBounds.Left() + 2, crBounds.Top() + (( crBounds.Height() / 2 ) - ( size.CY() / 2 )), m_strLoading );

	// Restore text color.
	pDC->SetTextColor( crold );
}

// Handle the TVN_ITEMEXPANDING notification.
LRESULT ClsFileDirTree::OnItemExpanding( LPNMTREEVIEW pTreeView, BOOL& bNotifyParent )
{
	// Obtain the item data.
	TVITEM tvi;
	tvi.mask  = TVIF_PARAM;
	tvi.hItem = pTreeView->itemNew.hItem;
	if ( GetItem( tvi ))
	{
		// Get the entry it's drive type.
		ClsString strPath = GetPath( pTreeView->itemNew.hItem );

		// Are we expanding?
		if ( ! ( pTreeView->itemNew.state & TVIS_EXPANDED ))
		{
			// Is it a drive?
			if ( tvi.lParam )
			{
				// Any entries?
				if ( HasEntries( pTreeView->itemNew.hItem ) == FALSE )
				{
					// Call the overidable.
					OnDriveEmpty( strPath );
					return TRUE;
				}
			}
		}
	}
	return FALSE;
}

// Handle the TVN_ITEMEXPANDED notification.
LRESULT ClsFileDirTree::OnItemExpanded( LPNMTREEVIEW pTreeView, BOOL& bNotifyParent )
{
	// Obtain the item data.
	TVITEM tvi;
	tvi.mask  = TVIF_PARAM;
	tvi.hItem = pTreeView->itemNew.hItem;
	if ( GetItem( tvi ))
	{
		// Did it exand?
		if ( pTreeView->itemNew.state & TVIS_EXPANDED )
		{
			// Force a redraw of the
			if ( ! m_bSilent ) 
			{
				SetRedraw();
				UpdateWindow();
			}

			// Compute the rectangle of the text label
			// including the icon.
			ClsRect rc, rctot;
			HTREEITEM hChild = GetChild( pTreeView->itemNew.hItem );
			GetItemRect( hChild, rctot, FALSE );
			GetItemRect( hChild, rc, TRUE );
			rc.Right() = rctot.Right();
			rc.Left() -= ::GetSystemMetrics( SM_CXSMICON ) + 3;

			// Prepare a device context for the rendering.
			{
				// Get a DC.
				ClsGetDC	dc( this );

				// Device context valid?
				if ( dc.IsValid())
				{
					// Setup the treeview font.
					ClsFont		font;
					GetFont( font );
					ClsSelector	sel( &dc, font );

					// Erase the item background.
					ClsBrush brush;
					COLORREF color = GetBkColor();
					if ( color == ( COLORREF )-1 ) color = ::GetSysColor( COLOR_WINDOW );
					brush.CreateSolidBrush( color );
					dc.FillRect( rc, &brush );

					// Call the overridable.
					OnRenderLoadingItem( &dc, rc );
				}
			}

			// Delete sub-items.
			hChild = GetChild( pTreeView->itemNew.hItem );
			while ( hChild )
			{
				DeleteItem( hChild );
				hChild = GetChild( pTreeView->itemNew.hItem );
			}
			
			// Add directory.
			AddDirectory( pTreeView->itemNew.hItem );
		}
		else
		{
			// Delete sub-items.
			HTREEITEM hChild = GetChild( pTreeView->itemNew.hItem );
			while ( hChild )
			{
				DeleteItem( hChild );
				hChild = GetChild( pTreeView->itemNew.hItem );
			}

			// Add a single empty item so the parent displays
			// the expansion box.
			AddTreeItem( pTreeView->itemNew.hItem, _T( "" ));
		}
	}
	return 0;
}

// Window procedure override.
LRESULT ClsFileDirTree::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 ClsTreeView::WindowProc( uMsg, wParam, lParam );
}

// We handle the background painting in the
// WM_PAINT message handler if the double buffering
// flag is truned on.
//
// If it is not we return -1 which tells the message
// handler to pass the message to the default message
// handler.
LRESULT ClsFileDirTree::OnEraseBkgnd( ClsDC *pDC )
{
	return m_bDoubleBuffer ? 0 : -1;
}

// WM_PAINT message handler. Will paint the tree contents
// into a buffer DC if appropiate.
LRESULT ClsFileDirTree::OnPaint( ClsDC *pDC )
{
	// Use double buffered rendering?
	if ( m_bDoubleBuffer )
	{
		// Use SEH to catch memory problems
		try
		{
			// Get the client rectangle.
			ClsRect rc;
			GetClientRect( rc );

			// If the input device context is valid we
			// use it to create a buffer off. Otherwise we
			// use the DC created by the ClsPaintDC class
			// to create a buffer off.
			BOOL bCreated = FALSE;
			ClsDC *pdc;
			if ( pDC )
				// Use the supplied DC.
				pdc = pDC;
			else	  
			{ 
				// Use the DC create by the ClsPaintDC object.
				// The DC is the one returned by the BeginPaint()
				// API.
				pdc = new ClsPaintDC( this ); 
				bCreated = TRUE; 
			}


			// Preset the return code.
			LRESULT lResult = -1;

			// Brace the code so that the buffer DC runs
			// out of scope before the ClsPaintDC object
			// is deleted.
			{
				// Create DC buffer.
				ClsBufferDC dc( *pdc, rc );

				// Render the background.
				SendMessage( WM_ERASEBKGND, ( WPARAM )( HDC )dc );

				// Call the default procedure to render the tree contents.
				lResult = WindowProc( WM_PAINT, ( WPARAM )( HDC )dc, 0 );
			}

			// Delete the DC if necessary.
			if ( bCreated ) delete pdc;

			// Return the result.
			return lResult;
		}

		// If a memory problem occures in the above code
		// -1 is returned which calls the default message
		// handler.
		catch ( ClsMemoryException& me )
		{
			me;
		}
	}
	return -1;
}

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