/*=============================================================================
 *
 *  Filename:						Sort3Lst.cpp
 *  Version:            1.0
 *  Date:								06/11/96
 *  Author:             V.L.
 *
 *  Description:				implementation of ThreeSortedList class
 *
 *===========================================================================*/

#include "stdafx.h"
#ifdef ACTIVE_EDITOR

#include "itf/Sort3lst.hpp"
#include "itf/CPADD.hpp"
#include "IncTUT.h"

/*=============================================================================
* implementation of CBitmapList
=============================================================================*/

/*BOOL CTextureDlg::m_fn_bIsAValidTexture(CString csFileName)
{
	FILE *hFile;
	BITMAPFILEHEADER bf;
	BITMAPINFOHEADER bi;
	
	if ( (hFile = fopen(csFileName, "rb")) == NULL)
		return FALSE;

	CString csExtension(strrchr(LPCTSTR(csFileName), '.'));
	if ( !csExtension.CompareNoCase(".tga") )
	{
		unsigned char a18_ucTgaHeader[18];
		//read TGA header
		if ( fread(a18_ucTgaHeader, 18, 1, hFile) != 1 )
		{
			fclose(hFile);
			return FALSE;
		}
		//get image width and height
		bi.biWidth = (a18_ucTgaHeader[13]<<8) + a18_ucTgaHeader[12];
		bi.biHeight = (a18_ucTgaHeader[15]<<8) + a18_ucTgaHeader[14];
		bi.biBitCount = a18_ucTgaHeader[16];
	}
	else if ( !csExtension.CompareNoCase(".bmp") )
	{
		if (sizeof(bf) != fread( &bf, 1, sizeof(bf), hFile ))
		{
			fclose(hFile);
			return FALSE;
		}
		if (bf.bfType != 0x4d42 ) //BM
		{
			fclose(hFile);
			return FALSE;
		}
		if (sizeof(bi) != fread( &bi, 1, sizeof(bi), hFile ))
		{
			fclose(hFile);
			return FALSE;
		}
	}
	else
	{
		fclose(hFile);
		return FALSE;
	}

	// verify color depth
	if ( (bi.biBitCount == 24) || (bi.biBitCount == 32) )
	{
		LONG lSize = bi.biWidth * bi.biHeight;
		LONG lMask = 1;
		while (lMask != 0)
		{
			if ((lSize ^ lMask) == 0)
			{
				fclose(hFile);
				return TRUE;
			}
			lMask <<= 1;
		}
	}	

	fclose(hFile);
	return FALSE;
}	*/

/*-----------------------------------------------------------------------------
 *  Description : load a bitmap file, returns the handle on the image
 *---------------------------------------------------------------------------*/
HBITMAP CBitmapList::m_hLoadImage(
	CString _csName, int _cx, int _cy,
	CString _csNameOfSmallVersion,
	BOOL _bOverrideOptimizedImages
)
{
	HBITMAP hBitmap = NULL;
	CString csExtension = strrchr(LPCTSTR(_csName), '.');
	short wImageType;

	if ( !csExtension.CompareNoCase(".bmp") ) // load a bmp file
		wImageType = 'BM';
	else if ( !csExtension.CompareNoCase(".tga") ) // load a tga file
		wImageType = 'TG';
	else
		wImageType = 0;

	if ( wImageType )
	{
		FILE *p_stTextureFile;
		p_stTextureFile = fopen(LPCTSTR(_csName),"rb");
		if ( p_stTextureFile )
		{
			BITMAPINFO stBitmapInfo;
			unsigned char aDEF_ucReadBuffer[512L*4L];
			short wTgaWidth, wTgaHeight;
			unsigned char ucBBP;

			if ( wImageType == 'BM' ) // load a bmp file
			{
				BITMAPFILEHEADER bf;
				BITMAPINFOHEADER bi;
				fread(&bf, 1, sizeof(bf), p_stTextureFile);
				fread(&bi, 1, sizeof(bi), p_stTextureFile);
				stBitmapInfo.bmiHeader = bi;
				wTgaWidth = (short) bi.biWidth;
				wTgaHeight = (short) bi.biHeight;
				ucBBP = (unsigned char) bi.biBitCount;
			}
			else if ( wImageType == 'TG' ) // load a bmp file
			{
				//read TGA header
				fread(aDEF_ucReadBuffer,18,1,p_stTextureFile);
				//get image width and height
				wTgaWidth = (aDEF_ucReadBuffer[13]<<8) + aDEF_ucReadBuffer[12];
				wTgaHeight = (aDEF_ucReadBuffer[15]<<8) + aDEF_ucReadBuffer[14];
				ucBBP = aDEF_ucReadBuffer[16];
			}
			else
			{
				if ( m_bLoadOnlyOptimizedImages )
				{
					CString csBadTextureName = _csName + "\n";
					if ( m_hBadTexturesLogFile )
					{
						fprintf(m_hBadTexturesLogFile, LPCTSTR(csBadTextureName));
						fflush(m_hBadTexturesLogFile);
					}
				}
				fclose(p_stTextureFile);
				return NULL;
			}
			//here, we have read the header of the image file, either BMP or TGA

			//check that the image has the proper size, only if we are trying to load the original one
			if 
			( 
				(m_bLoadOnlyOptimizedImages && !_bOverrideOptimizedImages)
#if defined(D_UseSmallBitmaps)
				&& !_csNameOfSmallVersion.IsEmpty() //do not test image optimisation on small versions
#endif //D_UseSmallBitmaps
			)
			{
				LONG lSize = wTgaWidth * wTgaHeight;
				LONG lMask = 1;
				while (lMask != 0)
				{
					if ((lSize ^ lMask) == 0)
						break;
					else
						lMask <<= 1;
				}
				if ( lMask == 0 )
				{
					CString csBadTextureName = _csName + "\n";
					if ( m_hBadTexturesLogFile )
					{
						fprintf(m_hBadTexturesLogFile, LPCTSTR(csBadTextureName));
						fflush(m_hBadTexturesLogFile);
					}
					fclose(p_stTextureFile);
					return NULL;
				}
			}
			//here, the file has a size that we accept

			if ( wImageType == 'BM' ) // load a bmp file with the Windows API
			{
				fclose(p_stTextureFile);
				hBitmap = (HBITMAP) LoadImage(NULL, _csName, IMAGE_BITMAP, _cx, _cy, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
			}
			else if ( wImageType = 'TG' ) // load a tga file
			{
				BITMAPINFO stBitmapInfo;
				unsigned char *pRawBits;

				//we load only true color images
				if ( ucBBP != 24 && ucBBP != 32 )
				{
					if ( m_bLoadOnlyOptimizedImages )
					{
						CString csBadTextureName = _csName + "\n";
						if ( m_hBadTexturesLogFile )
						{
							fprintf(m_hBadTexturesLogFile, LPCTSTR(csBadTextureName));
							fflush(m_hBadTexturesLogFile);
						}
					}
					fclose(p_stTextureFile);
					return NULL;
				}

				//get the number of bytes per pixel  (3 or 4)
				ucBBP >>= 3;

				ASSERT(wTgaWidth * ucBBP <= sizeof(aDEF_ucReadBuffer));
				//fill the other useful fields of the header
				stBitmapInfo.bmiHeader.biWidth = _cx;
				stBitmapInfo.bmiHeader.biHeight = _cy;
				stBitmapInfo.bmiHeader.biPlanes = 1;
				stBitmapInfo.bmiHeader.biBitCount = 24; //we dont care about alpha channel here, so we wont save it
				stBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
				stBitmapInfo.bmiHeader.biCompression = BI_RGB;
				stBitmapInfo.bmiHeader.biSizeImage = 0 /*stBitmapInfo.bmiHeader.biWidth * stBitmapInfo.bmiHeader.biHeight * ucBBP*/;
				stBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
				stBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
				stBitmapInfo.bmiHeader.biClrUsed = 0;
				stBitmapInfo.bmiHeader.biClrImportant = 0;
				//create the bitmap image of the size we want (_cx, _cy)
				hBitmap = CreateDIBSection(NULL, &stBitmapInfo, DIB_RGB_COLORS, (void **) &pRawBits, NULL, 0);
				//copy/stretch the tga raw data in the image's bits area
				if ( hBitmap && pRawBits )
				{
					long lDestLine, lSrcLine, lOldSrcLine = -1;
					for ( lDestLine = 0; lDestLine < _cy; lDestLine ++ )
					{
						lSrcLine = (lDestLine * wTgaHeight) / _cy;
						//read as many lines in the as required to reach the proper source data
						while ( lSrcLine != lOldSrcLine )
						{
							lOldSrcLine ++;
							//read a line in the file
							fread((void*)aDEF_ucReadBuffer, ucBBP, wTgaWidth, p_stTextureFile);
						}

						unsigned char *pSrcLine = aDEF_ucReadBuffer;
						unsigned char *pDestLine = pRawBits + lDestLine * _cx * 3;

						long lDestPixel, lSrcPixel;
						for	( lDestPixel = 0; lDestPixel < _cx; lDestPixel ++ )
						{
							lSrcPixel = (lDestPixel * wTgaWidth) / _cx;
							unsigned char *pSrcPixel = pSrcLine + lSrcPixel * ucBBP;
							unsigned char *pDestPixel = pDestLine + lDestPixel * 3;

							*(pDestPixel ++) = *(pSrcPixel ++);
							*(pDestPixel ++) = *(pSrcPixel ++);
							*(pDestPixel ++) = *(pSrcPixel ++);
						} //column scan loop
					} //image scan loop
				} //bitmap creation test
				else
					hBitmap = NULL;
				fclose(p_stTextureFile);
			} //image type test
		} //file open test
	}//image type test

#if defined(D_UseSmallBitmaps)
	//save a small copy if asked for
	if ( !_csNameOfSmallVersion.IsEmpty() && hBitmap )
	{
		//create or truncate the file
		CFile oFile;
		if ( oFile.Open(_csNameOfSmallVersion, CFile::modeCreate + CFile::typeBinary + CFile::modeWrite) )
		{
			BITMAPFILEHEADER bf;
			BITMAPINFOHEADER bi;
			CBitmap *p_oBitmap = CBitmap::FromHandle(hBitmap);
			BITMAP stBitmap;
			p_oBitmap->GetBitmap(&stBitmap);
			//prepare the file's headers
			bf.bfType = 'MB'; //reverse order because of f**ing intel byte swaps!!!
			//file header + image header + image data
			bf.bfSize = sizeof(bf) + sizeof(bi) + stBitmap.bmWidthBytes * stBitmap.bmHeight;
			bf.bfReserved1 = 0;
			bf.bfReserved2 = 0;
			bf.bfOffBits = sizeof(bf) + sizeof(bi);
			bi.biSize	= sizeof(BITMAPINFOHEADER);
			bi.biWidth = stBitmap.bmWidth;
			bi.biHeight = stBitmap.bmHeight;
			bi.biPlanes = stBitmap.bmPlanes;
			bi.biBitCount = stBitmap.bmBitsPixel;
			bi.biCompression = BI_RGB;
			bi.biSizeImage = 0;
			bi.biXPelsPerMeter = 0;
			bi.biYPelsPerMeter = 0;
			bi.biClrUsed = 0;
			bi.biClrImportant = 0;
			//write the headers
			oFile.Write(&bf, sizeof(bf));
			oFile.Write(&bi, sizeof(bi));
			//write the image data
			oFile.Write(stBitmap.bmBits, stBitmap.bmWidthBytes * stBitmap.bmHeight);
			oFile.Close();
		}
	}
#endif //D_UseSmallBitmaps

	return hBitmap;
}

/*-----------------------------------------------------------------------------
 *  Description : Constructor
 *---------------------------------------------------------------------------*/
CBitmapList::CBitmapList()
{
	m_oMaxSize.cx = -1;
	m_oMaxSize.cy = -1;
}

/*-----------------------------------------------------------------------------
 *  Description : Destructor
 *---------------------------------------------------------------------------*/
CBitmapList::~CBitmapList()
{
	POSITION				pos = m_oItemList.GetHeadPosition();
	tdstBitmapItem	*p_stItem;
	// delete all item in list
	while (pos)
	{
		p_stItem = m_oItemList.GetNext( pos );
		if (!p_stItem->m_bDeleted)
		{
			DeleteObject( p_stItem->m_hBitmap );
		}
		delete (p_stItem);
	}

	if ( m_bLoadOnlyOptimizedImages && m_hBadTexturesLogFile )
	{
		fclose(m_hBadTexturesLogFile);
		m_hBadTexturesLogFile = NULL;
	}
}

/*-----------------------------------------------------------------------------
 *  Description : check out the creation of bitmaplist
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vIsCreated()
{
	if ( (m_oMaxSize.cx == -1) && (m_oMaxSize.cy == -1) )
	{
		AfxMessageBox("Error : CBitmapList not created");
		exit(-1);
	}
}

/*-----------------------------------------------------------------------------
 *  Description : creation of CBitmapList
 *		three case : _bSize = C_cFixedSize : bitmap size is fixed and equal to (_cx, _cy)
 *								 _bSize = C_cVariableSize : bitmap size is variable and
 *										_cx = 0 or _cy = 0: not limited
 *										else (_cx,_cy) is the maximum the bitmap size can't be exceded
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vCreate( 
	BOOL _bSize /*= TRUE*/, 
	int _cx /*= 0*/, 
	int _cy /*= 0*/,
	BOOL _bLoadOnlyOptimizedImages /*= FALSE*/,
	BOOL _bUseSmallCopies /*= FALSE*/
)
{
	if ( (m_oMaxSize.cx == -1) && (m_oMaxSize.cy == -1) ) //only first call is to be handled
	{
#if defined(D_UseSmallBitmaps)
		m_bUseSmallCopies = 
#endif //D_UseSmallBitmaps
		_bUseSmallCopies;	//prevent compiler warning in case the argument is not used

		m_bLoadOnlyOptimizedImages = _bLoadOnlyOptimizedImages;
		m_bVariableSize = _bSize;

		if (m_bVariableSize)
		{
			if ( (_cx == 0) || (_cy == 0) )
				_cx = _cy = 0;
		}
		else
		{
			if (_cx < 8) _cx = 8;
			if (_cy < 8) _cy = 8;
		}

		m_oMaxSize.cx = _cx;
		m_oMaxSize.cy = _cy;

		if ( m_bLoadOnlyOptimizedImages )
			m_hBadTexturesLogFile = fopen("BadTextures.log", "wt");
	}
	else
		AfxMessageBox("Warning : CBitmapList created more than once");
}

