core/MenuStyle_Base.cpp
author David Anderson <dvander@alliedmods.net>
Sun May 13 04:41:10 2007 +0000 (2007-05-13)
changeset 776 c031ed44193c
parent 775 9c537df13ec8
child 778 07e49a8cf046
permissions -rw-r--r--
some minor fixups
     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 <stdarg.h>
    16 #include "sm_stringutil.h"
    17 #include "MenuStyle_Base.h"
    18 #include "PlayerManager.h"
    19 #include "MenuManager.h"
    20 
    21 BaseMenuStyle::BaseMenuStyle() : m_WatchList(256)
    22 {
    23 }
    24 
    25 void BaseMenuStyle::AddClientToWatch(int client)
    26 {
    27 	m_WatchList.push_back(client);
    28 }
    29 
    30 void BaseMenuStyle::RemoveClientFromWatch(int client)
    31 {
    32 	m_WatchList.remove(client);
    33 }
    34 
    35 void BaseMenuStyle::_CancelClientMenu(int client, bool bAutoIgnore/* =false */, MenuCancelReason reason/* =MenuCancel_Interrupt */)
    36 {
    37 	CBaseMenuPlayer *player = GetMenuPlayer(client);
    38 	menu_states_t &states = player->states;
    39 
    40 	bool bOldIgnore = player->bAutoIgnore;
    41 	if (bAutoIgnore)
    42 	{
    43 		player->bAutoIgnore = true;
    44 	}
    45 
    46 	/* Save states */
    47 	IMenuHandler *mh = states.mh;
    48 	IBaseMenu *menu = states.menu;
    49 
    50 	/* Clear menu */
    51 	player->bInMenu = false;
    52 	if (player->menuHoldTime)
    53 	{
    54 		RemoveClientFromWatch(client);
    55 	}
    56 
    57 	/* Fire callbacks */
    58 	mh->OnMenuCancel(menu, client, reason);
    59 	mh->OnMenuEnd(menu);
    60 
    61 	if (bAutoIgnore)
    62 	{
    63 		player->bAutoIgnore = bOldIgnore;
    64 	}
    65 }
    66 
    67 void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
    68 {
    69 	int maxClients = g_Players.GetMaxClients();
    70 	for (int i=1; i<=maxClients; i++)
    71 	{
    72 		CBaseMenuPlayer *player = GetMenuPlayer(i);
    73 		if (player->bInMenu)
    74 		{
    75 			menu_states_t &states = player->states;
    76 			if (states.menu == menu)
    77 			{
    78 				_CancelClientMenu(i);
    79 			}
    80 		}
    81 	}
    82 }
    83 
    84 bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
    85 {
    86 	if (client < 1 || client > g_Players.MaxClients())
    87 	{
    88 		return false;
    89 	}
    90 
    91 	CBaseMenuPlayer *player = GetMenuPlayer(client);
    92 	if (!player->bInMenu)
    93 	{
    94 		return false;
    95 	}
    96 
    97 	_CancelClientMenu(client, autoIgnore);
    98 
    99 	return true;
   100 }
   101 
   102 MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
   103 {
   104 	if (client < 1 || client > g_Players.GetMaxClients())
   105 	{
   106 		return MenuSource_None;
   107 	}
   108 
   109 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   110 
   111 	if (player->bInMenu)
   112 	{
   113 		IBaseMenu *menu;
   114 		if ((menu=player->states.menu) != NULL)
   115 		{
   116 			if (object)
   117 			{
   118 				*object = menu;
   119 			}
   120 			return MenuSource_BaseMenu;
   121 		}
   122 
   123 		return MenuSource_Display;
   124 	} else {
   125 		return GetClientExternMenu(client, object);
   126 	}
   127 }
   128 
   129 MenuSource BaseMenuStyle::GetClientExternMenu(int client, void **object)
   130 {
   131 	return MenuSource_None;
   132 }
   133 
   134 void BaseMenuStyle::OnClientDisconnected(int client)
   135 {
   136 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   137 	if (!player->bInMenu)
   138 	{
   139 		return;
   140 	}
   141 
   142 	_CancelClientMenu(client, true, MenuCancel_Disconnect);
   143 
   144 	player->bInMenu = false;
   145 }
   146 
   147 static int do_lookup[256];
   148 void BaseMenuStyle::ProcessWatchList()
   149 {
   150 	if (!m_WatchList.size())
   151 	{
   152 		return;
   153 	}
   154 
   155 	unsigned int total = 0;
   156 	for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
   157 	{
   158 		do_lookup[total++] = (*iter);
   159 	}
   160 
   161 	int client;
   162 	CBaseMenuPlayer *player;
   163 	float curTime = gpGlobals->curtime;
   164 	for (unsigned int i=0; i<total; i++)
   165 	{
   166 		client = do_lookup[i];
   167 		player = GetMenuPlayer(client);
   168 		if (!player->bInMenu || !player->menuHoldTime)
   169 		{
   170 			m_WatchList.remove(i);
   171 			continue;
   172 		}
   173 		if (curTime > player->menuStartTime + player->menuHoldTime)
   174 		{
   175 			_CancelClientMenu(client, false);
   176 		}
   177 	}
   178 }
   179 
   180 void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
   181 {
   182 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   183 
   184 	/* First question: Are we in a menu? */
   185 	if (!player->bInMenu)
   186 	{
   187 		return;
   188 	}
   189 
   190 	bool cancel = false;
   191 	unsigned int item = 0;
   192 	MenuCancelReason reason = MenuCancel_Exit;
   193 	menu_states_t &states = player->states;
   194 
   195 	assert(states.mh != NULL);
   196 
   197 	if (states.menu == NULL)
   198 	{
   199 		item = key_press;
   200 	} else if (key_press < 1 ||  key_press > 8) {
   201 		cancel = true;
   202 	} else {
   203 		ItemSelection type = states.slots[key_press].type;
   204 
   205 		/* For navigational items, we're going to redisplay */
   206 		if (type == ItemSel_Back)
   207 		{
   208 			if (!RedoClientMenu(client, ItemOrder_Descending))
   209 			{
   210 				cancel = true;
   211 				reason = MenuCancel_NoDisplay;
   212 			} else {
   213 				return;
   214 			}
   215 		} else if (type == ItemSel_Next) {
   216 			if (!RedoClientMenu(client, ItemOrder_Ascending))
   217 			{
   218 				cancel = true;						/* I like Saltines. */
   219 				reason = MenuCancel_NoDisplay;
   220 			} else {
   221 				return;
   222 			}
   223 		} else if (type == ItemSel_Exit || type == ItemSel_None) {
   224 			cancel = true;
   225 		} else {
   226 			item = states.slots[key_press].item;
   227 		}
   228 	}
   229 
   230 	/* Save variables */
   231 	IMenuHandler *mh = states.mh;
   232 	IBaseMenu *menu = states.menu;
   233 
   234 	/* Clear states */
   235 	player->bInMenu = false;
   236 	if (player->menuHoldTime)
   237 	{
   238 		m_WatchList.remove(client);
   239 	}
   240 
   241 	if (cancel)
   242 	{
   243 		mh->OnMenuCancel(menu, client, reason);
   244 	} else {
   245 		mh->OnMenuSelect(menu, client, item);
   246 	}
   247 
   248 	mh->OnMenuEnd(menu);
   249 }
   250 
   251 bool BaseMenuStyle::DoClientMenu(int client, IMenuDisplay *menu, IMenuHandler *mh, unsigned int time)
   252 {
   253 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   254 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   255 	{
   256 		return false;
   257 	}
   258 
   259 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   260 	if (player->bAutoIgnore)
   261 	{
   262 		return false;
   263 	}
   264 
   265 	/* For the duration of this, we are going to totally ignore whether
   266 	* the player is already in a menu or not (except to cancel the old one).
   267 	* Instead, we are simply going to ignore any further menu displays, so
   268 	* this display can't be interrupted.
   269 	*/
   270 	player->bAutoIgnore = true;
   271 
   272 	/* Cancel any old menus */
   273 	menu_states_t &states = player->states;
   274 	if (player->bInMenu)
   275 	{
   276 		/* We need to cancel the old menu */
   277 		if (player->menuHoldTime)
   278 		{
   279 			RemoveClientFromWatch(client);
   280 		}
   281 		states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt);
   282 		states.mh->OnMenuEnd(states.menu);
   283 	}
   284 
   285 	states.firstItem = 0;
   286 	states.lastItem = 0;
   287 	states.menu = NULL;
   288 	states.mh = mh;
   289 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   290 	player->bInMenu = true;
   291 	player->menuStartTime = gpGlobals->curtime;
   292 	player->menuHoldTime = time;
   293 
   294 	if (time)
   295 	{
   296 		AddClientToWatch(client);
   297 	}
   298 
   299 	/* Draw the display */
   300 	SendDisplay(client, menu);
   301 
   302 	/* We can be interrupted again! */
   303 	player->bAutoIgnore = false;
   304 
   305 	return true;
   306 }
   307 
   308 bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time)
   309 {
   310 	mh->OnMenuStart(menu);
   311 
   312 	if (!mh)
   313 	{
   314 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   315 		mh->OnMenuEnd(menu);
   316 		return false;
   317 	}
   318 
   319 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   320 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   321 	{
   322 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   323 		mh->OnMenuEnd(menu);
   324 		return false;
   325 	}
   326 
   327 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   328 	if (player->bAutoIgnore)
   329 	{
   330 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   331 		mh->OnMenuEnd(menu);
   332 		return false;
   333 	}
   334 
   335 	/* For the duration of this, we are going to totally ignore whether
   336 	* the player is already in a menu or not (except to cancel the old one).
   337 	* Instead, we are simply going to ignore any further menu displays, so
   338 	* this display can't be interrupted.
   339 	*/
   340 	player->bAutoIgnore = true;
   341 
   342 	/* Cancel any old menus */
   343 	menu_states_t &states = player->states;
   344 	if (player->bInMenu)
   345 	{
   346 		_CancelClientMenu(client, true);
   347 	}
   348 
   349 	states.firstItem = 0;
   350 	states.lastItem = 0;
   351 	states.menu = menu;
   352 	states.mh = mh;
   353 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   354 
   355 	IMenuDisplay *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
   356 	if (!display)
   357 	{
   358 		player->bAutoIgnore = false;
   359 		player->bInMenu = false;
   360 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   361 		mh->OnMenuEnd(menu);
   362 		return false;
   363 	}
   364 
   365 	/* Finally, set our states */
   366 	player->bInMenu = true;
   367 	player->menuStartTime = gpGlobals->curtime;
   368 	player->menuHoldTime = time;
   369 
   370 	if (time)
   371 	{
   372 		AddClientToWatch(client);
   373 	}
   374 
   375 	/* Draw the display */
   376 	SendDisplay(client, display);
   377 
   378 	/* Free the display pointer */
   379 	display->DeleteThis();
   380 
   381 	/* We can be interrupted again! */
   382 	player->bAutoIgnore = false;
   383 
   384 	return true;
   385 }
   386 
   387 bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
   388 {
   389 	CBaseMenuPlayer *player = GetMenuPlayer(client);
   390 	menu_states_t &states = player->states;
   391 
   392 	player->bAutoIgnore = true;
   393 	IMenuDisplay *display = g_Menus.RenderMenu(client, states, order);
   394 	if (!display)
   395 	{
   396 		if (player->menuHoldTime)
   397 		{
   398 			m_WatchList.remove(client);
   399 		}
   400 		player->bAutoIgnore = false;
   401 		return false;
   402 	}
   403 
   404 	SendDisplay(client, display);
   405 
   406 	display->DeleteThis();
   407 
   408 	player->bAutoIgnore = false;
   409 
   410 	return true;
   411 }
   412 
   413 CBaseMenu::CBaseMenu(IMenuStyle *pStyle) : 
   414 m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true), m_bShouldDelete(false), m_bCancelling(false)
   415 {
   416 }
   417 
   418 CBaseMenu::~CBaseMenu()
   419 {
   420 }
   421 
   422 bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
   423 {
   424 	if (m_Pagination == MENU_NO_PAGINATION
   425 		&& m_items.size() >= m_pStyle->GetMaxPageItems())
   426 	{
   427 		return false;
   428 	}
   429 
   430 	CItem item;
   431 
   432 	item.infoString = m_Strings.AddString(info);
   433 	if (draw.display)
   434 	{
   435 		item.displayString = m_Strings.AddString(draw.display);
   436 	}
   437 	item.style = draw.style;
   438 
   439 
   440 	m_items.push_back(item);
   441 
   442 	return true;
   443 }
   444 
   445 bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
   446 {
   447 	if (m_Pagination == MENU_NO_PAGINATION
   448 		&& m_items.size() >= m_pStyle->GetMaxPageItems())
   449 	{
   450 		return false;
   451 	}
   452 
   453 	if (position >= m_items.size())
   454 	{
   455 		return false;
   456 	}
   457 
   458 	CItem item;
   459 	item.infoString = m_Strings.AddString(info);
   460 	if (draw.display)
   461 	{
   462 		item.displayString = m_Strings.AddString(draw.display);
   463 	}
   464 	item.style = draw.style;
   465 
   466 	CVector<CItem>::iterator iter = m_items.iterAt(position);
   467 	m_items.insert(iter, item);
   468 
   469 	return true;
   470 }
   471 
   472 bool CBaseMenu::RemoveItem(unsigned int position)
   473 {
   474 	if (position >= m_items.size())
   475 	{
   476 		return false;
   477 	}
   478 
   479 	m_items.erase(m_items.iterAt(position));
   480 
   481 	if (m_items.size() == 0)
   482 	{
   483 		m_Strings.Reset();
   484 	}
   485 
   486 	return true;
   487 }
   488 
   489 void CBaseMenu::RemoveAllItems()
   490 {
   491 	m_items.clear();
   492 	m_Strings.Reset();
   493 }
   494 
   495 const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
   496 {
   497 	if (position >= m_items.size())
   498 	{
   499 		return NULL;
   500 	}
   501 
   502 	if (draw)
   503 	{
   504 		draw->display = m_Strings.GetString(m_items[position].displayString);
   505 		draw->style = m_items[position].style;
   506 	}
   507 
   508 	return m_Strings.GetString(m_items[position].infoString);
   509 }
   510 
   511 unsigned int CBaseMenu::GetItemCount()
   512 {
   513 	return m_items.size();
   514 }
   515 
   516 bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
   517 {
   518 	if (itemsPerPage > 7)
   519 	{
   520 		return false;
   521 	}
   522 
   523 	m_Pagination = itemsPerPage;
   524 
   525 	return true;
   526 }
   527 
   528 unsigned int CBaseMenu::GetPagination()
   529 {
   530 	return m_Pagination;
   531 }
   532 
   533 IMenuStyle *CBaseMenu::GetDrawStyle()
   534 {
   535 	return m_pStyle;
   536 }
   537 
   538 void CBaseMenu::SetDefaultTitle(const char *message)
   539 {
   540 	m_Title.assign(message);
   541 }
   542 
   543 const char *CBaseMenu::GetDefaultTitle()
   544 {
   545 	return m_Title.c_str();
   546 }
   547 
   548 bool CBaseMenu::GetExitButton()
   549 {
   550 	return m_ExitButton;
   551 }
   552 
   553 bool CBaseMenu::SetExitButton(bool set)
   554 {
   555 	m_ExitButton = set;
   556 	return true;
   557 }
   558 
   559 void CBaseMenu::Cancel()
   560 {
   561 	if (m_bCancelling)
   562 	{
   563 		return;
   564 	}
   565 
   566 	m_bCancelling = true;
   567 	Cancel_Finally();
   568 	m_bCancelling = false;
   569 
   570 	if (m_bShouldDelete)
   571 	{
   572 		delete this;
   573 	}
   574 }
   575 
   576 void CBaseMenu::Destroy()
   577 {
   578 	if (!m_bCancelling || m_bShouldDelete)
   579 	{
   580 		Cancel();
   581 		delete this;
   582 	} else {
   583 		m_bShouldDelete = true;
   584 	}
   585 }
   586