Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / MFC

UMLEditor - revisiting the vector editor

Rate me:
Please Sign up or sign in to vote.
4.99/5 (156 votes)
5 Jul 2006Public Domain8 min read 376.2K   37.1K   326  
An UML editor with code-generation capabilities derived from CDiagramEditor.
/* ==========================================================================
	File :			DocumentGenerator.cpp
	
	Class :			CDocumentGenerator

	Date :			06/16/04

	Purpose :		"CDocumentGenerator" is a class for generating 
					documentation for a c++-class from a cpp-file with 
					rigidly formatted comments.

	Description :	The class is an intermediate between the application 
					main dialog and the generator "CDocumentationGenerator" 
					- used to generate for single cpp-files.

	Usage :			Call "Generate" with the desired input and output 
					folder. All cpp-files in the folder will be processed. 
					If no output name is given, the input folder + '_docs' 
					will be used.
					If a file called 'index.html' is found in the output 
					folder, it will be scanned for the tags 
					"<!-- start-class-list -->" and "<!-- end-class-list -->". 
					Links to the generated ducumentation will be placed 
					between those two tags - the original information 
					removed. If index.html or the two tags are not found, 
					a new index.html will be created.

					If a file html_header.txt is found in the application 
					directory, it's used as the header of the resulting 
					HTML-files (up to and including the "<body>"-tag), 
					otherwise, a default header is created by the 
					application.

					If html_footer.txt is found, it's used as the footer 
					of the HTML-files (from and including the 
					"</body>"-tag), otherwise, a default footer is created 
					by the application.

   ========================================================================*/

#include "stdafx.h"
#include "DocumentGenerator.h"
#include "TextFile/TextFile.h"
#include "resource.h"
#include "DocumentationGenerator.h"
#include "DiskObject/DiskObject.h"

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

////////////////////////////////////////////////////////////////////
// Public functions
//
CDocumentGenerator::CDocumentGenerator( CListBox* feedback )
/* ============================================================
	Function :		CDocumentGenerator::CDocumentGenerator
	Description :	Constructor
	Access :		Public
					
	Return :		void
	Parameters :	none

	Usage :			Should normally be created on the stack

   ============================================================*/
{

	m_feedback = feedback;

}

CDocumentGenerator::~CDocumentGenerator(  )
/* ============================================================
	Function :		CDocumentGenerator::~CDocumentGenerator
	Description :	Destructor
	Access :		Public
					
	Return :		void
	Parameters :	none

	Usage :			

   ============================================================*/
{
}

CString CDocumentGenerator::GetErrorMessage(  ) const
/* ============================================================
	Function :		CDocumentGenerator::GetErrorMessage
	Description :	Accessor. Getter for "m_errorMessage"
	Access :		Public
					
	Return :		CString	-	The current error message
	Parameters :	none

	Usage :			Call to get an error message, if 
					appropriate.

   ============================================================*/
{

	return m_errorMessage;

}

void CDocumentGenerator::SetErrorMessage( CString value )
/* ============================================================
	Function :		CDocumentGenerator::SetErrorMessage
	Description :	Accessor. Setter for "m_errorMessage"
	Access :		Public
					
	Return :		void
	Parameters :	CString value	-	New value

	Usage :			Call to set the internal error message.

   ============================================================*/
{

	m_errorMessage = value;

	if( m_feedback )
		m_feedback->AddString( m_errorMessage );

}

#define STATE_STARTING			0
#define STATE_OVERVIEW			1
#define STATE_BETWEEN_FUNCTIONS	2
#define STATE_FUNCTION_HEADER	3
#define STATE_INSIDE_FUNCTION	4

#define INNERSTATE_DESCRIPTION	1
#define INNERSTATE_ACCESS		2
#define INNERSTATE_RETURN		3
#define INNERSTATE_PARAMETERS	4
#define INNERSTATE_USAGE		5

#define MODE_PURPOSE			0
#define MODE_DESCRIPTION		1
#define MODE_USAGE				2

