core/MenuStyle_Base.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Sun Mar 17 21:58:56 2013 +0000 (2013-03-17)
changeset 3837 f86a51ee4eb4
parent 3613 b0740093756d
child 3872 4c93f74cae51
permissions -rw-r--r--
Check for cloned handles when dumping (bug 5245, r=asherkin).
dvander@769
     1
/**
dvander@769
     2
 * vim: set ts=4 :
ds@1330
     3
 * =============================================================================
dvander@1231
     4
 * SourceMod
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1330
     6
 * =============================================================================
dvander@769
     7
 *
ds@1330
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1330
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330
    10
 * Free Software Foundation.
dvander@1231
    11
 * 
ds@1330
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1330
    15
 * details.
dvander@1231
    16
 *
ds@1330
    17
 * You should have received a copy of the GNU General Public License along with
ds@1330
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1231
    19
 *
ds@1330
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1330
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1330
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1330
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@769
    28
 *
dvander@769
    29
 * Version: $Id$
dvander@769
    30
 */
dvander@771
    31
dvander@769
    32
#include <stdarg.h>
dvander@769
    33
#include "sm_stringutil.h"
dvander@769
    34
#include "MenuStyle_Base.h"
dvander@775
    35
#include "PlayerManager.h"
dvander@775
    36
#include "MenuManager.h"
dvander@790
    37
#include "HandleSys.h"
dvander@1220
    38
#include "CellRecipientFilter.h"
dvander@970
    39
#if defined MENU_DEBUG
dvander@970
    40
#include "Logger.h"
dvander@970
    41
#endif
dvander@775
    42
dvander@790
    43
BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE)
dvander@790
    44
{
dvander@790
    45
}
dvander@790
    46
dvander@790
    47
Handle_t BaseMenuStyle::GetHandle()
dvander@775
    48
{
dvander@790
    49
	/* Don't create the handle until we need it */
dvander@790
    50
	if (m_hHandle == BAD_HANDLE)
dvander@790
    51
	{
dvander@790
    52
		m_hHandle = g_Menus.CreateStyleHandle(this);
dvander@790
    53
	}
dvander@790
    54
dvander@790
    55
	return m_hHandle;
dvander@775
    56
}
dvander@775
    57
dvander@775
    58
void BaseMenuStyle::AddClientToWatch(int client)
dvander@775
    59
{
dvander@970
    60
#if defined MENU_DEBUG
dvander@970
    61
	g_Logger.LogMessage("[SM_MENU] AddClientToWatch(%d)", client);
dvander@970
    62
#endif
dvander@775
    63
	m_WatchList.push_back(client);
dvander@775
    64
}
dvander@775
    65
dvander@775
    66
void BaseMenuStyle::RemoveClientFromWatch(int client)
dvander@775
    67
{
dvander@970
    68
#if defined MENU_DEBUG
dvander@970
    69
	g_Logger.LogMessage("[SM_MENU] RemoveClientFromWatch(%d)", client);
dvander@970
    70
#endif
dvander@775
    71
	m_WatchList.remove(client);
dvander@775
    72
}
dvander@775
    73
dvander@1005
    74
void BaseMenuStyle::_CancelClientMenu(int client, MenuCancelReason reason, bool bAutoIgnore/* =false */)
dvander@775
    75
{
dvander@970
    76
#if defined MENU_DEBUG
dvander@970
    77
	g_Logger.LogMessage("[SM_MENU] _CancelClientMenu() (client %d) (bAutoIgnore %d) (reason %d)", client, bAutoIgnore, reason);
dvander@970
    78
#endif
dvander@775
    79
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
    80
	menu_states_t &states = player->states;
dvander@775
    81
dvander@775
    82
	bool bOldIgnore = player->bAutoIgnore;
dvander@775
    83
	if (bAutoIgnore)
dvander@775
    84
	{
dvander@775
    85
		player->bAutoIgnore = true;
dvander@775
    86
	}
dvander@775
    87
dvander@775
    88
	/* Save states */
dvander@775
    89
	IMenuHandler *mh = states.mh;
dvander@775
    90
	IBaseMenu *menu = states.menu;
dvander@775
    91
dvander@775
    92
	/* Clear menu */
dvander@775
    93
	player->bInMenu = false;
dvander@775
    94
	if (player->menuHoldTime)
dvander@775
    95
	{
dvander@775
    96
		RemoveClientFromWatch(client);
dvander@775
    97
	}
dvander@775
    98
dvander@775
    99
	/* Fire callbacks */
dvander@775
   100
	mh->OnMenuCancel(menu, client, reason);
dvander@790
   101
	
dvander@790
   102
	/* Only fire end if there's a valid menu */
dvander@790
   103
	if (menu)
dvander@790
   104
	{
dvander@997
   105
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
dvander@790
   106
	}
dvander@775
   107
dvander@775
   108
	if (bAutoIgnore)
dvander@775
   109
	{
dvander@775
   110
		player->bAutoIgnore = bOldIgnore;
dvander@775
   111
	}
dvander@775
   112
}
dvander@775
   113
