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