BOOL CDocumentGenerator::Generate( const CString& inputdirectory, const CString& outputdirectory )
/* ============================================================
	Function :		CDocumentGenerator::Generate
	Description :	Generates html-files with documentation 
					from all cpp-files in "inputdirectory".
	Access :		Public
					
	Return :		BOOL							-	"TRUE" if OK
	Parameters :	const CString& inputdirectory	-	dir to 
														generate from
					const CString& outputdirectory	-	dir to 
														generate to.
														Defaults to empty

	Usage :			This is the main function of the class - 
					call to generate information from all 
					cpp-files in "inputdirectory". If the 
					function returns "FALSE", call 
					"GetErrorMessage" for an error message.
					If "outputdirectory" is empty, "inputdirectory" 
					+ '_docs' will be used.

   ============================================================*/
{
	CWaitCursor		wait;

	CDiskObject		dob;
	CStringArray	stra;

	CString			source( inputdirectory );
	if( source[ source.GetLength() - 1 ] == _TCHAR( '\\' ) )
		source = source.Left( source.GetLength() - 1 );

	CString			dest( outputdirectory );
	if( dest.IsEmpty() )
		dest = source + _T( "\\docs" );
	else
		if( dest[ dest.GetLength() - 1 ] == _TCHAR( '\\' ) )
			dest = dest.Left( dest.GetLength() - 1 );

	dob.EnumFilesInDirectoryWithFilter( _T( "*.cpp" ), source, stra );
	int max = stra.GetSize();
	for( int t = 0 ; t < max ; t++ )
		if( !GenerateDocumentation( source + _T( "\\" ) + stra[ t ] , dest ) )
			return FALSE;

	return GenerateIndexFile( dest );

}