dvander@775
   114
void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
dvander@775
   115
{
dvander@970
   116
#if defined MENU_DEBUG
dvander@970
   117
	g_Logger.LogMessage("[SM_MENU] CancelMenu() (menu %p)", menu);
dvander@970
   118
#endif
dvander@775
   119
	int maxClients = g_Players.GetMaxClients();
dvander@775
   120
	for (int i=1; i<=maxClients; i++)
dvander@775
   121
	{
dvander@775
   122
		CBaseMenuPlayer *player = GetMenuPlayer(i);
dvander@775
   123
		if (player->bInMenu)
dvander@775
   124
		{
dvander@775
   125
			menu_states_t &states = player->states;
dvander@775
   126
			if (states.menu == menu)
dvander@775
   127
			{
dvander@1005
   128
				_CancelClientMenu(i, MenuCancel_Interrupted);
dvander@775
   129
			}
dvander@775
   130
		}
dvander@775
   131
	}
dvander@775
   132
}
dvander@775
   133
dvander@775
   134
bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
dvander@775
   135
{
dvander@970
   136
#if defined MENU_DEBUG
dvander@970
   137
	g_Logger.LogMessage("[SM_MENU] CancelClientMenu() (client %d) (bAutoIgnore %d)", client, autoIgnore);
dvander@970
   138
#endif
dvander@775
   139
	if (client < 1 || client > g_Players.MaxClients())
dvander@775
   140
	{
dvander@775
   141
		return false;
dvander@775
   142
	}
dvander@775
   143
dvander@775
   144
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
   145
	if (!player->bInMenu)
dvander@775
   146
	{
dvander@775
   147
		return false;
dvander@775
   148
	}
dvander@775
   149
dvander@1005
   150
	_CancelClientMenu(client, MenuCancel_Interrupted, autoIgnore);
dvander@775
   151
dvander@775
   152
	return true;
dvander@775
   153
}
dvander@775
   154
dvander@775
   155
MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
dvander@775
   156
{
dvander@775
   157
	if (client < 1 || client > g_Players.GetMaxClients())
dvander@775
   158
	{
dvander@775
   159
		return MenuSource_None;
dvander@775
   160
	}
dvander@775
   161
dvander@775
   162
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
   163
dvander@775
   164
	if (player->bInMenu)
dvander@775
   165
	{
dvander@775
   166
		IBaseMenu *menu;
dvander@775
   167
		if ((menu=player->states.menu) != NULL)
dvander@775
   168
		{
dvander@775
   169
			if (object)
dvander@775
   170
			{
dvander@775
   171
				*object = menu;
dvander@775
   172
			}
dvander@775
   173
			return MenuSource_BaseMenu;
dvander@775
   174
		}
dvander@775
   175
dvander@775
   176
		return MenuSource_Display;
dvander@780
   177
	} else if (player->bInExternMenu) {
dvander@782
   178
		if (player->menuHoldTime != 0
dvander@782
   179
			&& (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime))
dvander@782
   180
		{
dvander@782
   181
			player->bInExternMenu = false;
dvander@782
   182
			return MenuSource_None;
dvander@782
   183
		}
dvander@780
   184
		return MenuSource_External;
dvander@775
   185
	}
dvander@775
   186
dvander@775
   187
	return MenuSource_None;
dvander@775
   188
}
dvander@775
   189
dvander@775
   190
void BaseMenuStyle::OnClientDisconnected(int client)
dvander@775
   191
{
dvander@970
   192
#if defined MENU_DEBUG
dvander@970
   193
	g_Logger.LogMessage("[SM_MENU] OnClientDisconnected(%d)", client);
dvander@970
   194
#endif
dvander@775
   195
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
   196
	if (!player->bInMenu)
dvander@775
   197
	{
dvander@775
   198
		return;
dvander@775
   199
	}
dvander@775
   200
dvander@1005
   201
	_CancelClientMenu(client, MenuCancel_Disconnected, true);
dvander@775
   202
dvander@775
   203
	player->bInMenu = false;
dvander@790
   204
	player->bInExternMenu = false;
dvander@775
   205
}
dvander@775
   206
dvander@775
   207
static int do_lookup[256];
dvander@775
   208