/*-----------------------------------------------------------------------------
 *  Description : return number of bitmap in list, warning some element could
 *  have been erased so number returned isn't always right
 *---------------------------------------------------------------------------*/
long CBitmapList::m_fn_lGetCount( void )
{
	return m_oItemList.GetCount();
}


/*-----------------------------------------------------------------------------
 *  Description : check out if a bitmap is already loaded or not
 *---------------------------------------------------------------------------*/
int CBitmapList::m_fn_iFindBitmapByName( CString csName )
{
	int				iPos = 0;
	POSITION		pos = m_oItemList.GetHeadPosition();
	tdstBitmapItem	*p_stItem;

	while (pos)
	{
		p_stItem = m_oItemList.GetNext( pos );
		if (!p_stItem->m_bDeleted)
			if (csName.CompareNoCase(p_stItem->m_csName) == 0)
				return iPos;
		iPos ++;
	}
	return -1;
}

/*-----------------------------------------------------------------------------
* Decription : get index of a bitmap
 *---------------------------------------------------------------------------*/
int CBitmapList::m_fn_iGetBitmapIndex( char *szBitmapName )
{
	return m_fn_iFindBitmapByName( szBitmapName );
}


/*-----------------------------------------------------------------------------
 *  Description : return size of bitmap given by it's name
 *---------------------------------------------------------------------------*/
BOOL CBitmapList::m_fn_bGetBitmapSize( CString csFileName, CSize *sz)
{
	FILE *hFile;
	BITMAPFILEHEADER bf;
	BITMAPINFOHEADER bi;

	CString csExtension = strrchr(LPCTSTR(csFileName), '.');

	if ( (hFile = fopen( csFileName, "rb")) == NULL)
		return FALSE;

	if ( !csExtension.CompareNoCase(".tga") )
	{
		unsigned char a18_ucTgaHeader[18];
		//read TGA header
		if ( fread(a18_ucTgaHeader, 18, 1, hFile) != 1 )
		{
			fclose(hFile);
			return FALSE;
		}
		//get image width and height
		bi.biWidth = (a18_ucTgaHeader[13]<<8) + a18_ucTgaHeader[12];
		bi.biHeight = (a18_ucTgaHeader[15]<<8) + a18_ucTgaHeader[14];
	}
	else if ( !csExtension.CompareNoCase(".bmp") )
	{
		if (sizeof(bf) != fread( &bf, 1, sizeof(bf), hFile ))
		{
			fclose(hFile);
			return FALSE;
		}
		if (bf.bfType != 0x4d42 /*BM*/)
		{
			fclose(hFile);
			return FALSE;
		}
		if (sizeof(bi) != fread( &bi, 1, sizeof(bi), hFile ))
		{
			fclose(hFile);
			return FALSE;
		}
	}

	sz->cx = bi.biWidth;
	sz->cy = bi.biHeight;
	fclose(hFile);
	return TRUE;
}

/*-----------------------------------------------------------------------------
 *  Description : return max size to englobe all bitmaps in list
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vGetBoundBox( int& _cx, int& _cy)
{
	POSITION				pos = m_oItemList.GetHeadPosition();
	tdstBitmapItem	*p_stItem;
	BITMAP					stBitmap;

	// init max
	_cx = _cy = 0;
	// for all bitmap in list
	while (pos)
	{
		p_stItem = m_oItemList.GetNext( pos );
		if (!p_stItem->m_bDeleted)
		{
			GetObject( p_stItem->m_hBitmap, sizeof(BITMAP), &stBitmap );
			if ( _cx < stBitmap.bmWidth) _cx = stBitmap.bmWidth;
			if ( _cy < stBitmap.bmHeight) _cy = stBitmap.bmHeight;
		}
	}
}


/*-----------------------------------------------------------------------------
 *  Description : add a bitmap in list, return -1 if it fails
 *								the size of bitmap depends on the creation parameters and on
 *								the _cx & _cy parameters
 *								If m_bVariableSize is false, we load bitmap with a size of m_oMaxSize
 *								else if _cx = 0 || _cy = 0 we load the bitmap with real size
 *								else with size (_cx,_cy)
 *---------------------------------------------------------------------------*/
