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