void BaseMenuStyle::ProcessWatchList()
dvander@775
   209
{
dvander@775
   210
	if (!m_WatchList.size())
dvander@775
   211
	{
dvander@775
   212
		return;
dvander@775
   213
	}
dvander@775
   214
dvander@1131
   215
#if defined MENU_DEBUG
dvander@1131
   216
	g_Logger.LogMessage("BaseMenuStyle::ProcessWatchList(%d,%d,%d,%d,%d,%p)", 
dvander@1131
   217
		m_WatchList.m_Size,
dvander@1131
   218
		m_WatchList.m_FirstLink,
dvander@1131
   219
		m_WatchList.m_FreeNodes,
dvander@1131
   220
		m_WatchList.m_LastLink,
dvander@1131
   221
		m_WatchList.m_MaxSize,
dvander@1131
   222
		m_WatchList.m_Nodes);
dvander@1131
   223
#endif
dvander@1131
   224
dvander@775
   225
	unsigned int total = 0;
dvander@775
   226
	for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
dvander@775
   227
	{
dvander@775
   228
		do_lookup[total++] = (*iter);
dvander@775
   229
	}
dvander@775
   230
dvander@970
   231
#if defined MENU_DEBUG
dvander@970
   232
	if (total)
dvander@970
   233
	{
dvander@970
   234
		g_Logger.LogMessage("[SM_MENU] ProcessWatchList() found %d clients", total);
dvander@970
   235
	}
dvander@970
   236
#endif
dvander@970
   237
dvander@775
   238
	int client;
dvander@775
   239
	CBaseMenuPlayer *player;
dvander@775
   240
	float curTime = gpGlobals->curtime;
dvander@775
   241
	for (unsigned int i=0; i<total; i++)
dvander@775
   242
	{
dvander@775
   243
		client = do_lookup[i];
dvander@775
   244
		player = GetMenuPlayer(client);
dvander@970
   245
#if defined MENU_DEBUG
dvander@970
   246
		g_Logger.LogMessage("[SM_MENU] ProcessWatchList() (client %d) (bInMenu %d) (menuHoldTime %d) (curTime %f) (menuStartTime %f)",
dvander@970
   247
			client,
dvander@970
   248
			player->bInMenu,
dvander@970
   249
			player->menuHoldTime,
dvander@970
   250
			curTime,
dvander@970
   251
			player->menuStartTime);
dvander@970
   252
#endif
dvander@775
   253
		if (!player->bInMenu || !player->menuHoldTime)
dvander@775
   254
		{
dvander@970
   255
#if defined MENU_DEBUG
dvander@970
   256
			g_Logger.LogMessage("[SM_MENU] ProcessWatchList() removing client %d", client);
dvander@970
   257
#endif
dvander@825
   258
			m_WatchList.remove(client);
dvander@775
   259
			continue;
dvander@775
   260
		}
dvander@775
   261
		if (curTime > player->menuStartTime + player->menuHoldTime)
dvander@775
   262
		{
dvander@1005
   263
			_CancelClientMenu(client, MenuCancel_Timeout, false);
dvander@775
   264
		}
dvander@775
   265
	}
dvander@775
   266
}
dvander@775
   267
dvander@775
   268