int CBitmapList::m_fn_iAdd(
	CString _csName, int _cx /*= 0*/, int _cy /*= 0*/, 
	BOOL _bSmallCopyIfRelevant /*= TRUE*/, BOOL _bOverrideOptimizedImages /*= FALSE*/
)
{
	_bSmallCopyIfRelevant; //prevent compiler warnings in case the argument is not used
	//verify the CBitmapList creation
	m_fn_vIsCreated();

	//create the directory in case it does not exist
#if defined(D_UseSmallBitmaps)
	CString         csNameOfSmallVersion;
	if ( m_bUseSmallCopies && _bSmallCopyIfRelevant )
	{
		char szFullName[_MAX_PATH], szDrive[_MAX_DRIVE], szPath[_MAX_DIR], szName[_MAX_FNAME], szExt[_MAX_EXT];
		//insert a subdirectory in the name of the image to compute the full name of the small file
		strcpy(szFullName, LPCTSTR(_csName));
		_splitpath(szFullName, szDrive, szPath, szName, szExt);
		strcat(szPath, "Small\\");
		_makepath(szFullName, szDrive, szPath, NULL, NULL);
		CreateDirectory(szFullName, NULL);
		_makepath(szFullName, szDrive, szPath, szName, szExt);
		//we will always generate a BMP file, so modify the name and add the extension
		char *pszExtDot = strrchr(szFullName, '.');
		if ( pszExtDot )
		{
			*pszExtDot = '_';
			strcat(szFullName, ".bmp");
		}
		csNameOfSmallVersion = szFullName;
	}
	else
		csNameOfSmallVersion = _csName;
#endif //D_UseSmallBitmaps

	int	iPos = m_fn_iFindBitmapByName(_csName);
	// bitmap is already loaded
	if (iPos != -1) 
		return iPos;

	if ( !m_bVariableSize )
	{
		_cx = m_oMaxSize.cx;
		_cy = m_oMaxSize.cy;
	}
	else
	{
		CSize						oBMSize;
		if ( (_cx == 0) || (_cy == 0))
		{ //if one value is equal to zero set the twice to zero
			_cx = _cy = 0;
		}
		// check if ther's a maximum size
		if ( m_oMaxSize.cx )
		{
			if 
			(	//try to read the size of one of the files containing the image to load
#if defined(D_UseSmallBitmaps)
				( (m_bUseSmallCopies && _bSmallCopyIfRelevant) && !m_fn_bGetBitmapSize(csNameOfSmallVersion, &oBMSize) )
				|| 
#endif //D_UseSmallBitmaps
				( !m_fn_bGetBitmapSize(_csName, &oBMSize) )
			)
				return -1;

			if ( (oBMSize.cx > m_oMaxSize.cx) || (oBMSize.cy > m_oMaxSize.cy))
			{ // image is to large, reduce size and keep ratio
				float fx = (float) oBMSize.cx / m_oMaxSize.cx;
				float fy = (float) oBMSize.cy / m_oMaxSize.cy;
				if (fx > fy)
				{
					_cx = m_oMaxSize.cx;
					_cy = (int) (oBMSize.cy / fx);
				}
				else
				{
					_cy = m_oMaxSize.cy;
					_cx = (int) (oBMSize.cx / fy);
				}
			}
		}
	}

	// load bitmap
	HBITMAP					hBitmap;
#if defined(D_UseSmallBitmaps)
	if ( m_bUseSmallCopies && _bSmallCopyIfRelevant )
	{
		//try to load the small one
		hBitmap = (HBITMAP) m_hLoadImage(csNameOfSmallVersion, _cx, _cy, _bOverrideOptimizedImages);
		if ( !hBitmap ) //try to load the original one and to save a small version
			hBitmap = (HBITMAP) m_hLoadImage(_csName, _cx, _cy, csNameOfSmallVersion, _bOverrideOptimizedImages);
	}
	else //try only with the original version here
#endif //D_UseSmallBitmaps
		hBitmap = (HBITMAP) m_hLoadImage(_csName, _cx, _cy, "", _bOverrideOptimizedImages);

	if ( !hBitmap )
		return -1;

	// search for the place where to put new item
	int iPlace;
	tdstBitmapItem	*p_stItem;
	if ( (iPlace = m_fn_iGetFreeIndex()) == -1)
		p_stItem = new tdstBitmapItem();
	else
	{
		POSITION xPos = m_oItemList.FindIndex(iPlace);
		p_stItem = m_oItemList.GetAt(xPos);
	}

	// set item members
	p_stItem->m_csName = _csName;
	p_stItem->m_hBitmap = hBitmap;
	p_stItem->m_bDeleted = FALSE;

	// put item in list in the good place
	if (iPlace == -1)
	{
		m_oItemList.AddTail(p_stItem);
		return (m_oItemList.GetCount() - 1);
	}
	else
		return iPlace;
}

/*-----------------------------------------------------------------------------
 *  Description : reload a bitmap
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vReload( CString _csBitmapName )
{
	POSITION		pos = m_oItemList.GetHeadPosition();
	tdstBitmapItem	*p_stItem;

	while (pos)
	{
		p_stItem = m_oItemList.GetNext( pos );
		if (!p_stItem->m_bDeleted)
			if (_csBitmapName.CompareNoCase(p_stItem->m_csName) == 0)
			{
				int		cx, cy;
				BITMAP  stBitmap;
				CBitmap *p_oBitmap = CBitmap::FromHandle( p_stItem->m_hBitmap );

				p_oBitmap->GetBitmap( &stBitmap );
				cx = stBitmap.bmWidth;
				cy = stBitmap.bmHeight;

				DeleteObject( p_stItem->m_hBitmap );

				p_stItem->m_hBitmap = (HBITMAP) m_hLoadImage(_csBitmapName, cx, cy, "", FALSE);
				break;
			}
	}
}

/*-----------------------------------------------------------------------------
 *  Description : get long associated with a bitmap
 *---------------------------------------------------------------------------*/
long CBitmapList::m_fn_lGetBitmapLong( int iImage )
{
	POSITION	pos;

	if ( (iImage>=0) && (pos = m_oItemList.FindIndex( iImage )) != NULL)
	{
		tdstBitmapItem *p_stItem = m_oItemList.GetAt( pos );
		return p_stItem->lData;
	}
	return 0;
}

/*-----------------------------------------------------------------------------
 *  Description : set long associated with a bitmap
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vSetBitmapLong( int iImage, long lLong )
{
	POSITION	pos;

	if (iImage < 0)
		return;

	if ((pos = m_oItemList.FindIndex( iImage )) != NULL)
	{
		tdstBitmapItem *p_stItem = m_oItemList.GetAt( pos );
		p_stItem->lData = lLong;
	}
}

/*-----------------------------------------------------------------------------
 *  Description : set long associated with a bitmap
 *---------------------------------------------------------------------------*/
int CBitmapList::m_fn_iGetBitmapIndexByLong( long lValue )
{
	int							iPos = 0;
	POSITION				pos = m_oItemList.GetHeadPosition();
	tdstBitmapItem	*p_stItem;

	while (pos)
	{
		p_stItem = m_oItemList.GetNext( pos );

		if ( (!p_stItem->m_bDeleted) && (p_stItem->lData == lValue) )
			return iPos;
		iPos ++;
	}
	return -1;
}

/*-----------------------------------------------------------------------------
 *  Description : get handle of bitmap
 *---------------------------------------------------------------------------*/
HBITMAP CBitmapList::m_fn_hGetBitmap( int iImage )
{
	POSITION	pos;

	if ( (iImage>=0) && (pos = m_oItemList.FindIndex( iImage )) != NULL)
	{
		tdstBitmapItem *p_stItem = m_oItemList.GetAt( pos );
		if (!p_stItem->m_bDeleted)
			return p_stItem->m_hBitmap;
	}
	return 0;
}

/*-----------------------------------------------------------------------------
 *  Description : get bitmap name
 *---------------------------------------------------------------------------*/
CString CBitmapList::m_fn_csGetBitmapName( int iImage )
{
	POSITION	pos;

	if ( (iImage>=0) && (pos = m_oItemList.FindIndex( iImage )) != NULL)
	{
		tdstBitmapItem *p_stItem = m_oItemList.GetAt( pos );
		if (!p_stItem->m_bDeleted)
			return p_stItem->m_csName;
	}
	return "";
}


/*-----------------------------------------------------------------------------
 *  Description : delete a bitmap
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vDelete( int iBitmap )
{
	POSITION	pos;

	if (iBitmap < 0)
		return;

	if ((pos= m_oItemList.FindIndex( iBitmap )) != NULL)
	{
		tdstBitmapItem *p_stItem = m_oItemList.GetAt( pos );
		if (!p_stItem->m_bDeleted)
		{
			DeleteObject( p_stItem->m_hBitmap );
			p_stItem->m_bDeleted = TRUE;
		}
	}
}

/*-----------------------------------------------------------------------------
 *  Description : delete all bitmaps
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vDeleteAll( void )
{
	POSITION				pos = m_oItemList.GetHeadPosition();
	tdstBitmapItem	*p_stItem;

	// delete all item in list
	while (pos)
	{
		p_stItem = m_oItemList.GetNext( pos );
		if (!p_stItem->m_bDeleted)
		{
			DeleteObject( p_stItem->m_hBitmap );
		}
		delete p_stItem;
	}
	// delete all pointer in list
	m_oItemList.RemoveAll();
}

/*-----------------------------------------------------------------------------
 *  Description : delete all bitmaps
 *---------------------------------------------------------------------------*/
int CBitmapList::m_fn_iGetFreeIndex( void )
{
	int							iIndex = 0;
	POSITION				pos = m_oItemList.GetHeadPosition();
	tdstBitmapItem	*p_stItem;

	//search for a free item
	while (pos)
	{
		p_stItem = m_oItemList.GetNext( pos );
		if (p_stItem->m_bDeleted)
			return iIndex;
		iIndex ++;
	}
	return -1;
}

/*-----------------------------------------------------------------------------
 *  Description : draw an image in the given CDC, at the given point
 *---------------------------------------------------------------------------*/
void CBitmapList::m_fn_vDraw( CDC* pDC, int iImage, POINT pt, RECT *pClipBox, char cAlignX /*= BA_LEFT*/, char cAlignY /*= BA_TOP*/ )
{
	//verify the CBitmapList creation
	m_fn_vIsCreated();
	// get position of image in list
	BITMAP		stBitmap;
	SIZE			sz;
	POSITION	pos;

	if (iImage < 0)
		return;

	if ((pos = m_oItemList.FindIndex( iImage )) != NULL)
	{
		// get bitmap item
		tdstBitmapItem *p_stItem = m_oItemList.GetAt( pos );
		if (p_stItem->m_bDeleted)
			return;
		// get bitmap information
		GetObject( p_stItem->m_hBitmap, sizeof(BITMAP), &stBitmap );
		// compute x coordinates and width
		switch (cAlignX)
		{
			case BA_LEFT:
					sz.cx = pClipBox->right - pt.x;
					if (sz.cx > stBitmap.bmWidth)
						sz.cx = stBitmap.bmWidth;
				break;
			case BA_RIGHT:
					sz.cx = pt.x - pClipBox->left;
					pt.x -= sz.cx;
					if (sz.cx < stBitmap.bmWidth)
						pt.x = 0;
					else
						sz.cx = stBitmap.bmWidth;
				break;
			case BA_CENTER:
					sz.cx = ( ((pt.x - pClipBox->left) < (pClipBox->right - pt.x)) ?
									(pt.x - pClipBox->left) : (pClipBox->right - pt.x) ) * 2;
					if (sz.cx > stBitmap.bmWidth)
						sz.cx = stBitmap.bmWidth;
					pt.x -= sz.cx / 2;
				break;
		}

		// compute y coordinates and width
		switch (cAlignY)
		{
			case BA_TOP:
					sz.cy = pClipBox->bottom - pt.y;
					if (sz.cy > stBitmap.bmHeight)
						sz.cy = stBitmap.bmHeight;
				break;
			case BA_BOTTOM:
					sz.cy = pt.y - pClipBox->top;
					if (sz.cy > stBitmap.bmHeight)
						sz.cy = stBitmap.bmHeight;
					pt.y -= sz.cy;
				break;
			case BA_CENTER:
					sz.cy = ( ((pt.y - pClipBox->top) < (pClipBox->bottom- pt.y)) ?
									(pt.y - pClipBox->top) : (pClipBox->bottom - pt.y) ) * 2;
					if (sz.cy > stBitmap.bmHeight)
						sz.cy = stBitmap.bmHeight;
					pt.y -= sz.cy / 2;
				break;
		}

		pDC->DrawState( pt, sz, p_stItem->m_hBitmap, DSS_NORMAL );
	}
}

