CnC_Remastered_Collection

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

WOL_CHAT.CPP (51166B)


      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 //	wol_chat.cpp
     19 //	ajw 7/8/98
     20 
     21 #include "function.h"
     22 #include "iconlist.h"
     23 #include "WolapiOb.h"
     24 #include "SEditDlg.h"
     25 #include "WolStrng.h"
     26 #include "ToolTip.h"
     27 
     28 //#include "WolDebug.h"
     29 
     30 void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE );
     31 void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap );
     32 bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame );
     33 bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame );
     34 bool ExitChatChannel( WolapiObject* pWO );
     35 void CreateChatChannel( WolapiObject* pWO );
     36 bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi );
     37 bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex );
     38 
     39 enum LIST_EXPAND_STATE
     40 {
     41 	LES_NORMAL,
     42 	LES_CHANNELS_EXPANDED,
     43 	LES_USERS_EXPANDED,
     44 };
     45 static LIST_EXPAND_STATE	lesCurrent = LES_NORMAL;
     46 
     47 bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist );
     48 bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist );
     49 void ResizeChannelList( IconListClass& chanlist, bool bExpand );
     50 void ResizeUserList( IconListClass& userlist, bool bExpand );
     51 
     52 bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind );
     53 
     54 extern CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO );
     55 
     56 static int d_chanlist_w;
     57 static int d_chanlist_h;
     58 static int d_chanlist_x;
     59 static int d_chanlist_y;
     60 
     61 static int d_userlist_w;
     62 static int d_userlist_h;
     63 static int d_userlist_x;
     64 static int d_userlist_y;
     65 
     66 #define DRAWTOGDOWN		Turn_Off()
     67 #define DRAWTOGUP		Turn_On()
     68 
     69 //***********************************************************************************************
     70 int WOL_Chat_Dialog( WolapiObject* pWO )
     71 {
     72 	int rc;
     73 	bool bFirsttime = true;
     74 	bool bHackFocus = true;
     75 
     76 	//------------------------------------------------------------------------
     77 	//	Dialog & button dimensions
     78 	//------------------------------------------------------------------------
     79 	int d_dialog_w = 320 *RESFACTOR;											// dialog width
     80 	int d_dialog_h = 200 *RESFACTOR;											// dialog height
     81 	int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2);				// dialog x-coord
     82 	int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2);				// centered y-coord
     83 	int d_dialog_cx = d_dialog_x + (d_dialog_w / 2);		// center x-coord
     84 
     85 	int d_text_h = 12;
     86 	int d_margin1 = 34;															// large margin
     87 	int d_margin2 = 14;															// small margin
     88 
     89 	int d_chatlist_w = 340;
     90 	int d_chatlist_x = d_dialog_x + d_margin1;
     91 	int d_chatlist_y = d_dialog_y + d_margin2 + d_margin1 + 27;
     92 	int d_chatlist_h = 337 - d_chatlist_y;
     93 
     94 	d_chanlist_w = 227;
     95 	d_chanlist_h = 50 * RESFACTOR;
     96 	d_chanlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_chanlist_w);
     97 	d_chanlist_y = d_chatlist_y;
     98 
     99 	d_userlist_w = d_chanlist_w;
    100 //		int d_userlist_h = ((10 * 6) + 3) *RESFACTOR;
    101 	d_userlist_x = d_chanlist_x;
    102 	d_userlist_y = d_chanlist_y + d_chanlist_h + 14 + 5;
    103 
    104 	d_userlist_h = d_chatlist_y + d_chatlist_h - d_userlist_y;
    105 
    106 	int d_action_w = 100;
    107 	int d_action_h = 9 *RESFACTOR;
    108 	int d_action_x = d_dialog_x + 500;
    109 	int d_action_y = 365;
    110 
    111 //	int d_chanpriv_w = 60;
    112 //	int d_chanpriv_h = 9 *RESFACTOR;
    113 //	int d_chanpriv_x = d_dialog_x + 150;
    114 //	int d_chanpriv_y = d_action_y;
    115 
    116 //	int d_cgame_w = 60;
    117 //	int d_cgame_h = 9 *RESFACTOR;
    118 //	int d_cgame_x = d_dialog_x + 390; //d_dialog_cx - d_cgame_w / 2;
    119 //	int d_cgame_y = d_action_y;
    120 
    121 	int d_back_w = 100;
    122 	int d_back_h = 9 *RESFACTOR;
    123 	int d_back_x = d_dialog_x + 100;
    124 	int d_back_y = d_action_y;
    125 
    126 	int d_join_w = 100;
    127 	int d_join_h = 9 *RESFACTOR;
    128 	int d_join_x = d_dialog_x + 210;
    129 	int d_join_y = d_action_y;
    130 
    131 	int d_create_w = 100;
    132 	int d_create_h = 9 *RESFACTOR;
    133 	int d_create_x = d_dialog_x + 320;	//((d_dialog_w * 5) / 6) - (d_create_w / 2);
    134 	int d_create_y = d_action_y;
    135 
    136 	int d_send_w = d_chanlist_x + d_chanlist_w - d_chatlist_x;
    137 	int d_send_h = 9 *RESFACTOR;
    138 	int d_send_x = d_chatlist_x;
    139 	int d_send_y = d_chatlist_y + d_chatlist_h + 5;
    140 
    141 	//------------------------------------------------------------------------
    142 	//	Button Enumerations
    143 	//------------------------------------------------------------------------
    144 	enum 
    145 	{
    146 		BUTTON_DISCONNECT = 100,	//	Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons().
    147 		BUTTON_LEAVE,
    148 		BUTTON_REFRESH,
    149 		BUTTON_SQUELCH,
    150 		BUTTON_BAN,
    151 		BUTTON_KICK,
    152 		BUTTON_FINDPAGE,
    153 		BUTTON_OPTIONS,
    154 		BUTTON_LADDER,
    155 		BUTTON_HELP,
    156 
    157 		BUTTON_CHATLIST,
    158 		BUTTON_CHANLIST,
    159 		BUTTON_USERLIST,
    160 		BUTTON_SENDEDIT,
    161 		BUTTON_ACTION,
    162 //		BUTTON_CGAME,
    163 		BUTTON_CREATE,
    164 		BUTTON_JOIN,
    165 		BUTTON_BACK,
    166 		BUTTON_EXPANDCHANNELS,
    167 		BUTTON_EXPANDUSERS,
    168 		BUTTON_RANKRA,
    169 		BUTTON_RANKAM
    170 	};
    171 
    172 	//------------------------------------------------------------------------
    173 	//	Redraw values: in order from "top" to "bottom" layer of the dialog
    174 	//------------------------------------------------------------------------
    175 	typedef enum {
    176 		REDRAW_NONE = 0,
    177 		REDRAW_BACKGROUND,
    178 		REDRAW_ALL = REDRAW_BACKGROUND
    179 	} RedrawType;
    180 
    181 	//------------------------------------------------------------------------
    182 	//	Dialog variables
    183 	//------------------------------------------------------------------------
    184 	RedrawType display = REDRAW_ALL;		// redraw level
    185 	bool process = true;						// process while true
    186 	KeyNumType input;
    187 
    188 	TTimerClass<SystemTimerClass> lastclick_timer;
    189 	int lastclick_idx = 0;					// index of item last clicked on
    190 	RemapControlType * scheme = GadgetClass::Get_Color_Scheme();
    191 
    192 	//------------------------------------------------------------------------
    193 	//	Buttons
    194 	//------------------------------------------------------------------------
    195 	GadgetClass *commands;										// button list
    196 
    197 	char* pShpExpand = (char*)MFCD::Retrieve( "exp.shp" );
    198 	char* pShpUnexpand = (char*)MFCD::Retrieve( "unexp.shp" );
    199 
    200 	IconListClass chatlist( BUTTON_CHATLIST, d_chatlist_x, d_chatlist_y, d_chatlist_w, d_chatlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 500 );
    201 	ShapeButtonClass ExpandChanBtn( BUTTON_EXPANDCHANNELS, pShpExpand, d_chanlist_x + d_chanlist_w - 17, d_chanlist_y - 14 );
    202 	IconListClass chanlist( BUTTON_CHANLIST, d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 );
    203 	ShapeButtonClass ExpandUserBtn( BUTTON_EXPANDUSERS, pShpExpand, d_userlist_x + d_userlist_w - 17, d_userlist_y - 14 );
    204 	IconListClass userlist( BUTTON_USERLIST, d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 );
    205 	TextButtonClass ActionBtn( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w );
    206 	TextButtonClass CreateBtn( BUTTON_CREATE, TXT_WOL_NEWSOMETHING, TPF_BUTTON, d_create_x, d_create_y, d_create_w );
    207 	TextButtonClass JoinBtn( BUTTON_JOIN, TXT_WOL_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w );
    208 	TextButtonClass BackBtn( BUTTON_BACK, TXT_WOL_BACK, TPF_BUTTON, d_back_x, d_back_y, d_back_w );
    209 	char* szRecordToStartWith;
    210 	if( pWO->bShowRankRA )
    211 		szRecordToStartWith = pWO->szMyRecord;
    212 	else
    213 		szRecordToStartWith = pWO->szMyRecordAM;
    214 	StaticButtonClass chatlistTitle( 0, szRecordToStartWith, TPF_TYPE, d_chatlist_x + 2, d_chatlist_y - 13, d_chatlist_w - 4, 12 );
    215 	StaticButtonClass chanlistTitle( 0, "", TPF_TYPE, d_chanlist_x + 2, d_chanlist_y - 16 + 4, d_chanlist_w - 4 - 16, 12 );
    216 	StaticButtonClass userlistTitle( 0, TXT_WOL_NOUSERLIST, TPF_TYPE, d_userlist_x + 2, d_userlist_y - 16 + 4, d_userlist_w - 4 - 16 * 4, 12 );
    217 
    218 	char szSendBuffer[MAXCHATSENDLENGTH] = "";
    219 	EditClass sendedit( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h );
    220 
    221 	char* pShpRankRA = (char*)MFCD::Retrieve( "rank_ra.shp" );
    222 	char* pShpRankAM = (char*)MFCD::Retrieve( "rank_am.shp" );
    223 	ShapeButtonClass RankRABtn( BUTTON_RANKRA, pShpRankRA, d_userlist_x + d_userlist_w - ( 16 * 4 + 1 ), d_userlist_y - 14 );
    224 	ShapeButtonClass RankAMBtn( BUTTON_RANKAM, pShpRankAM, d_userlist_x + d_userlist_w - ( 16 * 3 + 1 ), d_userlist_y - 14 );
    225 	//	Change draw behavior of toggle buttons.
    226 	RankRABtn.ReflectButtonState = true;
    227 	RankAMBtn.ReflectButtonState = true;
    228 
    229 	//	Build the button list.
    230 	commands = pWO->pShpBtnDiscon;
    231 	pWO->pShpBtnLeave->Add_Tail(*commands);
    232 	pWO->pShpBtnRefresh->Add_Tail(*commands);
    233 	pWO->pShpBtnSquelch->Add_Tail(*commands);
    234 	pWO->pShpBtnBan->Add_Tail(*commands);
    235 	pWO->pShpBtnKick->Add_Tail(*commands);
    236 	pWO->pShpBtnFindpage->Add_Tail(*commands);
    237 	pWO->pShpBtnOptions->Add_Tail(*commands);
    238 	pWO->pShpBtnLadder->Add_Tail(*commands);
    239 	pWO->pShpBtnHelp->Add_Tail(*commands);
    240 	chatlist.Add_Tail(*commands);
    241 	ExpandChanBtn.Add_Tail(*commands);
    242 	chanlist.Add_Tail(*commands);
    243 	ExpandUserBtn.Add_Tail(*commands);
    244 	userlist.Add_Tail(*commands);
    245 	ActionBtn.Add_Tail(*commands);
    246 	CreateBtn.Add_Tail(*commands);
    247 //	CreatePrivBtn.Add_Tail(*commands);
    248 	JoinBtn.Add_Tail(*commands);
    249 	BackBtn.Add_Tail(*commands);
    250 //	CGameBtn.Add_Tail(*commands);
    251 	chatlistTitle.Add_Tail(*commands);
    252 	chanlistTitle.Add_Tail(*commands);
    253 	userlistTitle.Add_Tail(*commands);
    254 	sendedit.Add_Tail(*commands);
    255 
    256 	//	Tooltips...
    257 	DWORD				timeToolTipAppear;
    258 	ToolTipClass*		pToolTipHead = NULL;		//	Head of list of ToolTips that parallels gadget list.
    259 	ToolTipClass*		pToolTipHitLast = NULL;		//	ToolTip the mouse was last over, or null.
    260 
    261 	ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon;
    262 	pToolTip->next = pWO->pTTipLeave;
    263 	pToolTip = pToolTip->next;
    264 	pToolTip->next = pWO->pTTipRefresh;
    265 	pToolTip = pToolTip->next;
    266 	pToolTip->next = pWO->pTTipSquelch;
    267 	pToolTip = pToolTip->next;
    268 	pToolTip->next = pWO->pTTipBan;
    269 	pToolTip = pToolTip->next;
    270 	pToolTip->next = pWO->pTTipKick;
    271 	pToolTip = pToolTip->next;
    272 	pToolTip->next = pWO->pTTipFindpage;
    273 	pToolTip = pToolTip->next;
    274 	pToolTip->next = pWO->pTTipOptions;
    275 	pToolTip = pToolTip->next;
    276 	pToolTip->next = pWO->pTTipLadder;
    277 	pToolTip = pToolTip->next;
    278 	pToolTip->next = pWO->pTTipHelp;
    279 	pToolTip = pToolTip->next;
    280 	ToolTipClass TTipChanExpand( &ExpandChanBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandChanBtn.X + 8, ExpandChanBtn.Y - 9, true );
    281 	pToolTip->next = &TTipChanExpand;
    282 	pToolTip = pToolTip->next;
    283 	ToolTipClass TTipUserExpand( &ExpandUserBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandUserBtn.X + 8, ExpandUserBtn.Y - 9, true );
    284 	pToolTip->next = &TTipUserExpand;
    285 	pToolTip = pToolTip->next;
    286 	ToolTipClass TTipChanList( &chanlist, 0, chanlist.X + 1, chanlist.Y + 1, true, true );
    287 	pToolTip->next = &TTipChanList;
    288 	pToolTip = pToolTip->next;
    289 	ToolTipClass TTipJoin( &JoinBtn, TXT_WOL_TTIP_JOIN, d_join_x + d_join_w/2, d_join_y - 6 );
    290 	pToolTip->next = &TTipJoin;
    291 	pToolTip = pToolTip->next;
    292 	ToolTipClass TTipBack( &BackBtn, TXT_WOL_TTIP_BACK, d_back_x + d_back_w/2, d_back_y - 6 );
    293 	pToolTip->next = &TTipBack;
    294 	pToolTip = pToolTip->next;
    295 	ToolTipClass TTipCreate( &CreateBtn, TXT_WOL_TTIP_CREATE, d_create_x + d_create_w/2, d_create_y - 6 );
    296 	pToolTip->next = &TTipCreate;
    297 	pToolTip = pToolTip->next;
    298 	ToolTipClass TTipAction( &ActionBtn, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true );
    299 	pToolTip->next = &TTipAction;
    300 	pToolTip = pToolTip->next;
    301 	ToolTipClass TTipRankRA( &RankRABtn, TXT_WOL_TTIP_RANKRA, RankRABtn.X + 8, RankRABtn.Y - 9, true );
    302 	pToolTip->next = &TTipRankRA;
    303 	pToolTip = pToolTip->next;
    304 	ToolTipClass TTipRankAM( &RankAMBtn, TXT_WOL_TTIP_RANKAM, RankAMBtn.X + 8, RankAMBtn.Y - 9, true );
    305 	pToolTip->next = &TTipRankAM;
    306 	pToolTip = pToolTip->next;
    307 	pToolTip->next = NULL;
    308 
    309 	//........................................................................
    310 	// List boxes
    311 	//........................................................................
    312 	int tabs[] = { 150 };			//	tabs for channel list
    313 	chanlist.Set_Tabs( tabs );
    314 
    315 //	Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT);
    316 
    317 	lastclick_timer = 0;
    318 
    319 	//	Tell WolapiObject about lists to use for output.
    320 	//	(Sure wish I'd gone against the grain and made this dialog a class...)
    321 	pWO->LinkToChatDlg( &chatlist, &chanlist, &userlist, &userlistTitle );
    322 
    323 	if( !pWO->bChatShownBefore )
    324 	{
    325 		//	Print message of the day.
    326 		chatlist.Add_Item( pWO->pChatSink->szMotd, NULL, NULL, ICON_SHAPE );
    327 	}
    328 	else
    329 	{
    330 		//	We have returned to the chat dialog after being in either game setup or an actual game.
    331 		pWO->RestoreChat();
    332 		pWO->DeleteSavedChat();
    333 
    334 		if( pWO->bReturningAfterGame )
    335 			pWO->RejoinLobbyAfterGame();
    336 		else
    337 		{
    338 			if( pWO->pChatSink->bGotKickedTrigger )
    339 			{
    340 				//	We got kicked out of a game setup.
    341 				WOL_PrintMessage( chatlist, TXT_WOL_YOUWEREKICKEDFROMGAME, WOLCOLORREMAP_KICKORBAN );
    342 				pWO->pChatSink->bGotKickedTrigger = false;
    343 			}
    344 		}
    345 
    346 		if( pWO->iLobbyReturnAfterGame != -1 )
    347 		{
    348 			char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ];
    349 			//sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, pWO->iLobbyReturnAfterGame );
    350 			sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, pWO->iLobbyReturnAfterGame );
    351 			pWO->OnEnteringChatChannel( szChannelToJoin, false, iChannelLobbyNumber( (unsigned char*)szChannelToJoin ) );
    352 		}
    353 		else
    354 			//	Will never happen presumably, if games are always entered via a lobby chat channel.
    355 			pWO->EnterLevel_Top();
    356 
    357 		pWO->iLobbyReturnAfterGame = -1;
    358 
    359 		if( pWO->bReturningAfterGame )
    360 		{
    361 			Sound_Effect( WOLSOUND_LOGIN );
    362 			pWO->bReturningAfterGame = false;
    363 		}
    364 		else
    365 			Sound_Effect( WOLSOUND_EXITGAME );
    366 	}
    367 
    368 	//	Cause a refresh of szMyRecord, the string showing my win/loss record.
    369 	pWO->RequestLadders( pWO->szMyName );
    370 
    371 	//------------------------------------------------------------------------
    372 	//	Init Mono Output
    373 	//------------------------------------------------------------------------
    374 	#if(SHOW_MONO)
    375  	Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13);
    376 	Ipx.Mono_Debug_Print(-1,1);
    377 	#endif
    378 
    379 	//------------------------------------------------------------------------
    380 	//	Processing loop
    381 	//------------------------------------------------------------------------
    382 	while (process) 
    383 	{
    384 		#if(SHOW_MONO)
    385 		Ipx.Mono_Debug_Print(-1,0);
    386 		#endif
    387 
    388 		//	Regularly check for incoming messages from wolapi.
    389 		if( ::timeGetTime() > pWO->dwTimeNextWolapiPump )
    390 		{
    391 /*
    392 			if( pToolTipHitLast && pToolTipHitLast->bShowing )	//	Lame hack. Problem is draws that occur in callbacks.
    393 			{
    394 				pToolTipHitLast->Unshow();
    395 				pWO->pChat->PumpMessages();
    396 				pWO->pNetUtil->PumpMessages();
    397 				pToolTipHitLast->Show();
    398 			}
    399 			else
    400 			{
    401 				pWO->pChat->PumpMessages();
    402 				pWO->pNetUtil->PumpMessages();
    403 			}
    404 */
    405 			pWO->pChat->PumpMessages();
    406 			pWO->pNetUtil->PumpMessages();
    407 
    408 			//	Special post-callback processing...
    409 			if( pWO->bSelfDestruct )
    410 			{
    411 				if( pWO->pChatSink->bConnected )
    412 					pWO->Logout();
    413 				rc = -1;		//	As if the user logged himself out.
    414 				process = false;
    415 				break;
    416 			}
    417 			if( pWO->pChatSink->bGotKickedTrigger )
    418 			{
    419 				if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
    420 				{
    421 					pWO->OnExitingChatChannel();
    422 					pWO->EnterLevel_UserChat();
    423 				}
    424 				else if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
    425 				{
    426 					pWO->OnExitingChatChannel();
    427 					pWO->EnterLevel_Lobbies();
    428 				}
    429 				else		//	Must be WOL_LEVEL_INOFFICIALCHATCHANNEL.
    430 				{
    431 					pWO->OnExitingChatChannel();
    432 					pWO->EnterLevel_OfficialChat();
    433 				}
    434 				pWO->pChatSink->bGotKickedTrigger = false;
    435 				display = REDRAW_ALL;
    436 			}
    437 			if( pWO->bMyRecordUpdated )
    438 			{
    439 				if( pWO->bShowRankRA )
    440 					chatlistTitle.Set_Text( pWO->szMyRecord );
    441 				else
    442 					chatlistTitle.Set_Text( pWO->szMyRecordAM );
    443 				pWO->bMyRecordUpdated = false;
    444 			}
    445 			if( pWO->bChannelListTitleUpdated )
    446 			{
    447 				chanlistTitle.Set_Text( pWO->szChannelListTitle );
    448 				pWO->bChannelListTitleUpdated = false;
    449 			}
    450 			pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT;
    451 		}
    452 
    453 		//	Synch rank toggle buttons state.
    454 		if( BackBtn.Get_Prev() == &RankAMBtn )
    455 		{
    456 			if( pWO->CurrentLevel != WOL_LEVEL_INLOBBY )
    457 			{
    458 				//	Rank buttons are there and shouldn't be.
    459 				RankRABtn.Remove();
    460 				RankAMBtn.Remove();
    461 				display = REDRAW_ALL;
    462 			}
    463 			else
    464 			{
    465 				if( pWO->bShowRankUpdated )
    466 				{
    467 					if( pWO->bShowRankRA )
    468 					{
    469 						RankRABtn.DRAWTOGDOWN;
    470 						RankAMBtn.DRAWTOGUP;
    471 					}
    472 					else
    473 					{
    474 						RankRABtn.DRAWTOGUP;
    475 						RankAMBtn.DRAWTOGDOWN;
    476 					}
    477 					//	Buttons have been refreshed.
    478 					pWO->bShowRankUpdated = false;
    479 					//	Cause my own record to get refreshed.
    480 					pWO->bMyRecordUpdated = true;
    481 					//	Refresh list to show different rankings type.
    482 					pWO->ListChannelUsers();
    483 				}
    484 			}
    485 		}
    486 		else
    487 		{
    488 			if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
    489 			{
    490 				//	Rank buttons aren't there and should be.
    491 				RankRABtn.Add( JoinBtn );
    492 				RankAMBtn.Add( RankRABtn );
    493 				RankRABtn.Flag_To_Redraw();
    494 				RankAMBtn.Flag_To_Redraw();
    495 				if( pWO->bShowRankRA )
    496 				{
    497 					RankRABtn.DRAWTOGDOWN;
    498 					RankAMBtn.DRAWTOGUP;
    499 				}
    500 				else
    501 				{
    502 					RankRABtn.DRAWTOGUP;
    503 					RankAMBtn.DRAWTOGDOWN;
    504 				}
    505 				//	Buttons have been refreshed.
    506 				pWO->bShowRankUpdated = false;
    507 			}
    508 		}
    509 
    510 		//	Regularly update the channels list in certain cases.
    511 		if( ( pWO->CurrentLevel == WOL_LEVEL_OFFICIALCHAT || pWO->CurrentLevel == WOL_LEVEL_USERCHAT || 
    512 				pWO->CurrentLevel == WOL_LEVEL_LOBBIES || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) && 
    513 				::timeGetTime() > pWO->dwTimeNextChannelUpdate )
    514 		{
    515 			switch( pWO->CurrentLevel )
    516 			{
    517 			case WOL_LEVEL_OFFICIALCHAT:
    518 				pWO->UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false );
    519 				break;
    520 			case WOL_LEVEL_USERCHAT:
    521 				pWO->UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false );
    522 				break;
    523 			case WOL_LEVEL_LOBBIES:		//	Overkill in this case to update so often...
    524 				pWO->UpdateChannels( 0, CHANNELFILTER_LOBBIES, false );
    525 				break;
    526 			case WOL_LEVEL_INLOBBY:
    527 				pWO->UpdateChannels( GAME_TYPE, CHANNELFILTER_LOCALLOBBYGAMES, true );
    528 				break;
    529 			}
    530 			pWO->dwTimeNextChannelUpdate = ::timeGetTime() + CHANNELUPDATEWAIT;
    531 		}
    532 
    533 #ifdef WIN32
    534 		/*
    535 		** If we have just received input focus again after running in the background then
    536 		** we need to redraw.
    537 		*/
    538 		if (AllSurfaces.SurfacesRestored) {
    539 			AllSurfaces.SurfacesRestored=FALSE;
    540 			display = REDRAW_ALL;
    541 		}
    542 #endif
    543 
    544 		if( bFirsttime && !pWO->bChatShownBefore )
    545 		{
    546 			WWMessageBox().Process( TXT_WOL_FINDINGLOBBY, TXT_NONE );
    547 			char szLobbyName[ WOL_CHANNAME_LEN_MAX ];
    548 			if( pWO->GetNameOfBeginningLobby( szLobbyName ) )
    549 			{
    550 //				debugprint( "Found lobby to go into: '%s'\n", szLobbyName );
    551 				if( !EnterChannel( pWO, chatlist, NULL, szLobbyName, false ) )
    552 				{
    553 					//	Could not enter channel for some reason. Go to top instead.
    554 					pWO->EnterLevel_Top();
    555 				}
    556 			}
    557 			else
    558 			{
    559 				//	Could not find name of a lobby for some reason. Go to top instead.
    560 				pWO->EnterLevel_Top();
    561 			}
    562 			pWO->bChatShownBefore = true;
    563 			display = REDRAW_ALL;
    564 			//	Play login sound.
    565 			Sound_Effect( WOLSOUND_LOGIN );
    566 		}
    567 		bFirsttime = false;
    568 
    569 		//.....................................................................
    570 		//	Refresh display if needed
    571 		//.....................................................................
    572 		if (display) 
    573 		{
    574 			Hide_Mouse();
    575 
    576 			//..................................................................
    577 			//	Redraw backgound & dialog box
    578 			//..................................................................
    579 			if (display >= REDRAW_BACKGROUND) 
    580 			{
    581 				if( pToolTipHitLast && pToolTipHitLast->bShowing )
    582 				{
    583 					pToolTipHitLast->Unshow();
    584 				}
    585 
    586 
    587 				Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h);
    588 
    589 				//...............................................................
    590 				//	Dialog & Field labels
    591 				//...............................................................
    592 
    593 				commands->Draw_All();
    594 
    595 				//	Draw title bar above channel list.
    596 				Draw_Box( d_chanlist_x, d_chanlist_y - 15, d_chanlist_w, 16, BOXSTYLE_BOX, false );
    597 				switch( lesCurrent )
    598 				{
    599 				case LES_CHANNELS_EXPANDED:
    600 					//	Draw users title bar at bottom.
    601 					Draw_Box( d_userlist_x, d_userlist_y + d_userlist_h - 16, d_userlist_w, 16, BOXSTYLE_BOX, false );
    602 					break;
    603 				case LES_USERS_EXPANDED:
    604 					//	Draw users title bar at top.
    605 					Draw_Box( d_chanlist_x, d_chanlist_y, d_chanlist_w, 16, BOXSTYLE_BOX, false );
    606 					break;
    607 				default:
    608 					//	Draw users title bar in middle.
    609 					Draw_Box( d_userlist_x, d_userlist_y - 15, d_userlist_w, 16, BOXSTYLE_BOX, false );
    610 					break;
    611 				}
    612 			}
    613 
    614 			Show_Mouse();
    615 			display = REDRAW_NONE;
    616 		}
    617 
    618 		//	Force mouse visible, as some beta testers report unexplicable disappearing cursors.
    619 		while( Get_Mouse_State() )
    620 			Show_Mouse();
    621 		//	Be nice to other apps.
    622 		Sleep( 50 );
    623 
    624 		//.....................................................................
    625 		//	Get user input
    626 		//.....................................................................
    627 		if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) )
    628 		{
    629 			//	Mouse button is down.
    630 			timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY;
    631 			if( pToolTipHitLast && pToolTipHitLast->bShowing )
    632 			{
    633 				pToolTipHitLast->Unshow();
    634 			}
    635 		}
    636 
    637 		//	If anything currently on the controls list is set to redraw, hide tooltip.
    638 		if( pToolTipHitLast && pToolTipHitLast->bShowing && commands->Is_List_To_Redraw() )
    639 		{
    640 			pToolTipHitLast->Unshow();
    641 		}
    642 
    643 		input = commands->Input();
    644 
    645 		//	This hack, used elsewhere in this form, appears to be the standard dodge around GadgetClass::Input's 
    646 		//	tendency to remove any focus the first time it runs for a 'commands' list.
    647 
    648 		//	ajw - Perhaps I could try doing this every cycle regardless - would avoid stupid non-focused editbox key reactions bug.
    649 		if( bHackFocus )
    650 		{
    651 			sendedit.Set_Focus();
    652 			sendedit.Flag_To_Redraw();
    653 			input = commands->Input();
    654 			bHackFocus = false;
    655 		}
    656 
    657 		//	Tooltips...
    658 		if( pToolTipHead )
    659 		{
    660 			ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit();
    661 			if( pToolTipHit == pToolTipHitLast )
    662 			{
    663 				if( pToolTipHit && bLinkInList( commands, pToolTipHit->pGadget ) )	//	(Gadget must be in controls list.)
    664 				{
    665 					if( !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && 
    666 						!( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) )
    667 					{
    668 						pToolTipHit->Show();
    669 					}
    670 					else if( pToolTipHit->bIconList && pToolTipHit->bOverDifferentLine() )
    671 					{
    672 						pToolTipHit->Unshow();
    673 						pToolTipHit->Show();
    674 					}
    675 				}
    676 			}
    677 			else
    678 			{
    679 				if( pToolTipHitLast && pToolTipHitLast->bShowing )
    680 					pToolTipHitLast->Unshow();
    681 				pToolTipHitLast = pToolTipHit;
    682 				timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY;
    683 			}
    684 		}
    685 
    686 		//.....................................................................
    687 		//	Process input
    688 		//.....................................................................
    689 		switch (input) 
    690 		{
    691 			case ( BUTTON_SENDEDIT | KN_BUTTON ):
    692 				//	Enter has been pressed - was caught by sendedit control.
    693 				if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
    694 				{
    695 					pWO->SendMessage( sendedit.Get_Text(), userlist, false );
    696 					//	Clear sendedit, reset focus.
    697 					szSendBuffer[0] = 0;
    698 					sendedit.Set_Focus();
    699 					//	Mark for redraw.
    700 					sendedit.Flag_To_Redraw();
    701 				}
    702 				else
    703 				{
    704 					WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS );
    705 					Sound_Effect( WOLSOUND_ERROR );
    706 					sendedit.Set_Focus();
    707 					//	Mark for redraw.
    708 					sendedit.Flag_To_Redraw();
    709 				}
    710 				break;
    711 
    712 			case KN_LMOUSE:
    713 				break;
    714 
    715 			case ( BUTTON_EXPANDCHANNELS | KN_BUTTON ):
    716 				if( OnExpandChannelList( chanlist, userlist ) )
    717 				{
    718 					//	Hide userlist.
    719 					if( ExpandUserBtn.Get_Next() == &userlist )
    720 						userlist.Remove();
    721 					//	Ensure chanlist is visible.
    722 					if( ExpandChanBtn.Get_Next() != &chanlist )
    723 						chanlist.Add( ExpandChanBtn );
    724 					//	Move userlist expand button.
    725 					ExpandUserBtn.Set_Position( ExpandUserBtn.X, chanlist.Y + chanlist.Height );
    726 					TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 );
    727 					userlistTitle.Set_Position( userlistTitle.X, chanlist.Y + chanlist.Height + 2 );
    728 					//	Set buttons.
    729 					ExpandChanBtn.Set_Shape( pShpUnexpand );
    730 					ExpandUserBtn.Set_Shape( pShpExpand );
    731 				}
    732 				else
    733 				{
    734 					//	Show userlist.
    735 					if( ExpandUserBtn.Get_Next() != &userlist )
    736 						userlist.Add( ExpandUserBtn );
    737 					//	Move userlist expand button.
    738 					ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 );
    739 					TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 );
    740 					userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 );
    741 					//	Set buttons.
    742 					ExpandChanBtn.Set_Shape( pShpExpand );
    743 					ExpandUserBtn.Set_Shape( pShpExpand );
    744 				}
    745 				//	Move rank buttons.
    746 				RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y );
    747 				RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y );
    748 				TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 );
    749 				TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 );
    750 				display = REDRAW_ALL;
    751 				break;
    752 
    753 			case ( BUTTON_EXPANDUSERS | KN_BUTTON ):
    754 				if( OnExpandUserList( chanlist, userlist ) )
    755 				{
    756 					//	Hide chanlist controls.
    757 					if( ExpandChanBtn.Get_Next() == &chanlist )
    758 						chanlist.Remove();
    759 					//	Ensure userlist is visible.
    760 					if( ExpandUserBtn.Get_Next() != &userlist )
    761 						userlist.Add( ExpandUserBtn );
    762 					//	Set buttons.
    763 					ExpandChanBtn.Set_Shape( pShpExpand );
    764 					ExpandUserBtn.Set_Shape( pShpUnexpand );
    765 				}
    766 				else
    767 				{
    768 					//	Show chanlist.
    769 					if( ExpandChanBtn.Get_Next() != &chanlist )
    770 						chanlist.Add( ExpandChanBtn );
    771 					//	Set buttons.
    772 					ExpandChanBtn.Set_Shape( pShpExpand );
    773 					ExpandUserBtn.Set_Shape( pShpExpand );
    774 				}
    775 				//	Move userlist expand button.
    776 				ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 );
    777 				TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 );
    778 				userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 );
    779 				//	Move rank buttons.
    780 				RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y );
    781 				RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y );
    782 				TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 );
    783 				TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 );
    784 				display = REDRAW_ALL;
    785 				break;
    786 
    787 			case ( BUTTON_CHANLIST | KN_BUTTON ):
    788 				//	User clicks on the game list.
    789 				//...............................................................
    790 				// Handle a double-click
    791 				//...............................................................
    792 				if( lastclick_timer < 30 && chanlist.Current_Index() == lastclick_idx )
    793 				{
    794 					//	Doubleclick on channel list.
    795 					if( ProcessChannelListSelection( pWO, chatlist, chanlist, lastclick_idx ) )
    796 					{
    797 						//	Exit the chat dialog, go to game dialog.
    798 						rc = 2;
    799 						process = false;
    800 					}
    801 					display = REDRAW_ALL;
    802 					bHackFocus = true;
    803 				}
    804 				else
    805 				{
    806 					//...............................................................
    807 					// Handle a single-click
    808 					//...............................................................
    809 					//............................................................
    810 					// If no double-click occurred, set the last-clicked index
    811 					// & double-click timer.
    812 					//............................................................
    813 					lastclick_timer = 0;
    814 					lastclick_idx = chanlist.Current_Index();
    815 
    816 				}
    817 				break;
    818 
    819 			case ( BUTTON_JOIN | KN_BUTTON ):
    820 				//	Pressing the join button is exactly like doubleclicking on the selected index in chanlist, except:
    821 				//		if the first item is selected, ignore, unless we are at the top level
    822 				if( pWO->CurrentLevel == WOL_LEVEL_TOP || chanlist.Current_Index() != 0 )
    823 				{
    824 					if( ProcessChannelListSelection( pWO, chatlist, chanlist, chanlist.Current_Index() ) )
    825 					{
    826 						//	Exit the chat dialog, go to game dialog.
    827 						rc = 2;
    828 						process = false;
    829 					}
    830 					display = REDRAW_ALL;
    831 					bHackFocus = true;
    832 				}
    833 				break;
    834 
    835 			case ( BUTTON_USERLIST | KN_BUTTON ):
    836 				//	User clicks on user list.
    837 				break;
    838 				
    839 			case ( BUTTON_CREATE | KN_BUTTON ):
    840 				switch( pWO->CurrentLevel )
    841 				{
    842 				case WOL_LEVEL_INCHATCHANNEL:
    843 //					debugprint( "%s\n", TXT_WOL_CANTCREATEINCHANNEL );
    844 					WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS );
    845 					Sound_Effect( WOLSOUND_ERROR );
    846 					break;
    847 				case WOL_LEVEL_INLOBBY:
    848 				{
    849 					pWO->bPump_In_Call_Back = true;
    850 					CREATEGAMEINFO CreateGameInfo = WOL_CreateGame_Dialog( pWO );
    851 					pWO->bPump_In_Call_Back = false;
    852 					if( CreateGameInfo.bCreateGame )
    853 					{
    854 						if( CreateGameChannel( pWO, CreateGameInfo ) )
    855 						{
    856 							rc = 1;
    857 							process = false;
    858 						}
    859 					}
    860 					break;
    861 				}
    862 				case WOL_LEVEL_GAMES:
    863 				case WOL_LEVEL_GAMESOFTYPE:
    864 				case WOL_LEVEL_LOBBIES:
    865 					WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEHERE, WOLCOLORREMAP_LOCALMACHINEMESS );
    866 					Sound_Effect( WOLSOUND_ERROR );
    867 					break;
    868 				default:
    869 					CreateChatChannel( pWO );
    870 				}
    871 
    872 				display = REDRAW_ALL;
    873 				bHackFocus = true;
    874 				break;
    875 
    876 			case ( BUTTON_LEAVE | KN_BUTTON ):
    877 				//	Because of the way things are set up, this is exactly like selecting the first item in chanlist.
    878 				//	(Button is disabled when this is not appropriate.)
    879 				ProcessChannelListSelection( pWO, chatlist, chanlist, 0 );
    880 				display = REDRAW_ALL;
    881 				break;
    882 
    883 			case ( BUTTON_REFRESH | KN_BUTTON ):
    884 				pWO->dwTimeNextChannelUpdate = ::timeGetTime();
    885 				break;
    886 
    887 			case ( BUTTON_SQUELCH | KN_BUTTON ):
    888 				pWO->DoSquelch( &userlist );
    889 				break;
    890 
    891 			case ( BUTTON_BAN | KN_BUTTON ):
    892 				pWO->DoKick( &userlist, true );
    893 //				display = REDRAW_ALL;
    894 				break;
    895 
    896 			case ( BUTTON_KICK | KN_BUTTON ):
    897 				pWO->DoKick( &userlist, false );
    898 //				display = REDRAW_ALL;
    899 				break;
    900 
    901 			case ( BUTTON_FINDPAGE | KN_BUTTON ):
    902 				pWO->DoFindPage();
    903 				display = REDRAW_ALL;
    904 				bHackFocus = true;
    905 				break;
    906 
    907 			case ( BUTTON_OPTIONS | KN_BUTTON ):
    908 				pWO->DoOptions();
    909 				display = REDRAW_ALL;
    910 				bHackFocus = true;
    911 				break;
    912 
    913 			case ( KN_ESC ):
    914 //				break;			ajw		Put back in?
    915 
    916 			case ( BUTTON_BACK | KN_BUTTON ):
    917 				//	Pressing the back button is exactly like doubleclicking on the top item in chanlist, except
    918 				//	when we're at the top level.
    919 				if( pWO->CurrentLevel != WOL_LEVEL_TOP )
    920 				{
    921 					ProcessChannelListSelection( pWO, chatlist, chanlist, 0 );
    922 					display = REDRAW_ALL;
    923 					break;
    924 				}
    925 				//	Note no break; here. Fall through if at top level.
    926 			case ( BUTTON_DISCONNECT | KN_BUTTON ):
    927 				if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 )
    928 				{
    929 					if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
    930 						ExitChatChannel( pWO );
    931 					pWO->Logout();
    932 					rc = -1;
    933 					process = false;
    934 				}
    935 				display = REDRAW_ALL;
    936 				bHackFocus = true;
    937 				break;
    938 
    939 			case ( BUTTON_ACTION | KN_BUTTON ):
    940 				if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
    941 				{
    942 					pWO->SendMessage( sendedit.Get_Text(), userlist, true );
    943 					//	Clear sendedit, reset focus.
    944 					szSendBuffer[0] = 0;
    945 					sendedit.Set_Focus();
    946 					//	Mark for redraw.
    947 					//chatlist.Flag_To_Redraw();
    948 					sendedit.Flag_To_Redraw();
    949 				}
    950 				else
    951 				{
    952 					WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS );
    953 					Sound_Effect( WOLSOUND_ERROR );
    954 					sendedit.Set_Focus();
    955 					//	Mark for redraw.
    956 					sendedit.Flag_To_Redraw();
    957 				}
    958 				break;
    959 
    960 			case ( BUTTON_LADDER | KN_BUTTON ):
    961 				pWO->DoLadder();
    962 				display = REDRAW_ALL;
    963 				bHackFocus = true;
    964 				break;
    965 
    966 			case ( BUTTON_HELP | KN_BUTTON ):
    967 				pWO->DoHelp();
    968 				display = REDRAW_ALL;
    969 				bHackFocus = true;
    970 				break;
    971 
    972 			case ( BUTTON_RANKRA | KN_BUTTON ):
    973 				pWO->bShowRankRA = true;
    974 				pWO->bShowRankUpdated = true;
    975 				break;
    976 			
    977 			case ( BUTTON_RANKAM | KN_BUTTON ):
    978 				pWO->bShowRankRA = false;
    979 				pWO->bShowRankUpdated = true;
    980 				break;
    981 
    982 			default:
    983 
    984 				break;
    985 		}
    986 
    987 		//.....................................................................
    988 		//	Service the sounds & score; GameActive must be false at this point,
    989 		//	so Call_Back() doesn't intercept global messages from me!
    990 		//.....................................................................
    991 		Call_Back();
    992 
    993 	}	// end of while
    994 
    995 	if( pToolTipHitLast && pToolTipHitLast->bShowing )
    996 		pToolTipHitLast->Unshow();
    997 
    998 /*
    999 	//------------------------------------------------------------------------
   1000 	//	Restore screen
   1001 	//------------------------------------------------------------------------
   1002 	Hide_Mouse();
   1003 	Load_Title_Page(true);
   1004 	Show_Mouse();
   1005 */
   1006 
   1007 	pWO->SaveChat();
   1008 
   1009 	pWO->ClearListPtrs();
   1010 
   1011 	return rc;
   1012 }
   1013 
   1014 //***********************************************************************************************
   1015 void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ )
   1016 {
   1017 	RemapControlType* pColorRemap = ( iColorRemap == PCOLOR_NONE ? NULL : &ColorRemaps[ iColorRemap ] );
   1018 	WOL_PrintMessage( ILTarget, szText, pColorRemap );
   1019 }
   1020 
   1021 //***********************************************************************************************
   1022 void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap )
   1023 {
   1024 	ILTarget.Add_Item( szText, NULL, NULL, ICON_SHAPE, NULL, NULL, pColorRemap );
   1025 	if( !ILTarget.bScrollBeingDragged() )
   1026 		ILTarget.Show_Last_Item();
   1027 }
   1028 
   1029 //***********************************************************************************************
   1030 bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist )
   1031 {
   1032 	//	Expand channel list button was pressed.
   1033 	//	Returns true if userlist controls are to be hidden, false if they are to be shown.
   1034 	switch( lesCurrent )
   1035 	{
   1036 	case LES_NORMAL:
   1037 		ResizeChannelList( chanlist, true );
   1038 		lesCurrent = LES_CHANNELS_EXPANDED;
   1039 		break;
   1040 	case LES_USERS_EXPANDED:
   1041 		ResizeUserList( userlist, false );
   1042 		ResizeChannelList( chanlist, true );
   1043 		lesCurrent = LES_CHANNELS_EXPANDED;
   1044 		break;
   1045 	case LES_CHANNELS_EXPANDED:
   1046 		ResizeChannelList( chanlist, false );
   1047 		lesCurrent = LES_NORMAL;
   1048 		return false;
   1049 	}
   1050 	return true;
   1051 }
   1052 
   1053 //***********************************************************************************************
   1054 bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist )
   1055 {
   1056 	//	Expand user list button was pressed.
   1057 	//	Returns true if chanlist controls are to be hidden, false if they are to be shown.
   1058 	switch( lesCurrent )
   1059 	{
   1060 	case LES_NORMAL:
   1061 		ResizeUserList( userlist, true );
   1062 		lesCurrent = LES_USERS_EXPANDED;
   1063 		break;
   1064 	case LES_CHANNELS_EXPANDED:
   1065 		ResizeChannelList( chanlist, false );
   1066 		ResizeUserList( userlist, true );
   1067 		lesCurrent = LES_USERS_EXPANDED;
   1068 		break;
   1069 	case LES_USERS_EXPANDED:
   1070 		ResizeUserList( userlist, false );
   1071 		lesCurrent = LES_NORMAL;
   1072 		return false;
   1073 	}
   1074 	return true;
   1075 }
   1076 
   1077 //***********************************************************************************************
   1078 void ResizeChannelList( IconListClass& chanlist, bool bExpand )
   1079 {
   1080 	//	If bExpand, makes list big, else normal size.
   1081 	if( bExpand )
   1082 		chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_userlist_y + d_userlist_h - 15 - d_chanlist_y );
   1083 	else
   1084 		chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h );
   1085 }
   1086 
   1087 //***********************************************************************************************
   1088 void ResizeUserList( IconListClass& userlist, bool bExpand )
   1089 {
   1090 	//	If bExpand, makes list big, else normal size.
   1091 	if( bExpand )
   1092 		userlist.Resize( d_userlist_x, d_chanlist_y + 15, d_userlist_w, d_userlist_y + d_userlist_h - ( d_chanlist_y + 15 ) );
   1093 	else
   1094 		userlist.Resize( d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h );
   1095 }
   1096 
   1097 //***********************************************************************************************
   1098 bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame )
   1099 {
   1100 	//	Enter the channel specified in chanlist at iIndex.
   1101 	//	Called to enter chat channels, "lobbies", and game channels.
   1102 
   1103 	//	We've stored the channel pointer in the hidden extra data field.
   1104 	//	( Be careful about calling RAChatEventSink::DeleteChannelList()! )
   1105 	Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex );
   1106 	return EnterChannel( pWO, chatlist, pChannel, NULL, bGame );
   1107 }
   1108 
   1109 //***********************************************************************************************
   1110 bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame )
   1111 {
   1112 	//	Called to cause the user to enter a channel (chat, lobby, or game).
   1113 	//	If pChannel is NULL, szChannelName will be used.
   1114 
   1115 	Channel ChannelWhenNameOnly;
   1116 	if( !pChannel )
   1117 	{
   1118 		if( !szChannelName )
   1119 		{
   1120 //			debugprint( "pChannel and szChannelName null in EnterChannel" );
   1121 			pWO->bSelfDestruct = true;
   1122 			return false;
   1123 		}
   1124 		pChannel = &ChannelWhenNameOnly;
   1125 		strcpy( (char*)pChannel->name, szChannelName );
   1126 	}
   1127 
   1128 	if( bGame && pChannel->currentUsers >= pChannel->maxUsers )			//	Pre-emptive fullness check.
   1129 	{
   1130 		WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS );
   1131 		Sound_Effect( WOLSOUND_ERROR );
   1132 		return false;
   1133 	}
   1134 
   1135 	if( bGame )
   1136 	{
   1137 		//	It is possible to enter a game channel while currently in a chat channel. (A lobby, presumably.)
   1138 		if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
   1139 			if( !pWO->ExitChatChannelForGameChannel() )
   1140 			{
   1141 				*pWO->szChannelReturnOnGameEnterFail = 0;
   1142 //				debugprint( "ExitChatChannelForGameChannel on join failed" );
   1143 				pWO->bSelfDestruct = true;
   1144 				return false;
   1145 			}
   1146 	}
   1147 
   1148 /*	The following doesn't work because the needpw field is not currently being set in wolapi for chat channels.
   1149 	Instead, we wait for a fail on join, then present this dialog and try again.
   1150 	if( pChannel->needpw )
   1151 	{
   1152 		SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
   1153 											WOL_CHANKEY_LEN_MAX );
   1154 		if( !pEditDlg->Show() || !*pEditDlg->szEdit )
   1155 			return false;
   1156 
   1157 		strcpy( (char*)pChannel->key, pEditDlg->szEdit );
   1158 	}
   1159 */
   1160 	bool bKeepTrying = true;
   1161 
   1162 	//	Set password automatically for our lobbies, if trying to join one.
   1163 	int iLobby = iChannelLobbyNumber( pChannel->name );
   1164 	if( iLobby != -1 )
   1165 		strcpy( (char*)pChannel->key, LOBBYPASSWORD );
   1166 
   1167 	char szSuccessfulPassword[ WOL_PASSWORD_LEN + 5 ];
   1168 	*szSuccessfulPassword = 0;
   1169 
   1170 	HRESULT hRes;
   1171 	while( bKeepTrying )
   1172 	{
   1173 		hRes = pWO->ChannelJoin( pChannel );
   1174 		switch( hRes )
   1175 		{
   1176 		case CHAT_E_BADCHANNELPASSWORD:
   1177 		{
   1178 			Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT );	//	Required before String_Pixel_Width() call, for god's sake.
   1179 #ifdef ENGLISH
   1180 			SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
   1181 												WOL_CHANKEY_LEN_MAX );
   1182 #else
   1183 #ifdef GERMAN
   1184 			SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 400, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
   1185 												WOL_CHANKEY_LEN_MAX );
   1186 #else
   1187 			SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 500, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT,
   1188 												WOL_CHANKEY_LEN_MAX );
   1189 #endif
   1190 #endif
   1191 			pWO->bPump_In_Call_Back = true;
   1192 			if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) != 0 || !*pEditDlg->szEdit )
   1193 			{
   1194 				pWO->bPump_In_Call_Back = false;
   1195 				delete pEditDlg;
   1196 				bKeepTrying = false;
   1197 				break;
   1198 			}
   1199 			pWO->bPump_In_Call_Back = false;
   1200 			strcpy( (char*)pChannel->key, pEditDlg->szEdit );
   1201 			strcpy( szSuccessfulPassword, pEditDlg->szEdit );
   1202 			delete pEditDlg;
   1203 			break;
   1204 		}
   1205 		case CHAT_E_TIMEOUT:
   1206 			pWO->bPump_In_Call_Back = true;
   1207 			WWMessageBox().Process( TXT_WOL_TIMEOUT );
   1208 			pWO->bPump_In_Call_Back = false;
   1209 			bKeepTrying = false;
   1210 			break;
   1211 		case CHAT_E_CHANNELDOESNOTEXIST:
   1212 			pWO->bPump_In_Call_Back = true;
   1213 			WWMessageBox().Process( TXT_WOL_CHANNELGONE );
   1214 			pWO->bPump_In_Call_Back = false;
   1215 			bKeepTrying = false;
   1216 			break;
   1217 		case CHAT_E_BANNED:
   1218 			WOL_PrintMessage( chatlist, TXT_WOL_YOUREBANNED, WOLCOLORREMAP_LOCALMACHINEMESS );
   1219 			Sound_Effect( WOLSOUND_ERROR );
   1220 			bKeepTrying = false;
   1221 			break;
   1222 		case CHAT_E_CHANNELFULL:
   1223 			WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS );
   1224 			Sound_Effect( WOLSOUND_ERROR );
   1225 			bKeepTrying = false;
   1226 			break;
   1227 		case E_FAIL:
   1228 			pWO->GenericErrorMessage();
   1229 			bKeepTrying = false;
   1230 			break;
   1231 		case S_OK:
   1232 			bKeepTrying = false;
   1233 			break;
   1234 		}
   1235 	}
   1236 
   1237 	if( !bGame )
   1238 	{
   1239 		if( hRes == S_OK )
   1240 			return pWO->OnEnteringChatChannel( (char*)pChannel->name, false, iLobby );
   1241 		else
   1242 			return false;
   1243 	}
   1244 	else
   1245 	{
   1246 		if( hRes == S_OK )
   1247 		{
   1248 			*pWO->szChannelReturnOnGameEnterFail = 0;
   1249 			//	Return later to the lobby of the channel creator - which was saved in the channel itself.
   1250 			pWO->iLobbyReturnAfterGame = pChannel->reserved & 0x00FFFFFF;
   1251 			if( pWO->iLobbyReturnAfterGame == 0x00FFFFFF )
   1252 				pWO->iLobbyReturnAfterGame = -1;
   1253 			CREATEGAMEINFO CreateGameInfo;
   1254 			//	Not all of these values are currently used during setup.
   1255 			CreateGameInfo.bCreateGame = false;
   1256 			CreateGameInfo.iPlayerMax = pChannel->maxUsers;
   1257 			CreateGameInfo.bTournament = pChannel->tournament;
   1258 			if( *szSuccessfulPassword )
   1259 			{
   1260 				CreateGameInfo.bPrivate = true;
   1261 				strcpy( CreateGameInfo.szPassword, szSuccessfulPassword );
   1262 			}
   1263 			else
   1264 			{
   1265 				CreateGameInfo.bPrivate = false;
   1266 				*CreateGameInfo.szPassword = 0;
   1267 			}
   1268 			CreateGameInfo.GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 );
   1269 			return pWO->OnEnteringGameChannel( (char*)pChannel->name, false, CreateGameInfo );
   1270 		}
   1271 		else
   1272 		{
   1273 			pWO->OnFailedToEnterGameChannel();
   1274 			*pWO->szChannelReturnOnGameEnterFail = 0;
   1275 			return false;
   1276 		}
   1277 	}
   1278 }
   1279 
   1280 //***********************************************************************************************
   1281 bool ExitChatChannel( WolapiObject* pWO )
   1282 {
   1283 	//	Called to cause user to leave current chat or lobby channel.
   1284 
   1285 //	debugprint( "ExitChatChannel\n" );
   1286 	if( !pWO->ChannelLeave() )
   1287 	{
   1288 		pWO->GenericErrorMessage();
   1289 		return false;
   1290 	}
   1291 
   1292 	pWO->OnExitingChatChannel();
   1293 
   1294 	return true;
   1295 }
   1296 
   1297 //***********************************************************************************************
   1298 //void CreateChatChannel( WolapiObject* pWO, bool bPrivate )
   1299 void CreateChatChannel( WolapiObject* pWO )
   1300 {
   1301 	SimpleEditDlgClass* pEditDlg;
   1302 /*	if( !bPrivate )
   1303 	{
   1304 		pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT,
   1305 											WOL_CHANNAME_LEN_MAX );
   1306 		if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit )
   1307 		{
   1308 			if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) )
   1309 				pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 );
   1310 		}
   1311 	}
   1312 	else
   1313 */	{
   1314 		Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT );	//	Required before String_Pixel_Width() call, for god's sake.
   1315 		pEditDlg = new SimpleEditDlgClass( 350, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT,
   1316 											WOL_CHANNAME_LEN_MAX, TXT_WOL_OPTIONALPASSPROMPT, WOL_CHANKEY_LEN_MAX );
   1317 		pWO->bPump_In_Call_Back = true;
   1318 		if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit )
   1319 		{
   1320 			pWO->bPump_In_Call_Back = false;
   1321 			if( *pEditDlg->szEdit2 )
   1322 			{
   1323 				if( pWO->ChannelCreate( pEditDlg->szEdit, pEditDlg->szEdit2 ) )
   1324 					pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 );
   1325 			}
   1326 			else
   1327 			{
   1328 				//	Create public channel.
   1329 				if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) )
   1330 					pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 );
   1331 			}
   1332 		}
   1333 		pWO->bPump_In_Call_Back = false;
   1334 	}
   1335 
   1336 	delete pEditDlg;
   1337 }
   1338 
   1339 //***********************************************************************************************
   1340 bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi )
   1341 {
   1342 	char szNewChannelName[ WOL_CHANNAME_LEN_MAX ];
   1343 	sprintf( szNewChannelName, "%s's_game", pWO->szMyName );
   1344 
   1345 	if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
   1346 		if( !pWO->ExitChatChannelForGameChannel() )
   1347 		{
   1348 			*pWO->szChannelReturnOnGameEnterFail = 0;
   1349 //			debugprint( "ExitChatChannelForGameChannel in CreateGameChannel() error" );
   1350 			pWO->bSelfDestruct = true;
   1351 			return false;
   1352 		}
   1353 
   1354 	const char* szKey;
   1355 	if( *cgi.szPassword )
   1356 		szKey = cgi.szPassword;
   1357 	else
   1358 		szKey = NULL;
   1359 
   1360 	if( pWO->ChannelCreate( szNewChannelName, szKey, true, cgi.iPlayerMax, cgi.bTournament, pWO->iLobbyLast, cgi.GameKind ) )
   1361 		pWO->OnEnteringGameChannel( szNewChannelName, true, cgi );
   1362 	else
   1363 	{
   1364 		pWO->OnFailedToEnterGameChannel();
   1365 //		debugprint( "CreateGameChannel fail" );
   1366 		return false;
   1367 	}
   1368 	*pWO->szChannelReturnOnGameEnterFail = 0;
   1369 
   1370 	return true;
   1371 }
   1372 
   1373 //***********************************************************************************************
   1374 bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex )
   1375 {
   1376 	//	Takes whatever action necessary due to user selecting iIndex from chanlist.
   1377 	//	Returns true if user selected to enter a game channel, else false.
   1378 	if( iIndex < 0 )
   1379 		return false;
   1380 
   1381 //	debugprint( "iIndex %i\n", iIndex );
   1382 
   1383 	const char* szChannelType = chanlist.Get_Item_ExtraDataString( iIndex );
   1384 	if( szChannelType )
   1385 	{
   1386 //debugprint( "szChannelType %s\n", szChannelType );
   1387 		if( strcmp( szChannelType, CHANNELTYPE_OFFICIALCHAT ) == 0 )
   1388 		{
   1389 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1390 			{
   1391 				if( !ExitChatChannel( pWO ) )
   1392 				{
   1393 					pWO->bSelfDestruct = true;
   1394 					return false;
   1395 				}
   1396 			}
   1397 			pWO->EnterLevel_OfficialChat();
   1398 		}
   1399 		else if( strcmp( szChannelType, CHANNELTYPE_USERCHAT ) == 0 )
   1400 		{
   1401 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1402 			{
   1403 				if( !ExitChatChannel( pWO ) )
   1404 				{
   1405 					pWO->bSelfDestruct = true;
   1406 					return false;
   1407 				}
   1408 			}
   1409 			pWO->EnterLevel_UserChat();
   1410 		}
   1411 		else if( strcmp( szChannelType, CHANNELTYPE_TOP ) == 0 )
   1412 		{
   1413 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1414 			{
   1415 				//	Now not possible.
   1416 				if( !ExitChatChannel( pWO ) )
   1417 				{
   1418 					pWO->bSelfDestruct = true;
   1419 					return false;
   1420 				}
   1421 			}
   1422 			pWO->EnterLevel_Top();
   1423 		}
   1424 		else if( strcmp( szChannelType, CHANNELTYPE_GAMES ) == 0 )
   1425 		{
   1426 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1427 			{
   1428 				//	Now not possible.
   1429 				if( !ExitChatChannel( pWO ) )
   1430 				{
   1431 					pWO->bSelfDestruct = true;
   1432 					return false;
   1433 				}
   1434 			}
   1435 			pWO->EnterLevel_Games();
   1436 		}
   1437 		else if( strcmp( szChannelType, CHANNELTYPE_GAMESOFTYPE ) == 0 )
   1438 		{
   1439 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1440 			{
   1441 				//	Now not possible.
   1442 				if( !ExitChatChannel( pWO ) )
   1443 				{
   1444 					pWO->bSelfDestruct = true;
   1445 					return false;
   1446 				}
   1447 			}
   1448 			void* pExtraData = chanlist.Get_Item_ExtraDataPtr( iIndex );
   1449 			pWO->EnterLevel_GamesOfType( (WOL_GAMETYPEINFO*)pExtraData );
   1450 		}
   1451 		else if( strcmp( szChannelType, CHANNELTYPE_CHATCHANNEL ) == 0 )
   1452 		{
   1453 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1454 			{
   1455 				//	Not currently possible.
   1456 //				debugprint( "Trying to jump from channel to channel?!\n" );
   1457 				pWO->bSelfDestruct = true;
   1458 				return false;
   1459 			}
   1460 			//	Join the chat channel.
   1461 			EnterChannel( pWO, chatlist, chanlist, iIndex, false );		//	Can fail.
   1462 		}
   1463 		else if( strcmp( szChannelType, CHANNELTYPE_GAMECHANNEL ) == 0 )
   1464 		{
   1465 			//	User attempting to join game channel.
   1466 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1467 			{
   1468 				//	Not currently possible.
   1469 //				debugprint( "Trying to jump from channel to channel?!\n" );
   1470 				pWO->bSelfDestruct = true;
   1471 				return false;
   1472 			}
   1473 			//	Check if local user is allowed to join GameKind.
   1474 			Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex );
   1475 			if( pChannel->type == GAME_TYPE )
   1476 			{
   1477 				//	It is a game of our type, at least.
   1478 				CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 );
   1479 				switch( GameKind )
   1480 				{
   1481 				case CREATEGAMEINFO::RAGAME:
   1482 					break;
   1483 				case CREATEGAMEINFO::CSGAME:
   1484 					if( !Is_Counterstrike_Installed() )
   1485 					{
   1486 						WOL_PrintMessage( chatlist, TXT_WOL_NEEDCOUNTERSTRIKE, WOLCOLORREMAP_LOCALMACHINEMESS );
   1487 						Sound_Effect( WOLSOUND_ERROR );
   1488 						return false;
   1489 					}
   1490 					break;
   1491 				case CREATEGAMEINFO::AMGAME:
   1492 					if( !Is_Aftermath_Installed() )
   1493 					{
   1494 						WOL_PrintMessage( chatlist, TXT_WOL_NEEDAFTERMATH, WOLCOLORREMAP_LOCALMACHINEMESS );
   1495 						Sound_Effect( WOLSOUND_ERROR );
   1496 						return false;
   1497 					}
   1498 					break;
   1499 				default:
   1500 //					debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name );
   1501 					Sound_Effect( WOLSOUND_ERROR );
   1502 					return false;
   1503 				}
   1504 				//	Join the game channel.
   1505 				if( EnterChannel( pWO, chatlist, chanlist, iIndex, true ) )
   1506 				{
   1507 					//	Exit the chat dialog, go to game dialog.
   1508 					return true;
   1509 				}
   1510 			}
   1511 			else
   1512 			{
   1513 				//	User doubleclicked on a game that is of a different type.
   1514 				//	Offer to take them to a web page regarding the game type.
   1515 				pWO->DoGameAdvertising( pChannel );
   1516 			}
   1517 		}
   1518 		else if( strcmp( szChannelType, CHANNELTYPE_LOBBIES ) == 0 )
   1519 		{
   1520 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL )
   1521 			{
   1522 				//	Not currently possible.
   1523 //				debugprint( "Chat channel to lobbies level?!\n" );
   1524 				pWO->bSelfDestruct = true;
   1525 				return false;
   1526 			}
   1527 			if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
   1528 			{
   1529 				if( !ExitChatChannel( pWO ) )
   1530 				{
   1531 					pWO->bSelfDestruct = true;
   1532 					return false;
   1533 				}
   1534 			}
   1535 			pWO->EnterLevel_Lobbies();
   1536 		}
   1537 		else if( strcmp( szChannelType, CHANNELTYPE_LOBBYCHANNEL ) == 0 )
   1538 		{
   1539 			if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ||
   1540 				pWO->CurrentLevel == WOL_LEVEL_INLOBBY )
   1541 			{
   1542 				//	Not currently possible.
   1543 //				debugprint( "Chat or lobby channel to lobby channel?!\n" );
   1544 				pWO->bSelfDestruct = true;
   1545 				return false;
   1546 			}
   1547 			//	Join the lobby chat channel.
   1548 			EnterChannel( pWO, chatlist, chanlist, iIndex, false );		//	Can fail.
   1549 		}
   1550 		else if( strcmp( szChannelType, CHANNELTYPE_LOADING ) == 0 )
   1551 		{
   1552 			//	User clicked on the channel list loading notification - do nothing.
   1553 		}
   1554 		else
   1555 		{
   1556 //			debugprint( "Item selected in channel list unidentifiable from extradata field\n" );
   1557 			pWO->bSelfDestruct = true;
   1558 			return false;
   1559 		}
   1560 
   1561 	}
   1562 	return false;
   1563 }
   1564 	
   1565 //***********************************************************************************************
   1566 bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind )
   1567 {
   1568 	const LinkClass* pLink = pListHead;
   1569 	while( pLink )
   1570 	{
   1571 		if( pLink == pLinkToFind )
   1572 			return true;
   1573 		pLink = pLink->Get_Next();
   1574 	}
   1575 	return false;
   1576 }
   1577 
   1578 #endif