void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
dvander@775
   269
{
dvander@970
   270
#if defined MENU_DEBUG
dvander@970
   271
	g_Logger.LogMessage("[SM_MENU] ClientPressedKey() (client %d) (key_press %d)", client, key_press);
dvander@970
   272
#endif
dvander@775
   273
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
   274
dvander@775
   275
	/* First question: Are we in a menu? */
dvander@775
   276
	if (!player->bInMenu)
dvander@775
   277
	{
dvander@775
   278
		return;
dvander@775
   279
	}
dvander@775
   280
dvander@775
   281
	bool cancel = false;
dvander@775
   282
	unsigned int item = 0;
dvander@775
   283
	MenuCancelReason reason = MenuCancel_Exit;
dvander@1005
   284
	MenuEndReason end_reason = MenuEnd_Selected;
dvander@775
   285
	menu_states_t &states = player->states;
dvander@775
   286
dvander@1259
   287
	/* Save variables */
dvander@1259
   288
	IMenuHandler *mh = states.mh;
dvander@1259
   289
	IBaseMenu *menu = states.menu;
dvander@775
   290
dvander@1476
   291
	unsigned int item_on_page = states.item_on_page;
dvander@1474
   292
dvander@1259
   293
	assert(mh != NULL);
dvander@1259
   294
dvander@1259
   295
	if (menu == NULL)
dvander@775
   296
	{
dvander@775
   297
		item = key_press;
dvander@785
   298
	} else if (key_press < 1 || key_press > GetMaxPageItems()) {
dvander@775
   299
		cancel = true;
dvander@775
   300
	} else {
dvander@775
   301
		ItemSelection type = states.slots[key_press].type;
dvander@775
   302
dvander@1220
   303
		/* Check if we should play a sound about the type */
dvander@1259
   304
		if (g_Menus.MenuSoundsEnabled() && 
dvander@1259
   305
			(!menu || (menu->GetMenuOptionFlags() & MENUFLAG_NO_SOUND) != MENUFLAG_NO_SOUND))
dvander@1220
   306
		{
dvander@1220
   307
			CellRecipientFilter filter;
dvander@1220
   308
			cell_t clients[1];
dvander@1220
   309
dvander@1220
   310
			clients[0] = client;
dvander@1220
   311
			filter.Initialize(clients, 1);
dvander@1220
   312
dvander@1220
   313
			const char *sound = g_Menus.GetMenuSound(type);
dvander@1220
   314
dvander@1220
   315
			if (sound != NULL)
dvander@1220
   316
			{
ds@2400
   317
				edict_t *pEdict = PEntityOfEntIndex(client);
dvander@1220
   318
				if (pEdict)
dvander@1220
   319
				{
dvander@1220
   320
					ICollideable *pCollideable = pEdict->GetCollideable();
dvander@1220
   321
dvander@1220
   322
					if (pCollideable)
dvander@1220
   323
					{
dvander@1220
   324
						const Vector & pos = pCollideable->GetCollisionOrigin();
dvander@1220
   325
						enginesound->EmitSound(filter, 
dvander@1220
   326
							client, 
dvander@1220
   327
							CHAN_AUTO, 
skamonkey@3515
   328
#if SOURCE_ENGINE >= SE_PORTAL2
skamonkey@3515
   329
							sound, 
skamonkey@3515
   330
							-1, 
skamonkey@3515
   331
#endif
dvander@1220
   332
							sound, 
dvander@1220
   333
							VOL_NORM, 
dvander@1220
   334
							ATTN_NORM, 
skamonkey@3515
   335
#if SOURCE_ENGINE >= SE_PORTAL2
skamonkey@3515
   336
							0, 
skamonkey@3515
   337
#endif
dvander@1220
   338
							0, 
dvander@1220
   339
							PITCH_NORM, 
skamonkey@3613
   340
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_ORANGEBOXVALVE
skamonkey@3564
   341
							0,
skamonkey@3564
   342
#endif
dvander@1220
   343
							&pos);
dvander@1220
   344
					}
dvander@1220
   345
				}
dvander@1220
   346
			}
dvander@1220
   347
		}
dvander@1220
   348
dvander@775
   349
		/* For navigational items, we're going to redisplay */
dvander@775
   350
		if (type == ItemSel_Back)
dvander@775
   351
		{
dvander@775
   352
			if (!RedoClientMenu(client, ItemOrder_Descending))
dvander@775
   353
			{
dvander@775
   354
				cancel = true;
dvander@775
   355
				reason = MenuCancel_NoDisplay;
dvander@1005
   356
				end_reason = MenuEnd_Cancelled;
dvander@775
   357
			} else {
dvander@775
   358
				return;
dvander@775
   359
			}
dvander@775
   360
		} else if (type == ItemSel_Next) {
dvander@775
   361
			if (!RedoClientMenu(client, ItemOrder_Ascending))
dvander@775
   362
			{
dvander@775
   363
				cancel = true;						/* I like Saltines. */
dvander@775
   364
				reason = MenuCancel_NoDisplay;
dvander@1005
   365
				end_reason = MenuEnd_Cancelled;
dvander@775
   366
			} else {
dvander@775
   367
				return;
dvander@775
   368
			}
dvander@775
   369
		} else if (type == ItemSel_Exit || type == ItemSel_None) {
dvander@775
   370
			cancel = true;
dvander@1005
   371
			reason = MenuCancel_Exit;
dvander@1005
   372
			end_reason = MenuEnd_Exit;
dvander@1005
   373
		} else if (type == ItemSel_ExitBack) {
dvander@1005
   374
			cancel = true;
dvander@1005
   375
			reason = MenuCancel_ExitBack;
dvander@1005
   376
			end_reason = MenuEnd_ExitBack;
dvander@775
   377
		} else {
dvander@775
   378
			item = states.slots[key_press].item;
dvander@775
   379
		}
dvander@775
   380
	}
dvander@775
   381
dvander@775
   382
	/* Clear states */
dvander@775
   383
	player->bInMenu = false;
dvander@775
   384
	if (player->menuHoldTime)
dvander@775
   385
	{
dvander@883
   386
		RemoveClientFromWatch(client);
dvander@775
   387
	}
dvander@775
   388
dvander@2964
   389
	Handle_t hndl = menu ? menu->GetHandle() : BAD_HANDLE;
dvander@2959
   390
	AutoHandleRooter ahr(hndl);
dvander@2959
   391
dvander@775
   392
	if (cancel)
dvander@775
   393
	{
dvander@775
   394
		mh->OnMenuCancel(menu, client, reason);
dvander@775
   395
	} else {
dvander@775
   396
		mh->OnMenuSelect(menu, client, item);
dvander@1474
   397
		if (mh->GetMenuAPIVersion2() >= 13)
dvander@1474
   398
		{
dvander@1474
   399
			mh->OnMenuSelect2(menu, client, item, item_on_page);
dvander@1474
   400
		}
dvander@775
   401
	}
dvander@775
   402
dvander@790
   403
	/* Only fire end for valid menus */
dvander@790
   404
	if (menu)
dvander@790
   405
	{
dvander@997
   406
		mh->OnMenuEnd(menu, end_reason);
dvander@790
   407
	}
dvander@775
   408
}
dvander@775
   409
