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