/*=============================================================================
*
* implementation of CThreeSortedList
*
=============================================================================*/

/******************************************************************************
 * INTERNAL FUNCTION
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 *  Description : macro that return a boolean that indicate if index is valid or not
 *---------------------------------------------------------------------------*/
#define M_bIsIndexValid(wIndex) ((wIndex >= 0) && (wIndex < m_lNumberOfElements))

/*-----------------------------------------------------------------------------
 *  Description : function that verify validity off index and give true index
 *								switch given order
 *---------------------------------------------------------------------------*/
short CThreeSortedList::m_fn_wGetIndexSwitchOrder( short wIndex, char cOrder )
{
	// verify index
	if ( !M_bIsIndexValid(wIndex) )
		return TSL_cItemNotFound;
	// transform index switch order
	switch (cOrder)
	{
	case C_cAlphaOrder:
		return wIndex;

	case C_cCommonOrder:
		return (short) m_d_lCommonIndex[wIndex];

	case C_cEditedOrder:
		return (short) m_d_lEditedIndex[wIndex];
	}
	// no correspondant order, return TSL_cItemNotFound
	return TSL_cItemNotFound;
}

/*-----------------------------------------------------------------------------
 *  Description : find index in order list of an item index in alpha order
 *---------------------------------------------------------------------------*/
short CThreeSortedList::m_fn_wAlphaToAnotherOrder(short wIndex, char cOrder)
{
	long *m_p_lOrder;
	short wResult;
	// verify index
	if ( !M_bIsIndexValid(wIndex) )
		return TSL_cItemNotFound;

	switch (cOrder)
	{
	case C_cAlphaOrder:
		return wIndex;

	case C_cCommonOrder:
		m_p_lOrder = m_d_lCommonIndex;
		break;

	case C_cEditedOrder:
		m_p_lOrder = m_d_lEditedIndex;
		break;

	default:
		return TSL_cItemNotFound;
	}
	//find in the reorder array which element references the alpha index we specified
	for ( wResult = 0; wResult < m_lNumberOfElements; wResult ++ )
	if ( m_p_lOrder[wResult] == wIndex )
		return wResult;

	return TSL_cItemNotFound;
}

/******************************************************************************
 * CONSTRUCTOR / DESTRUCTOR
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 *  Description : Constructor
 *---------------------------------------------------------------------------*/
CThreeSortedList::CThreeSortedList()
{
	// initialised item list to null, zero items
    //m_d_stItem = NULL;
	m_lMaxElements = 0;
	m_lNumberOfElements = 0;
	m_d_lCommonIndex = NULL;
	m_d_lEditedIndex = NULL;
	m_p_oBitmapList = NULL;
	m_lSelectedItem = TSL_cItemNotFound;
}

/*-----------------------------------------------------------------------------
 *  Description : destructor
 *---------------------------------------------------------------------------*/
CThreeSortedList::~CThreeSortedList()
{
	m_fn_bDeleteAllItems();
	m_p_oBitmapList = NULL;
}

/*-----------------------------------------------------------------------------
 *  Description : Add an item
 *---------------------------------------------------------------------------*/
int CThreeSortedList::m_fn_iAddItem(CString csName, short wImage, long lContent, char cSortType /* = C_cAlphaOrder*/)
{
	// allocate memory if necessary
	if ( m_lMaxElements == m_lNumberOfElements )
	{
		// if no more place in list, increase max of elemenst
		m_lMaxElements += 20;
		// reallocate pointers
    m_oArrayOfTSLItem . SetSize(m_lMaxElements);
		m_d_lCommonIndex = (long *) realloc(m_d_lCommonIndex, sizeof(long) * m_lMaxElements);
		m_d_lEditedIndex = (long *) realloc(m_d_lEditedIndex, sizeof(long) * m_lMaxElements);
	}

	// find place where to put new item in alphabetic order
	long lTopIndex, lBottomIndex, lInsertIndex;
	lTopIndex = 0; //up is first in alpha order
	lBottomIndex = m_lNumberOfElements; //bottom is last
	if ( lBottomIndex == lTopIndex )
		lInsertIndex = 0;
	else
	do 
	{
		lInsertIndex = (lTopIndex + lBottomIndex) / 2;
		short wCompareResult = csName.CompareNoCase(m_oArrayOfTSLItem[lInsertIndex]->m_csName);
		if ( wCompareResult < 0 ) //string is in the upper part of the array
			lBottomIndex = lInsertIndex; //move the bottom of the range up
		else if ( wCompareResult > 0 ) //string is in the lower part of the array
			lTopIndex = lInsertIndex + 1; //move the top of the range down, leaving the tested element out of the range
		else //the string is equal to the middle's string
			lTopIndex = lBottomIndex = lInsertIndex; //insert it here
	} while ( lTopIndex != lBottomIndex );
	lInsertIndex = lTopIndex;
	//create the new item and fill it
  tdstTSLItem *p_stTSLItem = new tdstTSLItem;
  p_stTSLItem->m_csName = csName;
  p_stTSLItem->m_wImage = wImage;
  p_stTSLItem->m_lContent = lContent;

  // insert item at the found position
  m_oArrayOfTSLItem.InsertAt(lInsertIndex, p_stTSLItem);
  // if item is inserted before the selected item, shift selection number
  if ( (m_lSelectedItem != TSL_cItemNotFound) && m_lSelectedItem >= lInsertIndex )
      m_lSelectedItem ++;

	//shift all items in the reorder lists that are after the one referenced
	for 
	( 
		long lIndex = 0, *p_lCommon = m_d_lCommonIndex, *p_lEdited = m_d_lEditedIndex;
		lIndex < m_lNumberOfElements; 
		lIndex ++, p_lCommon ++, p_lEdited ++
	)
	{
		if ( *p_lCommon >= lInsertIndex )
			(*p_lCommon) ++;
		if ( *p_lEdited >= lInsertIndex )
			(*p_lEdited) ++;
	}
	// add item in order list
	m_d_lCommonIndex[m_lNumberOfElements] = lInsertIndex;
	m_d_lEditedIndex[m_lNumberOfElements] = lInsertIndex;

	// increase number of elements
	m_lNumberOfElements ++;

	return m_fn_wAlphaToAnotherOrder((short) lInsertIndex, cSortType);
}

/*-----------------------------------------------------------------------------
 *  Description : change name for the given item, this operation change
 *								order in alphabetical list.
 *								return the new index in list of the item
 *---------------------------------------------------------------------------*/
int CThreeSortedList::m_fn_iSetItemName( long lIndex, char *szName, char cSortType /*= C_cAlphaOrder */)
{
	short				wImage;
	long				lContent;
	BOOL				bIsTheSelectedItem;

	// recuperate true index, return if invalid index
	if ( (lIndex = m_fn_wGetIndexSwitchOrder((short) lIndex, cSortType )) == TSL_cItemNotFound )
		return TSL_cItemNotFound;

	// check if item is the selected one
	bIsTheSelectedItem = (lIndex == m_lSelectedItem);

	// save item data
	wImage = m_oArrayOfTSLItem[lIndex]->m_wImage;
	lContent = m_oArrayOfTSLItem[lIndex]->m_lContent;

	// delete item
	m_fn_bDeleteItem(lIndex);
	// add an item with old image and content but new name
	lIndex = m_fn_iAddItem(szName, wImage, lContent);

	// set the new number for selected item if the item was the selected one
	if (bIsTheSelectedItem)
		m_lSelectedItem = lIndex;

	// return item index in the given order
	return m_fn_wAlphaToAnotherOrder((short) lIndex, cSortType);
}

/*-----------------------------------------------------------------------------
 *  Description : change image index for the given item
 *---------------------------------------------------------------------------*/
void CThreeSortedList::m_fn_vSetItemImage( long lIndex, short _wImage, char cSortType /*= C_cAlphaOrder */)
{
	// recuperate true index
	if ( (lIndex = m_fn_wGetIndexSwitchOrder((short) lIndex, cSortType )) == TSL_cItemNotFound )
		return;
	// set item image
	m_oArrayOfTSLItem[lIndex]->m_wImage = _wImage;
}

/*-----------------------------------------------------------------------------
 *  Description : change pointer on content
 *---------------------------------------------------------------------------*/
void CThreeSortedList::m_fn_vSetItemContent( long lIndex, long lContent, char cSortType /*= C_cAlphaOrder */)
{
	// recuperate true index
	if ( (lIndex = m_fn_wGetIndexSwitchOrder((short) lIndex, cSortType )) == TSL_cItemNotFound )
		return;
	// set content of item
	m_oArrayOfTSLItem[lIndex]->m_lContent = lContent;
}

/*-----------------------------------------------------------------------------
 *  Description : delete an item
 *---------------------------------------------------------------------------*/
BOOL CThreeSortedList::m_fn_bDeleteItem( long lIndex, char cSortType /*= C_cAlphaOrder */ )
{
	// recuperate true index
	if ( (lIndex = m_fn_wGetIndexSwitchOrder((short) lIndex, cSortType )) == TSL_cItemNotFound)
		return FALSE;

	// update selected item
	if (lIndex == m_lSelectedItem)
		m_lSelectedItem = TSL_cItemNotFound;
	else if (m_lSelectedItem > lIndex)
		m_lSelectedItem --;

	//destroy the item, and remove it from the array
	delete (m_oArrayOfTSLItem[lIndex]);
	m_oArrayOfTSLItem.RemoveAt(lIndex);


	//shift all items in the reorder lists that are after the one referenced
	for 
	( 
		long lOrder = 0, *p_lCommon = m_d_lCommonIndex, *p_lEdited = m_d_lEditedIndex;
		lOrder < m_lNumberOfElements; 
		lOrder ++, p_lCommon ++, p_lEdited ++
	)
	{
		if ( *p_lCommon >= lIndex )
			(*p_lCommon) --;
		if ( *p_lEdited >= lIndex )
			(*p_lEdited) --;
	}

	// decrease number of item
	m_lNumberOfElements --;

	return TRUE;
}

/*-----------------------------------------------------------------------------
 *  Description : delete all items
 *---------------------------------------------------------------------------*/