dvander@787
   410
bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
dvander@775
   411
{
dvander@970
   412
#if defined MENU_DEBUG
dvander@970
   413
	g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (panel %p) (mh %p) (time %d)",
dvander@970
   414
		client,
dvander@970
   415
		menu,
dvander@970
   416
		mh,
dvander@970
   417
		time);
dvander@970
   418
#endif
dvander@775
   419
	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
dvander@775
   420
	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
dvander@775
   421
	{
dvander@775
   422
		return false;
dvander@775
   423
	}
dvander@775
   424
dvander@775
   425
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
   426
	if (player->bAutoIgnore)
dvander@775
   427
	{
dvander@775
   428
		return false;
dvander@775
   429
	}
dvander@775
   430
dvander@775
   431
	/* For the duration of this, we are going to totally ignore whether
dvander@775
   432
	* the player is already in a menu or not (except to cancel the old one).
dvander@775
   433
	* Instead, we are simply going to ignore any further menu displays, so
dvander@775
   434
	* this display can't be interrupted.
dvander@775
   435
	*/
dvander@775
   436
	player->bAutoIgnore = true;
dvander@775
   437
dvander@775
   438
	/* Cancel any old menus */
dvander@775
   439
	menu_states_t &states = player->states;
dvander@775
   440
	if (player->bInMenu)
dvander@775
   441
	{
dvander@1005
   442
		_CancelClientMenu(client, MenuCancel_Interrupted, true);
dvander@775
   443
	}
dvander@775
   444
dvander@775
   445
	states.firstItem = 0;
dvander@775
   446
	states.lastItem = 0;
dvander@775
   447
	states.menu = NULL;
dvander@775
   448
	states.mh = mh;
dvander@775
   449
	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
dvander@775
   450
	player->bInMenu = true;
dvander@790
   451
	player->bInExternMenu = false;
dvander@775
   452
	player->menuStartTime = gpGlobals->curtime;
dvander@775
   453
	player->menuHoldTime = time;
dvander@775
   454
dvander@775
   455
	if (time)
dvander@775
   456
	{
dvander@775
   457
		AddClientToWatch(client);
dvander@775
   458
	}
dvander@775
   459
dvander@775
   460
	/* Draw the display */
dvander@775
   461
	SendDisplay(client, menu);
dvander@775
   462
dvander@775
   463
	/* We can be interrupted again! */
dvander@775
   464
	player->bAutoIgnore = false;
dvander@775
   465
dvander@775
   466
	return true;
dvander@775
   467
}
dvander@775
   468
dvander@1474
   469
bool BaseMenuStyle::DoClientMenu(int client,
dvander@1474
   470
								 CBaseMenu *menu,
dvander@1474
   471
								 unsigned int first_item,
dvander@1474
   472
								 IMenuHandler *mh,
dvander@1474
   473
								 unsigned int time)
dvander@775
   474
{
dvander@970
   475
#if defined MENU_DEBUG
dvander@970
   476
	g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (menu %p) (mh %p) (time %d)",
dvander@970
   477
		client,
dvander@970
   478
		menu,
dvander@970
   479
		mh,
dvander@970
   480
		time);
dvander@970
   481
#endif
dvander@775
   482
	mh->OnMenuStart(menu);
dvander@775
   483
dvander@775
   484
	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
dvander@775
   485
	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
dvander@775
   486
	{
dvander@970
   487
#if defined MENU_DEBUG
dvander@970
   488
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to display to client %d", client);
dvander@970
   489
#endif
dvander@775
   490
		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
dvander@997
   491
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
dvander@775
   492
		return false;
dvander@775
   493
	}
dvander@775
   494
dvander@775
   495
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
   496
	if (player->bAutoIgnore)
dvander@775
   497
	{
dvander@970
   498
#if defined MENU_DEBUG
dvander@970
   499
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Client %d is autoIgnoring", client);
dvander@970
   500
#endif
dvander@775
   501
		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
dvander@997
   502
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
dvander@775
   503
		return false;
dvander@775
   504
	}
dvander@775
   505
dvander@775
   506
	/* For the duration of this, we are going to totally ignore whether
dvander@1163
   507
	 * the player is already in a menu or not (except to cancel the old one).
dvander@1163
   508
	 * Instead, we are simply going to ignore any further menu displays, so
dvander@1163
   509
	 * this display can't be interrupted.
dvander@1163
   510
	 */
