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