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