dvander@775
   511
	player->bAutoIgnore = true;
dvander@775
   512
dvander@775
   513
	/* Cancel any old menus */
dvander@775
   514
	menu_states_t &states = player->states;
dvander@775
   515
	if (player->bInMenu)
dvander@775
   516
	{
dvander@970
   517
#if defined MENU_DEBUG
dvander@970
   518
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Cancelling old menu to client %d", client);
dvander@970
   519
#endif
dvander@1005
   520
		_CancelClientMenu(client, MenuCancel_Interrupted, true);
dvander@775
   521
	}
dvander@775
   522
dvander@1476
   523
	states.firstItem = 0;
dvander@1476
   524
	states.lastItem = first_item;
dvander@775
   525
	states.menu = menu;
dvander@775
   526
	states.mh = mh;
dvander@775
   527
	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
dvander@775
   528
dvander@787
   529
	IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
dvander@775
   530
	if (!display)
dvander@775
   531
	{
dvander@970
   532
#if defined MENU_DEBUG
dvander@970
   533
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to render to client %d", client);
dvander@970
   534
#endif
dvander@775
   535
		player->bAutoIgnore = false;
dvander@775
   536
		player->bInMenu = false;
dvander@775
   537
		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
dvander@997
   538
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
dvander@775
   539
		return false;
dvander@775
   540
	}
dvander@775
   541
dvander@775
   542
	/* Finally, set our states */
dvander@775
   543
	player->bInMenu = true;
dvander@790
   544
	player->bInExternMenu = false;
dvander@775
   545
	player->menuStartTime = gpGlobals->curtime;
dvander@775
   546
	player->menuHoldTime = time;
dvander@775
   547
dvander@775
   548
	if (time)
dvander@775
   549
	{
dvander@775
   550
		AddClientToWatch(client);
dvander@775
   551
	}
dvander@775
   552
dvander@775
   553
	/* Draw the display */
dvander@775
   554
	SendDisplay(client, display);
dvander@775
   555
dvander@775
   556
	/* Free the display pointer */
dvander@776
   557
	display->DeleteThis();
dvander@775
   558
dvander@775
   559
	/* We can be interrupted again! */
dvander@775
   560
	player->bAutoIgnore = false;
dvander@775
   561
dvander@970
   562
#if defined MENU_DEBUG
dvander@970
   563
	g_Logger.LogMessage("[SM_MENU] DoClientMenu() finished successfully (client %d)", client);
dvander@970
   564
#endif
dvander@970
   565
dvander@775
   566
	return true;
dvander@775
   567
}
dvander@775
   568
dvander@775
   569
bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
dvander@775
   570
{
dvander@970
   571
#if defined MENU_DEBUG
dvander@970
   572
	g_Logger.LogMessage("[SM_MENU] RedoClientMenu() (client %d) (order %d)", client, order);
dvander@970
   573
#endif
dvander@775
   574
	CBaseMenuPlayer *player = GetMenuPlayer(client);
dvander@775
   575
	menu_states_t &states = player->states;
dvander@775
   576
dvander@775
   577
	player->bAutoIgnore = true;
dvander@787
   578
	IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
dvander@775
   579
	if (!display)
dvander@775
   580
	{
dvander@970
   581
#if defined MENU_DEBUG
dvander@970
   582
		g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Failed to render menu");
dvander@970
   583
#endif
dvander@775
   584
		if (player->menuHoldTime)
dvander@775
   585
		{
dvander@883
   586
			RemoveClientFromWatch(client);
dvander@775
   587
		}
dvander@775
   588
		player->bAutoIgnore = false;
dvander@775
   589
		return false;
dvander@775
   590
	}
dvander@775
   591
dvander@775
   592
	SendDisplay(client, display);
dvander@775
   593
dvander@776
   594
	display->DeleteThis();
dvander@775
   595
dvander@775
   596
	player->bAutoIgnore = false;
dvander@775
   597
dvander@970
   598
#if defined MENU_DEBUG
dvander@970
   599
	g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Succeeded to client %d", client);
dvander@970
   600
#endif
dvander@970
   601
dvander@775
   602
	return true;
dvander@775
   603
}
dvander@769
   604
dvander@790
   605
CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) : 
dvander@1259
   606
m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false), 
dvander@1259
   607
m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false), 
dvander@1269
   608
m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
dvander@769
   609
{
dvander@769
   610
}
dvander@769
   611
dvander@769
   612
CBaseMenu::~CBaseMenu()
dvander@769
   613
{
dvander@769
   614
}
dvander@769
   615
dvander@790
   616
Handle_t CBaseMenu::GetHandle()
dvander@790
   617
{
dvander@790
   618
	if (!m_hHandle)
dvander@790
   619
	{
dvander@790
   620
		m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner);
dvander@790
   621
	}
dvander@790
   622