BOOL CDocumentGenerator::GenerateDocumentation( const CString& inputfilename, const CString& dest )
/* ============================================================
	Function :		CDocumentGenerator::GenerateDocumentation
	Description :	Generates documentation for the cpp-file 
					"inputfilename".
	Access :		Private
					
	Return :		BOOL	-	"TRUE" if OK.
	Parameters :	const CString& inputfilename	-	File to generate
					const CString& dest				-	Folder to generate to

	Usage :			Main engine of the class. Called for each 
					cpp-file in the input directory.

   ============================================================*/
{

	BOOL	result = FALSE;
	CString filename( inputfilename );
	CString purpose;
	CString description;
	CString usage;
	CString function_name;
	CString function_description;
	CString	function_returns;
	CString function_parameters;
	CString function_usage;
	CString function_return;
	CString function_access;
	CString class_name;

	CDocumentationGenerator	docgen;
	CFunction*	function = NULL;
	int overview_mode = MODE_PURPOSE;
	CString mess;
	if( !filename.IsEmpty() )
	{

		CTextFile		file( _T( "" ), _T( "\n" ) );
		CStringArray	stra;
		CStringArray	output;

		if( file.ReadTextFile( filename, stra ) )
		{
			if( m_feedback )
			{
				mess.LoadString( IDS_PROCESSING );
				m_feedback->AddString( mess + filename );
			}

			int max = stra.GetSize();
			int state = STATE_STARTING;
			int innerstate = STATE_STARTING;
			int bracketcount = 0;

			for( int t = 0 ; t < max ; t++ )
			{
				CString line = stra[ t ];
				BOOL hashflag = FALSE;
				if( state == STATE_FUNCTION_HEADER && innerstate == INNERSTATE_USAGE && line.GetLength() > 7 && line.Left( 6 ) == _T( "\t\t\t\t\t\t" ) && line.Left( 7 ) != _T( "\t\t\t\t\t\t\t" ) )
					hashflag = TRUE;
				line.TrimLeft();
				line.TrimRight();
				if( hashflag )
					line = _T( "#" ) + line;
				if( line.GetLength() )
				{

					line.Replace( _TCHAR( '\t' ), _TCHAR( ' ' ) );

					switch( state )
					{
						case STATE_STARTING:
						{
							// If starting overview, save and
							// switch states
							CString cls( _T( "Class :" ) );
							if( line.GetLength() >= cls.GetLength() && line.Left( cls.GetLength() ) == cls )
							{
								line = line.Right( line.GetLength() - cls.GetLength() );
								line.TrimLeft();
								docgen.SetClass( line );
								class_name = line;
							}

							CString purp( _T( "Purpose :" ) );
							if( line.GetLength() >= purp.GetLength() && line.Left( purp.GetLength() ) == purp )
							{
								line = line.Right( line.GetLength() - purp.GetLength() );
								line.TrimLeft();
								purpose = line;
								state = STATE_OVERVIEW;
								overview_mode = MODE_PURPOSE;
							}
						}
						break;

						case STATE_OVERVIEW:
						{
							// If at the end of the comments,
							// change state
							CString end( _T( "===" ) );
							if( line.GetLength() >= end.GetLength() && line.Left( end.GetLength() ) == end )
							{
								state = STATE_BETWEEN_FUNCTIONS;
							}
							else
							{
								CString desc( _T( "Description :" ) );
								CString usg( _T( "Usage :" ) );
								if( line.GetLength() >= desc.GetLength() && line.Left( desc.GetLength() ) == desc )
								{
									description = line.Right( line.GetLength() - desc.GetLength() );
									overview_mode = MODE_DESCRIPTION;
								}
								else if( line.GetLength() >= usg.GetLength() && line.Left( usg.GetLength() ) == usg )
								{
									usage = line.Right( line.GetLength() - usg.GetLength() );
									overview_mode = MODE_USAGE;
								}
								else
								{
									if( overview_mode == MODE_PURPOSE )
										purpose += _T( " " ) + line;
									else if( overview_mode == MODE_DESCRIPTION )
										description += _T( " " ) + line;
									else if( overview_mode == MODE_USAGE )
										usage += _T( " " ) + line;
								}
							}
						}
						break;

						case STATE_BETWEEN_FUNCTIONS:
						{
							// If we find a function definition,
							// save and change state
							int found = line.Find( _T( "::" ) );
							if( found != -1 )
							{
								CString functionreturn;
								CString classname( line.Left( found ) );
								int space = classname.ReverseFind( _TCHAR( ' ' ) );
								if( space != -1 )
								{
									functionreturn = classname.Left( space + 1 );
								}
								space = classname.Find( _T( "(" ) ); // Looking for macro
								if( space == -1 )
								{
									line = line.Right( line.GetLength() - ( found + 2 ) );
									innerstate = STATE_STARTING;
									state = STATE_FUNCTION_HEADER;

									function_return = functionreturn;
									function_name = line;

									function_description = _T( "" );
									function_returns = _T( "" );
									function_parameters = _T( "" );
									function_usage = _T( "" );
								}
							}
						}
						break;

						case STATE_FUNCTION_HEADER:
						{
							// If end of comments, switch state
							if( line.GetLength() > 2 && line.Right ( 2 ) == _T( "*/" ) )
							{
								state = STATE_INSIDE_FUNCTION;
							}
							else
							{
								switch( innerstate )
								{
									case STATE_STARTING:
									{
										CString desc( _T( "Description :" ) );
										if( line.GetLength() >= desc.GetLength() && line.Left( desc.GetLength() ) == desc )
										{
											line =line.Right( line.GetLength() - desc.GetLength() );
											line.TrimLeft();
											function_description = line;
											innerstate++;
										}
									}
									break;

									case INNERSTATE_DESCRIPTION:
									{
										CString ret( _T( "Access :" ) );
										if( line.GetLength() >= ret.GetLength() && line.Left( ret.GetLength() ) == ret )
										{
											line = line.Right( line.GetLength() - ret.GetLength() );
											line.TrimLeft();
											function_access = line;
											innerstate++;
										}
										else
											function_description += _T( " " ) + line;
									}
									break;

									case INNERSTATE_ACCESS:
									{
										CString access( _T( "Return :" ) );
										if( line.GetLength() >= access.GetLength() && line.Left( access.GetLength() ) == access )
										{
											line = line.Right( line.GetLength() - access.GetLength() );
											line.TrimLeft();
											function_returns = line;
											innerstate++;
										}
										else
											function_access += _T( " " ) + line;
									}
									break;

									case INNERSTATE_RETURN:
									{
										CString parameters( _T( "Parameters :" ) );
										if( line.GetLength() >= parameters.GetLength() && line.Left( parameters.GetLength() ) == parameters )
										{
											line = line.Right( line.GetLength() - parameters.GetLength() );
											line.TrimLeft();
											function_parameters = line;
											innerstate++;
										}
										else
											function_returns += _T( " " ) + line;
									}
									break;

									case INNERSTATE_PARAMETERS:
									{
										CString usage( _T( "Usage :" ) );
										if( line.GetLength() >= usage.GetLength() && line.Left( usage.GetLength() ) == usage )
										{
											line = line.Right( line.GetLength() - usage.GetLength() );
											line.TrimLeft();
											function_usage = line;
											innerstate++;
										}
										else
										{
											if( line.Find( _T( "-" ) ) != -1 )
												function_parameters += _T( "\n" );
											else
												function_parameters += _T( " " );

											function_parameters += line;
										}
									}
									break;

									case INNERSTATE_USAGE:
									{
										function_usage += _T( " " ) + line;
									}
									break;
								}
							}
						}
						break;

						case STATE_INSIDE_FUNCTION:
						{
							// If the end of the function, save function
							// data and switch state
							bracketcount += line.Remove( _TCHAR( '{' ) );
							bracketcount -= line.Remove( _TCHAR( '}' ) );
							if( bracketcount == 0 )
							{
								function = new CFunction;
								function->SetFunctionName( function_name );
								function->SetFunctionReturn( function_return );
								function->SetDescription( function_description );
								function->SetReturns( function_returns );
								function->SetParameters( function_parameters );
								function->SetUsage( function_usage );
								function->SetAccess( function_access );

								docgen.AddFunction( function );
								state = STATE_BETWEEN_FUNCTIONS;
							}

						}
						break;

					}
				}
			}

			if( class_name.GetLength() )
			{
				docgen.SetPurpose( purpose );
				docgen.SetDescription( description );
				docgen.SetUsage( usage );

				CString newfilename( filename );
				int backslash = newfilename.ReverseFind( _TCHAR( '\\' ) );
				if( backslash != -1 )
					newfilename = newfilename.Right( newfilename.GetLength() - ( backslash + 1 ) );
				int point = newfilename.ReverseFind( _TCHAR( '.' ) );
				if( point != -1 )
					newfilename = newfilename.Left( point );

				AddClassName( class_name );
				AddBaseFilename( newfilename );
				AddDescription( purpose );

				newfilename = _T( "\\" ) + newfilename + _T( ".html" );
				newfilename = dest + newfilename;

				SetHeader( output );
				docgen.Generate( output );
				SetFooter( output );

				CTextFile outfile( _T("html" ), _T( "\n" ) );
				if( m_feedback )
				{
					mess.LoadString( IDS_WRITING );
					m_feedback->AddString( mess + newfilename );
				}

				CDiskObject cdo;
				if( cdo.CreateFile( newfilename ) )
				{
					if( outfile.WriteTextFile( newfilename, output ) )
						result = TRUE;
					else
					{
						CString error;
						error.LoadString( IDS_COULD_NOT_WRITE_FILE );
						error += newfilename;
						SetErrorMessage( error + _T( ", " ) + outfile.GetErrorMessage() );
						result = FALSE;
					}
				}
				else
				{
					CString error;
					error.LoadString( IDS_COULD_NOT_WRITE_FILE );
					error += newfilename;
					SetErrorMessage( error + _T( ", " ) + cdo.GetErrorMessage() );
					result = FALSE;
				}
			}
			else
				result = TRUE;

		}
		else
		{
			CString error;
			error.LoadString( IDS_COULD_NOT_READ_FILE );
			error += inputfilename;
			SetErrorMessage( error );
			result = FALSE;
		}
	}
	else
	{
		CString error;
		error.LoadString( IDS_NO_FILENAME );
		SetErrorMessage( error );
	}

	return result;

}

