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;
124 } else if (player->bInExternMenu) {
125 if (player->menuHoldTime != 0
126 && (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime))
128 player->bInExternMenu = false;
129 return MenuSource_None;
131 return MenuSource_External;
134 return MenuSource_None;
137 void BaseMenuStyle::OnClientDisconnected(int client)
139 CBaseMenuPlayer *player = GetMenuPlayer(client);
140 if (!player->bInMenu)
145 _CancelClientMenu(client, true, MenuCancel_Disconnect);
147 player->bInMenu = false;
150 static int do_lookup[256];
151 void BaseMenuStyle::ProcessWatchList()
153 if (!m_WatchList.size())
158 unsigned int total = 0;
159 for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
161 do_lookup[total++] = (*iter);
165 CBaseMenuPlayer *player;
166 float curTime = gpGlobals->curtime;
167 for (unsigned int i=0; i<total; i++)
169 client = do_lookup[i];
170 player = GetMenuPlayer(client);
171 if (!player->bInMenu || !player->menuHoldTime)
173 m_WatchList.remove(i);
176 if (curTime > player->menuStartTime + player->menuHoldTime)
178 _CancelClientMenu(client, false);
183 void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
185 CBaseMenuPlayer *player = GetMenuPlayer(client);
187 /* First question: Are we in a menu? */
188 if (!player->bInMenu)
194 unsigned int item = 0;
195 MenuCancelReason reason = MenuCancel_Exit;
196 menu_states_t &states = player->states;
198 assert(states.mh != NULL);
200 if (states.menu == NULL)
203 } else if (key_press < 1 || key_press > GetMaxPageItems()) {
206 ItemSelection type = states.slots[key_press].type;
208 /* For navigational items, we're going to redisplay */
209 if (type == ItemSel_Back)
211 if (!RedoClientMenu(client, ItemOrder_Descending))
214 reason = MenuCancel_NoDisplay;
218 } else if (type == ItemSel_Next) {
219 if (!RedoClientMenu(client, ItemOrder_Ascending))
221 cancel = true; /* I like Saltines. */
222 reason = MenuCancel_NoDisplay;
226 } else if (type == ItemSel_Exit || type == ItemSel_None) {
229 item = states.slots[key_press].item;
234 IMenuHandler *mh = states.mh;
235 IBaseMenu *menu = states.menu;
238 player->bInMenu = false;
239 if (player->menuHoldTime)
241 m_WatchList.remove(client);
246 mh->OnMenuCancel(menu, client, reason);
248 mh->OnMenuSelect(menu, client, item);
254 bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
256 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
257 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
262 CBaseMenuPlayer *player = GetMenuPlayer(client);
263 if (player->bAutoIgnore)
268 /* For the duration of this, we are going to totally ignore whether
269 * the player is already in a menu or not (except to cancel the old one).
270 * Instead, we are simply going to ignore any further menu displays, so
271 * this display can't be interrupted.
273 player->bAutoIgnore = true;
275 /* Cancel any old menus */
276 menu_states_t &states = player->states;
279 /* We need to cancel the old menu */
280 if (player->menuHoldTime)
282 RemoveClientFromWatch(client);
284 states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt);
285 states.mh->OnMenuEnd(states.menu);
288 states.firstItem = 0;
292 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
293 player->bInMenu = true;
294 player->menuStartTime = gpGlobals->curtime;
295 player->menuHoldTime = time;
299 AddClientToWatch(client);
302 /* Draw the display */
303 SendDisplay(client, menu);
305 /* We can be interrupted again! */
306 player->bAutoIgnore = false;
311 bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time)
313 mh->OnMenuStart(menu);
317 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
322 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
323 if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
325 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
330 CBaseMenuPlayer *player = GetMenuPlayer(client);
331 if (player->bAutoIgnore)
333 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
338 /* For the duration of this, we are going to totally ignore whether
339 * the player is already in a menu or not (except to cancel the old one).
340 * Instead, we are simply going to ignore any further menu displays, so
341 * this display can't be interrupted.
343 player->bAutoIgnore = true;
345 /* Cancel any old menus */
346 menu_states_t &states = player->states;
349 _CancelClientMenu(client, true);
352 states.firstItem = 0;
356 states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
358 IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
361 player->bAutoIgnore = false;
362 player->bInMenu = false;
363 mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
368 /* Finally, set our states */
369 player->bInMenu = true;
370 player->menuStartTime = gpGlobals->curtime;
371 player->menuHoldTime = time;
375 AddClientToWatch(client);
378 /* Draw the display */
379 SendDisplay(client, display);
381 /* Free the display pointer */
382 display->DeleteThis();
384 /* We can be interrupted again! */
385 player->bAutoIgnore = false;
390 bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
392 CBaseMenuPlayer *player = GetMenuPlayer(client);
393 menu_states_t &states = player->states;
395 player->bAutoIgnore = true;
396 IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
399 if (player->menuHoldTime)
401 m_WatchList.remove(client);
403 player->bAutoIgnore = false;
407 SendDisplay(client, display);
409 display->DeleteThis();
411 player->bAutoIgnore = false;
416 CBaseMenu::CBaseMenu(IMenuStyle *pStyle) :
417 m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true), m_bShouldDelete(false), m_bCancelling(false)
421 CBaseMenu::~CBaseMenu()
425 bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
427 if (m_Pagination == (unsigned)MENU_NO_PAGINATION
428 && m_items.size() >= m_pStyle->GetMaxPageItems())
435 item.infoString = m_Strings.AddString(info);
438 item.displayString = m_Strings.AddString(draw.display);
440 item.style = draw.style;
443 m_items.push_back(item);
448 bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
450 if (m_Pagination == (unsigned)MENU_NO_PAGINATION
451 && m_items.size() >= m_pStyle->GetMaxPageItems())
456 if (position >= m_items.size())
462 item.infoString = m_Strings.AddString(info);
465 item.displayString = m_Strings.AddString(draw.display);
467 item.style = draw.style;
469 CVector<CItem>::iterator iter = m_items.iterAt(position);
470 m_items.insert(iter, item);
475 bool CBaseMenu::RemoveItem(unsigned int position)
477 if (position >= m_items.size())
482 m_items.erase(m_items.iterAt(position));
484 if (m_items.size() == 0)
492 void CBaseMenu::RemoveAllItems()
498 const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
500 if (position >= m_items.size())
507 draw->display = m_Strings.GetString(m_items[position].displayString);
508 draw->style = m_items[position].style;
511 return m_Strings.GetString(m_items[position].infoString);
514 unsigned int CBaseMenu::GetItemCount()
516 return m_items.size();
519 bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
521 if (itemsPerPage > 7)
526 m_Pagination = itemsPerPage;
531 unsigned int CBaseMenu::GetPagination()
536 IMenuStyle *CBaseMenu::GetDrawStyle()
541 void CBaseMenu::SetDefaultTitle(const char *message)
543 m_Title.assign(message);
546 const char *CBaseMenu::GetDefaultTitle()
548 return m_Title.c_str();
551 bool CBaseMenu::GetExitButton()
556 bool CBaseMenu::SetExitButton(bool set)
562 void CBaseMenu::Cancel()
569 m_bCancelling = true;
571 m_bCancelling = false;
579 void CBaseMenu::Destroy()
581 if (!m_bCancelling || m_bShouldDelete)
586 m_bShouldDelete = true;