dvander@790
   623
	return m_hHandle;
dvander@790
   624
}
dvander@790
   625
dvander@769
   626
bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
dvander@769
   627
{
dvander@778
   628
	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
dvander@769
   629
		&& m_items.size() >= m_pStyle->GetMaxPageItems())
dvander@769
   630
	{
dvander@769
   631
		return false;
dvander@769
   632
	}
dvander@769
   633
dvander@769
   634
	CItem item;
dvander@769
   635
dvander@769
   636
	item.infoString = m_Strings.AddString(info);
dvander@769
   637
	if (draw.display)
dvander@769
   638
	{
dvander@769
   639
		item.displayString = m_Strings.AddString(draw.display);
dvander@769
   640
	}
dvander@769
   641
	item.style = draw.style;
dvander@769
   642
dvander@769
   643
	m_items.push_back(item);
dvander@769
   644
dvander@769
   645
	return true;
dvander@769
   646
}
dvander@769
   647
dvander@769
   648
bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
dvander@769
   649
{
dvander@778
   650
	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
dvander@769
   651
		&& m_items.size() >= m_pStyle->GetMaxPageItems())
dvander@769
   652
	{
dvander@769
   653
		return false;
dvander@769
   654
	}
dvander@769
   655
dvander@769
   656
	if (position >= m_items.size())
dvander@769
   657
	{
dvander@769
   658
		return false;
dvander@769
   659
	}
dvander@769
   660
dvander@769
   661
	CItem item;
dvander@769
   662
	item.infoString = m_Strings.AddString(info);
dvander@769
   663
	if (draw.display)
dvander@769
   664
	{
dvander@769
   665
		item.displayString = m_Strings.AddString(draw.display);
dvander@769
   666
	}
dvander@769
   667
	item.style = draw.style;
dvander@769
   668
dvander@769
   669
	CVector<CItem>::iterator iter = m_items.iterAt(position);
dvander@769
   670
	m_items.insert(iter, item);
dvander@769
   671
dvander@769
   672
	return true;
dvander@769
   673
}
dvander@769
   674
dvander@769
   675
bool CBaseMenu::RemoveItem(unsigned int position)
dvander@769
   676
{
dvander@769
   677
	if (position >= m_items.size())
dvander@769
   678
	{
dvander@769
   679
		return false;
dvander@769
   680
	}
dvander@769
   681
dvander@769
   682
	m_items.erase(m_items.iterAt(position));
dvander@769
   683
dvander@769
   684
	if (m_items.size() == 0)
dvander@769
   685
	{
dvander@769
   686
		m_Strings.Reset();
dvander@769
   687
	}
dvander@769
   688
dvander@769
   689
	return true;
dvander@769
   690
}
dvander@769
   691
dvander@769
   692
void CBaseMenu::RemoveAllItems()
dvander@769
   693
{
dvander@769
   694
	m_items.clear();
dvander@769
   695
	m_Strings.Reset();
dvander@769
   696
}
dvander@769
   697
dvander@769
   698
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
dvander@769
   699
{
dvander@769
   700
	if (position >= m_items.size())
dvander@769
   701
	{
dvander@769
   702
		return NULL;
dvander@769
   703
	}
dvander@769
   704
dvander@769
   705
	if (draw)
dvander@769
   706
	{
dvander@769
   707
		draw->display = m_Strings.GetString(m_items[position].displayString);
dvander@769
   708
		draw->style = m_items[position].style;
dvander@769
   709
	}
dvander@769
   710
dvander@769
   711
	return m_Strings.GetString(m_items[position].infoString);
dvander@769
   712
}
dvander@769
   713
dvander@769
   714
unsigned int CBaseMenu::GetItemCount()
dvander@769
   715
{
dvander@769
   716
	return m_items.size();
dvander@769
   717
}
dvander@769
   718
dvander@769
   719
bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
dvander@769
   720
{
dvander@820
   721
	if (itemsPerPage > 7 || itemsPerPage == 1)
dvander@769
   722
	{
dvander@769
   723
		return false;
dvander@769
   724
	}
dvander@769
   725
dvander@1653
   726
	if (itemsPerPage == MENU_NO_PAGINATION
dvander@1653
   727
		&& m_Pagination != MENU_NO_PAGINATION)
dvander@1653
   728
	{
dvander@1653
   729
		m_nFlags &= ~MENUFLAG_BUTTON_EXIT;
dvander@1653
   730
	}
dvander@1653
   731
dvander@769
   732
	m_Pagination = itemsPerPage;
dvander@769
   733
dvander@769
   734
	return true;
dvander@769
   735
}
dvander@769
   736
dvander@769
   737
unsigned int CBaseMenu::GetPagination()
dvander@769
   738
{
dvander@769
   739
	return m_Pagination;
dvander@769
   740
}
dvander@769
   741