void CDocumentGenerator::SetHeader( CStringArray& output )
/* ============================================================
	Function :		CDocumentGenerator::SetHeader
	Description :	Fills "output" with an appropriate 
					HTML-header.
	Access :		Private
					
	Return :		void
	Parameters :	CStringArray& output	-	Array to fill
					
	Usage :			Call this function to create a HTML-header. 
					If the file html_header.txt is found in the 
					application directory, it is used as a 
					header. Otherwise, a hardcoded version is 
					used.

   ============================================================*/
{

	CTextFile		file;
	CString			filename;
	CStringArray	stra;
	filename = GetApplicationDirectory() + _T( "html_header.txt" );

	if( !file.ReadTextFile( filename, stra ) )
	{
		output.Add( _T( "<html>" ) );
		output.Add( _T( "<head>" ) );
		output.Add( _T( "<style>" ) );
		output.Add( _T( "body{font-size: 10pt;color: black;font-family: Verdana, Helvetica, Arial, sans-serif;background-color: #ffffff}" ) );
		output.Add( _T( "p{font-size: 10pt;color: black;font-family: Verdana, Helvetica, Arial, sans-serif;}" ) );
		output.Add( _T( "h2{font-size: 13pt;color: #ff9900;font-family: Verdana, Helvetica, Arial, sans-serif;font-weight: bold;}" ) );
		output.Add( _T( "h3{color: #ff9900;font-weight: bold;font-size: 11pt;font-family: Arial, sans-serif;}" ) );
		output.Add( _T( "table{background-color:#f0f0ff;}" ) );
		output.Add( _T( "td{font-size: 10pt;color: black;font-family: Verdana, Helvetica, Arial, sans-serif;padding:4px;}" ) );
		output.Add( _T( "pre{padding-right: 7pt;padding-left: 7pt;padding-bottom: 7pt;font: 9pt 'Courier New', Courier, mono;width: 100%;padding-top: 7pt;white-space: pre;background-color: #fbedbb}" ) );
		output.Add( _T( "code{color: #990000;font-family: 'Courier New', Courier, mono;}" ) );
		output.Add( _T( "A:link{text-decoration: none}" ) );
		output.Add( _T( "A:visited{text-decoration: none}" ) );
		output.Add( _T( "A:active{text-decoration: underline}" ) );
		output.Add( _T( "A:hover{text-decoration: underline}" ) );
		output.Add( _T( "A.top:link{font-size:8pt;color:red;font-family:Verdana,Helvetica,Arial,sans-serif;}" ) );
		output.Add( _T( "A.top:visited{font-size:8pt;color:red;font-family:Verdana,Helvetica,Arial,sans-serif;}" ) );
		output.Add( _T( "A.top:active{font-size:8pt;color:red;font-family:Verdana,Helvetica,Arial,sans-serif;}" ) );
		output.Add( _T( "A.top:hoover{font-size:8pt;color:red;font-family:Verdana,Helvetica,Arial,sans-serif;}" ) );
		output.Add( _T( "</style>" ) );
		output.Add( _T( "</head>" ) );
		output.Add( _T( "<body>" ) );
	}
	else
		output.Append( stra );

}

