CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

ICONLIST.CPP (33870B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 
     16 #ifdef WOLAPI_INTEGRATION
     17 
     18 //	Iconlist.cpp - created by ajw 07/07/98
     19 
     20 //	IconListClass is ListClass plus the option to include an icon on each line entry,
     21 //	the option to have the class maintain its own copies of strings passed to it
     22 //	for display, and the option to limit the maximum number of these strings that are
     23 //	kept (entries are removed from the top when this maximum is reached).
     24 //	Also added: multiple item selection capability. Note that the old selection code
     25 //	runs as normal, but it simply not used when it comes time to display.
     26 //	Also added: if mem. allocation is being done by this, the ability to break new items
     27 //	into multiple lines of text is enabled.
     28 //	Also added: extra data can be invisibly stored with each item, if memory allocation is
     29 //	being done by this.
     30 //	Extra data included 3 item preceding icons, 1 fixed position icon, an extra string,
     31 //	an extra void pointer, and a color remapping value.
     32 
     33 #include "iconlist.h"
     34 #include "dibapi.h"
     35 
     36 int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars );
     37 void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window );
     38 
     39 //***********************************************************************************************
     40 IconListClass::IconListClass( int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down, 
     41 								bool bResponsibleForStringAlloc, int iSelectionType, int iMaxItemsSaved ) :
     42 	ListClass( id, x, y, w, h, flags, up, down )
     43 {
     44 	//	If bResponsibleForStringAlloc, COPIES of strings are stored in the list. Deletion is
     45 	//	handled by this class. Icons are different - the caller is responsible for what's on
     46 	//	the other end of the pointer.
     47 	bDoAlloc = bResponsibleForStringAlloc;
     48 	//	iSelectionType = 0 for no selection shown, 1 for normal ListClass selection, 2 for n multiple selections
     49 	if( iSelectionType < 0 || iSelectionType > 2 )		iSelectionType = 1;
     50 	iSelectType = iSelectionType;
     51 	//	If iMaxItemsSaved is 0, there is no limit to the number of text lines. The list can grow forever.
     52 	//	Otherwise items are deleted from the head of the list when the maximum is passed.
     53 	//	iMaxItemsSaved only applies when bResponsibleForStringAlloc.
     54 	iMaxItems = iMaxItemsSaved;
     55 }
     56 
     57 //***********************************************************************************************
     58 IconListClass::~IconListClass( void )
     59 {
     60 	//	Delete the IconList_ItemExtras structs created to hold extra info on each item.
     61 	for( int i = 0; i < ExtrasList.Count(); i++ )
     62 		delete (IconList_ItemExtras*)ExtrasList[ i ];
     63 
     64 	if( bDoAlloc )
     65 	{
     66 		//	Delete all alloc'ed strings.
     67 		for( int i = 0; i < List.Count(); i++ )
     68 			delete [] (char*)List[i];
     69 	}
     70 }
     71 
     72 /***********************************************************************************************
     73  * IconListClass::Add_Item -- Adds an item to the list box.                                        *
     74  *                                                                                             *
     75  *    This will add the specified string to the list box. The string is added to the end       *
     76  *    of the list.                                                                             *
     77  *                                                                                             *
     78  * INPUT:      text  -- Pointer to the string to add to the list box.                          *
     79  *             pIcon -- Pointer to the shape to add.
     80  *             IconKind -- Indicates what type of image pIcon points to.
     81  *             szExtraDataString -- Extra string data that gets copied and stored along with item.
     82  *             szExtraDataPtr -- Extra data that gets stored along with item.
     83  *             pColorRemap -- Points to a color remapping used when drawing the item.
     84  *
     85  * OUTPUT:     Returns new item index.                                                         *
     86  * WARNINGS:   none                                                                            *
     87  * HISTORY:    07/07/1998 ajw : Created.                                                       *
     88  *=============================================================================================*/
     89 int IconListClass::Add_Item(char const * text)
     90 {
     91 	return Add_Item( text, NULL, NULL, ICON_SHAPE );
     92 }
     93 
     94 int IconListClass::Add_Item( const char* text, const char* szHelp, 
     95 								void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, 
     96 								void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */,
     97 								void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, 
     98 								void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */,
     99 								void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ )
    100 {
    101 	if( text )
    102 	{
    103 		if( bDoAlloc )
    104 		{
    105 			int iRetVal;
    106 
    107 			char* szText = new char[ strlen( text ) + 51 ];			//	50 extra chars added for line breaks later.
    108 			strcpy( szText, text );
    109 
    110 			int iWidthMax, iHeight;
    111 			//	Stupid usage of globals for font stuff... <grumble>
    112 			if( TextFlags == TPF_TYPE )
    113 			{
    114 				void* pFontBefore = Set_Font( TypeFontPtr );
    115 				DWORD FontXSpacingBefore = FontXSpacing;
    116 				FontXSpacing = -2;
    117 
    118 				int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width;
    119 				//	This call will place '\r's in the string where line breaks should occur.
    120 				Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 );
    121 
    122 				Set_Font( pFontBefore );
    123 				FontXSpacing = FontXSpacingBefore;		//	Just in case it matters... Doubt it.
    124 			}
    125 			else
    126 			{
    127 				//	Currently never called. Test well if you use IconList with a font other than TPF_TYPE,
    128 				//	as the character spacing globals get set weirdly, I've found.
    129 				int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width;
    130 				//	This call will place '\r's in the string where line breaks should occur.
    131 				Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 );
    132 			}
    133 
    134 			//	Each break character causes a line to be added to list.
    135 			char szBreakchars[] = "\r\n\v\f";
    136 			char* szToken;
    137 			char* szNextChar = szText;
    138 			szToken = strtok( szText, szBreakchars );
    139 			while( szToken )
    140 			{
    141 				while( szNextChar < szToken )
    142 				{
    143 					//	We expected szToken to begin at szNextChar. Since it doesn't, extra break
    144 					//	characters must have been removed by strtok as they were adjacent. We want 
    145 					//	a line break for every break character, so add lines for each space that
    146 					//	szNextChar is off by.
    147 					szNextChar++;
    148 					Add_Item_Detail( " ", szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth );
    149 				}
    150 				iRetVal = Add_Item_Detail( szToken, szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth );
    151 
    152 				//	Expect next token two chars after the end of this one.
    153 				szNextChar = szToken + strlen( szToken ) + 1;
    154 
    155 				//	Get next token.
    156 				szToken = strtok( NULL, szBreakchars );
    157 			}
    158 			delete [] szText;
    159 			return iRetVal;				//	Last value returned by ListClass::Add_Item
    160 		}
    161 		else
    162 		{
    163 			//	Add one item to list.
    164 			IconList_ItemExtras* pItemExtra = new IconList_ItemExtras;
    165 			pItemExtra->bMultiSelected = false;
    166 			pItemExtra->pIcon[0] = pIcon0;
    167 			pItemExtra->IconKind[0] = IconKind0;
    168 			pItemExtra->pIcon[1] = pIcon1;
    169 			pItemExtra->IconKind[1] = IconKind1;
    170 			pItemExtra->pIcon[2] = pIcon2;
    171 			pItemExtra->IconKind[2] = IconKind2;
    172 			pItemExtra->FixedIcon.pIcon = pFixedIcon;
    173 			pItemExtra->FixedIcon.IconKind = FixedIconKind;
    174 			pItemExtra->FixedIcon.xOffset = iXFixedIcon;
    175 			pItemExtra->FixedIcon.yOffset = iYFixedIcon;
    176 			pItemExtra->FixedIcon.iWidth = iFixedIconWidth;
    177 			pItemExtra->pvExtraData = pvExtraDataPtr;
    178 			pItemExtra->pColorRemap = pColorRemap;
    179 			if( szHelp )
    180 			{
    181 				//	Copy help into new help string.
    182 				pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ];
    183 				strcpy( pItemExtra->szHelp, szHelp );
    184 			}
    185 			if( szExtraDataString )
    186 			{
    187 				//	Copy special data string into new extradata string.
    188 				pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ];
    189 				strcpy( pItemExtra->szExtraData, szExtraDataString );
    190 			}
    191 			ExtrasList.Add( pItemExtra );
    192 
    193 			return ListClass::Add_Item( text );
    194 		}
    195 	}
    196 	else
    197 	{
    198 		//	(no text for new item)
    199 		if( pIcon0 || pIcon1 || pIcon2 )
    200 		{
    201 			//	Note: Cannot add an entry without text unless string allocation is being handled by me.
    202 			//	Otherwise, because we want the icon to show up, create a blank entry for the ListClass.
    203 			if( bDoAlloc )
    204 			{
    205 				IconList_ItemExtras* pItemExtra = new IconList_ItemExtras;
    206 				pItemExtra->bMultiSelected = false;
    207 				pItemExtra->pIcon[0] = pIcon0;
    208 				pItemExtra->IconKind[0] = IconKind0;
    209 				pItemExtra->pIcon[1] = pIcon1;
    210 				pItemExtra->IconKind[1] = IconKind1;
    211 				pItemExtra->pIcon[2] = pIcon2;
    212 				pItemExtra->IconKind[2] = IconKind2;
    213 				pItemExtra->FixedIcon.pIcon = pFixedIcon;
    214 				pItemExtra->FixedIcon.IconKind = FixedIconKind;
    215 				pItemExtra->FixedIcon.xOffset = iXFixedIcon;
    216 				pItemExtra->FixedIcon.yOffset = iYFixedIcon;
    217 				pItemExtra->FixedIcon.iWidth = iFixedIconWidth;
    218 				pItemExtra->pvExtraData = pvExtraDataPtr;
    219 				pItemExtra->pColorRemap = pColorRemap;
    220 				if( szHelp )
    221 				{
    222 					//	Copy help into new help string.
    223 					pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ];
    224 					strcpy( pItemExtra->szHelp, szHelp );
    225 				}
    226 				if( szExtraDataString )
    227 				{
    228 					//	Copy special data string into new extradata string.
    229 					pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ];
    230 					strcpy( pItemExtra->szExtraData, szExtraDataString );
    231 				}
    232 				ExtrasList.Add( pItemExtra );
    233 
    234 				if( iMaxItems && List.Count() == iMaxItems )
    235 				{
    236 					//	Delete head of list.
    237 					Remove_Item( 0 );
    238 				}
    239 				//	Create new string, essentially blank.
    240 				char* szText = new char[2];
    241 				strcpy( szText, " " );
    242 				return ListClass::Add_Item( szText );
    243 			}
    244 			else
    245 				//	Cannot add entry, as text is blank and ListClass::Add_Item will do nothing.
    246 				//	The Icon we want will not show up.
    247 				return List.Count() - 1;
    248 		}
    249 		else
    250 			return ListClass::Add_Item( text );
    251 	}
    252 }
    253 
    254 //***********************************************************************************************
    255 int IconListClass::Add_Item_Detail( const char* szToken, const char* szHelp,
    256 										void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString, 
    257 										void* pvExtraData, RemapControlType* pColorRemap,
    258 										void* pIcon1, ICONKIND IconKind1, 
    259 										void* pIcon2, ICONKIND IconKind2,
    260 										void* pFixedIcon, ICONKIND FixedIconKind, int iXFixedIcon, int iYFixedIcon, int iFixedIconWidth )
    261 {
    262 	//	Broken out of above function as it is repeated.
    263 
    264 	//	Add one item to list.
    265 	//	Too many entries?
    266 	if( iMaxItems && List.Count() == iMaxItems )
    267 	{
    268 		//	Delete head of list.
    269 		Remove_Item( 0 );
    270 	}
    271 	//	Create icon entry.
    272 	IconList_ItemExtras* pItemExtra = new IconList_ItemExtras;
    273 	pItemExtra->bMultiSelected = false;
    274 	pItemExtra->pIcon[0] = pIcon0;				//	ajw - Question: repeat the icon for each entry? make it optional?
    275 	pItemExtra->IconKind[0] = IconKind0;
    276 	pItemExtra->pIcon[1] = pIcon1;
    277 	pItemExtra->IconKind[1] = IconKind1;
    278 	pItemExtra->pIcon[2] = pIcon2;
    279 	pItemExtra->IconKind[2] = IconKind2;
    280 	pItemExtra->FixedIcon.pIcon = pFixedIcon;
    281 	pItemExtra->FixedIcon.IconKind = FixedIconKind;
    282 	pItemExtra->FixedIcon.xOffset = iXFixedIcon;
    283 	pItemExtra->FixedIcon.yOffset = iYFixedIcon;
    284 	pItemExtra->FixedIcon.iWidth = iFixedIconWidth;
    285 	pItemExtra->pvExtraData = pvExtraData;
    286 	pItemExtra->pColorRemap = pColorRemap;
    287 	if( szHelp )
    288 	{
    289 		//	Copy help into new help string.
    290 		pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ];
    291 		strcpy( pItemExtra->szHelp, szHelp );
    292 	}
    293 	if( szExtraDataString )
    294 	{
    295 		//	Copy special data string into new extradata string.
    296 		pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ];
    297 		strcpy( pItemExtra->szExtraData, szExtraDataString );
    298 	}
    299 	ExtrasList.Add( pItemExtra );
    300 	//	Create text entry.
    301 	//	Copy text to new string.
    302 	char* szTextBit = new char[ strlen( szToken ) + 1 ];
    303 	strcpy( szTextBit, szToken );
    304 	return ListClass::Add_Item( szTextBit );
    305 }
    306 
    307 //***********************************************************************************************
    308 int IconListClass::Add_Item( int text )
    309 {
    310 	return Add_Item( Text_String(text), NULL, NULL, ICON_SHAPE );
    311 }
    312 
    313 //***********************************************************************************************
    314 int IconListClass::Add_Item( int text, const char* szHelp,
    315 								void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, 
    316 								void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */,
    317 								void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, 
    318 								void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */,
    319 								void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ )
    320 {
    321 	return Add_Item( Text_String(text), szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, 
    322 						pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth );
    323 }
    324 
    325 //***********************************************************************************************
    326 void IconListClass::Remove_Item( char const * text )
    327 {
    328 	if( text )
    329 		Remove_Item( List.ID(text) );
    330 }
    331 
    332 //***********************************************************************************************
    333 void IconListClass::Remove_Item( int index )
    334 {
    335 	if( (unsigned)index < List.Count() )
    336 	{
    337 		delete (IconList_ItemExtras*)ExtrasList[ index ];
    338 		ExtrasList.Delete( index );
    339 		if( bDoAlloc )
    340 			//	Delete alloc'ed string.
    341 			delete [] (char*)List[index];
    342 		ListClass::Remove_Item( index );
    343 		
    344 		//	I should probably put this in ListClass:Remove_Item(), as it seems clearly to be
    345 		//	missing, but I want to only affect my own new code, to not introduce possible bugs.
    346 		//	Shift the selected index if appropriate...
    347 		if( SelectedIndex >= index )
    348 		{
    349 			SelectedIndex--;
    350 			if( SelectedIndex < 0 )
    351 				SelectedIndex = 0;
    352 		}
    353 	}
    354 }
    355 
    356 /***********************************************************************************************
    357  * IconListClass::Draw_Entry -- Calls ListClass::Draw_Entry, then adds icon.                   *
    358  *                                                                                             *
    359  *    This routine is called by the Draw_Me function when it desired to redraw a particular    *
    360  *    text line in the list box.                                                               *
    361  *                                                                                             *
    362  * INPUT:   index    -- The index of the list entry to draw. This index is based on the        *
    363  *                      total list and NOT the current visible view page.                      *
    364  *                                                                                             *
    365  *          x,y      -- Pixel coordinates for the upper left corner of the text entry.         *
    366  *                                                                                             *
    367  *          width    -- The maximum width that the text may draw over. It is expected that     *
    368  *                      this drawing routine entirely fills this length.                       *
    369  *                                                                                             *
    370  *          selected -- bool; Is this a selected (highlighted) listbox entry?                  *
    371  *                                                                                             *
    372  * OUTPUT:  none                                                                               *
    373  * WARNINGS:   none                                                                            *
    374  * HISTORY:                                                                                    *
    375  *   07/07/1998  ajw: Created.                                                                 *
    376  *=============================================================================================*/
    377 
    378 #define PREICONGAP		1
    379 #define ICONTEXTGAP		2
    380 
    381 void IconListClass::Draw_Entry( int index, int x, int y, int width, int selected )
    382 {
    383 	IconList_ItemExtras* pExtras = (IconList_ItemExtras*)ExtrasList[ index ];
    384 
    385 	int xText = x;
    386 	//	ajw If I end up needing to use SHAPEs for icons, figure out shape width here and offset x.
    387 	bool bIconsPresent = false;
    388 	for( int iIcon = 0; iIcon != 3; iIcon++ )
    389 		if( pExtras->pIcon[ iIcon ] && pExtras->IconKind[ iIcon ] == ICON_DIB )
    390 		{
    391 			//	Push text over to accommodate icon.
    392 			int iWidthIcon = PREICONGAP + DIBWidth( (char*)pExtras->pIcon[ iIcon ] );
    393 			xText += iWidthIcon;
    394 			width -= iWidthIcon;
    395 			bIconsPresent = true;
    396 		}
    397 	if( bIconsPresent )
    398 	{
    399 		xText += ICONTEXTGAP;
    400 		width -= ICONTEXTGAP;
    401 	}
    402 
    403 	RemapControlType* pRemap = pExtras->pColorRemap;
    404 	if( !pRemap )
    405 	{
    406 		//	Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately.
    407 		//	(Ignore others. This is a hack because having more than one tab will now break this.)
    408 		//	See local version of this same hack, below.
    409 		int TempTabs;
    410 		const int* TabsSave;
    411 		if( Tabs )
    412 		{
    413 			TempTabs = *Tabs - ( xText - x );
    414 			TabsSave = Tabs;
    415 			Tabs = &TempTabs;
    416 		}
    417 		switch( iSelectType )
    418 		{
    419 		case 0:
    420 			//	Don't draw any items selected (even if they are, really, in ListClass).
    421 			ListClass::Draw_Entry( index, xText, y, width, false );
    422 			break;
    423 		case 1:
    424 			ListClass::Draw_Entry( index, xText, y, width, selected );
    425 			break;
    426 		case 2:
    427 			//	Ignore 'selected' parameter. We use our own records.
    428 			ListClass::Draw_Entry( index, xText, y, width, pExtras->bMultiSelected );
    429 			break;
    430 		}
    431 		//	Restore Tabs.
    432 		if( Tabs )
    433 			Tabs = TabsSave;
    434 	}
    435 	else
    436 	{
    437 		//	Use different color remapping.
    438 		//	This is largely copied straight from ListClass::Draw_Entry()...
    439 
    440 		TextPrintType flags = TextFlags;
    441 
    442 		bool bShowSelected;
    443 
    444 		switch( iSelectType )
    445 		{
    446 		case 0:
    447 			bShowSelected = false;
    448 			break;
    449 		case 1:
    450 			bShowSelected = selected;
    451 			break;
    452 		case 2:
    453 			bShowSelected = pExtras->bMultiSelected;
    454 			break;
    455 		}
    456 
    457 		if( bShowSelected )
    458 		{
    459 			flags = flags | TPF_BRIGHT_COLOR;
    460 			LogicPage->Fill_Rect( xText, y, xText + width - 1, y + LineHeight - 1, pRemap->Shadow );
    461 		}
    462 		else
    463 		{
    464 			if (!(flags & TPF_USE_GRAD_PAL))
    465 			{
    466 				flags = flags | TPF_MEDIUM_COLOR;
    467 			}
    468 		}
    469 		//	Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately.
    470 		//	(Ignore others. This is a hack because having more than one tab will now break this.)
    471 		if( Tabs )
    472 		{
    473 			int tab = *Tabs - ( xText - x );
    474 			Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, &tab );
    475 		}
    476 		else
    477 			Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, NULL );
    478 	}
    479 
    480 	//	Draw fixed position icon.
    481 	if( pExtras->FixedIcon.pIcon )
    482 	{
    483 		if( pExtras->FixedIcon.IconKind == ICON_SHAPE )
    484 			CC_Draw_Shape( pExtras->FixedIcon.pIcon, 0, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, WINDOW_MAIN, SHAPE_NORMAL );
    485 			//	Put similar code in here for shapes if used...
    486 		else
    487 			CC_Draw_DIB( (char*)pExtras->FixedIcon.pIcon, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, pExtras->FixedIcon.iWidth, WINDOW_MAIN );
    488 	}
    489 
    490 	//	Draw variable position left-of-text icons.
    491 	for( iIcon = 0; iIcon != 3; iIcon++ )
    492 	{
    493 		if( pExtras->pIcon[ iIcon ] )
    494 		{
    495 			x += PREICONGAP;
    496 			if( pExtras->IconKind[ iIcon ] == ICON_SHAPE )
    497 				CC_Draw_Shape( pExtras->pIcon[ iIcon ], 0, x, y, WINDOW_MAIN, SHAPE_NORMAL );
    498 				//	Put similar code in here for shapes if used...
    499 			else
    500 			{
    501 				CC_Draw_DIB( (char*)pExtras->pIcon[ iIcon ], x, y, 9999, WINDOW_MAIN );
    502 				x += DIBWidth( (char*)pExtras->pIcon[ iIcon ] );
    503 			}
    504 		}
    505 	}
    506 }
    507 
    508 //***********************************************************************************************
    509 int IconListClass::Action(unsigned flags, KeyNumType & key)
    510 {
    511 	//	Overriding of function is for the sake of MultiSelecting only.
    512 	if( iSelectType == 2 )
    513 	{
    514 		if( !( flags & LEFTRELEASE ) )
    515 		{
    516 			if( !( flags & KEYBOARD ) )
    517 			{
    518 				int index = Get_Mouse_Y() - (Y+1);
    519 				index = index / LineHeight;
    520 				int iSelected = CurrentTopIndex + index;
    521 				iSelected = min( iSelected, List.Count() - 1 );
    522 				if( iSelected >= 0 )
    523 					((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected = 
    524 						!((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected;
    525 			}
    526 		}
    527 	}
    528 	return ListClass::Action( flags, key );
    529 }
    530 
    531 //***********************************************************************************************
    532 // * IconListClass::Show_Last_Item -- Scrolls listbox down to ensure that last entry is visible.
    533 //	ajw 07/09/98
    534 void IconListClass::Show_Last_Item()
    535 {
    536 	int iItemLast = List.Count() - 1;
    537 	if( iItemLast - LineCount + 1 != CurrentTopIndex )
    538 	{
    539 		Flag_To_Redraw();
    540 		Set_View_Index( iItemLast - LineCount + 1 );
    541 	}
    542 }
    543 
    544 //***********************************************************************************************
    545 bool IconListClass::bItemIsMultiSelected( int index ) const
    546 {
    547 	if( index < ExtrasList.Count() && index > -1 )
    548 		return ( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected;
    549 	else
    550 		return false;
    551 }
    552 
    553 //***********************************************************************************************
    554 void IconListClass::MultiSelect( int index, bool bSelect )
    555 {
    556 	if( index < ExtrasList.Count() && index > -1 )
    557 		( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected = bSelect;
    558 }
    559 
    560 //***********************************************************************************************
    561 const char* IconListClass::Get_Item_ExtraDataString( int index ) const
    562 {
    563 	//	Returns const pointer to the hidden "extra data" string that can be associated with each item.
    564 	//	This is NULL if no extra data was assigned.
    565 	if( index < ExtrasList.Count() && index > -1 )
    566 	{
    567 		return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szExtraData;
    568 	}
    569 	return NULL;
    570 }
    571 
    572 //***********************************************************************************************
    573 void IconListClass::Set_Item_ExtraDataString( int index, const char* szNewString )
    574 {
    575 	if( index < ExtrasList.Count() && index > -1 )
    576 	{
    577 		IconList_ItemExtras* pItemExtra = (IconList_ItemExtras*)ExtrasList[ index ];
    578 		if( pItemExtra->szExtraData )
    579 		{
    580 			//	Delete the existing string.
    581 			delete [] pItemExtra->szExtraData;
    582 		}
    583 		if( szNewString )
    584 		{
    585 			//	Copy special data string into new extradata string.
    586 			pItemExtra->szExtraData = new char[ strlen( szNewString ) + 1 ];
    587 			strcpy( pItemExtra->szExtraData, szNewString );
    588 		}
    589 		else
    590 			pItemExtra->szExtraData = NULL;
    591 	}
    592 }
    593 				
    594 //***********************************************************************************************
    595 void* IconListClass::Get_Item_ExtraDataPtr( int index ) const
    596 {
    597 	//	Returns the hidden "extra data" void pointer that can be associated with each item.
    598 	//	This is NULL if no value was assigned.
    599 	if( index < ExtrasList.Count() && index > -1 )
    600 		return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData;
    601 	else
    602 		return NULL;
    603 }
    604 
    605 //***********************************************************************************************
    606 void IconListClass::Set_Item_ExtraDataPtr( int index, void* pNewValue )
    607 {
    608 	//	Sets the hidden "extra data" void pointer that can be associated with each item.
    609 	if( index < ExtrasList.Count() && index > -1 )
    610 		( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData = pNewValue;
    611 }
    612 
    613 //***********************************************************************************************
    614 const IconList_ItemExtras* IconListClass::Get_ItemExtras( int index ) const
    615 {
    616 	if( index < ExtrasList.Count() && index > -1 )
    617 		return (IconList_ItemExtras*)ExtrasList[ index ];
    618 	else
    619 		return NULL;
    620 }
    621 		
    622 //***********************************************************************************************
    623 const char* IconListClass::Get_Item_Help( int index ) const
    624 {
    625 	//	Returns pointer to the string allocated for tooltip help.
    626 	if( index < ExtrasList.Count() && index > -1 )
    627 		return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szHelp;
    628 	else
    629 		return NULL;
    630 }
    631 
    632 //***********************************************************************************************
    633 void IconListClass::Clear()
    634 {
    635 	//	Removes all items from list.
    636 
    637 	//	Delete the IconList_ItemExtras structs created to hold extra info on each item.
    638 	for( int i = 0; i < ExtrasList.Count(); i++ )
    639 		delete (IconList_ItemExtras*)ExtrasList[ i ];
    640 	ExtrasList.Clear();
    641 
    642 	if( bDoAlloc )
    643 	{
    644 		//	Delete all alloc'ed strings.
    645 		for( int i = 0; i < List.Count(); i++ )
    646 			delete [] (char*)List[i];
    647 	}
    648 
    649 	List.Clear();
    650 	Remove_Scroll_Bar();
    651 	CurrentTopIndex = 0;
    652 }
    653 
    654 //***********************************************************************************************
    655 RemapControlType* IconListClass::Get_Item_Color( int index )
    656 {
    657 	if( index < ExtrasList.Count() && index > -1 )
    658 		return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap;
    659 	else
    660 		return NULL;
    661 }
    662 
    663 //***********************************************************************************************
    664 void IconListClass::Set_Item_Color( int index, RemapControlType* pColorRemap )
    665 {
    666 	if( index < ExtrasList.Count() && index > -1 )
    667 		( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap = pColorRemap;
    668 }
    669 
    670 //***********************************************************************************************
    671 int IconListClass::Find( const char* szItemToFind )
    672 {
    673 	//	Returns -1 if szItemToFind is not found as the text BEGINNING one of the list entries, else index of item.
    674 	//	Compare is case-sensitive.
    675 	for( int i = 0; i < List.Count(); i++ )
    676 	{
    677 		if( strncmp( List[ i ], szItemToFind, strlen( szItemToFind ) ) == 0 )
    678 			return i;
    679 	}
    680 	return -1;
    681 }
    682 
    683 //***********************************************************************************************
    684 int IconListClass::FindColor( RemapControlType* pColorRemap )
    685 {
    686 	//	Returns -1 if no items of specified color are found, else first index. Assumes colorptr == colorptr is a valid equality test.
    687 	for( int i = 0; i < List.Count(); i++ )
    688 	{
    689 		if( Get_Item_Color( i ) == pColorRemap )
    690 			return i;
    691 	}
    692 	return -1;
    693 }
    694 
    695 //***********************************************************************************************
    696 bool IconListClass::Set_Item( unsigned int index, const char* szText )
    697 {
    698 	//	Resets the text string allocated for an item.
    699 	if( !bDoAlloc || index >= List.Count() )
    700 		return false;
    701 
    702 	//	Delete alloc'ed string.
    703 	delete [] (char*)List[ index ];
    704 
    705 	//	Copy text to new string.
    706 	char* szTextNew = new char[ strlen( szText ) + 1 ];
    707 	strcpy( szTextNew, szText );
    708 
    709 	//	Reassign List's ptr.
    710 	List[ index ] = szTextNew;
    711 
    712 	return true;
    713 }
    714 
    715 //***********************************************************************************************
    716 bool IconListClass::Set_Icon( unsigned int index, unsigned int iIconNumber, void* pIcon, ICONKIND IconKind )
    717 {
    718 	if( index >= List.Count() )
    719 		return false;
    720 
    721 	//	Sets one of the left-aligned icons.
    722 	( (IconList_ItemExtras*)ExtrasList[ index ] )->pIcon[ iIconNumber ] = pIcon;
    723 	( (IconList_ItemExtras*)ExtrasList[ index ] )->IconKind[ iIconNumber ] = IconKind;
    724 	return true;
    725 }
    726 
    727 //***********************************************************************************************
    728 int IconListClass::GetRealWidth()		//	sigh
    729 {
    730 	if( IsScrollActive )
    731 		return Width + ScrollGadget.Width;
    732 	return Width;
    733 }
    734 
    735 //***********************************************************************************************
    736 void IconListClass::Resize( int x, int y, int w, int h )
    737 {
    738 	Remove_Scroll_Bar();	//	If there is one.
    739 
    740 	X = x;
    741 	Y = y;
    742 	Width = w;
    743 	Height = h;
    744 
    745 	Set_Position( x, y );
    746 
    747 	LineCount = (h-1) / LineHeight;
    748 
    749 
    750 	if (List.Count() > LineCount) {
    751 		Add_Scroll_Bar();
    752 	}
    753 
    754 	Flag_To_Redraw();
    755 }
    756 
    757 //***********************************************************************************************
    758 int IconListClass::IndexUnderMouse()
    759 {
    760 	//	Returns index of line that mouse is currently over, or -1 for mouse not hitting valid index.
    761 	//	Assumes that x position of mouse is already known to be over the iconlist.
    762 	int index = Get_Mouse_Y() - (Y+1);
    763 	index = index / LineHeight + CurrentTopIndex;
    764 	if( index > List.Count() - 1 || index < 0 )
    765 		return -1;
    766 	return index;
    767 }
    768 
    769 //***********************************************************************************************
    770 int IconListClass::OffsetToIndex( int iIndex, int y )
    771 {
    772 	//	Finds the current offset of item iIndex from the current top view index, in pixels, and add it to y.
    773 	return y + ( iIndex - CurrentTopIndex ) * LineHeight;
    774 }
    775 
    776 //***********************************************************************************************
    777 //***********************************************************************************************
    778 //	* Format_Window_String_New
    779 //	Functions like Format_Window_String except it fixes an infinite loop bug that occurred when strings
    780 //	lacked suitable break points, eliminates the '@' as an escape character, and operates differently
    781 //	in that it leaves the original string along, writing results instead to a second string parameter,
    782 //	that is iExtraChars longer than the original string. This is all a big hack so that I can insert 
    783 //	extra break characters when a break in a long single word has to be made.
    784 //	Hey - it's better than an infinite loop that forces you to reset your machine, as in the original code...
    785 
    786 int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars )
    787 {
    788 	int	linelen;
    789 	int	lines = 0;
    790 	width	= 0;
    791 	height = 0;
    792 
    793 	// In no string was passed in, then there are no lines.
    794 	if (!string) return(0);
    795 
    796 	// While there are more letters left divide the line up.
    797 	while (*string) {
    798 		linelen = 0;
    799 		height += FontHeight + FontYSpacing;
    800 		lines++;
    801 
    802 		// While the current line is less then the max length...
    803 		*szReturn = *string;
    804 		linelen += Char_Pixel_Width( *string );
    805 		while ( linelen < maxlinelen && *string != '\r' && *string != '\0' )
    806 		{
    807 			*++szReturn = *++string;
    808 			linelen += Char_Pixel_Width( *string );
    809 		}
    810 
    811 		// if the line is too long...
    812 		if (linelen >= maxlinelen) 
    813 		{
    814 			/*
    815 			**	Back up to an appropriate location to break.
    816 			*/
    817 			const char* stringOverEnd = string;
    818 			while( linelen > 0 && *string != ' ' && *string != '\r' && *string != '\0' )
    819 			{
    820 				linelen -= Char_Pixel_Width(*string--);
    821 			}
    822 			if( linelen <= 0 )
    823 			{
    824 				//	We could not find a nice break point.
    825 				//	Go back one char from over-the-end point and add in a break there.
    826 				string = stringOverEnd - 1;
    827 				if( iExtraChars > 0 )
    828 					iExtraChars--;			//	One less to make use of later.
    829 				else
    830 					//	We've used up all our extras characters.
    831 					//	Put in a break below by wiping out a valid char here.
    832 					szReturn--;
    833 			}
    834 			else
    835 			{
    836 				//	Back up szReturn to same location.
    837 				szReturn -= ( stringOverEnd - string );
    838 			}
    839 		}
    840 
    841 		/*
    842 		**	Record the largest width of the worst case string.
    843 		*/
    844 		if (linelen > width) {
    845 			width = linelen;
    846 		}
    847 
    848 		/*
    849 		**	Force a break at the end of the line.
    850 		*/
    851 		if (*string) {
    852 		 	*szReturn++ = '\r';
    853 			string++;
    854 		}
    855 	}
    856 	return(lines);
    857 }
    858 
    859 //***********************************************************************************************
    860 void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window )
    861 {
    862 	//	A very basic DIB drawing routine. No clipping. No edge of window overrun checking.
    863 	//	If iWidth is too large, default width of dib is used.
    864 	//	If iWidth is negative, dib isn't drawn.
    865 	if( pDIB && iWidth >= 0 )
    866 	{
    867 
    868 		int iWidthDIB = DIBWidth( pDIB );
    869 		int iHeight = DIBHeight( pDIB );
    870 		const char* pBits = FindDIBBits( pDIB );
    871 
    872 		int iSrcPitch = ( iWidthDIB + 3 ) & ~3;
    873 
    874 		if( iWidth > iWidthDIB )
    875 			iWidth = iWidthDIB;
    876 
    877 		GraphicViewPortClass draw_window(	LogicPage->Get_Graphic_Buffer(),
    878 											WindowList[window][WINDOWX] + LogicPage->Get_XPos(),
    879 											WindowList[window][WINDOWY] + LogicPage->Get_YPos(),
    880 											WindowList[window][WINDOWWIDTH],
    881 											WindowList[window][WINDOWHEIGHT] );
    882 		if( draw_window.Lock() )
    883 		{
    884 			int iDestPitch = draw_window.Get_Pitch() + draw_window.Get_Width();	//	Meaning of "Pitch" in this class seems to mean the eol skip.
    885 			char* pLineDest = (char*)draw_window.Get_Offset() + xDest + ( yDest + iHeight - 1 ) * iDestPitch;
    886 
    887 			const char* pLineSrc = pBits;
    888 			for( int y = 0; y != iHeight; y++ )
    889 			{
    890 				char* pDest = pLineDest;
    891 				const char* pSrc = pLineSrc;
    892 				for( int x = 0; x != iWidth; x++ )
    893 				{
    894 					*pDest++ = *pSrc++;
    895 				}
    896 				pLineDest -= iDestPitch;
    897 				pLineSrc += iSrcPitch;
    898 			}
    899 			draw_window.Unlock();
    900 		}
    901 	}
    902 //	else
    903 //		debugprint( "CC_Draw_DIB bad case ------------ pDib %i, iWidth %i\n", pDIB, iWidth );
    904 }
    905 
    906 
    907 #endif