dvander@769
   742
IMenuStyle *CBaseMenu::GetDrawStyle()
dvander@769
   743
{
dvander@769
   744
	return m_pStyle;
dvander@769
   745
}
dvander@769
   746
dvander@769
   747
void CBaseMenu::SetDefaultTitle(const char *message)
dvander@769
   748
{
dvander@769
   749
	m_Title.assign(message);
dvander@769
   750
}
dvander@769
   751
dvander@769
   752
const char *CBaseMenu::GetDefaultTitle()
dvander@769
   753
{
dvander@769
   754
	return m_Title.c_str();
dvander@769
   755
}
dvander@769
   756
dvander@775
   757
void CBaseMenu::Cancel()
dvander@775
   758
{
dvander@775
   759
	if (m_bCancelling)
dvander@775
   760
	{
dvander@775
   761
		return;
dvander@775
   762
	}
dvander@775
   763
dvander@970
   764
#if defined MENU_DEBUG
dvander@1269
   765
	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
dvander@970
   766
		this,
dvander@970
   767
		m_bShouldDelete);
dvander@970
   768
#endif
dvander@970
   769
dvander@775
   770
	m_bCancelling = true;
dvander@775
   771
	Cancel_Finally();
dvander@775
   772
	m_bCancelling = false;
dvander@775
   773
dvander@775
   774
	if (m_bShouldDelete)
dvander@775
   775
	{
dvander@790
   776
		InternalDelete();
dvander@775
   777
	}
dvander@775
   778
}
dvander@775
   779
dvander@790
   780
void CBaseMenu::Destroy(bool releaseHandle)
dvander@775
   781
{
dvander@790
   782
	/* Check if we shouldn't be here */
dvander@790
   783
	if (m_bDeleting)
dvander@790
   784
	{
dvander@790
   785
		return;
dvander@790
   786
	}
dvander@790
   787
dvander@970
   788
#if defined MENU_DEBUG
dvander@970
   789
	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Destroy(%p) (release %d) (m_bCancelling %d) (m_bShouldDelete %d)",
dvander@970
   790
		this,
dvander@970
   791
		releaseHandle,
dvander@970
   792
		m_bCancelling,
dvander@970
   793
		m_bShouldDelete);
dvander@970
   794
#endif
dvander@970
   795
dvander@790
   796
	/* Save the destruction hint about our handle */
dvander@790
   797
	m_bWillFreeHandle = releaseHandle;
dvander@790
   798
dvander@790
   799
	/* Now actually do stuff */
dvander@775
   800
	if (!m_bCancelling || m_bShouldDelete)
dvander@775
   801
	{
dvander@775
   802
		Cancel();
dvander@790
   803
		InternalDelete();
dvander@775
   804
	} else {
dvander@775
   805
		m_bShouldDelete = true;
dvander@775
   806
	}
dvander@775
   807
}
dvander@775
   808
dvander@790
   809
void CBaseMenu::InternalDelete()
dvander@790
   810
{
dvander@790
   811
	if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE)
dvander@790
   812
	{
dvander@790
   813
		Handle_t hndl = m_hHandle;
dvander@790
   814
		HandleSecurity sec;
dvander@790
   815
dvander@790
   816
		sec.pOwner = m_pOwner;
dvander@790
   817
		sec.pIdentity = g_pCoreIdent;
dvander@790
   818
dvander@790
   819
		m_hHandle = BAD_HANDLE;
dvander@790
   820
		m_bDeleting = true;
dvander@790
   821
		g_HandleSys.FreeHandle(hndl, &sec);
dvander@790
   822
	}
dvander@790
   823
dvander@790
   824
	m_pHandler->OnMenuDestroy(this);
dvander@790
   825
dvander@790
   826
	delete this;
dvander@790
   827
}
dvander@790
   828
dvander@1259
   829
unsigned int CBaseMenu::GetMenuOptionFlags()
dvander@1259
   830
{
dvander@1259
   831
	return m_nFlags;
dvander@1259
   832
}
dvander@1259
   833
dvander@1259
   834
void CBaseMenu::SetMenuOptionFlags(unsigned int flags)
dvander@1259
   835
{
dvander@1259
   836
	m_nFlags = flags;
dvander@1259
   837
}
dvander@1269
   838
dvander@1269
   839
IMenuHandler *CBaseMenu::GetHandler()
dvander@1269
   840
{
dvander@1269
   841
	return m_pHandler;
dvander@1269
   842
}
dvander@1766
   843
dvander@1766
   844
unsigned int CBaseMenu::GetBaseMemUsage()
dvander@1766
   845
{
dvander@1766
   846
	return m_Title.size() 
dvander@1766
   847
		+ m_Strings.GetMemTable()->GetMemUsage()
dvander@1766
   848
		+ (m_items.size() * sizeof(CItem));
dvander@1766
   849
}