BOOL CThreeSortedList::m_fn_bDeleteAllItems(void)
{
	for( int i = 0; i< m_lNumberOfElements ; i ++ )
		delete m_oArrayOfTSLItem[i];
	if ( m_d_lCommonIndex )
	{
		free(m_d_lCommonIndex);
		m_d_lCommonIndex = NULL;
	}
	if ( m_d_lEditedIndex )
	{
		free(m_d_lEditedIndex);
		m_d_lEditedIndex = NULL;
	}
	m_lSelectedItem = TSL_cItemNotFound;
	m_lMaxElements = 0;
	m_lNumberOfElements = 0;
	m_oArrayOfTSLItem.SetSize(0);
	return TRUE;
}

/*-----------------------------------------------------------------------------
 *  Description : swaps two items
 *---------------------------------------------------------------------------*/
void CThreeSortedList::m_fn_vSwapEditedItems( long lIndex1, long lIndex2 )
{
	long lTemp;
	if ( M_bIsIndexValid(lIndex1) && M_bIsIndexValid(lIndex2) )
	{
		lTemp = m_d_lEditedIndex[lIndex1];
		m_d_lEditedIndex[lIndex1] = m_d_lEditedIndex[lIndex2];
		m_d_lEditedIndex[lIndex2] = lTemp;
	}
}

/*-----------------------------------------------------------------------------
*  description : deplace one item in a order list
 *---------------------------------------------------------------------------*/
void CThreeSortedList::m_fn_vDeplaceOrderedItem(long lIndex, long lNewIndex, char cSortType)
{
	long *m_p_lOrder;
	// get order list, or exit if order is invalid (unknown or alpha order)
	switch (cSortType)
	{
	case C_cCommonOrder:
		m_p_lOrder = m_d_lCommonIndex;
		break;

	case C_cEditedOrder:
		m_p_lOrder = m_d_lEditedIndex;
		break;

	default:
		return;
	}
	// verify data (the two index)
	if (M_bIsIndexValid(lIndex) && M_bIsIndexValid(lNewIndex) && (lIndex != lNewIndex) )
	{
		// save index item to deplace
		long lTemp = m_p_lOrder[lIndex];
		// move other index to make a place
		if (lIndex > lNewIndex)
			memmove(m_p_lOrder + lNewIndex + 1, m_p_lOrder + lNewIndex, sizeof(long) * (lIndex - lNewIndex));
		else // lIndex < lNewIndex
			memmove(m_p_lOrder + lIndex, m_p_lOrder + lIndex + 1, sizeof(long) * (lNewIndex - lIndex));
		// set item in the new place
		m_p_lOrder[lNewIndex] = lTemp;
	}
}

/*-----------------------------------------------------------------------------
*  description : set the more common item
 *---------------------------------------------------------------------------*/
void CThreeSortedList::m_fn_vSetCommonItem(long lIndex)
{
	m_fn_vDeplaceOrderedItem(lIndex, 0, C_cCommonOrder);
}

/*-----------------------------------------------------------------------------
 *  Description : get an item
 *---------------------------------------------------------------------------*/
tdstTSLItem *CThreeSortedList::m_fn_p_stGetItem(long lIndex, char cSortType /*= C_cAlphaOrder */)
{
	// recuperate true index
	if ( (lIndex = m_fn_wGetIndexSwitchOrder((short) lIndex, cSortType )) == TSL_cItemNotFound)
		return NULL;

  //return &m_d_stItem[lIndex];
  return m_oArrayOfTSLItem[lIndex];
}

/*-----------------------------------------------------------------------------
 *  Description : get name of an item
 *---------------------------------------------------------------------------*/
CString CThreeSortedList::m_fn_csGetName( long lIndex, char cSortType /*= C_cAlphaOrder */)
{
	// get item
	tdstTSLItem *p_stItem = m_fn_p_stGetItem(lIndex, cSortType);
	// return item string if item was found
	if (p_stItem)
		return ( p_stItem->m_csName );
	return "";
}

/*-----------------------------------------------------------------------------
 *  Description : get image of an item
 *---------------------------------------------------------------------------*/
short CThreeSortedList::m_fn_wGetImage(long lIndex, char cSortType /*= C_cAlphaOrder */)
{
	// get item
	tdstTSLItem *p_stItem = m_fn_p_stGetItem(lIndex, cSortType);
	// return image index of item
	if (p_stItem)
		return ( p_stItem->m_wImage);
	return -1;
}

/*-----------------------------------------------------------------------------
 *  Description : get content of an item
 *---------------------------------------------------------------------------*/
long CThreeSortedList::m_fn_lGetContent( long lIndex, char cSortType /*= C_cAlphaOrder */ )
{
	// get item
	tdstTSLItem *p_stItem = m_fn_p_stGetItem( lIndex, cSortType );
	// return item content if item was found
	if (p_stItem)
		return ( p_stItem->m_lContent );
	return NULL;
}

/*-----------------------------------------------------------------------------
 *  Description : find an item that correspond to the given item pointer
 *---------------------------------------------------------------------------*/
long CThreeSortedList::m_fn_lFindItem(long lStartAt, tdstTSLItem *p_stItem, char cSortType /*= C_cAlphaOrder */)
{
	if ( lStartAt < 0 ) 
		lStartAt = 0;

	for (short wIndex = (short) lStartAt; wIndex < (short) m_lNumberOfElements; wIndex ++)
	{
    //if ( &m_d_stItem[ m_fn_wGetIndexSwitchOrder(wIndex, cSortType) ] == p_stItem )
    if ( (m_oArrayOfTSLItem[m_fn_wGetIndexSwitchOrder(wIndex, cSortType)]) == p_stItem )
			return wIndex;
	}
	return TSL_cItemNotFound;
}

/*-----------------------------------------------------------------------------
 *  Description : find item with same name as the given one
 *---------------------------------------------------------------------------*/
long CThreeSortedList::m_fn_lFindName(long lStartAt, CString csName, char cSortType /*= C_cAlphaOrder */)
{
	if ( lStartAt < 0 ) 
		lStartAt = 0;

	for (short wIndex = (short) lStartAt; wIndex < (short) m_lNumberOfElements; wIndex ++)
	{
    if (csName == (m_oArrayOfTSLItem[m_fn_wGetIndexSwitchOrder(wIndex, cSortType)])->m_csName )
			return wIndex;
	}
	return TSL_cItemNotFound;
}

/*-----------------------------------------------------------------------------
 *  Description : find item with the same image as the given one
 *---------------------------------------------------------------------------*/
long CThreeSortedList::m_fn_lFindImage( long lStartAt, short wImage, char cSortType /*= C_cAlphaOrder */)
{
	if ( lStartAt < 0 ) 
		lStartAt = 0;

	for (short wIndex = (short) lStartAt; wIndex < (short) m_lNumberOfElements; wIndex ++)
	{
    if (m_oArrayOfTSLItem[m_fn_wGetIndexSwitchOrder(wIndex, cSortType)]->m_wImage == wImage )
			return wIndex;
	}
	return TSL_cItemNotFound;
}

/*-----------------------------------------------------------------------------
 *  Description : find item with the same content as the given one
 *---------------------------------------------------------------------------*/
long CThreeSortedList::m_fn_lFindContent( long lStartAt, long lContent, char cSortType /*= C_cAlphaOrder*/ )
{
	if ( lStartAt < 0 ) 
		lStartAt = 0;

	for (short wIndex = (short) lStartAt; wIndex < (short) m_lNumberOfElements; wIndex ++)
	{
    if (m_oArrayOfTSLItem[m_fn_wGetIndexSwitchOrder(wIndex, cSortType)]->m_lContent == lContent )
			return wIndex;
	}
	return TSL_cItemNotFound;
}


/*-----------------------------------------------------------------------------
 *  Description : set selected item
 *---------------------------------------------------------------------------*/
void CThreeSortedList::m_fn_vSetSelectedItem( long lIndex, char cSortType )
{
	m_lSelectedItem = m_fn_wGetIndexSwitchOrder( (short) lIndex, cSortType );
}

/*-----------------------------------------------------------------------------
 *  Description : return selected item
 *---------------------------------------------------------------------------*/
long CThreeSortedList::m_fn_lGetSelectedItem( char cSortType )
{
	return m_fn_wAlphaToAnotherOrder( (short) m_lSelectedItem, cSortType );
}

/*-----------------------------------------------------------------------------
 *  Description : is the given item the selected one ?
 *---------------------------------------------------------------------------*/
BOOL CThreeSortedList::m_fn_bIsItemSelected( long lIndex, char cSortType )
{
	return (m_lSelectedItem == m_fn_wGetIndexSwitchOrder( (short) lIndex, cSortType ));
}


/*-----------------------------------------------------------------------------
 *  Description : unselect item
 *---------------------------------------------------------------------------*/
void CThreeSortedList::m_fn_vUnselectItem( void )
{
	m_lSelectedItem = TSL_cItemNotFound;
}


/*=============================================================================
* implementation of class C3ListFrame
=============================================================================*/
IMPLEMENT_DYNCREATE(C3ListFrame, FRMBase)
BEGIN_MESSAGE_MAP(C3ListFrame, FRMBase)
    ON_WM_CREATE()
    ON_WM_CLOSE()
END_MESSAGE_MAP()

/*-----------------------------------------------------------------------------
 *  Description : constructor
 *---------------------------------------------------------------------------*/
C3ListFrame::C3ListFrame(void)
{
}

/*-----------------------------------------------------------------------------
 *  Description : create the window
 *---------------------------------------------------------------------------*/
int C3ListFrame::OnCreate(LPCREATESTRUCT _p_stCreateStruct)
{
  // Create frame
	if (CFrameWnd::OnCreate(_p_stCreateStruct) == -1)
		return -1;

	return 0;
}

/*-----------------------------------------------------------------------------
 *  Description : close the window
 *---------------------------------------------------------------------------*/
void C3ListFrame::OnClose(void)
{
    CFrameWnd::OnClose();
}


/*=============================================================================
* implementation off C3ListView class
=============================================================================*/
IMPLEMENT_DYNCREATE(C3ListView, CScrollView)
BEGIN_MESSAGE_MAP(C3ListView, CScrollView)
	ON_WM_PAINT()
	ON_WM_CREATE()
	ON_WM_RBUTTONDOWN()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_MESSAGE( WM_DRAGDROP_END, OnDragDropEnd )
	ON_MESSAGE( WM_DRAGDROP_MOVE, OnDragDropMove )
	ON_MESSAGE( WM_DRAGDROP_SCROLL, OnDragDropScroll )
	ON_WM_WINDOWPOSCHANGED()
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()

