core/MenuManager.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1291 c6d26de53926
child 1345 508c26f10e4f
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 <time.h>
    33 #include <stdarg.h>
    34 #include <stdlib.h>
    35 #include "MenuManager.h"
    36 #include "MenuVoting.h"
    37 #include "sm_stringutil.h"
    38 #include "sourcemm_api.h"
    39 #include "PlayerManager.h"
    40 #include "MenuStyle_Valve.h"
    41 #include "ShareSys.h"
    42 #include "HandleSys.h"
    43 #include "sourcemm_api.h"
    44 
    45 MenuManager g_Menus;
    46 VoteMenuHandler s_VoteHandler;
    47 
    48 ConVar sm_menu_sounds("sm_menu_sounds", "1", 0, "Sets whether SourceMod menus play trigger sounds");
    49 
    50 MenuManager::MenuManager()
    51 {
    52 	m_Styles.push_back(&g_ValveMenuStyle);
    53 	SetDefaultStyle(&g_ValveMenuStyle);
    54 }
    55 
    56 void MenuManager::OnSourceModAllInitialized()
    57 {
    58 	g_ShareSys.AddInterface(NULL, this);
    59 
    60 	HandleAccess access;
    61 	g_HandleSys.InitAccessDefaults(NULL, &access);
    62 
    63 	/* Deny cloning to menus */
    64 	access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
    65 	m_MenuType = g_HandleSys.CreateType("IBaseMenu", this, 0, NULL, &access, g_pCoreIdent, NULL);
    66 
    67 	/* Also deny deletion to styles */
    68 	access.access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
    69 	m_StyleType = g_HandleSys.CreateType("IMenuStyle", this, 0, NULL, &access, g_pCoreIdent, NULL);
    70 }
    71 
    72 void MenuManager::OnSourceModAllShutdown()
    73 {
    74 	g_HandleSys.RemoveType(m_MenuType, g_pCoreIdent);
    75 	g_HandleSys.RemoveType(m_StyleType, g_pCoreIdent);
    76 }
    77 
    78 void MenuManager::OnHandleDestroy(HandleType_t type, void *object)
    79 {
    80 	if (type == m_MenuType)
    81 	{
    82 		IBaseMenu *menu = (IBaseMenu *)object;
    83 		menu->Destroy(false);
    84 	} else if (type == m_StyleType) {
    85 		/* Do nothing */
    86 	}
    87 }
    88 
    89 Handle_t MenuManager::CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner)
    90 {
    91 	if (m_MenuType == NO_HANDLE_TYPE)
    92 	{
    93 		return BAD_HANDLE;
    94 	}
    95 
    96 	return g_HandleSys.CreateHandle(m_MenuType, menu, pOwner, g_pCoreIdent, NULL);
    97 }
    98 
    99 Handle_t MenuManager::CreateStyleHandle(IMenuStyle *style)
   100 {
   101 	if (m_StyleType == NO_HANDLE_TYPE)
   102 	{
   103 		return BAD_HANDLE;
   104 	}
   105 
   106 	return g_HandleSys.CreateHandle(m_StyleType, style, g_pCoreIdent, g_pCoreIdent, NULL);
   107 }
   108 
   109 HandleError MenuManager::ReadMenuHandle(Handle_t handle, IBaseMenu **menu)
   110 {
   111 	HandleSecurity sec;
   112 
   113 	sec.pIdentity = g_pCoreIdent;
   114 	sec.pOwner = NULL;
   115 
   116 	return g_HandleSys.ReadHandle(handle, m_MenuType, &sec, (void **)menu);
   117 }
   118 
   119 HandleError MenuManager::ReadStyleHandle(Handle_t handle, IMenuStyle **style)
   120 {
   121 	HandleSecurity sec;
   122 
   123 	sec.pIdentity = g_pCoreIdent;
   124 	sec.pOwner = g_pCoreIdent;
   125 
   126 	return g_HandleSys.ReadHandle(handle, m_StyleType, &sec, (void **)style);
   127 }
   128 
   129 bool MenuManager::SetDefaultStyle(IMenuStyle *style)
   130 {
   131 	if (!style)
   132 	{
   133 		return false;
   134 	}
   135 
   136 	m_pDefaultStyle = style;
   137 
   138 	return true;
   139 }
   140 
   141 IMenuStyle *MenuManager::GetStyle(unsigned int index)
   142 {
   143 	if (index >= GetStyleCount())
   144 	{
   145 		return NULL;
   146 	}
   147 
   148 	return m_Styles[index];
   149 }
   150 
   151 void MenuManager::AddStyle(IMenuStyle *style)
   152 {
   153 	m_Styles.push_back(style);
   154 }
   155 
   156 unsigned int MenuManager::GetStyleCount()
   157 {
   158 	return (unsigned int)m_Styles.size();
   159 }
   160 
   161 IMenuStyle *MenuManager::FindStyleByName(const char *name)
   162 {
   163 	unsigned int count = GetStyleCount();
   164 	for (unsigned int i=0; i<count; i++)
   165 	{
   166 		IMenuStyle *ptr = GetStyle(i);
   167 		if (strcasecmp(ptr->GetStyleName(), name) == 0)
   168 		{
   169 			return ptr;
   170 		}
   171 	}
   172 
   173 	return NULL;
   174 }
   175 
   176 inline bool IsSlotItem(IMenuPanel *display,
   177 					   unsigned int style)
   178 {
   179 	if (!display->CanDrawItem(style))
   180 	{
   181 	return false;
   182 	}
   183 	if ((style & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
   184 	{
   185 		return false;
   186 	}
   187 	if (style & ITEMDRAW_RAWLINE)
   188 	{
   189 		return false;
   190 	}
   191 
   192 	return true;
   193 }
   194 
   195 IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder order)
   196 {
   197 	IBaseMenu *menu = md.menu;
   198 
   199 	if (!menu)
   200 	{
   201 		return NULL;
   202 	}
   203 
   204 	struct
   205 	{
   206 		unsigned int position;
   207 		ItemDrawInfo draw;
   208 	} drawItems[10];
   209 
   210 	/* Figure out how many items to draw */
   211 	IMenuStyle *style = menu->GetDrawStyle();
   212 	unsigned int pgn = menu->GetPagination();
   213 	unsigned int maxItems = style->GetMaxPageItems();
   214 	if (pgn != MENU_NO_PAGINATION)
   215 	{
   216 		maxItems = pgn;
   217 	}
   218 
   219 	unsigned int totalItems = menu->GetItemCount();
   220 	unsigned int startItem = 0;
   221 
   222 	/* For pagination, find the starting point. */
   223 	if (pgn != MENU_NO_PAGINATION)
   224 	{
   225 		if (order == ItemOrder_Ascending)
   226 		{
   227 			startItem = md.lastItem;
   228 			/* This shouldn't happen with well-coded menus.
   229 			 * If the item is out of bounds, switch the order to
   230 			 * Items_Descending and make us start from the top.
   231 			 */
   232 			if (startItem >= totalItems)
   233 			{
   234 				startItem = totalItems - 1;
   235 				order = ItemOrder_Descending;
   236 			}
   237 		} else if (order == ItemOrder_Descending) {
   238 			startItem = md.firstItem;
   239 			/* This shouldn't happen with well-coded menus.
   240 			 * If searching backwards doesn't give us enough room,
   241 			 * start from the beginning and change to ascending.
   242 			 */
   243 			if (startItem <= maxItems)
   244 			{
   245 				startItem = 0;
   246 				order = ItemOrder_Ascending;
   247 			}
   248 		}
   249 	}
   250 
   251 	/* Get our Display pointer and initialize some crap */
   252 	IMenuPanel *display = menu->CreatePanel();
   253 	IMenuHandler *mh = md.mh;
   254 	bool foundExtra = false;
   255 	unsigned int extraItem = 0;
   256 
   257 	/**
   258 	 * We keep searching until:
   259 	 * 1) There are no more items
   260 	 * 2) We reach one OVER the maximum number of slot items
   261 	 * 3) We have reached maxItems and pagination is MENU_NO_PAGINATION
   262 	 */
   263 	unsigned int i = startItem;
   264 	unsigned int foundItems = 0;
   265 	while (true)
   266 	{
   267 		ItemDrawInfo &dr = drawItems[foundItems].draw;
   268 		/* Is the item valid? */
   269 		if (menu->GetItemInfo(i, &dr) != NULL)
   270 		{
   271 			/* Ask the user to change the style, if necessary */
   272 			mh->OnMenuDrawItem(menu, client, i, dr.style);
   273 			/* Check if it's renderable */
   274 			if (IsSlotItem(display, dr.style))
   275 			{
   276 				/* If we've already found the max number of items,
   277 				 * This means we should just cancel out and log our
   278 				 * "last item."
   279 				 */
   280 				if (foundItems >= maxItems)
   281 				{
   282 					foundExtra = true;
   283 					extraItem = i;
   284 					break;
   285 				}
   286 				drawItems[foundItems++].position = i;
   287 			}
   288 		}
   289 		/* If there's no pagination, stop once the menu is full. */
   290 		if (pgn == MENU_NO_PAGINATION)
   291 		{
   292 			/* If we've filled up, then stop */
   293 			if (foundItems >= maxItems)
   294 			{
   295 				break;
   296 			}
   297 		}
   298 		/* If we're descending and this is the first item, stop */
   299 		if (order == ItemOrder_Descending)
   300 		{
   301 			if (i == 0)
   302 			{
   303 				break;
   304 			}
   305 			i--;
   306 		} 
   307 		/* If we're ascending and this is the last item, stop */
   308 		else if (order == ItemOrder_Ascending)
   309 		{
   310 			if (i >= totalItems - 1)
   311 			{
   312 				break;
   313 			}
   314 			i++;
   315 		}
   316 	}
   317 
   318 	/* There were no items to draw! */
   319 	if (!foundItems)
   320 	{
   321 		display->DeleteThis();
   322 		return NULL;
   323 	}
   324 
   325 	/* Check initial buttons */
   326 	bool displayPrev = false;
   327 	bool displayNext = false;
   328 	if (foundExtra)
   329 	{
   330 		if (order == ItemOrder_Descending)
   331 		{
   332 			displayPrev = true;
   333 			md.firstItem = extraItem;
   334 		} else if (order == ItemOrder_Ascending) {
   335 			displayNext = true;
   336 			md.lastItem = extraItem;
   337 		}
   338 	}
   339 
   340 	/**
   341 	 * If we're paginated, we have to find if there is another page.
   342 	 * Sadly, the only way to do this is to try drawing more items!
   343 	 */
   344 	if (pgn != MENU_NO_PAGINATION)
   345 	{
   346 		unsigned int lastItem = 0;
   347 		ItemDrawInfo dr;
   348 		/* Find the last feasible item to search from. */
   349 		if (order == ItemOrder_Descending)
   350 		{
   351 			lastItem = drawItems[0].position;
   352 			if (lastItem >= totalItems - 1)
   353 			{
   354 				goto skip_search;
   355 			}
   356 			while (++lastItem < totalItems)
   357 			{
   358 				if (menu->GetItemInfo(lastItem, &dr) != NULL)
   359 				{
   360 					mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
   361 					if (IsSlotItem(display, dr.style))
   362 					{
   363 						displayNext = true;
   364 						md.lastItem = lastItem;
   365 						break;
   366 					}
   367 				}
   368 			}
   369 		} else if (order == ItemOrder_Ascending) {
   370 			lastItem = drawItems[0].position;
   371 			if (lastItem == 0)
   372 			{
   373 				goto skip_search;
   374 			}
   375 			lastItem--;
   376 			while (lastItem != 0)
   377 			{
   378 				if (menu->GetItemInfo(lastItem, &dr) != NULL)
   379 				{
   380 					mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
   381 					if (IsSlotItem(display, dr.style))
   382 					{
   383 						displayPrev = true;
   384 						md.firstItem = lastItem;
   385 						break;
   386 					}
   387 				}
   388 				lastItem--;
   389 			}
   390 		}
   391 	}
   392 skip_search:
   393 
   394 	/* Draw the item according to the order */
   395 	menu_slots_t *slots = md.slots;
   396 	unsigned int position = 0;			/* Keep track of the last position */
   397 	if (order == ItemOrder_Ascending)
   398 	{
   399 		for (unsigned int i=0; i<foundItems; i++)
   400 		{
   401 			ItemDrawInfo &dr = drawItems[i].draw;
   402 			if ((position = mh->OnMenuDisplayItem(menu, client, display, drawItems[i].position, dr)) == 0)
   403 			{
   404 				position = display->DrawItem(dr);
   405 			}
   406 			if (position != 0)
   407 			{
   408 				slots[position].item = drawItems[i].position;
   409 				slots[position].type = ItemSel_Item;
   410 			}
   411 		}
   412 	} else if (order == ItemOrder_Descending) {
   413 		unsigned int i = foundItems;
   414 		/* NOTE: There will always be at least one item because
   415 		 * of the check earlier.
   416 		 */
   417 		while (i--)
   418 		{
   419 			ItemDrawInfo &dr = drawItems[i].draw;
   420 			if ((position = mh->OnMenuDisplayItem(menu, client, display, drawItems[i].position, dr)) == 0)
   421 			{
   422 				position = display->DrawItem(dr);
   423 			}
   424 			if (position != 0)
   425 			{
   426 				slots[position].item = drawItems[i].position;
   427 				slots[position].type = ItemSel_Item;
   428 			}
   429 		}
   430 	}
   431 
   432 	/* Now, we need to check if we need to add anything extra */
   433 	if (pgn != MENU_NO_PAGINATION)
   434 	{
   435 		bool canDrawDisabled = display->CanDrawItem(ITEMDRAW_DISABLED|ITEMDRAW_CONTROL);
   436 		bool exitButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT;
   437 		bool exitBackButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK;
   438 		char text[50];
   439 
   440 		/* Calculate how many items we are allowed for control stuff */
   441 		unsigned int padding = style->GetMaxPageItems() - maxItems;
   442 		
   443 		/* Add the number of available slots */
   444 		padding += (maxItems - foundItems);
   445 
   446 		/* Someday, if we are able to re-enable this, we will be very lucky men. */
   447 #if 0
   448 		if (!style->FeatureExists(MenuStyleFeature_ImplicitExit))
   449 		{
   450 #endif
   451 		/* Even if we don't draw an exit button, we invalidate the slot. */
   452 		padding--;
   453 #if 0
   454 		} else {
   455 			/* Otherwise, we don't draw anything and leave the slot available */
   456 			exitButton = false;
   457 		}
   458 #endif
   459 
   460 		/* Subtract two slots for the displayNext/displayPrev padding */
   461 		padding -= 2;
   462 
   463 		/* If we have an "Exit Back" button and the space to draw it, do so. */
   464 		if (exitBackButton)
   465 		{
   466 			if (!displayPrev)
   467 			{
   468 				displayPrev = true;
   469 			} else {
   470 				exitBackButton = false;
   471 			}
   472 		}
   473 
   474 		/**
   475 		 * We allow next/prev to be undrawn if neither exists.
   476 		 * Thus, we only need padding if one of them will be drawn,
   477 		 * or the exit button will be drawn.
   478 		 */
   479 		ItemDrawInfo padItem(NULL, ITEMDRAW_SPACER);
   480 		if (exitButton || (displayNext || displayPrev))
   481 		{
   482 			/* If there are no control options,
   483 			 * Instead just pad with invisible slots.
   484 			 */
   485 			if (!displayPrev && !displayPrev)
   486 			{
   487 				padItem.style = ITEMDRAW_NOTEXT;
   488 			}
   489 			/* Add spacers so we can pad to the end */
   490 			for (unsigned int i=0; i<padding; i++)
   491 			{
   492 				position = display->DrawItem(padItem);
   493 				slots[position].type = ItemSel_None;
   494 			}
   495 		}
   496 
   497 		/* Put a fake spacer before control stuff, if possible */
   498 		if ((displayPrev || displayNext) || exitButton)
   499 		{
   500 			ItemDrawInfo draw("", ITEMDRAW_RAWLINE|ITEMDRAW_SPACER);
   501 			display->DrawItem(draw);
   502 		}
   503 
   504 		ItemDrawInfo dr(text, 0);
   505 		/**
   506 		 * If we have one or the other, we need to have spacers for both.
   507 		 */
   508 		if (displayPrev || displayNext)
   509 		{
   510 			/* PREVIOUS */
   511 			ItemDrawInfo padCtrlItem(NULL, ITEMDRAW_SPACER|ITEMDRAW_CONTROL);
   512 			if (displayPrev || canDrawDisabled)
   513 			{
   514 				if (exitBackButton)
   515 				{
   516 					CorePlayerTranslate(client, text, sizeof(text), "Back", NULL);
   517 					dr.style = ITEMDRAW_CONTROL;
   518 					position = display->DrawItem(dr);
   519 					slots[position].type = ItemSel_ExitBack;
   520 				} else {
   521 					CorePlayerTranslate(client, text, sizeof(text), "Previous", NULL);
   522 					dr.style = (displayPrev ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
   523 					position = display->DrawItem(dr);
   524 					slots[position].type = ItemSel_Back;
   525 				}
   526 			} else if (displayNext || exitButton) {
   527 				/* If we can't display this, and there is an exit button,
   528 				 * we need to pad!
   529 				 */
   530 				position = display->DrawItem(padCtrlItem);
   531 				slots[position].type = ItemSel_None;
   532 			}
   533 
   534 			/* NEXT */
   535 			if (displayNext || canDrawDisabled)
   536 			{
   537 				CorePlayerTranslate(client, text, sizeof(text), "Next", NULL);
   538 				dr.style = (displayNext ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
   539 				position = display->DrawItem(dr);
   540 				slots[position].type = ItemSel_Next;
   541 			} else if (exitButton) {
   542 				/* If we can't display this,
   543 				 * but there is an "exit" button, we need to pad!
   544 				 */
   545 				position = display->DrawItem(padCtrlItem);
   546 				slots[position].type = ItemSel_None;
   547 			}
   548 		} else {
   549 			/* Otherwise, bump to two slots! */
   550 			ItemDrawInfo numBump(NULL, ITEMDRAW_NOTEXT);
   551 			position = display->DrawItem(numBump);
   552 			slots[position].type = ItemSel_None;
   553 			position = display->DrawItem(numBump);
   554 			slots[position].type = ItemSel_None;
   555 		}
   556 
   557 		/* EXIT */
   558 		if (exitButton)
   559 		{
   560 			CorePlayerTranslate(client, text, sizeof(text), "Exit", NULL);
   561 			dr.style = ITEMDRAW_CONTROL;
   562 			position = display->DrawItem(dr);
   563 			slots[position].type = ItemSel_Exit;
   564 		}
   565 	}
   566 
   567 	/* Lastly, fill in any slots we could have missed */
   568 	for (unsigned int i = position + 1; i < 10; i++)
   569 	{
   570 		slots[i].type = ItemSel_None;
   571 	}
   572 
   573 	/* Do title stuff */
   574 	mh->OnMenuDisplay(menu, client, display);
   575 	display->DrawTitle(menu->GetDefaultTitle(), true);
   576 
   577 	return display;
   578 }
   579 
   580 IMenuStyle *MenuManager::GetDefaultStyle()
   581 {
   582 	return m_pDefaultStyle;
   583 }
   584 
   585 bool MenuManager::MenuSoundsEnabled()
   586 {
   587 	return (sm_menu_sounds.GetInt() != 0);
   588 }
   589 
   590 ConfigResult MenuManager::OnSourceModConfigChanged(const char *key,
   591 												   const char *value,
   592 												   ConfigSource source,
   593 												   char *error,
   594 												   size_t maxlength)
   595 {
   596 	if (strcmp(key, "MenuItemSound") == 0)
   597 	{
   598 		m_SelectSound.assign(value);
   599 		return ConfigResult_Accept;
   600 	} else if (strcmp(key, "MenuExitBackSound") == 0) {
   601 		m_ExitBackSound.assign(value);
   602 		return ConfigResult_Accept;
   603 	} else if (strcmp(key, "MenuExitSound") == 0) {
   604 		m_ExitSound.assign(value);
   605 		return ConfigResult_Accept;
   606 	}
   607 
   608 	return ConfigResult_Ignore;
   609 }
   610 
   611 const char *MenuManager::GetMenuSound(ItemSelection sel)
   612 {
   613 	const char *sound = NULL;
   614 
   615 	switch (sel)
   616 	{
   617 	case ItemSel_Back:
   618 	case ItemSel_Next:
   619 	case ItemSel_Item:
   620 		{
   621 			if (m_SelectSound.size() > 0)
   622 			{
   623 				sound = m_SelectSound.c_str();
   624 			}
   625 			break;
   626 		}
   627 	case ItemSel_ExitBack:
   628 		{
   629 			if (m_ExitBackSound.size() > 0)
   630 			{
   631 				sound = m_ExitBackSound.c_str();
   632 			}
   633 			break;
   634 		}
   635 	case ItemSel_Exit:
   636 		{
   637 			if (m_ExitSound.size() > 0)
   638 			{
   639 				sound = m_ExitSound.c_str();
   640 			}
   641 			break;
   642 		}
   643 	default:
   644 		{
   645 			break;
   646 		}
   647 	}
   648 
   649 	return sound;
   650 }
   651 
   652 void MenuManager::OnSourceModLevelChange(const char *mapName)
   653 {
   654 	if (m_SelectSound.size() > 0)
   655 	{
   656 		enginesound->PrecacheSound(m_SelectSound.c_str(), true);
   657 	}
   658 	if (m_ExitBackSound.size() > 0)
   659 	{
   660 		enginesound->PrecacheSound(m_ExitBackSound.c_str(), true);
   661 	}
   662 	if (m_ExitSound.size() > 0)
   663 	{
   664 		enginesound->PrecacheSound(m_ExitSound.c_str(), true);
   665 	}
   666 }
   667 
   668 void MenuManager::CancelMenu(IBaseMenu *menu)
   669 {
   670 	if (s_VoteHandler.GetCurrentMenu() == menu
   671 		&& !s_VoteHandler.IsCancelling())
   672 	{
   673 		s_VoteHandler.CancelVoting();
   674 		return;
   675 	}
   676 
   677 	menu->Cancel();
   678 }
   679 
   680 bool MenuManager::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags)
   681 {
   682 	return s_VoteHandler.StartVote(menu, num_clients, clients, max_time, flags);
   683 }
   684 
   685 bool MenuManager::IsVoteInProgress()
   686 {
   687 	return s_VoteHandler.IsVoteInProgress();
   688 }
   689 
   690 void MenuManager::CancelVoting()
   691 {
   692 	s_VoteHandler.CancelVoting();
   693 }
   694 
   695 unsigned int MenuManager::GetRemainingVoteDelay()
   696 {
   697 	return s_VoteHandler.GetRemainingVoteDelay();
   698 }