3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
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
16 #include "sm_stringutil.h"
17 #include "MenuStyle_Base.h"
18 #include "PlayerManager.h"
19 #include "MenuManager.h"
20 #include "HandleSys.h"
22 BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE)
26 Handle_t BaseMenuStyle::GetHandle()
28 /* Don't create the handle until we need it */
29 if (m_hHandle == BAD_HANDLE)
31 m_hHandle = g_Menus.CreateStyleHandle(this);
37 void BaseMenuStyle::AddClientToWatch(int client)
39 m_WatchList.push_back(client);
42 void BaseMenuStyle::RemoveClientFromWatch(int client)
44 m_WatchList.remove(client);
47 void BaseMenuStyle::_CancelClientMenu(int client, bool bAutoIgnore/* =false */, MenuCancelReason reason/* =MenuCancel_Interrupt */)
49 CBaseMenuPlayer *player = GetMenuPlayer(client);
50 menu_states_t &states = player->states;
52 bool bOldIgnore = player->bAutoIgnore;
55 player->bAutoIgnore = true;
59 IMenuHandler *mh = states.mh;
60 IBaseMenu *menu = states.menu;
63 player->bInMenu = false;
64 if (player->menuHoldTime)
66 RemoveClientFromWatch(client);
70 mh->OnMenuCancel(menu, client, reason);
72 /* Only fire end if there's a valid menu */
80 player->bAutoIgnore = bOldIgnore;
84 void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
86 int maxClients = g_Players.GetMaxClients();
87 for (int i=1; i<=maxClients; i++)
89 CBaseMenuPlayer *player = GetMenuPlayer(i);
92 menu_states_t &states = player->states;
93 if (states.menu == menu)
101 bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
103 if (client < 1 || client > g_Players.MaxClients())
108 CBaseMenuPlayer *player = GetMenuPlayer(client);
109 if (!player->bInMenu)
114 _CancelClientMenu(client, autoIgnore);
119 MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
121 if (client < 1 || client > g_Players.GetMaxClients())
123 return MenuSource_None;
126 CBaseMenuPlayer *player = GetMenuPlayer(client);
131 if ((menu=player->states.menu) != NULL)
137 return MenuSource_BaseMenu;
140 return MenuSource_Display;
141 } else if (player->bInExternMenu) {
142 if (player->menuHoldTime != 0
143 && (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime))
145 player->bInExternMenu = false;
146 return MenuSource_None;
148 return MenuSource_External;
151 return MenuSource_None;
154 void BaseMenuStyle::OnClientDisconnected(int client)
156 CBaseMenuPlayer *player = GetMenuPlayer(client);
157 if (!player->bInMenu)
162 _CancelClientMenu(client, true, MenuCancel_Disconnect);
164 player->bInMenu = false;
165 player->bInExternMenu = false;
168 static int do_lookup[256];
169 void BaseMenuStyle::ProcessWatchList()
171 if (!m_WatchList.size())
176 unsigned int total = 0;
177 for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
179 do_lookup[total++] = (*iter);
183 CBaseMenuPlayer *player;
184 float curTime = gpGlobals->curtime;
185 for (unsigned int i=0; i<total; i++)
187 client = do_lookup[i];
188 player = GetMenuPlayer(client);
189 if (!player->bInMenu || !player->menuHoldTime)
191 m_WatchList.remove(i);
194 if (curTime > player->menuStartTime + player->menuHoldTime)
196 _CancelClientMenu(client, false);
201 void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
203 CBaseMenuPlayer *player = GetMenuPlayer(client);
205 /* First question: Are we in a menu? */
206 if (!player->bInMenu)
212 unsigned int item = 0;
213 MenuCancelReason reason = MenuCancel_Exit;
214 menu_states_t &states = player->states;
216 assert(states.mh != NULL);
218 if (states.menu == NULL)
221 } else if (key_press < 1 || key_press > GetMaxPageItems()) {
224 ItemSelection type = states.slots[key_press].type;
226 /* For navigational items, we're going to redisplay */
227 if (type == ItemSel_Back)
229 if (!RedoClientMenu(client, ItemOrder_Descending))
232 reason = MenuCancel_NoDisplay;
236 } else if (type == ItemSel_Next) {
237 if (!RedoClientMenu(client, ItemOrder_Ascending))
239 cancel = true; /* I like Saltines. */
240 reason = MenuCancel_NoDisplay;
244 } else if (type == ItemSel_Exit || type == ItemSel_None) {
247 item = states.slots[key_press].item;
252 IMenuHandler *mh = states.mh;
253 IBaseMenu *menu = states.menu;
256 player->bInMenu = false;
257 if (player->menuHoldTime)
259 m_WatchList.remove(client);
264 mh->OnMenuCancel(menu, client, reason);
266 mh->OnMenuSelect(menu, client, item);
269 /* Only fire end for valid menus */
276 bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
278 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
279 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
284 CBaseMenuPlayer *player = GetMenuPlayer(client);
285 if (player->bAutoIgnore)
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.
295 player->bAutoIgnore = true;
297 /* Cancel any old menus */
298 menu_states_t &states = player->states;
301 _CancelClientMenu(client, true);
304 states.firstItem = 0;
308 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
309 player->bInMenu = true;
310 player->bInExternMenu = false;
311 player->menuStartTime = gpGlobals->curtime;
312 player->menuHoldTime = time;
316 AddClientToWatch(client);
319 /* Draw the display */
320 SendDisplay(client, menu);
322 /* We can be interrupted again! */
323 player->bAutoIgnore = false;
328 bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time)
330 mh->OnMenuStart(menu);
334 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
339 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
340 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
342 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
347 CBaseMenuPlayer *player = GetMenuPlayer(client);
348 if (player->bAutoIgnore)
350 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
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.
360 player->bAutoIgnore = true;
362 /* Cancel any old menus */
363 menu_states_t &states = player->states;
366 _CancelClientMenu(client, true);
369 states.firstItem = 0;
373 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
375 IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
378 player->bAutoIgnore = false;
379 player->bInMenu = false;
380 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
385 /* Finally, set our states */
386 player->bInMenu = true;
387 player->bInExternMenu = false;
388 player->menuStartTime = gpGlobals->curtime;
389 player->menuHoldTime = time;
393 AddClientToWatch(client);
396 /* Draw the display */
397 SendDisplay(client, display);
399 /* Free the display pointer */
400 display->DeleteThis();
402 /* We can be interrupted again! */
403 player->bAutoIgnore = false;
408 bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
410 CBaseMenuPlayer *player = GetMenuPlayer(client);
411 menu_states_t &states = player->states;
413 player->bAutoIgnore = true;
414 IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
417 if (player->menuHoldTime)
419 m_WatchList.remove(client);
421 player->bAutoIgnore = false;
425 SendDisplay(client, display);
427 display->DeleteThis();
429 player->bAutoIgnore = false;
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)
441 CBaseMenu::~CBaseMenu()
445 Handle_t CBaseMenu::GetHandle()
449 m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner);
455 bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
457 if (m_Pagination == (unsigned)MENU_NO_PAGINATION
458 && m_items.size() >= m_pStyle->GetMaxPageItems())
465 item.infoString = m_Strings.AddString(info);
468 item.displayString = m_Strings.AddString(draw.display);
470 item.style = draw.style;
473 m_items.push_back(item);
478 bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
480 if (m_Pagination == (unsigned)MENU_NO_PAGINATION
481 && m_items.size() >= m_pStyle->GetMaxPageItems())
486 if (position >= m_items.size())
492 item.infoString = m_Strings.AddString(info);
495 item.displayString = m_Strings.AddString(draw.display);
497 item.style = draw.style;
499 CVector<CItem>::iterator iter = m_items.iterAt(position);
500 m_items.insert(iter, item);
505 bool CBaseMenu::RemoveItem(unsigned int position)
507 if (position >= m_items.size())
512 m_items.erase(m_items.iterAt(position));
514 if (m_items.size() == 0)
522 void CBaseMenu::RemoveAllItems()
528 const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
530 if (position >= m_items.size())
537 draw->display = m_Strings.GetString(m_items[position].displayString);
538 draw->style = m_items[position].style;
541 return m_Strings.GetString(m_items[position].infoString);
544 unsigned int CBaseMenu::GetItemCount()
546 return m_items.size();
549 bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
551 if (itemsPerPage > 7)
556 m_Pagination = itemsPerPage;
561 unsigned int CBaseMenu::GetPagination()
566 IMenuStyle *CBaseMenu::GetDrawStyle()
571 void CBaseMenu::SetDefaultTitle(const char *message)
573 m_Title.assign(message);
576 const char *CBaseMenu::GetDefaultTitle()
578 return m_Title.c_str();
581 bool CBaseMenu::GetExitButton()
586 bool CBaseMenu::SetExitButton(bool set)
592 void CBaseMenu::Cancel()
599 m_bCancelling = true;
601 m_bCancelling = false;
609 void CBaseMenu::Destroy(bool releaseHandle)
611 /* Check if we shouldn't be here */
617 /* Save the destruction hint about our handle */
618 m_bWillFreeHandle = releaseHandle;
620 /* Now actually do stuff */
621 if (!m_bCancelling || m_bShouldDelete)
626 m_bShouldDelete = true;
630 void CBaseMenu::InternalDelete()
632 if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE)
634 Handle_t hndl = m_hHandle;
637 sec.pOwner = m_pOwner;
638 sec.pIdentity = g_pCoreIdent;
640 m_hHandle = BAD_HANDLE;
642 g_HandleSys.FreeHandle(hndl, &sec);
645 m_pHandler->OnMenuDestroy(this);