void CDocumentGenerator::SetFooter( CStringArray& output )
/* ============================================================
	Function :		CDocumentGenerator::SetFooter
	Description :	Creates a HTML-footer in "stra"
	Access :		Private
					
	Return :		void
	Parameters :	CStringArray& output	-	Array to add 
												footer to.
					
	Usage :			Call to finish a HTML-file.
					If the file html_footer.txt is found in the 
					application directory, it is used as a 
					footer. Otherwise, a hardcoded version is 
					used.

   ============================================================*/
{

	CTextFile		file;
	CString			filename;
	CStringArray	stra;
	
	filename = GetApplicationDirectory() + _T( "html_footer.txt" );
	if( !file.ReadTextFile( filename, stra ) )
	{
		output.Add( _T( "</body>" ) );
		output.Add( _T( "</html>" ) );
	}
	else
		output.Append( stra );

}

void CDocumentGenerator::AddClassName( const CString& cls )
/* ============================================================
	Function :		CDocumentGenerator::AddClassName
	Description :	Adds a class to the internal class list.
	Access :		Private

	Return :		void
	Parameters :	const CString& cls	-	Class to add
					
	Usage :			Call as soon as a new class is processed. 
					The internal class list will be used to 
					create an index file.

   ============================================================*/
{

	m_classes.Add( cls );

}

void CDocumentGenerator::AddDescription( const CString& desc )
/* ============================================================
	Function :		CDocumentGenerator::AddDescription
	Description :	Adds a description of a class to the 
					internal description list.
	Access :		Private

	Return :		void
	Parameters :	const CString& desc -	Description to add
					
	Usage :			Call as soon as a new class is processed. 
					The internal class description list will 
					be used to create an index file.

   ============================================================*/
{
	CString description( desc );

	description.Replace( _T( "<" ), _T( "&lt;" ) );
	description.Replace( _T( ">" ), _T( "&gt;" ) );

	AddCode( description );
	m_descriptions.Add( description );

}

void CDocumentGenerator::AddBaseFilename( const CString& str )
/* ============================================================
	Function :		CDocumentGenerator::AddBaseFilename
	Description :	Adds a filename to the internal filename 
					list.
	Access :		Private

	Return :		void
	Parameters :	const CString& str	-	Filename to add
					
	Usage :			Call after a cpp-file has been processed.
					The filenames are used to generate an index 
					file.

   ============================================================*/
{

	m_baseFilename.Add( str );

}

