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