/*-----------------------------------------------------------------------------
 *  Description : constructor
 *---------------------------------------------------------------------------*/
C3ListView::C3ListView( void ): CScrollView(), CThreeSortedList()
{
	// initialise member variables
	// box to display bitmap alone
	m_a_oDisplayBox[C_cDisplayBitmap].cx = 30;
	m_a_oDisplayBox[C_cDisplayBitmap].cy = 30;
	// box to display text & bitmap
	m_a_oDisplayBox[C_cDisplayBitmapAndText].cx = 80;
	m_a_oDisplayBox[C_cDisplayBitmapAndText].cy = 50;
	// box to display text alone
	m_a_oDisplayBox[C_cDisplayText].cx = 80;
	m_a_oDisplayBox[C_cDisplayText].cy = 20;
	// display grid
	m_bGrid = TRUE;
	// current order
	m_cCurrentOrder = C_cAlphaOrder;
	// current deplacement type
	m_cMoveType = C_cSwap;
	// init menu flags
	m_lMenuFlags =	TLVM_lMenu | TLVM_lGrid | TLVM_lOrder | TLVM_lDisplay;

	//Init Drag&Drop data
	m_oDDCase.fn_vSetDataType( 1234 );
	m_oDDCase.fn_vSetEditorName("coucou");

	m_wCaseWithTips = -1;
}

/*-----------------------------------------------------------------------------
 *  Description : set Drag and drop data and editor type
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vSetDDTypeAndEditor( UINT uiDataType, char *szEditorName )
{
	m_oDDCase.fn_vSetDataType( uiDataType );
	m_oDDCase.fn_vSetEditorName( szEditorName );
}

/*-----------------------------------------------------------------------------
 *  Description : get case index following point in client window rect
 *---------------------------------------------------------------------------*/
short C3ListView::m_fn_wGetCaseFromClientPoint( int x, int y)
{
	short wLine, wCase, wCol;
	// compute coordinates in View
	x += GetScrollPosition().x;
	y += GetScrollPosition().y;
	// compute col, line and case were point is
	wCol  = (short) (x / m_oCurrentBox.cx);
	wLine = (short) (y / m_oCurrentBox.cy);
	wCase = wLine * m_wNumberOfCols + wCol;
	// check if case is valid
	if ( (wCol >= m_wNumberOfCols) || (wLine >= m_wNumberOfLines) || (wCase >= m_fn_lGetCount()) )
		return TSL_cItemNotFound;
	return wCase;
}

/*-----------------------------------------------------------------------------
 *  Description : get case rectangle of a case given by it's index
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vGetCaseRect( short wIndex, RECT *pRect )
{
	// get line and column of case
	short wLine = wIndex / m_wNumberOfCols;
	short wCol = wIndex - wLine * m_wNumberOfCols;
	// compute rect
	pRect->left   = wCol * m_oCurrentBox.cx;
	pRect->right  = pRect->left + m_oCurrentBox.cx;
	pRect->top    = wLine * m_oCurrentBox.cy;
	pRect->bottom = pRect->top + m_oCurrentBox.cy;
}


/*-----------------------------------------------------------------------------
 *  Description : Hide the tips for the m_wCaseWithTips case
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vHideTips(  )
{
	CDC			*p_oDC;
	long		lWidth;
	long		lHeight;
	
	if (m_wCaseWithTips == -1)
		return;

	m_wCaseWithTips = -1;

	//p_oDC = GetWindowDC();
	p_oDC = GetDesktopWindow()->GetWindowDC();

	lWidth = m_stTipsRect.right - m_stTipsRect.left + 1;
	lHeight = m_stTipsRect.bottom - m_stTipsRect.top + 1;

	p_oDC->BitBlt( m_stTipsRect.left, m_stTipsRect.top, lWidth, lHeight, &m_oCDCTip, 0, 0, SRCCOPY );

	ReleaseDC( p_oDC );
	m_oCDCTip.DeleteDC();
	m_oBitmapTip.DeleteObject();
}

/*-----------------------------------------------------------------------------
 *  Description : show the tips for the m_wCaseWithTips case
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vShowTips( void )
{
	CDC			*p_oDC;
	CFont		 xFnt, *xFntPrev = NULL;
	PLOGFONT	plf = (PLOGFONT) LocalAlloc(LPTR, sizeof(LOGFONT));
	CString		csString;
	CSize		oTextSize;
	RECT		stWinRect;

	if (m_wCaseWithTips == -1)
		return;

	p_oDC = GetDesktopWindow()->GetWindowDC();

	/*
	 * compute tip rect
	 */
	m_fn_vGetCaseRect( m_wCaseWithTips, &m_stTipsRect );
	m_stTipsRect.left -= GetScrollPosition().x;
	m_stTipsRect.right -= GetScrollPosition().x;
	m_stTipsRect.top -= GetScrollPosition().y;
	m_stTipsRect.bottom -= GetScrollPosition().y;

	ClientToScreen( &m_stTipsRect );

	lstrcpy(plf->lfFaceName, "Arial Narrow");
	plf->lfWeight = FW_NORMAL;
	plf->lfHeight = 16;
	plf->lfEscapement = 0;
	if (xFnt.CreateFontIndirect(plf))
		xFntPrev = p_oDC->SelectObject( &xFnt );

	csString = m_fn_csGetName( m_wCaseWithTips, m_cCurrentOrder );

	oTextSize = p_oDC->GetTextExtent( csString, csString.GetLength() );
	oTextSize.cx += 4;
	oTextSize.cy += 4;

	m_stTipsRect.left = (m_stTipsRect.left + m_stTipsRect.right - 1 - oTextSize.cx) / 2;
	m_stTipsRect.right = m_stTipsRect.left + oTextSize.cx;

	if (m_cDisplayType == C_cDisplayBitmap)
	{
		m_stTipsRect.top = m_stTipsRect.bottom;
		m_stTipsRect.bottom += oTextSize.cy;
	}
	else
		m_stTipsRect.top = m_stTipsRect.bottom - oTextSize.cy;

	GetWindowRect( &stWinRect );
	if (m_stTipsRect.top > stWinRect.bottom)
	{
		m_stTipsRect.top = stWinRect.bottom;
		m_stTipsRect.bottom = m_stTipsRect.top + oTextSize.cy;
	}

	/*
	 * save tip background
	 */
	m_oCDCTip.CreateCompatibleDC( p_oDC ); 
	m_oBitmapTip.CreateCompatibleBitmap( p_oDC, oTextSize.cx , oTextSize.cy ); 
	m_oCDCTip.SelectObject( &m_oBitmapTip );

	m_oCDCTip.BitBlt( 0, 0, oTextSize.cx, oTextSize.cy, p_oDC, m_stTipsRect.left, m_stTipsRect.top, SRCCOPY);

	/*
	 * draw tip
	 */
	p_oDC->Rectangle( &m_stTipsRect );
	//p_oDC->FillSolidRect( &m_stTipsRect, RGB(255,255,255) );

	// draw string
	p_oDC->SetBkMode( TRANSPARENT );
	p_oDC->SetTextAlign( TA_CENTER | TA_BOTTOM );
	p_oDC->TextOut((m_stTipsRect.left + m_stTipsRect.right + 1) / 2, m_stTipsRect.bottom - 2, csString, csString.GetLength());
	

	if ( xFntPrev )
	{
		p_oDC->SelectObject(xFntPrev);
		xFnt.DeleteObject();
	}
	//do not forget to free the memory for the font
	LocalFree(plf);
	

	ReleaseDC( p_oDC);
}

/*-----------------------------------------------------------------------------
 *  Description : make the view scroll so that the item is visible, if necessary
 *---------------------------------------------------------------------------*/
void C3ListView::m_vScrollToMakeItemVisible(short _wItem)
{
	Invalidate();
	if ( _wItem == TSL_cItemNotFound )
		return;

	CRect oItemRect, oClientRect;
	GetClientRect(oClientRect);
	oClientRect.OffsetRect(GetScrollPosition());
	m_fn_vGetCaseRect(_wItem, &oItemRect);
	//if the item's rectangle is not entirely visible in the client 
	if ( (oClientRect & oItemRect) != oItemRect )
	{
		//make the view scroll so that the center of the item's rectangle is
		//vertically moved to the middle of the client area
		POINT stPoint;
		stPoint.x = GetScrollPosition().x;
		stPoint.y = oItemRect.CenterPoint().y - oClientRect.Height() / 2;
		ScrollToPosition(stPoint);
	}
}


/*-----------------------------------------------------------------------------
 *  Description : change display type
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vChangeDisplayType( char _cDisplayType )
{
	// check display type
	if 
	(
		(((unsigned char) _cDisplayType) >= C_cNumberOfDisplayTypes)
		//|| (m_cDisplayType == _cDisplayType)
	)
		
		return;

	// change current case size
	m_oCurrentBox = m_a_oDisplayBox[ _cDisplayType ];
	// change display type varialbe
	m_cDisplayType = _cDisplayType;

	// calculate new size of CScrollView
	RECT rect;
	CSize sz;

	//GetParent()->GetClientRect( &rect );
	GetWindowRect( &rect );

	// compute number of columns that fit in view rect
	//m_wNumberOfCols = rect.right / m_oCurrentCase.cx;
	m_wNumberOfCols = (short) ((rect.right - rect.left - 14) / m_oCurrentBox.cx);
	if (m_wNumberOfCols <= 0) m_wNumberOfCols = 1;
	// compute number of lines
	m_wNumberOfLines = (m_fn_lGetCount() + m_wNumberOfCols - 1) / m_wNumberOfCols;
	// compute size of view
	sz.cx = m_wNumberOfCols * m_oCurrentBox.cx;
	sz.cy = m_wNumberOfLines * m_oCurrentBox.cy;
	if (sz.cy == 0) sz.cy = 1;
	SetScrollSizes( MM_TEXT, sz );
	// redraw view
	m_vScrollToMakeItemVisible((short) m_fn_lGetSelectedItem(m_cCurrentOrder));
}

/*-----------------------------------------------------------------------------
 *  Description : change order
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vChangeOrder( char _cOrder )
{
	if (_cOrder == m_cCurrentOrder)
		return;
	// resize view if necessary
	/*
	if ( _cOrder == C_cEditedOrder || m_cCurrentOrder == C_cEditedOrder)
	{
		CWnd *pFrameBase = GetParent();
		while ( !pFrameBase->IsKindOf( RUNTIME_CLASS( FRMBase ) ) )
		{
			pFrameBase = pFrameBase->GetParent();
		}
		((FRMBase *) pFrameBase)->m_fn_vSwapFullScreen();
	}
	*/
	// change order variable
	m_cCurrentOrder = _cOrder;

	// redraw view
	m_vScrollToMakeItemVisible((short) m_fn_lGetSelectedItem(m_cCurrentOrder));
}

