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