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