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