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"
21 BaseMenuStyle::BaseMenuStyle() : m_WatchList(256)
25 void BaseMenuStyle::AddClientToWatch(int client)
27 m_WatchList.push_back(client);
30 void BaseMenuStyle::RemoveClientFromWatch(int client)
32 m_WatchList.remove(client);
35 void BaseMenuStyle::_CancelClientMenu(int client, bool bAutoIgnore/* =false */, MenuCancelReason reason/* =MenuCancel_Interrupt */)
37 CBaseMenuPlayer *player = GetMenuPlayer(client);
38 menu_states_t &states = player->states;
40 bool bOldIgnore = player->bAutoIgnore;
43 player->bAutoIgnore = true;
47 IMenuHandler *mh = states.mh;
48 IBaseMenu *menu = states.menu;
51 player->bInMenu = false;
52 if (player->menuHoldTime)
54 RemoveClientFromWatch(client);
58 mh->OnMenuCancel(menu, client, reason);
63 player->bAutoIgnore = bOldIgnore;
67 void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
69 int maxClients = g_Players.GetMaxClients();
70 for (int i=1; i<=maxClients; i++)
72 CBaseMenuPlayer *player = GetMenuPlayer(i);
75 menu_states_t &states = player->states;
76 if (states.menu == menu)
84 bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
86 if (client < 1 || client > g_Players.MaxClients())
91 CBaseMenuPlayer *player = GetMenuPlayer(client);
97 _CancelClientMenu(client, autoIgnore);
102 MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
104 if (client < 1 || client > g_Players.GetMaxClients())
106 return MenuSource_None;
109 CBaseMenuPlayer *player = GetMenuPlayer(client);
114 if ((menu=player->states.menu) != NULL)
120 return MenuSource_BaseMenu;
123 return MenuSource_Display;
125 return GetClientExternMenu(client, object);
129 MenuSource BaseMenuStyle::GetClientExternMenu(int client, void **object)
131 return MenuSource_None;
134 void BaseMenuStyle::OnClientDisconnected(int client)
136 CBaseMenuPlayer *player = GetMenuPlayer(client);
137 if (!player->bInMenu)
142 _CancelClientMenu(client, true, MenuCancel_Disconnect);
144 player->bInMenu = false;
147 static int do_lookup[256];
148 void BaseMenuStyle::ProcessWatchList()
150 if (!m_WatchList.size())
155 unsigned int total = 0;
156 for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
158 do_lookup[total++] = (*iter);
162 CBaseMenuPlayer *player;
163 float curTime = gpGlobals->curtime;
164 for (unsigned int i=0; i<total; i++)
166 client = do_lookup[i];
167 player = GetMenuPlayer(client);
168 if (!player->bInMenu || !player->menuHoldTime)
170 m_WatchList.remove(i);
173 if (curTime > player->menuStartTime + player->menuHoldTime)
175 _CancelClientMenu(client, false);
180 void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
182 CBaseMenuPlayer *player = GetMenuPlayer(client);
184 /* First question: Are we in a menu? */
185 if (!player->bInMenu)
191 unsigned int item = 0;
192 MenuCancelReason reason = MenuCancel_Exit;
193 menu_states_t &states = player->states;
195 assert(states.mh != NULL);
197 if (states.menu == NULL)
200 } else if (key_press < 1 || key_press > 8) {
203 ItemSelection type = states.slots[key_press].type;
205 /* For navigational items, we're going to redisplay */
206 if (type == ItemSel_Back)
208 if (!RedoClientMenu(client, ItemOrder_Descending))
211 reason = MenuCancel_NoDisplay;
215 } else if (type == ItemSel_Next) {
216 if (!RedoClientMenu(client, ItemOrder_Ascending))
218 cancel = true; /* I like Saltines. */
219 reason = MenuCancel_NoDisplay;
223 } else if (type == ItemSel_Exit || type == ItemSel_None) {
226 item = states.slots[key_press].item;
231 IMenuHandler *mh = states.mh;
232 IBaseMenu *menu = states.menu;
235 player->bInMenu = false;
236 if (player->menuHoldTime)
238 m_WatchList.remove(client);
243 mh->OnMenuCancel(menu, client, reason);
245 mh->OnMenuSelect(menu, client, item);
251 bool BaseMenuStyle::DoClientMenu(int client, IMenuDisplay *menu, IMenuHandler *mh, unsigned int time)
253 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
254 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
259 CBaseMenuPlayer *player = GetMenuPlayer(client);
260 if (player->bAutoIgnore)
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.
270 player->bAutoIgnore = true;
272 /* Cancel any old menus */
273 menu_states_t &states = player->states;
276 /* We need to cancel the old menu */
277 if (player->menuHoldTime)
279 RemoveClientFromWatch(client);
281 states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt);
282 states.mh->OnMenuEnd(states.menu);
285 states.firstItem = 0;
289 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
290 player->bInMenu = true;
291 player->menuStartTime = gpGlobals->curtime;
292 player->menuHoldTime = time;
296 AddClientToWatch(client);
299 /* Draw the display */
300 SendDisplay(client, menu);
302 /* We can be interrupted again! */
303 player->bAutoIgnore = false;
308 bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time)
310 mh->OnMenuStart(menu);
314 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
319 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
320 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
322 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
327 CBaseMenuPlayer *player = GetMenuPlayer(client);
328 if (player->bAutoIgnore)
330 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
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.
340 player->bAutoIgnore = true;
342 /* Cancel any old menus */
343 menu_states_t &states = player->states;
346 _CancelClientMenu(client, true);
349 states.firstItem = 0;
353 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
355 IMenuDisplay *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
358 player->bAutoIgnore = false;
359 player->bInMenu = false;
360 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
365 /* Finally, set our states */
366 player->bInMenu = true;
367 player->menuStartTime = gpGlobals->curtime;
368 player->menuHoldTime = time;
372 AddClientToWatch(client);
375 /* Draw the display */
376 SendDisplay(client, display);
378 /* Free the display pointer */
379 display->DeleteThis();
381 /* We can be interrupted again! */
382 player->bAutoIgnore = false;
387 bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
389 CBaseMenuPlayer *player = GetMenuPlayer(client);
390 menu_states_t &states = player->states;
392 player->bAutoIgnore = true;
393 IMenuDisplay *display = g_Menus.RenderMenu(client, states, order);
396 if (player->menuHoldTime)
398 m_WatchList.remove(client);
400 player->bAutoIgnore = false;
404 SendDisplay(client, display);
406 display->DeleteThis();
408 player->bAutoIgnore = false;
413 CBaseMenu::CBaseMenu(IMenuStyle *pStyle) :
414 m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true), m_bShouldDelete(false), m_bCancelling(false)
418 CBaseMenu::~CBaseMenu()
422 bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
424 if (m_Pagination == MENU_NO_PAGINATION
425 && m_items.size() >= m_pStyle->GetMaxPageItems())
432 item.infoString = m_Strings.AddString(info);
435 item.displayString = m_Strings.AddString(draw.display);
437 item.style = draw.style;
440 m_items.push_back(item);
445 bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
447 if (m_Pagination == MENU_NO_PAGINATION
448 && m_items.size() >= m_pStyle->GetMaxPageItems())
453 if (position >= m_items.size())
459 item.infoString = m_Strings.AddString(info);
462 item.displayString = m_Strings.AddString(draw.display);
464 item.style = draw.style;
466 CVector<CItem>::iterator iter = m_items.iterAt(position);
467 m_items.insert(iter, item);
472 bool CBaseMenu::RemoveItem(unsigned int position)
474 if (position >= m_items.size())
479 m_items.erase(m_items.iterAt(position));
481 if (m_items.size() == 0)
489 void CBaseMenu::RemoveAllItems()
495 const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
497 if (position >= m_items.size())
504 draw->display = m_Strings.GetString(m_items[position].displayString);
505 draw->style = m_items[position].style;
508 return m_Strings.GetString(m_items[position].infoString);
511 unsigned int CBaseMenu::GetItemCount()
513 return m_items.size();
516 bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
518 if (itemsPerPage > 7)
523 m_Pagination = itemsPerPage;
528 unsigned int CBaseMenu::GetPagination()
533 IMenuStyle *CBaseMenu::GetDrawStyle()
538 void CBaseMenu::SetDefaultTitle(const char *message)
540 m_Title.assign(message);
543 const char *CBaseMenu::GetDefaultTitle()
545 return m_Title.c_str();
548 bool CBaseMenu::GetExitButton()
553 bool CBaseMenu::SetExitButton(bool set)
559 void CBaseMenu::Cancel()
566 m_bCancelling = true;
568 m_bCancelling = false;
576 void CBaseMenu::Destroy()
578 if (!m_bCancelling || m_bShouldDelete)
583 m_bShouldDelete = true;