dvander@769: /** dvander@769: * vim: set ts=4 : dvander@769: * =============================================================== dvander@769: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. dvander@769: * =============================================================== dvander@769: * dvander@769: * This file is not open source and may not be copied without explicit dvander@769: * written permission of AlliedModders LLC. This file may not be redistributed dvander@769: * in whole or significant part. dvander@769: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php dvander@769: * dvander@769: * Version: $Id$ dvander@769: */ dvander@771: dvander@769: #include dvander@769: #include "sm_stringutil.h" dvander@769: #include "MenuStyle_Base.h" dvander@775: #include "PlayerManager.h" dvander@775: #include "MenuManager.h" dvander@790: #include "HandleSys.h" dvander@775: dvander@790: BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE) dvander@790: { dvander@790: } dvander@790: dvander@790: Handle_t BaseMenuStyle::GetHandle() dvander@775: { dvander@790: /* Don't create the handle until we need it */ dvander@790: if (m_hHandle == BAD_HANDLE) dvander@790: { dvander@790: m_hHandle = g_Menus.CreateStyleHandle(this); dvander@790: } dvander@790: dvander@790: return m_hHandle; dvander@775: } dvander@775: dvander@775: void BaseMenuStyle::AddClientToWatch(int client) dvander@775: { dvander@775: m_WatchList.push_back(client); dvander@775: } dvander@775: dvander@775: void BaseMenuStyle::RemoveClientFromWatch(int client) dvander@775: { dvander@775: m_WatchList.remove(client); dvander@775: } dvander@775: dvander@775: void BaseMenuStyle::_CancelClientMenu(int client, bool bAutoIgnore/* =false */, MenuCancelReason reason/* =MenuCancel_Interrupt */) dvander@775: { dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: menu_states_t &states = player->states; dvander@775: dvander@775: bool bOldIgnore = player->bAutoIgnore; dvander@775: if (bAutoIgnore) dvander@775: { dvander@775: player->bAutoIgnore = true; dvander@775: } dvander@775: dvander@775: /* Save states */ dvander@775: IMenuHandler *mh = states.mh; dvander@775: IBaseMenu *menu = states.menu; dvander@775: dvander@775: /* Clear menu */ dvander@775: player->bInMenu = false; dvander@775: if (player->menuHoldTime) dvander@775: { dvander@775: RemoveClientFromWatch(client); dvander@775: } dvander@775: dvander@775: /* Fire callbacks */ dvander@775: mh->OnMenuCancel(menu, client, reason); dvander@790: dvander@790: /* Only fire end if there's a valid menu */ dvander@790: if (menu) dvander@790: { dvander@790: mh->OnMenuEnd(menu); dvander@790: } dvander@775: dvander@775: if (bAutoIgnore) dvander@775: { dvander@775: player->bAutoIgnore = bOldIgnore; dvander@775: } dvander@775: } dvander@775: dvander@775: void BaseMenuStyle::CancelMenu(CBaseMenu *menu) dvander@775: { dvander@775: int maxClients = g_Players.GetMaxClients(); dvander@775: for (int i=1; i<=maxClients; i++) dvander@775: { dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(i); dvander@775: if (player->bInMenu) dvander@775: { dvander@775: menu_states_t &states = player->states; dvander@775: if (states.menu == menu) dvander@775: { dvander@775: _CancelClientMenu(i); dvander@775: } dvander@775: } dvander@775: } dvander@775: } dvander@775: dvander@775: bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore) dvander@775: { dvander@775: if (client < 1 || client > g_Players.MaxClients()) dvander@775: { dvander@775: return false; dvander@775: } dvander@775: dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: if (!player->bInMenu) dvander@775: { dvander@775: return false; dvander@775: } dvander@775: dvander@775: _CancelClientMenu(client, autoIgnore); dvander@775: dvander@775: return true; dvander@775: } dvander@775: dvander@775: MenuSource BaseMenuStyle::GetClientMenu(int client, void **object) dvander@775: { dvander@775: if (client < 1 || client > g_Players.GetMaxClients()) dvander@775: { dvander@775: return MenuSource_None; dvander@775: } dvander@775: dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: dvander@775: if (player->bInMenu) dvander@775: { dvander@775: IBaseMenu *menu; dvander@775: if ((menu=player->states.menu) != NULL) dvander@775: { dvander@775: if (object) dvander@775: { dvander@775: *object = menu; dvander@775: } dvander@775: return MenuSource_BaseMenu; dvander@775: } dvander@775: dvander@775: return MenuSource_Display; dvander@780: } else if (player->bInExternMenu) { dvander@782: if (player->menuHoldTime != 0 dvander@782: && (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime)) dvander@782: { dvander@782: player->bInExternMenu = false; dvander@782: return MenuSource_None; dvander@782: } dvander@780: return MenuSource_External; dvander@775: } dvander@775: dvander@775: return MenuSource_None; dvander@775: } dvander@775: dvander@775: void BaseMenuStyle::OnClientDisconnected(int client) dvander@775: { dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: if (!player->bInMenu) dvander@775: { dvander@775: return; dvander@775: } dvander@775: dvander@775: _CancelClientMenu(client, true, MenuCancel_Disconnect); dvander@775: dvander@775: player->bInMenu = false; dvander@790: player->bInExternMenu = false; dvander@775: } dvander@775: dvander@775: static int do_lookup[256]; dvander@775: void BaseMenuStyle::ProcessWatchList() dvander@775: { dvander@775: if (!m_WatchList.size()) dvander@775: { dvander@775: return; dvander@775: } dvander@775: dvander@775: unsigned int total = 0; dvander@775: for (FastLink::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter) dvander@775: { dvander@775: do_lookup[total++] = (*iter); dvander@775: } dvander@775: dvander@775: int client; dvander@775: CBaseMenuPlayer *player; dvander@775: float curTime = gpGlobals->curtime; dvander@775: for (unsigned int i=0; ibInMenu || !player->menuHoldTime) dvander@775: { dvander@825: m_WatchList.remove(client); dvander@775: continue; dvander@775: } dvander@775: if (curTime > player->menuStartTime + player->menuHoldTime) dvander@775: { dvander@775: _CancelClientMenu(client, false); dvander@775: } dvander@775: } dvander@775: } dvander@775: dvander@775: void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press) dvander@775: { dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: dvander@775: /* First question: Are we in a menu? */ dvander@775: if (!player->bInMenu) dvander@775: { dvander@775: return; dvander@775: } dvander@775: dvander@775: bool cancel = false; dvander@775: unsigned int item = 0; dvander@775: MenuCancelReason reason = MenuCancel_Exit; dvander@775: menu_states_t &states = player->states; dvander@775: dvander@775: assert(states.mh != NULL); dvander@775: dvander@775: if (states.menu == NULL) dvander@775: { dvander@775: item = key_press; dvander@785: } else if (key_press < 1 || key_press > GetMaxPageItems()) { dvander@775: cancel = true; dvander@775: } else { dvander@775: ItemSelection type = states.slots[key_press].type; dvander@775: dvander@775: /* For navigational items, we're going to redisplay */ dvander@775: if (type == ItemSel_Back) dvander@775: { dvander@775: if (!RedoClientMenu(client, ItemOrder_Descending)) dvander@775: { dvander@775: cancel = true; dvander@775: reason = MenuCancel_NoDisplay; dvander@775: } else { dvander@775: return; dvander@775: } dvander@775: } else if (type == ItemSel_Next) { dvander@775: if (!RedoClientMenu(client, ItemOrder_Ascending)) dvander@775: { dvander@775: cancel = true; /* I like Saltines. */ dvander@775: reason = MenuCancel_NoDisplay; dvander@775: } else { dvander@775: return; dvander@775: } dvander@775: } else if (type == ItemSel_Exit || type == ItemSel_None) { dvander@775: cancel = true; dvander@775: } else { dvander@775: item = states.slots[key_press].item; dvander@775: } dvander@775: } dvander@775: dvander@775: /* Save variables */ dvander@775: IMenuHandler *mh = states.mh; dvander@775: IBaseMenu *menu = states.menu; dvander@775: dvander@775: /* Clear states */ dvander@775: player->bInMenu = false; dvander@775: if (player->menuHoldTime) dvander@775: { dvander@775: m_WatchList.remove(client); dvander@775: } dvander@775: dvander@775: if (cancel) dvander@775: { dvander@775: mh->OnMenuCancel(menu, client, reason); dvander@775: } else { dvander@775: mh->OnMenuSelect(menu, client, item); dvander@775: } dvander@775: dvander@790: /* Only fire end for valid menus */ dvander@790: if (menu) dvander@790: { dvander@790: mh->OnMenuEnd(menu); dvander@790: } dvander@775: } dvander@775: dvander@787: bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time) dvander@775: { dvander@775: CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); dvander@775: if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame()) dvander@775: { dvander@775: return false; dvander@775: } dvander@775: dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: if (player->bAutoIgnore) dvander@775: { dvander@775: return false; dvander@775: } dvander@775: dvander@775: /* For the duration of this, we are going to totally ignore whether dvander@775: * the player is already in a menu or not (except to cancel the old one). dvander@775: * Instead, we are simply going to ignore any further menu displays, so dvander@775: * this display can't be interrupted. dvander@775: */ dvander@775: player->bAutoIgnore = true; dvander@775: dvander@775: /* Cancel any old menus */ dvander@775: menu_states_t &states = player->states; dvander@775: if (player->bInMenu) dvander@775: { dvander@790: _CancelClientMenu(client, true); dvander@775: } dvander@775: dvander@775: states.firstItem = 0; dvander@775: states.lastItem = 0; dvander@775: states.menu = NULL; dvander@775: states.mh = mh; dvander@775: states.apiVers = SMINTERFACE_MENUMANAGER_VERSION; dvander@775: player->bInMenu = true; dvander@790: player->bInExternMenu = false; dvander@775: player->menuStartTime = gpGlobals->curtime; dvander@775: player->menuHoldTime = time; dvander@775: dvander@775: if (time) dvander@775: { dvander@775: AddClientToWatch(client); dvander@775: } dvander@775: dvander@775: /* Draw the display */ dvander@775: SendDisplay(client, menu); dvander@775: dvander@775: /* We can be interrupted again! */ dvander@775: player->bAutoIgnore = false; dvander@775: dvander@775: return true; dvander@775: } dvander@775: dvander@775: bool BaseMenuStyle::DoClientMenu(int client, CBaseMenu *menu, IMenuHandler *mh, unsigned int time) dvander@775: { dvander@775: mh->OnMenuStart(menu); dvander@775: dvander@775: if (!mh) dvander@775: { dvander@775: mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); dvander@775: mh->OnMenuEnd(menu); dvander@775: return false; dvander@775: } dvander@775: dvander@775: CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); dvander@775: if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame()) dvander@775: { dvander@775: mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); dvander@775: mh->OnMenuEnd(menu); dvander@775: return false; dvander@775: } dvander@775: dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: if (player->bAutoIgnore) dvander@775: { dvander@775: mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); dvander@775: mh->OnMenuEnd(menu); dvander@775: return false; dvander@775: } dvander@775: dvander@775: /* For the duration of this, we are going to totally ignore whether dvander@775: * the player is already in a menu or not (except to cancel the old one). dvander@775: * Instead, we are simply going to ignore any further menu displays, so dvander@775: * this display can't be interrupted. dvander@775: */ dvander@775: player->bAutoIgnore = true; dvander@775: dvander@775: /* Cancel any old menus */ dvander@775: menu_states_t &states = player->states; dvander@775: if (player->bInMenu) dvander@775: { dvander@775: _CancelClientMenu(client, true); dvander@775: } dvander@775: dvander@775: states.firstItem = 0; dvander@775: states.lastItem = 0; dvander@775: states.menu = menu; dvander@775: states.mh = mh; dvander@775: states.apiVers = SMINTERFACE_MENUMANAGER_VERSION; dvander@775: dvander@787: IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending); dvander@775: if (!display) dvander@775: { dvander@775: player->bAutoIgnore = false; dvander@775: player->bInMenu = false; dvander@775: mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); dvander@775: mh->OnMenuEnd(menu); dvander@775: return false; dvander@775: } dvander@775: dvander@775: /* Finally, set our states */ dvander@775: player->bInMenu = true; dvander@790: player->bInExternMenu = false; dvander@775: player->menuStartTime = gpGlobals->curtime; dvander@775: player->menuHoldTime = time; dvander@775: dvander@775: if (time) dvander@775: { dvander@775: AddClientToWatch(client); dvander@775: } dvander@775: dvander@775: /* Draw the display */ dvander@775: SendDisplay(client, display); dvander@775: dvander@775: /* Free the display pointer */ dvander@776: display->DeleteThis(); dvander@775: dvander@775: /* We can be interrupted again! */ dvander@775: player->bAutoIgnore = false; dvander@775: dvander@775: return true; dvander@775: } dvander@775: dvander@775: bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order) dvander@775: { dvander@775: CBaseMenuPlayer *player = GetMenuPlayer(client); dvander@775: menu_states_t &states = player->states; dvander@775: dvander@775: player->bAutoIgnore = true; dvander@787: IMenuPanel *display = g_Menus.RenderMenu(client, states, order); dvander@775: if (!display) dvander@775: { dvander@775: if (player->menuHoldTime) dvander@775: { dvander@775: m_WatchList.remove(client); dvander@775: } dvander@775: player->bAutoIgnore = false; dvander@775: return false; dvander@775: } dvander@775: dvander@775: SendDisplay(client, display); dvander@775: dvander@776: display->DeleteThis(); dvander@775: dvander@775: player->bAutoIgnore = false; dvander@775: dvander@775: return true; dvander@775: } dvander@769: dvander@790: CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) : dvander@790: m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true), dvander@790: m_bShouldDelete(false), m_bCancelling(false), m_pOwner(pOwner ? pOwner : g_pCoreIdent), dvander@877: m_bDeleting(false), m_bWillFreeHandle(false), m_hHandle(BAD_HANDLE), m_pHandler(pHandler), dvander@877: m_pVoteHandler(NULL) dvander@769: { dvander@769: } dvander@769: dvander@769: CBaseMenu::~CBaseMenu() dvander@769: { dvander@877: g_Menus.ReleaseVoteWrapper(m_pVoteHandler); dvander@769: } dvander@769: dvander@790: Handle_t CBaseMenu::GetHandle() dvander@790: { dvander@790: if (!m_hHandle) dvander@790: { dvander@790: m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner); dvander@790: } dvander@790: dvander@790: return m_hHandle; dvander@790: } dvander@790: dvander@769: bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw) dvander@769: { dvander@778: if (m_Pagination == (unsigned)MENU_NO_PAGINATION dvander@769: && m_items.size() >= m_pStyle->GetMaxPageItems()) dvander@769: { dvander@769: return false; dvander@769: } dvander@769: dvander@769: CItem item; dvander@769: dvander@769: item.infoString = m_Strings.AddString(info); dvander@769: if (draw.display) dvander@769: { dvander@769: item.displayString = m_Strings.AddString(draw.display); dvander@769: } dvander@769: item.style = draw.style; dvander@769: dvander@769: dvander@769: m_items.push_back(item); dvander@769: dvander@769: return true; dvander@769: } dvander@769: dvander@769: bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw) dvander@769: { dvander@778: if (m_Pagination == (unsigned)MENU_NO_PAGINATION dvander@769: && m_items.size() >= m_pStyle->GetMaxPageItems()) dvander@769: { dvander@769: return false; dvander@769: } dvander@769: dvander@769: if (position >= m_items.size()) dvander@769: { dvander@769: return false; dvander@769: } dvander@769: dvander@769: CItem item; dvander@769: item.infoString = m_Strings.AddString(info); dvander@769: if (draw.display) dvander@769: { dvander@769: item.displayString = m_Strings.AddString(draw.display); dvander@769: } dvander@769: item.style = draw.style; dvander@769: dvander@769: CVector::iterator iter = m_items.iterAt(position); dvander@769: m_items.insert(iter, item); dvander@769: dvander@769: return true; dvander@769: } dvander@769: dvander@769: bool CBaseMenu::RemoveItem(unsigned int position) dvander@769: { dvander@769: if (position >= m_items.size()) dvander@769: { dvander@769: return false; dvander@769: } dvander@769: dvander@769: m_items.erase(m_items.iterAt(position)); dvander@769: dvander@769: if (m_items.size() == 0) dvander@769: { dvander@769: m_Strings.Reset(); dvander@769: } dvander@769: dvander@769: return true; dvander@769: } dvander@769: dvander@769: void CBaseMenu::RemoveAllItems() dvander@769: { dvander@769: m_items.clear(); dvander@769: m_Strings.Reset(); dvander@769: } dvander@769: dvander@769: const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */) dvander@769: { dvander@769: if (position >= m_items.size()) dvander@769: { dvander@769: return NULL; dvander@769: } dvander@769: dvander@769: if (draw) dvander@769: { dvander@769: draw->display = m_Strings.GetString(m_items[position].displayString); dvander@769: draw->style = m_items[position].style; dvander@769: } dvander@769: dvander@769: return m_Strings.GetString(m_items[position].infoString); dvander@769: } dvander@769: dvander@769: unsigned int CBaseMenu::GetItemCount() dvander@769: { dvander@769: return m_items.size(); dvander@769: } dvander@769: dvander@769: bool CBaseMenu::SetPagination(unsigned int itemsPerPage) dvander@769: { dvander@820: if (itemsPerPage > 7 || itemsPerPage == 1) dvander@769: { dvander@769: return false; dvander@769: } dvander@769: dvander@769: m_Pagination = itemsPerPage; dvander@769: dvander@769: return true; dvander@769: } dvander@769: dvander@769: unsigned int CBaseMenu::GetPagination() dvander@769: { dvander@769: return m_Pagination; dvander@769: } dvander@769: dvander@769: IMenuStyle *CBaseMenu::GetDrawStyle() dvander@769: { dvander@769: return m_pStyle; dvander@769: } dvander@769: dvander@769: void CBaseMenu::SetDefaultTitle(const char *message) dvander@769: { dvander@769: m_Title.assign(message); dvander@769: } dvander@769: dvander@769: const char *CBaseMenu::GetDefaultTitle() dvander@769: { dvander@769: return m_Title.c_str(); dvander@769: } dvander@769: dvander@769: bool CBaseMenu::GetExitButton() dvander@769: { dvander@769: return m_ExitButton; dvander@769: } dvander@769: dvander@769: bool CBaseMenu::SetExitButton(bool set) dvander@769: { dvander@769: m_ExitButton = set; dvander@769: return true; dvander@769: } dvander@775: dvander@775: void CBaseMenu::Cancel() dvander@775: { dvander@775: if (m_bCancelling) dvander@775: { dvander@775: return; dvander@775: } dvander@775: dvander@775: m_bCancelling = true; dvander@775: Cancel_Finally(); dvander@775: m_bCancelling = false; dvander@775: dvander@775: if (m_bShouldDelete) dvander@775: { dvander@790: InternalDelete(); dvander@775: } dvander@775: } dvander@775: dvander@790: void CBaseMenu::Destroy(bool releaseHandle) dvander@775: { dvander@790: /* Check if we shouldn't be here */ dvander@790: if (m_bDeleting) dvander@790: { dvander@790: return; dvander@790: } dvander@790: dvander@790: /* Save the destruction hint about our handle */ dvander@790: m_bWillFreeHandle = releaseHandle; dvander@790: dvander@790: /* Now actually do stuff */ dvander@775: if (!m_bCancelling || m_bShouldDelete) dvander@775: { dvander@775: Cancel(); dvander@790: InternalDelete(); dvander@775: } else { dvander@775: m_bShouldDelete = true; dvander@775: } dvander@775: } dvander@775: dvander@790: void CBaseMenu::InternalDelete() dvander@790: { dvander@790: if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE) dvander@790: { dvander@790: Handle_t hndl = m_hHandle; dvander@790: HandleSecurity sec; dvander@790: dvander@790: sec.pOwner = m_pOwner; dvander@790: sec.pIdentity = g_pCoreIdent; dvander@790: dvander@790: m_hHandle = BAD_HANDLE; dvander@790: m_bDeleting = true; dvander@790: g_HandleSys.FreeHandle(hndl, &sec); dvander@790: } dvander@790: dvander@790: m_pHandler->OnMenuDestroy(this); dvander@790: dvander@790: delete this; dvander@790: } dvander@790: dvander@877: bool CBaseMenu::BroadcastVote(int clients[], dvander@877: unsigned int numClients, dvander@877: unsigned int maxTime, dvander@877: unsigned int flags) dvander@877: { dvander@877: if (!m_pVoteHandler) dvander@877: { dvander@877: m_pVoteHandler = g_Menus.CreateVoteWrapper(m_pHandler); dvander@877: } else if (m_pVoteHandler->IsVoteInProgress()) { dvander@877: return false; dvander@877: } dvander@877: dvander@877: m_pVoteHandler->InitializeVoting(this); dvander@877: dvander@877: for (unsigned int i=0; iStartVoting(); dvander@879: dvander@879: return true; dvander@877: } dvander@879: