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