core/MenuStyle_Base.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1269 27397584df9c
child 1347 b1e02bc27316
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

(This day shall henceforth be known as the Eighty Column Massacre of '07)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 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 	assert(mh != NULL);
   292 
   293 	if (menu == NULL)
   294 	{
   295 		item = key_press;
   296 	} else if (key_press < 1 || key_press > GetMaxPageItems()) {
   297 		cancel = true;
   298 	} else {
   299 		ItemSelection type = states.slots[key_press].type;
   300 
   301 		/* Check if we should play a sound about the type */
   302 		if (g_Menus.MenuSoundsEnabled() && 
   303 			(!menu || (menu->GetMenuOptionFlags() & MENUFLAG_NO_SOUND) != MENUFLAG_NO_SOUND))
   304 		{
   305 			CellRecipientFilter filter;
   306 			cell_t clients[1];
   307 
   308 			clients[0] = client;
   309 			filter.Initialize(clients, 1);
   310 
   311 			const char *sound = g_Menus.GetMenuSound(type);
   312 
   313 			if (sound != NULL)
   314 			{
   315 				edict_t *pEdict = engine->PEntityOfEntIndex(client);
   316 				if (pEdict)
   317 				{
   318 					ICollideable *pCollideable = pEdict->GetCollideable();
   319 
   320 					if (pCollideable)
   321 					{
   322 						const Vector & pos = pCollideable->GetCollisionOrigin();
   323 		
   324 						enginesound->EmitSound(filter, 
   325 							client, 
   326 							CHAN_AUTO, 
   327 							sound, 
   328 							VOL_NORM, 
   329 							ATTN_NORM, 
   330 							0, 
   331 							PITCH_NORM, 
   332 							&pos);
   333 					}
   334 				}
   335 			}
   336 		}
   337 
   338 		/* For navigational items, we're going to redisplay */
   339 		if (type == ItemSel_Back)
   340 		{
   341 			if (!RedoClientMenu(client, ItemOrder_Descending))
   342 			{
   343 				cancel = true;
   344 				reason = MenuCancel_NoDisplay;
   345 				end_reason = MenuEnd_Cancelled;
   346 			} else {
   347 				return;
   348 			}
   349 		} else if (type == ItemSel_Next) {
   350 			if (!RedoClientMenu(client, ItemOrder_Ascending))
   351 			{
   352 				cancel = true;						/* I like Saltines. */
   353 				reason = MenuCancel_NoDisplay;
   354 				end_reason = MenuEnd_Cancelled;
   355 			} else {
   356 				return;
   357 			}
   358 		} else if (type == ItemSel_Exit || type == ItemSel_None) {
   359 			cancel = true;
   360 			reason = MenuCancel_Exit;
   361 			end_reason = MenuEnd_Exit;
   362 		} else if (type == ItemSel_ExitBack) {
   363 			cancel = true;
   364 			reason = MenuCancel_ExitBack;
   365 			end_reason = MenuEnd_ExitBack;
   366 		} else {
   367 			item = states.slots[key_press].item;
   368 		}
   369 	}
   370 
   371 	/* Clear states */
   372 	player->bInMenu = false;
   373 	if (player->menuHoldTime)
   374 	{
   375 		RemoveClientFromWatch(client);
   376 	}
   377 
   378 	if (cancel)
   379 	{
   380 		mh->OnMenuCancel(menu, client, reason);
   381 	} else {
   382 		mh->OnMenuSelect(menu, client, item);
   383 	}
   384 
   385 	/* Only fire end for valid menus */
   386 	if (menu)
   387 	{
   388 		mh->OnMenuEnd(menu, end_reason);
   389 	}
   390 }
   391 
   392 bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
   393 {
   394 #if defined MENU_DEBUG
   395 	g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (panel %p) (mh %p) (time %d)",
   396 		client,
   397 		menu,
   398 		mh,
   399 		time);
   400 #endif
   401 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   402 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   403 	{
   404 		return false;
   405 	}
   406 
   407 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   408 	if (player->bAutoIgnore)
   409 	{
   410 		return false;
   411 	}
   412 
   413 	/* For the duration of this, we are going to totally ignore whether
   414 	* the player is already in a menu or not (except to cancel the old one).
   415 	* Instead, we are simply going to ignore any further menu displays, so
   416 	* this display can't be interrupted.
   417 	*/
   418 	player->bAutoIgnore = true;
   419 
   420 	/* Cancel any old menus */
   421 	menu_states_t &states = player->states;
   422 	if (player->bInMenu)
   423 	{
   424 		_CancelClientMenu(client, MenuCancel_Interrupted, true);
   425 	}
   426 
   427 	states.firstItem = 0;
   428 	states.lastItem = 0;
   429 	states.menu = NULL;
   430 	states.mh = mh;
   431 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   432 	player->bInMenu = true;
   433 	player->bInExternMenu = false;
   434 	player->menuStartTime = gpGlobals->curtime;
   435 	player->menuHoldTime = time;
   436 
   437 	if (time)
   438 	{
   439 		AddClientToWatch(client);
   440 	}
   441 
   442 	/* Draw the display */
   443 	SendDisplay(client, menu);
   444 
   445 	/* We can be interrupted again! */
   446 	player->bAutoIgnore = false;
   447 
   448 	return true;
   449 }
   450 
   451 bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time)
   452 {
   453 #if defined MENU_DEBUG
   454 	g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (menu %p) (mh %p) (time %d)",
   455 		client,
   456 		menu,
   457 		mh,
   458 		time);
   459 #endif
   460 	mh->OnMenuStart(menu);
   461 
   462 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   463 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   464 	{
   465 #if defined MENU_DEBUG
   466 		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to display to client %d", client);
   467 #endif
   468 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   469 		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
   470 		return false;
   471 	}
   472 
   473 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   474 	if (player->bAutoIgnore)
   475 	{
   476 #if defined MENU_DEBUG
   477 		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Client %d is autoIgnoring", client);
   478 #endif
   479 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   480 		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
   481 		return false;
   482 	}
   483 
   484 	/* For the duration of this, we are going to totally ignore whether
   485 	 * the player is already in a menu or not (except to cancel the old one).
   486 	 * Instead, we are simply going to ignore any further menu displays, so
   487 	 * this display can't be interrupted.
   488 	 */
   489 	player->bAutoIgnore = true;
   490 
   491 	/* Cancel any old menus */
   492 	menu_states_t &states = player->states;
   493 	if (player->bInMenu)
   494 	{
   495 #if defined MENU_DEBUG
   496 		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Cancelling old menu to client %d", client);
   497 #endif
   498 		_CancelClientMenu(client, MenuCancel_Interrupted, true);
   499 	}
   500 
   501 	states.firstItem = 0;
   502 	states.lastItem = 0;
   503 	states.menu = menu;
   504 	states.mh = mh;
   505 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   506 
   507 	IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
   508 	if (!display)
   509 	{
   510 #if defined MENU_DEBUG
   511 		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to render to client %d", client);
   512 #endif
   513 		player->bAutoIgnore = false;
   514 		player->bInMenu = false;
   515 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   516 		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
   517 		return false;
   518 	}
   519 
   520 	/* Finally, set our states */
   521 	player->bInMenu = true;
   522 	player->bInExternMenu = false;
   523 	player->menuStartTime = gpGlobals->curtime;
   524 	player->menuHoldTime = time;
   525 
   526 	if (time)
   527 	{
   528 		AddClientToWatch(client);
   529 	}
   530 
   531 	/* Draw the display */
   532 	SendDisplay(client, display);
   533 
   534 	/* Free the display pointer */
   535 	display->DeleteThis();
   536 
   537 	/* We can be interrupted again! */
   538 	player->bAutoIgnore = false;
   539 
   540 #if defined MENU_DEBUG
   541 	g_Logger.LogMessage("[SM_MENU] DoClientMenu() finished successfully (client %d)", client);
   542 #endif
   543 
   544 	return true;
   545 }
   546 
   547 bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
   548 {
   549 #if defined MENU_DEBUG
   550 	g_Logger.LogMessage("[SM_MENU] RedoClientMenu() (client %d) (order %d)", client, order);
   551 #endif
   552 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   553 	menu_states_t &states = player->states;
   554 
   555 	player->bAutoIgnore = true;
   556 	IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
   557 	if (!display)
   558 	{
   559 #if defined MENU_DEBUG
   560 		g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Failed to render menu");
   561 #endif
   562 		if (player->menuHoldTime)
   563 		{
   564 			RemoveClientFromWatch(client);
   565 		}
   566 		player->bAutoIgnore = false;
   567 		return false;
   568 	}
   569 
   570 	SendDisplay(client, display);
   571 
   572 	display->DeleteThis();
   573 
   574 	player->bAutoIgnore = false;
   575 
   576 #if defined MENU_DEBUG
   577 	g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Succeeded to client %d", client);
   578 #endif
   579 
   580 	return true;
   581 }
   582 
   583 CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) : 
   584 m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false), 
   585 m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false), 
   586 m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
   587 {
   588 }
   589 
   590 CBaseMenu::~CBaseMenu()
   591 {
   592 }
   593 
   594 Handle_t CBaseMenu::GetHandle()
   595 {
   596 	if (!m_hHandle)
   597 	{
   598 		m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner);
   599 	}
   600 
   601 	return m_hHandle;
   602 }
   603 
   604 bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
   605 {
   606 	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
   607 		&& m_items.size() >= m_pStyle->GetMaxPageItems())
   608 	{
   609 		return false;
   610 	}
   611 
   612 	CItem item;
   613 
   614 	item.infoString = m_Strings.AddString(info);
   615 	if (draw.display)
   616 	{
   617 		item.displayString = m_Strings.AddString(draw.display);
   618 	}
   619 	item.style = draw.style;
   620 	item.access = draw.access;
   621 
   622 
   623 	m_items.push_back(item);
   624 
   625 	return true;
   626 }
   627 
   628 bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
   629 {
   630 	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
   631 		&& m_items.size() >= m_pStyle->GetMaxPageItems())
   632 	{
   633 		return false;
   634 	}
   635 
   636 	if (position >= m_items.size())
   637 	{
   638 		return false;
   639 	}
   640 
   641 	CItem item;
   642 	item.infoString = m_Strings.AddString(info);
   643 	if (draw.display)
   644 	{
   645 		item.displayString = m_Strings.AddString(draw.display);
   646 	}
   647 	item.style = draw.style;
   648 	item.access = draw.access;
   649 
   650 	CVector<CItem>::iterator iter = m_items.iterAt(position);
   651 	m_items.insert(iter, item);
   652 
   653 	return true;
   654 }
   655 
   656 bool CBaseMenu::RemoveItem(unsigned int position)
   657 {
   658 	if (position >= m_items.size())
   659 	{
   660 		return false;
   661 	}
   662 
   663 	m_items.erase(m_items.iterAt(position));
   664 
   665 	if (m_items.size() == 0)
   666 	{
   667 		m_Strings.Reset();
   668 	}
   669 
   670 	return true;
   671 }
   672 
   673 void CBaseMenu::RemoveAllItems()
   674 {
   675 	m_items.clear();
   676 	m_Strings.Reset();
   677 }
   678 
   679 const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
   680 {
   681 	if (position >= m_items.size())
   682 	{
   683 		return NULL;
   684 	}
   685 
   686 	if (draw)
   687 	{
   688 		draw->display = m_Strings.GetString(m_items[position].displayString);
   689 		draw->style = m_items[position].style;
   690 	}
   691 
   692 	return m_Strings.GetString(m_items[position].infoString);
   693 }
   694 
   695 unsigned int CBaseMenu::GetItemCount()
   696 {
   697 	return m_items.size();
   698 }
   699 
   700 bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
   701 {
   702 	if (itemsPerPage > 7 || itemsPerPage == 1)
   703 	{
   704 		return false;
   705 	}
   706 
   707 	m_Pagination = itemsPerPage;
   708 
   709 	return true;
   710 }
   711 
   712 unsigned int CBaseMenu::GetPagination()
   713 {
   714 	return m_Pagination;
   715 }
   716 
   717 IMenuStyle *CBaseMenu::GetDrawStyle()
   718 {
   719 	return m_pStyle;
   720 }
   721 
   722 void CBaseMenu::SetDefaultTitle(const char *message)
   723 {
   724 	m_Title.assign(message);
   725 }
   726 
   727 const char *CBaseMenu::GetDefaultTitle()
   728 {
   729 	return m_Title.c_str();
   730 }
   731 
   732 void CBaseMenu::Cancel()
   733 {
   734 	if (m_bCancelling)
   735 	{
   736 		return;
   737 	}
   738 
   739 #if defined MENU_DEBUG
   740 	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
   741 		this,
   742 		m_bShouldDelete);
   743 #endif
   744 
   745 	m_bCancelling = true;
   746 	Cancel_Finally();
   747 	m_bCancelling = false;
   748 
   749 	if (m_bShouldDelete)
   750 	{
   751 		InternalDelete();
   752 	}
   753 }
   754 
   755 void CBaseMenu::Destroy(bool releaseHandle)
   756 {
   757 	/* Check if we shouldn't be here */
   758 	if (m_bDeleting)
   759 	{
   760 		return;
   761 	}
   762 
   763 #if defined MENU_DEBUG
   764 	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Destroy(%p) (release %d) (m_bCancelling %d) (m_bShouldDelete %d)",
   765 		this,
   766 		releaseHandle,
   767 		m_bCancelling,
   768 		m_bShouldDelete);
   769 #endif
   770 
   771 	/* Save the destruction hint about our handle */
   772 	m_bWillFreeHandle = releaseHandle;
   773 
   774 	/* Now actually do stuff */
   775 	if (!m_bCancelling || m_bShouldDelete)
   776 	{
   777 		Cancel();
   778 		InternalDelete();
   779 	} else {
   780 		m_bShouldDelete = true;
   781 	}
   782 }
   783 
   784 void CBaseMenu::InternalDelete()
   785 {
   786 	if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE)
   787 	{
   788 		Handle_t hndl = m_hHandle;
   789 		HandleSecurity sec;
   790 
   791 		sec.pOwner = m_pOwner;
   792 		sec.pIdentity = g_pCoreIdent;
   793 
   794 		m_hHandle = BAD_HANDLE;
   795 		m_bDeleting = true;
   796 		g_HandleSys.FreeHandle(hndl, &sec);
   797 	}
   798 
   799 	m_pHandler->OnMenuDestroy(this);
   800 
   801 	delete this;
   802 }
   803 
   804 unsigned int CBaseMenu::GetMenuOptionFlags()
   805 {
   806 	return m_nFlags;
   807 }
   808 
   809 void CBaseMenu::SetMenuOptionFlags(unsigned int flags)
   810 {
   811 	m_nFlags = flags;
   812 }
   813 
   814 IMenuHandler *CBaseMenu::GetHandler()
   815 {
   816 	return m_pHandler;
   817 }