BOOL CDocumentGenerator::GenerateIndexFile( const CString& dest )
/* ============================================================
	Function :		CDocumentGenerator::GenerateIndexFile
	Description :	Generates an index file for all the 
					document files created.
	Access :		Private
					
	Return :		BOOL				-	"TRUE" if OK.
	Parameters :	const CString& dest	-	Directory to create
											index file in

	Usage :			Call to generate an index file after the 
					individual cpp-files are created.

   ============================================================*/
{

	BOOL result = TRUE;
	CStringArray stra;

	int max = m_classes.GetSize();
	for( int t = 0 ; t < max ; t++ )
	{
		if( m_classes[ t ].GetLength() )
		{
			CString link;
			link.Format( _T( "<p><a href='%s.html'>%s</a> - %s</p>" ), m_baseFilename[ t ], m_classes[ t ], m_descriptions[ t ] );
			stra.Add( link );
		}
	}

	SortStringArray( stra );

	CString filename = dest + _T( "\\index.html" );

	// First of all, we check if there is
	// already an index.html in dest
	CDiskObject cdo;
	CTextFile	file( _T( "" ), _T( "\n" ) );

	if( m_feedback )
	{
		CString mess;
		mess.LoadString( IDS_WRITING );
		m_feedback->AddString( mess + filename );
	}

	BOOL	eating = FALSE;
	if( cdo.FileExists( filename ) )
	{
		CStringArray index;
		BOOL insertion = FALSE;
		if( file.ReadTextFile( filename, index ) )
		{
			int max = index.GetSize();
			for( int t = 0 ; t < max ; t++ )
			{

				if( index[ t ].Find( _T( "<!-- start-class-list -->" ) ) != -1 )
				{
					t++;
					eating = TRUE;
					while( eating && index.GetSize() > t )
					{
						if( index[ t ].Find( _T( "<!-- end-class-list -->" ) ) != -1 )
							eating = FALSE;
						else
							index.RemoveAt( t );
					}

					index.InsertAt( t, &stra );
					if( !eating )
						insertion = TRUE;
					t = max;
				}
			}
		}

		if( insertion )
		{
			if( !file.WriteTextFile( filename, index ) )
			{
				SetErrorMessage( file.GetErrorMessage() );
				result = FALSE;
			}
		}
		else
		{
			// Not the expected format in index.html
			CStringArray	stuff;

			SetHeader( stuff );
			stuff.Add( _T( "<h2>Classes</h2>" ) );
			stuff.Add( _T( "<!-- start-class-list -->" ) );

			stra.InsertAt( 0, &stuff );

			stra.Add( _T( "<!-- end-class-list -->" ) );
			SetFooter( stra );

			if( !file.WriteTextFile( filename, stra ) )
			{
				SetErrorMessage( file.GetErrorMessage() );
				result = FALSE;
			}
		}
	}
	else
	{
		CStringArray	stuff;

		SetHeader( stuff );
		stuff.Add( _T( "<h2>Classes</h2>" ) );
		stuff.Add( _T( "<!-- start-class-list -->" ) );

		stra.InsertAt( 0, &stuff );

		stra.Add( _T( "<!-- end-class-list -->" ) );
		SetFooter( stra );

		if( !file.WriteTextFile( filename, stra ) )
		{
			SetErrorMessage( file.GetErrorMessage() );
			result = FALSE;
		}
	}

	return result;

}

CString GetApplicationDirectory()
{

	// Returns the folder where the application
	// is installed

	TCHAR buffer[ _MAX_PATH + 1 ];
	::GetModuleFileName( NULL, buffer, _MAX_PATH );
	CString path( buffer );

	int find = path.ReverseFind( _TCHAR( '\\' ) );
	if( find != -1 )
		path = path.Left( find );

	path += _TCHAR( '\\' );

	return path;

}

void SortStringArray( CStringArray& stra )
{
	BOOL replacing = TRUE;
	while( replacing )
	{
		replacing = FALSE;
		int max = stra.GetSize();
		for( int t = 0 ; t < max - 1 ; t++ )
		{
			if( stra[ t ] > stra[ t + 1 ] )
			{
				replacing = TRUE;
				CString temp( stra[ t ] );
				stra[ t ] = stra[ t + 1 ];
				stra[ t + 1 ] = temp;
			}
		}
	}
}

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 A Public Domain dedication


Written By
Software Developer (Senior) Abstrakt Mekanik AB
Sweden Sweden
45 years old, married, three kids.

Started with computers more than 20 years ago on a CBM-64.

Read Theoretical Philosophy at the University of Lund.

Working as a C++ consultant developer.

Science-fiction freak. Enjoy vintage punkrock.

Comments and Discussions