/*-----------------------------------------------------------------------------
 *  Description : change size of a display box
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vSetBox( char _cDisplayType, int _iWidth, int _iHeight)
{
	// check display type
	if (((unsigned char) _cDisplayType) >= C_cNumberOfDisplayTypes)
		return;
	// set new size
	m_a_oDisplayBox[ _cDisplayType ].cx = _iWidth;
	m_a_oDisplayBox[ _cDisplayType ].cy = _iHeight;

	if (_cDisplayType == m_cDisplayType )
		m_fn_vChangeDisplayType( _cDisplayType ); // to refresh display
}

/*-----------------------------------------------------------------------------
 *  Description : enable a menu flag
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vEnableMenuItem( long lFlags )
{
	m_lMenuFlags |= lFlags;
}

/*-----------------------------------------------------------------------------
 *  Description : disable some menu flag
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vDisableMenuItem( long lFlags )
{
	m_lMenuFlags &= ~lFlags;
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_CREATE message
 *---------------------------------------------------------------------------*/
int C3ListView::OnCreate( LPCREATESTRUCT _p_stCreateStruct )
{
	if ( CScrollView::OnCreate(_p_stCreateStruct) == -1 )
		return -1;

	m_fn_vChangeDisplayType( C_cDisplayText );

	m_oDDCase.fn_vSetOwner( this );

	return 0;
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_RBUTTONDOWN message
 *---------------------------------------------------------------------------*/
void C3ListView::OnRButtonDown(UINT nFlags, CPoint pt )
{
	if ((m_lMenuFlags & TLVM_lMenu) == 0)
		return;

	// hide tips if one
	m_fn_vHideTips();

	ClientToScreen( &pt );
	// create popup menu
	CMenu oMenu;
	oMenu.CreatePopupMenu();
	// entries to change display type
	if (m_lMenuFlags & TLVM_lBitmapAndText)
		oMenu.AppendMenu( MF_STRING | ((m_cDisplayType == C_cDisplayBitmapAndText)?MF_CHECKED:MF_UNCHECKED), 1, "Bitmap and Text");
	if (m_lMenuFlags & TLVM_lText)
		oMenu.AppendMenu( MF_STRING | ((m_cDisplayType == C_cDisplayText)?MF_CHECKED:MF_UNCHECKED), 2, "Only Text");
	if (m_lMenuFlags & TLVM_lBitmap)
		oMenu.AppendMenu( MF_STRING | ((m_cDisplayType == C_cDisplayBitmap)?MF_CHECKED:MF_UNCHECKED), 3, "Only Bitmap");

	if ((m_lMenuFlags & TLVM_lDisplay ) && (m_lMenuFlags & (TLVM_lGrid | TLVM_lOrder)) )
		oMenu.AppendMenu( MF_SEPARATOR );
	// entry to swap grid on/off
	if (m_lMenuFlags & TLVM_lGrid)
	{
		oMenu.AppendMenu( (m_bGrid ? MF_CHECKED : MF_UNCHECKED) | MF_STRING, 4, "Grid");
		if (m_lMenuFlags & TLVM_lOrder)
			oMenu.AppendMenu( MF_SEPARATOR );
	}
	// entries to change display order
	if (m_lMenuFlags & TLVM_lAlphabetic)
		oMenu.AppendMenu( MF_STRING | ((m_cCurrentOrder == C_cAlphaOrder)?MF_CHECKED:MF_UNCHECKED), 5, "Alphabetic");
	if (m_lMenuFlags & TLVM_lCommon)
		oMenu.AppendMenu( MF_STRING | ((m_cCurrentOrder == C_cCommonOrder)?MF_CHECKED:MF_UNCHECKED), 6, "Common");
	if (m_lMenuFlags & TLVM_lEdited)
		oMenu.AppendMenu( MF_STRING | ((m_cCurrentOrder == C_cEditedOrder)?MF_CHECKED:MF_UNCHECKED), 7, "Edited");
	// entry to change move type (if in edited order)
	if (m_cCurrentOrder == C_cEditedOrder)
	{
		oMenu.AppendMenu( MF_SEPARATOR );
		if (m_cMoveType == C_cSwap)
			oMenu.AppendMenu( MF_STRING, 8, "Deplace");
		else
			oMenu.AppendMenu( MF_STRING, 8, "Swap");
	}

	//enable derived instances to add their own entries in the menu, starting with this value
	_InitPopupMenu(oMenu, 9);

    // register the popup menu for the tutorial module
    TUT_M_vGetTutDll    ();
    TUT_M_vRegisterMenu (this -> m_hWnd , oMenu . m_hMenu , pt. x , pt . y);

	// display & track popup menu
	oMenu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
	// destroy menus
	oMenu.DestroyMenu();
}

/*-----------------------------------------------------------------------------
 *  Description : on select item
 *---------------------------------------------------------------------------*/
void C3ListView::OnSelectItem( short wCase )
{
	RECT		rect;
	CDC			*pDC = GetDC();
	CPoint	oPt = GetScrollPosition();

	long lOldSelected = m_fn_lGetSelectedItem(m_cCurrentOrder);

	m_fn_vSetSelectedItem( wCase, m_cCurrentOrder );
	if (wCase != lOldSelected )
	{
		if ( lOldSelected != TSL_cItemNotFound )
		{
			m_fn_vGetCaseRect((short) lOldSelected, &rect );
			rect.top -= oPt.y;
			rect.bottom -= oPt.y;
			rect.left -= oPt.x;
			rect.right -= oPt.x;
			pDC->InvertRect( &rect );
		}
		if ( wCase != TSL_cItemNotFound )
		{
			m_fn_vGetCaseRect(wCase, &rect );
			rect.top -= oPt.y;
			rect.bottom -= oPt.y;
			rect.left -= oPt.x;
			rect.right -= oPt.x;
			pDC->InvertRect( &rect );
		}
	}
	ReleaseDC( pDC );
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_LBUTTONDOWN message
 *---------------------------------------------------------------------------*/
void C3ListView::OnLButtonDown(UINT nFlags, CPoint pt )
{
	short	wCase;
	RECT	rect;

	// hide tips if one
	m_fn_vHideTips();

	// compute case under mouse
	wCase = m_fn_wGetCaseFromClientPoint( pt.x, pt.y);
	OnSelectItem( wCase );

	if ( wCase == TSL_cItemNotFound )
		return;

	// get case rectangle
	m_fn_vGetCaseRect( wCase, &rect );

	// Set Drag & drop data
	m_oDDCase.fn_vSetData( (void *) wCase );

	// Add reference on window that accept Drop message
	// can drag and drop in this window only if we are in Edited order
	if (m_cCurrentOrder == C_cEditedOrder )
	{
		RECT windowRect;
		RECT scrollRect;
		int  iWidth;

		GetWindowRect( &windowRect );
		if ((iWidth = (windowRect.bottom - windowRect.top) / 2) > 10)
			iWidth = 10;

		scrollRect.right = windowRect.right;
		scrollRect.left = windowRect.left;

		scrollRect.top = windowRect.top;
		scrollRect.bottom = scrollRect.top + iWidth;
		DD_vRegisterRect( &scrollRect, this->GetSafeHwnd() );

		scrollRect.bottom = windowRect.bottom;
		scrollRect.top = scrollRect.bottom - iWidth;
		DD_vRegisterRect( &scrollRect, this->GetSafeHwnd() );
		DD_vSetScrollTime( 100 );
		DD_eAddReference( this->GetSafeHwnd(), m_oDDCase.fn_p_szGetEditorName(), m_oDDCase.fn_uiGetDataType(), FALSE  );
	}

	// launch drag & drop operation
	HWND hWnd = DD_hBeginDragDrop( &rect, &m_oDDCase );

	// if item was dragged to another window, set first in the common order
	if ( (hWnd != this->GetSafeHwnd()) && (hWnd != NULL) )
	{
		m_fn_vSetCommonItem( wCase );
		Invalidate();
	}
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_MOUSEMOVE message
 *---------------------------------------------------------------------------*/
void C3ListView::OnMouseMove( UINT nFlags, CPoint pt)
{
	short wCase;


	if (GetCapture() == this)
	{
		RECT stRect;
		GetClientRect( &stRect );
		if( (pt.x < 0) || (pt.y < 0) || (pt.x>stRect.right) || (pt.y>stRect.bottom) )
		{
			m_fn_vHideTips();
			ReleaseCapture();
			return;
		}
		if ((GetForegroundWindow() != this) || (GetActiveWindow() != this))
		{
			m_fn_vHideTips();
			ReleaseCapture();
			return;
		}
	}
	else
	{
		SetCapture();
	}

	wCase = m_fn_wGetCaseFromClientPoint( pt.x, pt.y);

	if (wCase == TSL_cItemNotFound)
	{
		if (m_wCaseWithTips != -1 )
			m_fn_vHideTips();
	}
	else
	{
		if (m_wCaseWithTips != wCase)
		{
			if (m_wCaseWithTips != -1)
				m_fn_vHideTips();
			m_wCaseWithTips = wCase;
			m_fn_vShowTips();
		}
	}
}


/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_DRAGDROP_SCROLL message
 *---------------------------------------------------------------------------*/
LONG C3ListView::OnDragDropScroll( UINT wParam , LONG lParam)
{
	RECT		windowRect;
	RECT		*scrollRect = (RECT *) lParam;
	POINT		pos = GetScrollPosition();

	GetWindowRect( &windowRect );

	if (scrollRect->top == windowRect.top)
		pos.y -= 20;
	else
		pos.y += 20;

	ScrollToPosition( pos );
	return 0;
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_DRAGDROP_END message
 *---------------------------------------------------------------------------*/
LONG C3ListView::OnDragDropEnd( UINT wParam , LONG lParam)
{
	short wCase;

	// check current order
	if (m_cCurrentOrder != C_cEditedOrder)
		return 0;

	DD_Object *p_oDDObject = (DD_Object *) lParam;

	// check if data come from this
	if (p_oDDObject != &m_oDDCase )
		return 0;

	// compute case where object were dropped
	ScreenToClient( &p_oDDObject->m_stMousePos );
	wCase = m_fn_wGetCaseFromClientPoint( p_oDDObject->m_stMousePos.x, p_oDDObject->m_stMousePos.y );
	if ( wCase == TSL_cItemNotFound )
		return 0;

	// swap item or deplace item switch move type
	if (m_cMoveType == C_cSwap)
		m_fn_vSwapEditedItems( (short) p_oDDObject->fn_p_vGetData(), wCase );
	else
		m_fn_vDeplaceOrderedItem( (short) p_oDDObject->fn_p_vGetData(), wCase, C_cEditedOrder );

	//redraw list;
	Invalidate();
	return 0;
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_DRAGDROP_MOVE message
 *---------------------------------------------------------------------------*/
LONG C3ListView::OnDragDropMove( UINT wParam , LONG lParam)
{
	return 0;
}


/*-----------------------------------------------------------------------------
 *  Description : pretranslate message
 *---------------------------------------------------------------------------*/
BOOL C3ListView::PreTranslateMessage( MSG* pMsg )
{
	if (pMsg->message == WM_KEYDOWN)
	{
		if (pMsg->wParam != VK_TAB)
		{
			OnKeyDown( pMsg->wParam, LOWORD(pMsg->lParam),HIWORD(pMsg->lParam) );
			return 1;
		}
	}
	return 0;
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_COMMAND message
 *---------------------------------------------------------------------------*/
BOOL C3ListView::OnCommand( WPARAM wParam, LPARAM lParam )
{
	if (HIWORD(wParam) == 0) // command from a menu
	{
		switch (LOWORD(wParam) ) //ID of menu item
		{
			case 1:
					m_fn_vChangeDisplayType( C_cDisplayBitmapAndText );
				break;
			case 2:
					m_fn_vChangeDisplayType( C_cDisplayText );
				break;
			case 3:
					m_fn_vChangeDisplayType( C_cDisplayBitmap );
				break;
			case 4:
					m_bGrid = !m_bGrid;
					Invalidate();
				break;
			case 5:
					m_fn_vChangeOrder( C_cAlphaOrder );
				break;
			case 6:
					m_fn_vChangeOrder( C_cCommonOrder );
				break;
			case 7:
					m_fn_vChangeOrder( C_cEditedOrder );
				break;
			case 8:
					m_cMoveType = 1 - m_cMoveType;
				break;
			//handle entries added by derived instances
			default:
				_OnCommand(LOWORD(wParam));
				break;
		}
	}
	return FALSE;
}

/*-----------------------------------------------------------------------------
 *  Description : handle function fo WM_KEYDOW message
 *---------------------------------------------------------------------------*/
void C3ListView::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
	short wDeltaY = 0;
	switch (nChar)
	{
		case VK_DOWN:		wDeltaY = 10;		break;
		case VK_UP:			wDeltaY = -10;	break;
		case VK_PRIOR:	wDeltaY = -50;  break;
		case VK_NEXT:		wDeltaY = 50;		break;
		case VK_HOME:		wDeltaY = (short) -GetScrollPosition().y;	break;
		case VK_END:		wDeltaY = (short) (GetTotalSize().cy - GetScrollPosition().y); break;
	}
	if (wDeltaY)
	{
		CPoint pt = GetScrollPosition();
		pt.y += wDeltaY;
		ScrollToPosition( (POINT) pt );
	}
}

/*-----------------------------------------------------------------------------
 *  Description : handle function for WM_WINDOWPOSCHANGED message
 *---------------------------------------------------------------------------*/
void C3ListView::OnWindowPosChanged( WINDOWPOS* lpwndpos )
{
	m_fn_vChangeDisplayType(m_cDisplayType);
}

/*-----------------------------------------------------------------------------
 *  Description : handle function for drawing message
 *---------------------------------------------------------------------------*/
void C3ListView::OnDraw(CDC *pDC )
{
	RECT rect;
	pDC->GetClipBox( &rect);

	m_fn_vHideTips();

	// calculate first case and last case in drawing zone
	short wFirstLine = (short) (rect.top / m_oCurrentBox.cy);
	short wLastLine = (short) (rect.bottom / m_oCurrentBox.cy);
	if (wFirstLine < 0) wFirstLine = 0;
	if (wLastLine >= m_wNumberOfLines) wLastLine = m_wNumberOfLines - 1;

	rect.top = wFirstLine * m_oCurrentBox.cy;
	rect.bottom = rect.top + m_oCurrentBox.cy;

	short wCase = wFirstLine * m_wNumberOfCols;

	for (	short wLine = wFirstLine; wLine <= wLastLine ; wLine ++)
	{
		rect.left = 0;
		rect.right = m_oCurrentBox.cx;
		for ( short wCol = 0; wCol < m_wNumberOfCols && wCase < m_fn_lGetCount(); wCase ++, wCol ++ )
		{
			m_fn_vOverriddenDrawItem( wCase, pDC, &rect );
			rect.left		+= m_oCurrentBox.cx;
			rect.right	+= m_oCurrentBox.cx;
		}
		rect.top += m_oCurrentBox.cy;
		rect.bottom += m_oCurrentBox.cy;
	}
}

/*-----------------------------------------------------------------------------
 *  Description : draw an item
 *---------------------------------------------------------------------------*/
void C3ListView::m_fn_vDrawItem(short wItem, CDC *pDC, RECT *p_Rect, short _wSpecialImage /*= TSL_cItemNotFound */ )
{
	RECT		stBitmapRect;
	CPoint	oImageOrg;
	short		wImage = m_fn_wGetImage(wItem, m_cCurrentOrder);

	// draw rectangle
	if ( m_bGrid )
		pDC->Rectangle(p_Rect);
	else
		pDC->FillRect(p_Rect, pDC->GetCurrentBrush());

	switch ( m_cDisplayType )
	{
	case C_cDisplayBitmap:
		if ( (m_fn_p_oGetBitmapList()) && (wImage != TSL_cItemNotFound) )
		{
			stBitmapRect.top = p_Rect->top - GetScrollPosition().y + 1;
			stBitmapRect.bottom = p_Rect->bottom - GetScrollPosition().y - 1;
			stBitmapRect.left = p_Rect->left - GetScrollPosition().x + 1;
			stBitmapRect.right = p_Rect->right - GetScrollPosition().x - 1;

			oImageOrg.x = (stBitmapRect.left + stBitmapRect.right) / 2;
			oImageOrg.y = (stBitmapRect.top + stBitmapRect.bottom) / 2;

			m_fn_p_oGetBitmapList()->m_fn_vDraw( pDC, wImage, oImageOrg, &stBitmapRect, BA_CENTER , BA_CENTER );
		}
		break;

	case C_cDisplayBitmapAndText:
		// display bitmap
		if ( (m_fn_p_oGetBitmapList()) && (wImage != TSL_cItemNotFound) )
		{
			stBitmapRect.top = p_Rect->top - GetScrollPosition().y + 2;
			stBitmapRect.bottom = p_Rect->bottom - GetScrollPosition().y - 20;
			stBitmapRect.left = p_Rect->left - GetScrollPosition().x + 1;
			stBitmapRect.right = p_Rect->right - GetScrollPosition().x - 1;

			oImageOrg.x = (stBitmapRect.left + stBitmapRect.right) / 2;
			oImageOrg.y = stBitmapRect.top;

			m_fn_p_oGetBitmapList()->m_fn_vDraw( pDC, wImage, oImageOrg, &stBitmapRect, BA_CENTER , BA_TOP );
		}
		// no break : draw bitmap too

	case C_cDisplayText:
		// compute max number char to show to stay in rect
		{
			CFont		 xFnt, *xFntPrev = NULL;
			PLOGFONT plf = (PLOGFONT) LocalAlloc(LPTR, sizeof(LOGFONT));

			lstrcpy(plf->lfFaceName, "Arial Narrow");
			plf->lfWeight = (m_cDisplayType == C_cDisplayBitmapAndText || wImage == _wSpecialImage) ? FW_NORMAL : FW_BOLD;
			plf->lfHeight = 16;
			plf->lfEscapement = 0;
			if (xFnt.CreateFontIndirect(plf))
				xFntPrev = pDC->SelectObject( &xFnt );

			CString csString = m_fn_csGetName( wItem, m_cCurrentOrder );
			short wNbChar = csString.GetLength() + 1;
			CSize	oTextSize;
			do
			{
				wNbChar --;
				oTextSize = pDC->GetTextExtent( csString, wNbChar );
			} while (oTextSize.cx > (p_Rect->right - p_Rect->left -2) );
			// draw string
			pDC->SetBkMode( TRANSPARENT );
			pDC->SetTextAlign( TA_CENTER | TA_BOTTOM );
			pDC->TextOut((p_Rect->left + p_Rect->right) / 2, p_Rect->bottom - 2, csString, wNbChar);
			if ( xFntPrev )
			{
				pDC->SelectObject(xFntPrev);
				xFnt.DeleteObject();
			}
			//do not forget to free the memory for the font
			LocalFree(plf);
		}
		break;
	}

	if ( m_fn_bIsItemSelected( wItem, m_cCurrentOrder) )
		pDC->InvertRect( p_Rect );
}

/*-----------------------------------------------------------------------------
 *  Description : add an item
 *---------------------------------------------------------------------------*/
int C3ListView::m_fn_iAddItem( CString csName, short wImage , long lContent, char cSortType /* C_cAlphaOrder*/ )
{
	int iResult = CThreeSortedList::m_fn_iAddItem( csName, wImage, lContent, cSortType );
	m_fn_vChangeDisplayType( m_cDisplayType );
	return iResult;
}

/*-----------------------------------------------------------------------------
 *  Description : delete an item
 *---------------------------------------------------------------------------*/
BOOL C3ListView::m_fn_bDeleteItem( long lIndex, char cSortType /*= C_cAlphaOrder*/ )
{
	BOOL bResult = CThreeSortedList::m_fn_bDeleteItem( lIndex, cSortType );
	if (bResult)
		m_fn_vChangeDisplayType( m_cDisplayType);
	return bResult;
}

/*-----------------------------------------------------------------------------
 *  Description : delete all items
 *---------------------------------------------------------------------------*/
BOOL C3ListView::m_fn_bDeleteAllItems()
{
	BOOL bResult = CThreeSortedList::m_fn_bDeleteAllItems();
	if (bResult)
		m_fn_vChangeDisplayType(m_cDisplayType);
	return bResult;
}


#endif // ACTIVE_EDITOR
