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