submitting patch from amb1181 - retrieving topmenu object name
authorDavid Anderson <dvander@alliedmods.net>
Tue Nov 20 02:22:26 2007 +0000 (2007-11-20)
changeset 16994368f4cc417b
parent 1698 7caa46a2eb2c
child 1700 25d92f8c1035
submitting patch from amb1181 - retrieving topmenu object name
extensions/topmenus/TopMenu.cpp
extensions/topmenus/TopMenu.h
extensions/topmenus/smn_topmenus.cpp
plugins/include/topmenus.inc
     1.1 --- a/extensions/topmenus/TopMenu.cpp	Mon Nov 19 23:11:10 2007 +0000
     1.2 +++ b/extensions/topmenus/TopMenu.cpp	Tue Nov 20 02:22:26 2007 +0000
     1.3 @@ -1,1105 +1,1119 @@
     1.4 -/**
     1.5 - * vim: set ts=4 :
     1.6 - * =============================================================================
     1.7 - * SourceMod Sample Extension
     1.8 - * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     1.9 - * =============================================================================
    1.10 - *
    1.11 - * This program is free software; you can redistribute it and/or modify it under
    1.12 - * the terms of the GNU General Public License, version 3.0, as published by the
    1.13 - * Free Software Foundation.
    1.14 - * 
    1.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
    1.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    1.17 - * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    1.18 - * details.
    1.19 - *
    1.20 - * You should have received a copy of the GNU General Public License along with
    1.21 - * this program.  If not, see <http://www.gnu.org/licenses/>.
    1.22 - *
    1.23 - * As a special exception, AlliedModders LLC gives you permission to link the
    1.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
    1.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    1.26 - * by the Valve Corporation.  You must obey the GNU General Public License in
    1.27 - * all respects for all other code used.  Additionally, AlliedModders LLC grants
    1.28 - * this exception to all derivative works.  AlliedModders LLC defines further
    1.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    1.30 - * or <http://www.sourcemod.net/license.php>.
    1.31 - *
    1.32 - * Version: $Id$
    1.33 - */
    1.34 -
    1.35 -#include <stdlib.h>
    1.36 -#include <stdarg.h>
    1.37 -#include "TopMenu.h"
    1.38 -
    1.39 -struct obj_by_name_t
    1.40 -{
    1.41 -	unsigned int obj_index;
    1.42 -	char name[64];
    1.43 -};
    1.44 -
    1.45 -int _SortObjectNamesDescending(const void *ptr1, const void *ptr2);
    1.46 -unsigned int strncopy(char *dest, const char *src, size_t count);
    1.47 -size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
    1.48 -
    1.49 -TopMenu::TopMenu(ITopMenuObjectCallbacks *callbacks)
    1.50 -{
    1.51 -	m_clients = NULL;
    1.52 -	m_SerialNo = 1;
    1.53 -	m_pTitle = callbacks;
    1.54 -	m_max_clients = 0;
    1.55 -
    1.56 -	if (playerhelpers->IsServerActivated())
    1.57 -	{
    1.58 -		CreatePlayers(playerhelpers->GetMaxClients());
    1.59 -	}
    1.60 -}
    1.61 -
    1.62 -TopMenu::~TopMenu()
    1.63 -{
    1.64 -	/* Delete all categories */
    1.65 -	while (m_Categories.size())
    1.66 -	{
    1.67 -		RemoveFromMenu(m_Categories[0]->obj->object_id);
    1.68 -	}
    1.69 -
    1.70 -	/* Remove all objects */
    1.71 -	for (size_t i = 0; i < m_Objects.size(); i++)
    1.72 -	{
    1.73 -		assert(m_Objects[i]->is_free == true);
    1.74 -		delete m_Objects[i];
    1.75 -	}
    1.76 -
    1.77 -	m_pTitle->OnTopMenuObjectRemoved(this, 0);
    1.78 -
    1.79 -	/* Delete all cached config entries */
    1.80 -	for (size_t i = 0; i < m_Config.cats.size(); i++)
    1.81 -	{
    1.82 -		delete m_Config.cats[i];
    1.83 -	}
    1.84 -
    1.85 -	/* Sweep players */
    1.86 -	for (size_t i = 0; i <= (size_t)m_max_clients; i++)
    1.87 -	{
    1.88 -		TearDownClient(&m_clients[i]);
    1.89 -	}
    1.90 -	delete [] m_clients;
    1.91 -}
    1.92 -
    1.93 -void TopMenu::OnClientConnected(int client)
    1.94 -{
    1.95 -	if (m_clients == NULL)
    1.96 -	{
    1.97 -		return;
    1.98 -	}
    1.99 -
   1.100 -	topmenu_player_t *player = &m_clients[client];
   1.101 -	TearDownClient(player);
   1.102 -}
   1.103 -
   1.104 -void TopMenu::OnClientDisconnected(int client)
   1.105 -{
   1.106 -	if (m_clients == NULL)
   1.107 -	{
   1.108 -		return;
   1.109 -	}
   1.110 -
   1.111 -	topmenu_player_t *player = &m_clients[client];
   1.112 -	TearDownClient(player);
   1.113 -}
   1.114 -
   1.115 -void TopMenu::OnServerActivated(int max_clients)
   1.116 -{
   1.117 -	if (m_clients == NULL)
   1.118 -	{
   1.119 -		CreatePlayers(max_clients);
   1.120 -	}
   1.121 -}
   1.122 -
   1.123 -unsigned int TopMenu::AddToMenu(const char *name,
   1.124 -								TopMenuObjectType type,
   1.125 -								ITopMenuObjectCallbacks *callbacks,
   1.126 -								IdentityToken_t *owner,
   1.127 -								const char *cmdname,
   1.128 -								FlagBits flags,
   1.129 -								unsigned int parent)
   1.130 -{
   1.131 -	return AddToMenu2(name, type, callbacks, owner, cmdname, flags, parent, NULL);
   1.132 -}
   1.133 -
   1.134 -unsigned int TopMenu::AddToMenu2(const char *name,
   1.135 -								 TopMenuObjectType type,
   1.136 -								 ITopMenuObjectCallbacks *callbacks,
   1.137 -								 IdentityToken_t *owner,
   1.138 -								 const char *cmdname,
   1.139 -								 FlagBits flags,
   1.140 -								 unsigned int parent,
   1.141 -								 const char *info_string)
   1.142 -{
   1.143 -	/* Sanity checks */
   1.144 -	if (type == TopMenuObject_Category && parent != 0)
   1.145 -	{
   1.146 -		return 0;
   1.147 -	}
   1.148 -	else if (type == TopMenuObject_Item && parent == 0)
   1.149 -	{
   1.150 -		return 0;
   1.151 -	}
   1.152 -	else if (m_ObjLookup.retrieve(name) != NULL)
   1.153 -	{
   1.154 -		return 0;
   1.155 -	}
   1.156 -	else if (type != TopMenuObject_Item && type != TopMenuObject_Category)
   1.157 -	{
   1.158 -		return 0;
   1.159 -	}
   1.160 -
   1.161 -	/* If we're adding an item, make sure the parent is valid, 
   1.162 -	 * and that the parent is a category.
   1.163 -	 */
   1.164 -	topmenu_object_t *parent_obj = NULL;
   1.165 -	topmenu_category_t *parent_cat = NULL;
   1.166 -	if (type == TopMenuObject_Item)
   1.167 -	{
   1.168 -		/* Check parent index.  Note it will be >= 1 here. */
   1.169 -		if (parent > m_Objects.size() || m_Objects[parent - 1]->is_free)
   1.170 -		{
   1.171 -			return 0;
   1.172 -		}
   1.173 -		parent_obj = m_Objects[parent - 1];
   1.174 -
   1.175 -		/* Find an equivalent pointer in the category array. */
   1.176 -		for (size_t i = 0; i < m_Categories.size(); i++)
   1.177 -		{
   1.178 -			if (m_Categories[i]->obj == parent_obj)
   1.179 -			{
   1.180 -				parent_cat = m_Categories[i];
   1.181 -				break;
   1.182 -			}
   1.183 -		}
   1.184 -
   1.185 -		/* If none was found, leave. */
   1.186 -		if (parent_cat == NULL)
   1.187 -		{
   1.188 -			return 0;
   1.189 -		}
   1.190 -	}
   1.191 -
   1.192 -	/* Re-use an old object pointer if we can. */
   1.193 -	topmenu_object_t *obj = NULL;
   1.194 -	for (size_t i = 0; i < m_Objects.size(); i++)
   1.195 -	{
   1.196 -		if (m_Objects[i]->is_free == true)
   1.197 -		{
   1.198 -			obj = m_Objects[i];
   1.199 -			break;
   1.200 -		}
   1.201 -	}
   1.202 -
   1.203 -	/* Otherwise, allocate a new one. */
   1.204 -	if (obj == NULL)
   1.205 -	{
   1.206 -		obj = new topmenu_object_t;
   1.207 -		obj->object_id = ((unsigned int)m_Objects.size()) + 1;
   1.208 -		m_Objects.push_back(obj);
   1.209 -	}
   1.210 -
   1.211 -	/* Initialize the object's properties. */
   1.212 -	obj->callbacks = callbacks;
   1.213 -	obj->flags = flags;
   1.214 -	obj->owner = owner;
   1.215 -	obj->type = type;
   1.216 -	obj->is_free = false;
   1.217 -	obj->parent = parent_obj;
   1.218 -	strncopy(obj->name, name, sizeof(obj->name));
   1.219 -	strncopy(obj->cmdname, cmdname ? cmdname : "", sizeof(obj->cmdname));
   1.220 -	strncopy(obj->info, info_string ? info_string : "", sizeof(obj->info));
   1.221 -
   1.222 -	if (obj->type == TopMenuObject_Category)
   1.223 -	{
   1.224 -		/* Create a new category entry */
   1.225 -		topmenu_category_t *cat = new topmenu_category_t;
   1.226 -		cat->obj = obj;
   1.227 -		cat->reorder = false;
   1.228 -		cat->serial = 1;
   1.229 -
   1.230 -		/* Add it, then update our serial change number. */
   1.231 -		m_Categories.push_back(cat);
   1.232 -		m_SerialNo++;
   1.233 -
   1.234 -		/* Updating sorting info */
   1.235 -		m_bCatsNeedResort = true;
   1.236 -	}
   1.237 -	else if (obj->type == TopMenuObject_Item)
   1.238 -	{
   1.239 -		/* Update the category, mark it as needing changes */
   1.240 -		parent_cat->obj_list.push_back(obj);
   1.241 -		parent_cat->reorder = true;
   1.242 -		parent_cat->serial++;
   1.243 -
   1.244 -		/* If the category just went from 0 to 1 items, mark it as 
   1.245 -		 * changed, so clients get the category drawn.
   1.246 -		 */
   1.247 -		if (parent_cat->obj_list.size() == 1)
   1.248 -		{
   1.249 -			m_SerialNo++;
   1.250 -		}
   1.251 -	}
   1.252 -
   1.253 -	m_ObjLookup.insert(name, obj);
   1.254 -
   1.255 -	return obj->object_id;
   1.256 -}
   1.257 -
   1.258 -const char *TopMenu::GetObjectInfoString(unsigned int object_id)
   1.259 -{
   1.260 -	if (object_id == 0 
   1.261 -		|| object_id > m_Objects.size() 
   1.262 -		|| m_Objects[object_id - 1]->is_free)
   1.263 -	{
   1.264 -		return NULL;
   1.265 -	}
   1.266 -
   1.267 -	topmenu_object_t *obj = m_Objects[object_id - 1];
   1.268 -
   1.269 -	return obj->info;
   1.270 -}
   1.271 -
   1.272 -void TopMenu::RemoveFromMenu(unsigned int object_id)
   1.273 -{
   1.274 -	if (object_id == 0 
   1.275 -		|| object_id > m_Objects.size() 
   1.276 -		|| m_Objects[object_id - 1]->is_free)
   1.277 -	{
   1.278 -		return;
   1.279 -	}
   1.280 -
   1.281 -	topmenu_object_t *obj = m_Objects[object_id - 1];
   1.282 -
   1.283 -	m_ObjLookup.remove(obj->name);
   1.284 -
   1.285 -	if (obj->type == TopMenuObject_Category)
   1.286 -	{
   1.287 -		/* Find it in the category list. */
   1.288 -		for (size_t i = 0; i < m_Categories.size(); i++)
   1.289 -		{
   1.290 -			if (m_Categories[i]->obj == obj)
   1.291 -			{
   1.292 -				/* Mark all children as removed + free.  Note we could
   1.293 -				 * call into RemoveMenuItem() for this, but it'd be very 
   1.294 -				 * inefficient!
   1.295 -				 */
   1.296 -				topmenu_category_t *cat = m_Categories[i];
   1.297 -				for (size_t j = 0; j < m_Categories[i]->obj_list.size(); j++)
   1.298 -				{
   1.299 -					cat->obj_list[j]->callbacks->OnTopMenuObjectRemoved(this, cat->obj_list[j]->object_id);
   1.300 -					cat->obj_list[j]->is_free = true;
   1.301 -				}
   1.302 -				
   1.303 -				/* Remove the category from the list, then delete it. */
   1.304 -				m_Categories.erase(m_Categories.iterAt(i));
   1.305 -				delete cat;
   1.306 -				break;
   1.307 -			}
   1.308 -		}
   1.309 -
   1.310 -		/* Update the root as changed. */
   1.311 -		m_SerialNo++;
   1.312 -		m_bCatsNeedResort = true;
   1.313 -	}
   1.314 -	else if (obj->type == TopMenuObject_Item)
   1.315 -	{
   1.316 -		/* Find the category this item is in. */
   1.317 -		topmenu_category_t *parent_cat = NULL;
   1.318 -		for (size_t i = 0; i < m_Categories.size(); i++)
   1.319 -		{
   1.320 -			if (m_Categories[i]->obj == obj->parent)
   1.321 -			{
   1.322 -				parent_cat = m_Categories[i];
   1.323 -				break;
   1.324 -			}
   1.325 -		}
   1.326 -
   1.327 -		/* Erase it from the category's lists. */
   1.328 -		if (parent_cat)
   1.329 -		{
   1.330 -			for (size_t i = 0; i < parent_cat->obj_list.size(); i++)
   1.331 -			{
   1.332 -				if (parent_cat->obj_list[i] == obj)
   1.333 -				{
   1.334 -					parent_cat->obj_list.erase(parent_cat->obj_list.iterAt(i));
   1.335 -
   1.336 -					/* If this category now has no items, mark root as changed 
   1.337 -					 * so clients won't get the category drawn anymore.
   1.338 -					 */
   1.339 -					if (parent_cat->obj_list.size() == 0)
   1.340 -					{
   1.341 -						m_SerialNo++;
   1.342 -					}
   1.343 -					break;
   1.344 -				}
   1.345 -			}
   1.346 -
   1.347 -			/* Update the category as changed. */
   1.348 -			parent_cat->reorder = true;
   1.349 -			parent_cat->serial++;
   1.350 -		}
   1.351 -	}
   1.352 -
   1.353 -	/* The callbacks pointer is still valid, so fire away! */
   1.354 -	obj->callbacks->OnTopMenuObjectRemoved(this, object_id);
   1.355 -
   1.356 -	/* Finally, mark the object as free. */
   1.357 -	obj->is_free = true;
   1.358 -}
   1.359 -
   1.360 -bool TopMenu::DisplayMenu(int client, unsigned int hold_time, TopMenuPosition position)
   1.361 -{
   1.362 -	if (m_clients == NULL)
   1.363 -	{
   1.364 -		return false;
   1.365 -	}
   1.366 -
   1.367 -	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
   1.368 -	if (!pPlayer->IsInGame())
   1.369 -	{
   1.370 -		return false;
   1.371 -	}
   1.372 -
   1.373 -	UpdateClientRoot(client, pPlayer);
   1.374 -
   1.375 -	topmenu_player_t *pClient = &m_clients[client];
   1.376 -	if (pClient->root == NULL)
   1.377 -	{
   1.378 -		return false;
   1.379 -	}
   1.380 -
   1.381 -	bool return_value = false;
   1.382 -
   1.383 -	if (position == TopMenuPosition_LastCategory && 
   1.384 -		pClient->last_category < m_Categories.size())
   1.385 -	{
   1.386 -		return_value = DisplayCategory(client, pClient->last_category, hold_time, true);
   1.387 -		if (!return_value)
   1.388 -		{
   1.389 -			return_value = pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
   1.390 -		}
   1.391 -	}
   1.392 -	else if (position == TopMenuPosition_LastRoot)
   1.393 -	{
   1.394 -		pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
   1.395 -	}
   1.396 -	else if (position == TopMenuPosition_Start)
   1.397 -	{
   1.398 -		pClient->last_position = 0;
   1.399 -		pClient->last_category = 0;
   1.400 -		return_value = pClient->root->Display(client, hold_time);
   1.401 -	}
   1.402 -
   1.403 -	return return_value;
   1.404 -}
   1.405 -
   1.406 -bool TopMenu::DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position)
   1.407 -{
   1.408 -	UpdateClientCategory(client, category);
   1.409 -
   1.410 -	topmenu_player_t *pClient = &m_clients[client];
   1.411 -	if (category >= pClient->cat_count
   1.412 -		|| pClient->cats[category].menu == NULL)
   1.413 -	{
   1.414 -		return false;
   1.415 -	}
   1.416 -
   1.417 -	bool return_value = false;
   1.418 -
   1.419 -	topmenu_player_category_t *player_cat = &(pClient->cats[category]);
   1.420 -
   1.421 -	pClient->last_category = category;
   1.422 -	if (last_position)
   1.423 -	{
   1.424 -		return_value = player_cat->menu->DisplayAtItem(client, hold_time, pClient->last_position);
   1.425 -	}
   1.426 -	else
   1.427 -	{
   1.428 -		return_value = player_cat->menu->Display(client, hold_time);
   1.429 -	}
   1.430 -
   1.431 -	return return_value;
   1.432 -}
   1.433 -
   1.434 -void TopMenu::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
   1.435 -{
   1.436 -	const char *item_name = menu->GetItemInfo(item, NULL);
   1.437 -	if (!item_name)
   1.438 -	{
   1.439 -		return;
   1.440 -	}
   1.441 -
   1.442 -	topmenu_object_t *obj;
   1.443 -	topmenu_player_t *pClient = &m_clients[client];
   1.444 -	topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
   1.445 -	if (pObject == NULL)
   1.446 -	{
   1.447 -		return;
   1.448 -	}
   1.449 -
   1.450 -	obj = *pObject;
   1.451 -
   1.452 -	/* We now have the object... what do we do with it? */
   1.453 -	if (obj->type == TopMenuObject_Category)
   1.454 -	{
   1.455 -		/* If it's a category, the user wants to view it.. */
   1.456 -		for (size_t i = 0; i < m_Categories.size(); i++)
   1.457 -		{
   1.458 -			if (m_Categories[i]->obj == obj)
   1.459 -			{
   1.460 -				pClient->last_root_pos = item_on_page;
   1.461 -				if (!DisplayCategory(client, (unsigned int)i, MENU_TIME_FOREVER, false))
   1.462 -				{
   1.463 -					/* If we can't display the category, re-display the root menu */
   1.464 -					DisplayMenu(client, MENU_TIME_FOREVER, TopMenuPosition_LastRoot);
   1.465 -				}
   1.466 -				break;
   1.467 -			}
   1.468 -		}
   1.469 -	}
   1.470 -	else
   1.471 -	{
   1.472 -		pClient->last_position = item_on_page;
   1.473 -		
   1.474 -		/* Re-check access in case this user had their credentials revoked */
   1.475 -		if (obj->cmdname[0] != '\0' && !adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
   1.476 -		{
   1.477 -			DisplayMenu(client, 0, TopMenuPosition_LastCategory);
   1.478 -			return;
   1.479 -		}
   1.480 -		
   1.481 -		/* Pass the information on to the callback */
   1.482 -		obj->callbacks->OnTopMenuSelectOption(this, client, obj->object_id);
   1.483 -	}
   1.484 -}
   1.485 -
   1.486 -void TopMenu::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
   1.487 -{
   1.488 -	const char *item_name = menu->GetItemInfo(item, NULL);
   1.489 -	if (!item_name)
   1.490 -	{
   1.491 -		return;
   1.492 -	}
   1.493 -
   1.494 -	topmenu_object_t *obj;
   1.495 -	topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
   1.496 -	if (pObject == NULL)
   1.497 -	{
   1.498 -		return;
   1.499 -	}
   1.500 -
   1.501 -	obj = *pObject;
   1.502 -
   1.503 -	style = obj->callbacks->OnTopMenuDrawOption(this, client, obj->object_id);
   1.504 -	if (style != ITEMDRAW_DEFAULT)
   1.505 -	{
   1.506 -		return;
   1.507 -	}
   1.508 -
   1.509 -	if (obj->cmdname[0] == '\0')
   1.510 -	{
   1.511 -		return;
   1.512 -	}
   1.513 -
   1.514 -	if (!adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
   1.515 -	{
   1.516 -		style = ITEMDRAW_IGNORE;
   1.517 -	}
   1.518 -}
   1.519 -
   1.520 -unsigned int TopMenu::OnMenuDisplayItem(IBaseMenu *menu,
   1.521 -								int client,
   1.522 -								IMenuPanel *panel,
   1.523 -								unsigned int item,
   1.524 -								const ItemDrawInfo &dr)
   1.525 -{
   1.526 -	const char *item_name = menu->GetItemInfo(item, NULL);
   1.527 -	if (!item_name)
   1.528 -	{
   1.529 -		return 0;
   1.530 -	}
   1.531 -
   1.532 -	topmenu_object_t *obj;
   1.533 -	topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
   1.534 -	if (pObject == NULL)
   1.535 -	{
   1.536 -		return 0;
   1.537 -	}
   1.538 -
   1.539 -	obj = *pObject;
   1.540 -
   1.541 -	/* Ask the object to render the text for this client */
   1.542 -	char renderbuf[64];
   1.543 -	obj->callbacks->OnTopMenuDisplayOption(this, client, obj->object_id, renderbuf, sizeof(renderbuf));
   1.544 -
   1.545 -	/* Build the new draw info */
   1.546 -	ItemDrawInfo new_dr = dr;
   1.547 -	new_dr.display = renderbuf;
   1.548 -
   1.549 -	/* Man I love the menu API.  Ask the panel to draw the item and give the position 
   1.550 -	 * back to Core's renderer.  This way we don't have to worry about keeping the 
   1.551 -	 * render buffer static!
   1.552 -	 */
   1.553 -	return panel->DrawItem(new_dr);
   1.554 -}
   1.555 -
   1.556 -void TopMenu::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   1.557 -{
   1.558 -	if (reason == MenuCancel_ExitBack)
   1.559 -	{
   1.560 -		/* If the client chose exit back, they were on a category menu, and we can
   1.561 -		 * now display the root menu from the last known position.
   1.562 -		 */
   1.563 -		DisplayMenu(client, 0, TopMenuPosition_LastRoot);
   1.564 -	}
   1.565 -}
   1.566 -
   1.567 -void TopMenu::UpdateClientRoot(int client, IGamePlayer *pGamePlayer)
   1.568 -{
   1.569 -	topmenu_player_t *pClient = &m_clients[client];
   1.570 -	IGamePlayer *pPlayer = pGamePlayer ? pGamePlayer : playerhelpers->GetGamePlayer(client);
   1.571 -
   1.572 -	/* Determine if an update is necessary */
   1.573 -	bool is_update_needed = false;
   1.574 -	if (pClient->menu_serial != m_SerialNo)
   1.575 -	{
   1.576 -		is_update_needed = true;
   1.577 -	}
   1.578 -	else if (pPlayer->GetUserId() != pClient->user_id)
   1.579 -	{
   1.580 -		is_update_needed = true;
   1.581 -	}
   1.582 -
   1.583 -	/* If no update is needed at the root level, just leave now */
   1.584 -	if (!is_update_needed)
   1.585 -	{
   1.586 -		return;
   1.587 -	}
   1.588 -
   1.589 -	/* First we need to flush the cache... */
   1.590 -	TearDownClient(pClient);
   1.591 -
   1.592 -	/* Now, rebuild the category list, but don't create menus */
   1.593 -	if (m_Categories.size() == 0)
   1.594 -	{
   1.595 -		pClient->cat_count = 0;
   1.596 -		pClient->cats = NULL;
   1.597 -	}
   1.598 -	else
   1.599 -	{
   1.600 -		pClient->cat_count = (unsigned int)m_Categories.size();
   1.601 -		pClient->cats = new topmenu_player_category_t[pClient->cat_count];
   1.602 -		memset(pClient->cats, 0, sizeof(topmenu_player_category_t) * pClient->cat_count);
   1.603 -	}
   1.604 -
   1.605 -	/* Re-sort the root categories if needed */
   1.606 -	SortCategoriesIfNeeded();
   1.607 -
   1.608 -	/* Build the root menu */
   1.609 -	IBaseMenu *root_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
   1.610 -
   1.611 -	/* Add the sorted items */
   1.612 -	for (size_t i = 0; i < m_SortedCats.size(); i++)
   1.613 -	{
   1.614 -		if (m_Categories[m_SortedCats[i]]->obj_list.size() == 0)
   1.615 -		{
   1.616 -			continue;
   1.617 -		}
   1.618 -		root_menu->AppendItem(m_Categories[m_SortedCats[i]]->obj->name, ItemDrawInfo(""));
   1.619 -	}
   1.620 -
   1.621 -	/* Now we need to handle un-sorted items.  This is slightly trickier, as we need to 
   1.622 -	 * pre-render each category name, and cache those names.  Phew!
   1.623 -	 */
   1.624 -	if (m_UnsortedCats.size())
   1.625 -	{
   1.626 -		obj_by_name_t *item_list = new obj_by_name_t[m_UnsortedCats.size()];
   1.627 -		for (size_t i = 0; i < m_UnsortedCats.size(); i++)
   1.628 -		{
   1.629 -			obj_by_name_t *temp_obj = &item_list[i];
   1.630 -			topmenu_object_t *obj = m_Categories[m_UnsortedCats[i]]->obj;
   1.631 -			obj->callbacks->OnTopMenuDisplayOption(this,
   1.632 -				client, 
   1.633 -				obj->object_id,
   1.634 -				temp_obj->name,
   1.635 -				sizeof(temp_obj->name));
   1.636 -			temp_obj->obj_index = m_UnsortedCats[i];
   1.637 -		}
   1.638 -
   1.639 -		/* Sort our temp list */
   1.640 -		qsort(item_list, m_UnsortedCats.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
   1.641 -
   1.642 -		/* Add the new sorted categories */
   1.643 -		for (size_t i = 0; i < m_UnsortedCats.size(); i++)
   1.644 -		{
   1.645 -			if (m_Categories[item_list[i].obj_index]->obj_list.size() == 0)
   1.646 -			{
   1.647 -				continue;
   1.648 -			}
   1.649 -			root_menu->AppendItem(m_Categories[item_list[i].obj_index]->obj->name, ItemDrawInfo(""));
   1.650 -		}
   1.651 -
   1.652 -		delete [] item_list;
   1.653 -	}
   1.654 -
   1.655 -	/* Set the menu's title */
   1.656 -	char renderbuf[128];
   1.657 -	m_pTitle->OnTopMenuDisplayTitle(this, client, 0, renderbuf, sizeof(renderbuf));
   1.658 -	root_menu->SetDefaultTitle(renderbuf);
   1.659 -
   1.660 -	/* The client is now fully updated */
   1.661 -	pClient->root = root_menu;
   1.662 -	pClient->user_id = pPlayer->GetUserId();
   1.663 -	pClient->menu_serial = m_SerialNo;
   1.664 -	pClient->last_position = 0;
   1.665 -	pClient->last_category = 0;
   1.666 -	pClient->last_root_pos = 0;
   1.667 -}
   1.668 -
   1.669 -void TopMenu::UpdateClientCategory(int client, unsigned int category)
   1.670 -{
   1.671 -	/* Update the client's root menu just in case it needs it.  This 
   1.672 -	 * will validate that we have both a valid client and a valid
   1.673 -	 * category structure for that client.
   1.674 -	 */
   1.675 -	UpdateClientRoot(client);
   1.676 -
   1.677 -	/* Now it's guaranteed that our category tables will be usable */
   1.678 -	topmenu_player_t *pClient = &m_clients[client];
   1.679 -	topmenu_category_t *cat = m_Categories[category];
   1.680 -	topmenu_player_category_t *player_cat = &(pClient->cats[category]);
   1.681 -
   1.682 -	/* Does the category actually need updating? */
   1.683 -	if (player_cat->serial == cat->serial)
   1.684 -	{
   1.685 -		return;
   1.686 -	}
   1.687 -
   1.688 -	/* Destroy any existing menu */
   1.689 -	if (player_cat->menu)
   1.690 -	{
   1.691 -		player_cat->menu->Destroy();
   1.692 -		player_cat->menu = NULL;
   1.693 -	}
   1.694 -
   1.695 -	if (pClient->last_category == category)
   1.696 -	{
   1.697 -		pClient->last_position = 0;
   1.698 -	}
   1.699 -
   1.700 -	IBaseMenu *cat_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
   1.701 -	
   1.702 -	/* Categories get an "exit back" button */
   1.703 -	cat_menu->SetMenuOptionFlags(cat_menu->GetMenuOptionFlags() | MENUFLAG_BUTTON_EXITBACK);
   1.704 -
   1.705 -	/* Re-sort the category if needed */
   1.706 -	SortCategoryIfNeeded(category);
   1.707 -
   1.708 -	/* Build the menu with the sorted items first */
   1.709 -	for (size_t i = 0; i < cat->sorted.size(); i++)
   1.710 -	{
   1.711 -		cat_menu->AppendItem(cat->sorted[i]->name, ItemDrawInfo(""));
   1.712 -	}
   1.713 -
   1.714 -	/* Now handle unsorted items */
   1.715 -	if (cat->unsorted.size())
   1.716 -	{
   1.717 -		/* Build a list of the item names */
   1.718 -		obj_by_name_t *item_list = new obj_by_name_t[cat->unsorted.size()];
   1.719 -		for (size_t i = 0; i < cat->unsorted.size(); i++)
   1.720 -		{
   1.721 -			obj_by_name_t *item = &item_list[i];
   1.722 -			topmenu_object_t *obj = cat->unsorted[i];
   1.723 -			obj->callbacks->OnTopMenuDisplayOption(this,
   1.724 -				client,
   1.725 -				obj->object_id,
   1.726 -				item->name,
   1.727 -				sizeof(item->name));
   1.728 -			item->obj_index = (unsigned int)i;
   1.729 -		}
   1.730 -
   1.731 -		/* Sort the names */
   1.732 -		qsort(item_list, cat->unsorted.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
   1.733 -
   1.734 -		/* Add to the menu */
   1.735 -		for (size_t i = 0; i < cat->unsorted.size(); i++)
   1.736 -		{
   1.737 -			cat_menu->AppendItem(cat->unsorted[item_list[i].obj_index]->name, ItemDrawInfo(""));
   1.738 -		}
   1.739 -
   1.740 -		delete [] item_list;
   1.741 -	}
   1.742 -
   1.743 -	/* Set the menu's title */
   1.744 -	char renderbuf[128];
   1.745 -	cat->obj->callbacks->OnTopMenuDisplayTitle(this, 
   1.746 -		client, 
   1.747 -		cat->obj->object_id, 
   1.748 -		renderbuf, 
   1.749 -		sizeof(renderbuf));
   1.750 -	cat_menu->SetDefaultTitle(renderbuf);
   1.751 -
   1.752 -	/* We're done! */
   1.753 -	player_cat->menu = cat_menu;
   1.754 -	player_cat->serial = cat->serial;
   1.755 -}
   1.756 -
   1.757 -void TopMenu::SortCategoryIfNeeded(unsigned int category)
   1.758 -{
   1.759 -	topmenu_category_t *cat = m_Categories[category];
   1.760 -	if (!cat->reorder)
   1.761 -	{
   1.762 -		return;
   1.763 -	}
   1.764 -
   1.765 -	cat->sorted.clear();
   1.766 -	cat->unsorted.clear();
   1.767 -
   1.768 -	if (cat->obj_list.size() == 0)
   1.769 -	{
   1.770 -		cat->reorder = false;
   1.771 -		return;
   1.772 -	}
   1.773 -
   1.774 -	CVector<unsigned int> to_sort;
   1.775 -	for (size_t i = 0; i < cat->obj_list.size(); i++)
   1.776 -	{
   1.777 -		to_sort.push_back(i);
   1.778 -	}
   1.779 -
   1.780 -	/* Find a matching category in the configs */
   1.781 -	config_category_t *config_cat = NULL; 
   1.782 -	for (size_t i = 0; i < m_Config.cats.size(); i++)
   1.783 -	{
   1.784 -		if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), cat->obj->name) == 0)
   1.785 -		{
   1.786 -			config_cat = m_Config.cats[i];
   1.787 -			break;
   1.788 -		}
   1.789 -	}
   1.790 -
   1.791 -	/* If there is a matching category, build a pre-sorted item list */
   1.792 -	if (config_cat != NULL)
   1.793 -	{
   1.794 -		/* Find matching objects in this category */
   1.795 -		for (size_t i = 0; i < config_cat->commands.size(); i++)
   1.796 -		{
   1.797 -			const char *config_name = m_Config.strings.GetString(config_cat->commands[i]);
   1.798 -			for (size_t j = 0; j < to_sort.size(); j++)
   1.799 -			{
   1.800 -				if (strcmp(config_name, cat->obj_list[to_sort[j]]->name) == 0)
   1.801 -				{
   1.802 -					/* Place in the final list, then remove from the temporary list */
   1.803 -					cat->sorted.push_back(cat->obj_list[to_sort[j]]);
   1.804 -					to_sort.erase(to_sort.iterAt(j));
   1.805 -					break;
   1.806 -				}
   1.807 -			}
   1.808 -		}
   1.809 -	}
   1.810 -
   1.811 -	/* Push any remaining items onto the unsorted list */
   1.812 -	for (size_t i = 0; i < to_sort.size(); i++)
   1.813 -	{
   1.814 -		cat->unsorted.push_back(cat->obj_list[to_sort[i]]);
   1.815 -	}
   1.816 -
   1.817 -	cat->reorder = false;
   1.818 -}
   1.819 -
   1.820 -void TopMenu::SortCategoriesIfNeeded()
   1.821 -{
   1.822 -	if (!m_bCatsNeedResort)
   1.823 -	{
   1.824 -		return;
   1.825 -	}
   1.826 -
   1.827 -	/* Clear sort results */
   1.828 -	m_SortedCats.clear();
   1.829 -	m_UnsortedCats.clear();
   1.830 -
   1.831 -	if (m_Categories.size() == 0)
   1.832 -	{
   1.833 -		m_bCatsNeedResort = false;
   1.834 -		return;
   1.835 -	}
   1.836 -
   1.837 -	CVector<unsigned int> to_sort;
   1.838 -	for (unsigned int i = 0; i < (unsigned int)m_Categories.size(); i++)
   1.839 -	{
   1.840 -		to_sort.push_back(i);
   1.841 -	}
   1.842 -
   1.843 -	/* If we have any predefined categories, add them in as they appear. */
   1.844 -	for (size_t i= 0; i < m_Config.cats.size(); i++)
   1.845 -	{
   1.846 -		/* Find this category and map it in if we can */
   1.847 -		for (size_t j = 0; j < to_sort.size(); j++)
   1.848 -		{
   1.849 -			if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), 
   1.850 -					   m_Categories[to_sort[j]]->obj->name) == 0)
   1.851 -			{
   1.852 -				/* Add to the real list and remove from the temporary */
   1.853 -				m_SortedCats.push_back(to_sort[j]);
   1.854 -				to_sort.erase(to_sort.iterAt(j));
   1.855 -				break;
   1.856 -			}
   1.857 -		}
   1.858 -	}
   1.859 -
   1.860 -	/* Push any remaining items onto the unsorted list */
   1.861 -	for (size_t i = 0; i < to_sort.size(); i++)
   1.862 -	{
   1.863 -		m_UnsortedCats.push_back(to_sort[i]);
   1.864 -	}
   1.865 -
   1.866 -	m_bCatsNeedResort = false;
   1.867 -}
   1.868 -
   1.869 -void TopMenu::CreatePlayers(int max_clients)
   1.870 -{
   1.871 -	m_max_clients = max_clients;
   1.872 -	m_clients = (topmenu_player_t *)malloc(sizeof(topmenu_player_t) * (max_clients + 1));
   1.873 -	memset(m_clients, 0, sizeof(topmenu_player_t) * (max_clients + 1));
   1.874 -}
   1.875 -
   1.876 -void TopMenu::TearDownClient(topmenu_player_t *player)
   1.877 -{
   1.878 -	if (player->cats != NULL)
   1.879 -	{
   1.880 -		for (unsigned int i = 0; i < player->cat_count; i++)
   1.881 -		{
   1.882 -			topmenu_player_category_t *player_cat = &(player->cats[i]);
   1.883 -			if (player_cat->menu != NULL)
   1.884 -			{
   1.885 -				player_cat->menu->Destroy();
   1.886 -			}
   1.887 -		}
   1.888 -		delete [] player->cats;
   1.889 -	}
   1.890 -
   1.891 -	if (player->root != NULL)
   1.892 -	{
   1.893 -		player->root->Destroy();
   1.894 -	}
   1.895 -
   1.896 -	memset(player, 0, sizeof(topmenu_player_t));
   1.897 -}
   1.898 -
   1.899 -bool TopMenu::LoadConfiguration(const char *file, char *error, size_t maxlength)
   1.900 -{
   1.901 -	SMCError err;
   1.902 -	SMCStates states;
   1.903 -
   1.904 -	if ((err = textparsers->ParseFile_SMC(file, this, &states))
   1.905 -		!= SMCError_Okay)
   1.906 -	{
   1.907 -		const char *err_string = textparsers->GetSMCErrorString(err);
   1.908 -		if (!err_string)
   1.909 -		{
   1.910 -			err_string = "Unknown";
   1.911 -		}
   1.912 -
   1.913 -		UTIL_Format(error, maxlength, "%s", err_string);
   1.914 -
   1.915 -		return false;
   1.916 -	}
   1.917 -
   1.918 -	return true;
   1.919 -}
   1.920 -
   1.921 -bool TopMenu::OnIdentityRemoval(IdentityToken_t *owner)
   1.922 -{
   1.923 -	/* First sweep the categories owned by us */
   1.924 -	CVector<unsigned int> obj_list;
   1.925 -	for (size_t i = 0; i < m_Categories.size(); i++)
   1.926 -	{
   1.927 -		if (m_Categories[i]->obj->owner == owner)
   1.928 -		{
   1.929 -			obj_list.push_back(m_Categories[i]->obj->object_id);
   1.930 -		}
   1.931 -	}
   1.932 -
   1.933 -	for (size_t i = 0; i < obj_list.size(); i++)
   1.934 -	{
   1.935 -		RemoveFromMenu(obj_list[i]);
   1.936 -	}
   1.937 -
   1.938 -	/* Now we can look for actual items */
   1.939 -	for (size_t i = 0; i < m_Objects.size(); i++)
   1.940 -	{
   1.941 -		if (m_Objects[i]->is_free)
   1.942 -		{
   1.943 -			continue;
   1.944 -		}
   1.945 -		if (m_Objects[i]->owner == owner)
   1.946 -		{
   1.947 -			assert(m_Objects[i]->type != TopMenuObject_Category);
   1.948 -			RemoveFromMenu(m_Objects[i]->object_id);
   1.949 -		}
   1.950 -	}
   1.951 -
   1.952 -	return true;
   1.953 -}
   1.954 -
   1.955 -#define PARSE_STATE_NONE		0
   1.956 -#define PARSE_STATE_MAIN		1
   1.957 -#define PARSE_STATE_CATEGORY	2
   1.958 -unsigned int ignore_parse_level = 0;
   1.959 -unsigned int current_parse_state = 0;
   1.960 -config_category_t *cur_cat = NULL;
   1.961 -
   1.962 -void TopMenu::ReadSMC_ParseStart()
   1.963 -{
   1.964 -	current_parse_state = PARSE_STATE_NONE;
   1.965 -	ignore_parse_level = 0;
   1.966 -	cur_cat = NULL;
   1.967 -
   1.968 -	/* Reset the old config */
   1.969 -	m_Config.strings.Reset();
   1.970 -	for (size_t i = 0; i < m_Config.cats.size(); i++)
   1.971 -	{
   1.972 -		delete m_Config.cats[i];
   1.973 -	}
   1.974 -	m_Config.cats.clear();
   1.975 -}
   1.976 -
   1.977 -SMCResult TopMenu::ReadSMC_NewSection(const SMCStates *states, const char *name)
   1.978 -{
   1.979 -	if (ignore_parse_level)
   1.980 -	{
   1.981 -		ignore_parse_level++;
   1.982 -	}
   1.983 -	else
   1.984 -	{
   1.985 -		if (current_parse_state == PARSE_STATE_NONE)
   1.986 -		{
   1.987 -			if (strcmp(name, "Menu") == 0)
   1.988 -			{
   1.989 -				current_parse_state = PARSE_STATE_MAIN;
   1.990 -			}
   1.991 -			else
   1.992 -			{
   1.993 -				ignore_parse_level = 1;
   1.994 -			}
   1.995 -		}
   1.996 -		else if (current_parse_state == PARSE_STATE_MAIN)
   1.997 -		{
   1.998 -			cur_cat = new config_category_t;
   1.999 -			cur_cat->name = m_Config.strings.AddString(name);
  1.1000 -			m_Config.cats.push_back(cur_cat);
  1.1001 -			current_parse_state = PARSE_STATE_CATEGORY;
  1.1002 -		}
  1.1003 -		else
  1.1004 -		{
  1.1005 -			ignore_parse_level = 1;
  1.1006 -		}
  1.1007 -	}
  1.1008 -
  1.1009 -	return SMCResult_Continue;
  1.1010 -}
  1.1011 -
  1.1012 -SMCResult TopMenu::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
  1.1013 -{
  1.1014 -	if (ignore_parse_level > 0
  1.1015 -		|| current_parse_state != PARSE_STATE_CATEGORY
  1.1016 -		|| cur_cat == NULL)
  1.1017 -	{
  1.1018 -		return SMCResult_Continue;
  1.1019 -	}
  1.1020 -
  1.1021 -	if (strcmp(key, "item") == 0)
  1.1022 -	{
  1.1023 -		cur_cat->commands.push_back(m_Config.strings.AddString(value));
  1.1024 -	}
  1.1025 -
  1.1026 -	return SMCResult_Continue;
  1.1027 -}
  1.1028 -
  1.1029 -SMCResult TopMenu::ReadSMC_LeavingSection(const SMCStates *states)
  1.1030 -{
  1.1031 -	if (ignore_parse_level)
  1.1032 -	{
  1.1033 -		ignore_parse_level--;
  1.1034 -	}
  1.1035 -	else
  1.1036 -	{
  1.1037 -		if (current_parse_state == PARSE_STATE_CATEGORY)
  1.1038 -		{
  1.1039 -			cur_cat = NULL;
  1.1040 -			current_parse_state = PARSE_STATE_MAIN;
  1.1041 -		}
  1.1042 -		else if (current_parse_state == PARSE_STATE_MAIN)
  1.1043 -		{
  1.1044 -			current_parse_state = PARSE_STATE_NONE;
  1.1045 -		}
  1.1046 -	}
  1.1047 -
  1.1048 -	return SMCResult_Continue;
  1.1049 -}
  1.1050 -
  1.1051 -unsigned int TopMenu::FindCategory(const char *name)
  1.1052 -{
  1.1053 -	topmenu_object_t **p_obj = m_ObjLookup.retrieve(name);
  1.1054 -	if (!p_obj)
  1.1055 -	{
  1.1056 -		return 0;
  1.1057 -	}
  1.1058 -
  1.1059 -	topmenu_object_t *obj = *p_obj;
  1.1060 -	if (obj->type != TopMenuObject_Category)
  1.1061 -	{
  1.1062 -		return 0;
  1.1063 -	}
  1.1064 -
  1.1065 -	return obj->object_id;
  1.1066 -}
  1.1067 -
  1.1068 -int _SortObjectNamesDescending(const void *ptr1, const void *ptr2)
  1.1069 -{
  1.1070 -	obj_by_name_t *obj1 = (obj_by_name_t *)ptr1;
  1.1071 -	obj_by_name_t *obj2 = (obj_by_name_t *)ptr2;
  1.1072 -	return strcmp(obj1->name, obj2->name);
  1.1073 -}
  1.1074 -
  1.1075 -unsigned int strncopy(char *dest, const char *src, size_t count)
  1.1076 -{
  1.1077 -	if (!count)
  1.1078 -	{
  1.1079 -		return 0;
  1.1080 -	}
  1.1081 -
  1.1082 -	char *start = dest;
  1.1083 -	while ((*src) && (--count))
  1.1084 -	{
  1.1085 -		*dest++ = *src++;
  1.1086 -	}
  1.1087 -	*dest = '\0';
  1.1088 -
  1.1089 -	return (dest - start);
  1.1090 -}
  1.1091 -
  1.1092 -size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
  1.1093 -{
  1.1094 -	va_list ap;
  1.1095 -	va_start(ap, fmt);
  1.1096 -	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1.1097 -	va_end(ap);
  1.1098 -
  1.1099 -	if (len >= maxlength)
  1.1100 -	{
  1.1101 -		buffer[maxlength - 1] = '\0';
  1.1102 -		return (maxlength - 1);
  1.1103 -	}
  1.1104 -	else
  1.1105 -	{
  1.1106 -		return len;
  1.1107 -	}
  1.1108 -}
  1.1109 +/**
  1.1110 + * vim: set ts=4 :
  1.1111 + * =============================================================================
  1.1112 + * SourceMod Sample Extension
  1.1113 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
  1.1114 + * =============================================================================
  1.1115 + *
  1.1116 + * This program is free software; you can redistribute it and/or modify it under
  1.1117 + * the terms of the GNU General Public License, version 3.0, as published by the
  1.1118 + * Free Software Foundation.
  1.1119 + * 
  1.1120 + * This program is distributed in the hope that it will be useful, but WITHOUT
  1.1121 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  1.1122 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  1.1123 + * details.
  1.1124 + *
  1.1125 + * You should have received a copy of the GNU General Public License along with
  1.1126 + * this program.  If not, see <http://www.gnu.org/licenses/>.
  1.1127 + *
  1.1128 + * As a special exception, AlliedModders LLC gives you permission to link the
  1.1129 + * code of this program (as well as its derivative works) to "Half-Life 2," the
  1.1130 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
  1.1131 + * by the Valve Corporation.  You must obey the GNU General Public License in
  1.1132 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
  1.1133 + * this exception to all derivative works.  AlliedModders LLC defines further
  1.1134 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
  1.1135 + * or <http://www.sourcemod.net/license.php>.
  1.1136 + *
  1.1137 + * Version: $Id$
  1.1138 + */
  1.1139 +
  1.1140 +#include <stdlib.h>
  1.1141 +#include <stdarg.h>
  1.1142 +#include "TopMenu.h"
  1.1143 +
  1.1144 +struct obj_by_name_t
  1.1145 +{
  1.1146 +	unsigned int obj_index;
  1.1147 +	char name[64];
  1.1148 +};
  1.1149 +
  1.1150 +int _SortObjectNamesDescending(const void *ptr1, const void *ptr2);
  1.1151 +unsigned int strncopy(char *dest, const char *src, size_t count);
  1.1152 +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
  1.1153 +
  1.1154 +TopMenu::TopMenu(ITopMenuObjectCallbacks *callbacks)
  1.1155 +{
  1.1156 +	m_clients = NULL;
  1.1157 +	m_SerialNo = 1;
  1.1158 +	m_pTitle = callbacks;
  1.1159 +	m_max_clients = 0;
  1.1160 +
  1.1161 +	if (playerhelpers->IsServerActivated())
  1.1162 +	{
  1.1163 +		CreatePlayers(playerhelpers->GetMaxClients());
  1.1164 +	}
  1.1165 +}
  1.1166 +
  1.1167 +TopMenu::~TopMenu()
  1.1168 +{
  1.1169 +	/* Delete all categories */
  1.1170 +	while (m_Categories.size())
  1.1171 +	{
  1.1172 +		RemoveFromMenu(m_Categories[0]->obj->object_id);
  1.1173 +	}
  1.1174 +
  1.1175 +	/* Remove all objects */
  1.1176 +	for (size_t i = 0; i < m_Objects.size(); i++)
  1.1177 +	{
  1.1178 +		assert(m_Objects[i]->is_free == true);
  1.1179 +		delete m_Objects[i];
  1.1180 +	}
  1.1181 +
  1.1182 +	m_pTitle->OnTopMenuObjectRemoved(this, 0);
  1.1183 +
  1.1184 +	/* Delete all cached config entries */
  1.1185 +	for (size_t i = 0; i < m_Config.cats.size(); i++)
  1.1186 +	{
  1.1187 +		delete m_Config.cats[i];
  1.1188 +	}
  1.1189 +
  1.1190 +	/* Sweep players */
  1.1191 +	for (size_t i = 0; i <= (size_t)m_max_clients; i++)
  1.1192 +	{
  1.1193 +		TearDownClient(&m_clients[i]);
  1.1194 +	}
  1.1195 +	delete [] m_clients;
  1.1196 +}
  1.1197 +
  1.1198 +void TopMenu::OnClientConnected(int client)
  1.1199 +{
  1.1200 +	if (m_clients == NULL)
  1.1201 +	{
  1.1202 +		return;
  1.1203 +	}
  1.1204 +
  1.1205 +	topmenu_player_t *player = &m_clients[client];
  1.1206 +	TearDownClient(player);
  1.1207 +}
  1.1208 +
  1.1209 +void TopMenu::OnClientDisconnected(int client)
  1.1210 +{
  1.1211 +	if (m_clients == NULL)
  1.1212 +	{
  1.1213 +		return;
  1.1214 +	}
  1.1215 +
  1.1216 +	topmenu_player_t *player = &m_clients[client];
  1.1217 +	TearDownClient(player);
  1.1218 +}
  1.1219 +
  1.1220 +void TopMenu::OnServerActivated(int max_clients)
  1.1221 +{
  1.1222 +	if (m_clients == NULL)
  1.1223 +	{
  1.1224 +		CreatePlayers(max_clients);
  1.1225 +	}
  1.1226 +}
  1.1227 +
  1.1228 +unsigned int TopMenu::AddToMenu(const char *name,
  1.1229 +								TopMenuObjectType type,
  1.1230 +								ITopMenuObjectCallbacks *callbacks,
  1.1231 +								IdentityToken_t *owner,
  1.1232 +								const char *cmdname,
  1.1233 +								FlagBits flags,
  1.1234 +								unsigned int parent)
  1.1235 +{
  1.1236 +	return AddToMenu2(name, type, callbacks, owner, cmdname, flags, parent, NULL);
  1.1237 +}
  1.1238 +
  1.1239 +unsigned int TopMenu::AddToMenu2(const char *name,
  1.1240 +								 TopMenuObjectType type,
  1.1241 +								 ITopMenuObjectCallbacks *callbacks,
  1.1242 +								 IdentityToken_t *owner,
  1.1243 +								 const char *cmdname,
  1.1244 +								 FlagBits flags,
  1.1245 +								 unsigned int parent,
  1.1246 +								 const char *info_string)
  1.1247 +{
  1.1248 +	/* Sanity checks */
  1.1249 +	if (type == TopMenuObject_Category && parent != 0)
  1.1250 +	{
  1.1251 +		return 0;
  1.1252 +	}
  1.1253 +	else if (type == TopMenuObject_Item && parent == 0)
  1.1254 +	{
  1.1255 +		return 0;
  1.1256 +	}
  1.1257 +	else if (m_ObjLookup.retrieve(name) != NULL)
  1.1258 +	{
  1.1259 +		return 0;
  1.1260 +	}
  1.1261 +	else if (type != TopMenuObject_Item && type != TopMenuObject_Category)
  1.1262 +	{
  1.1263 +		return 0;
  1.1264 +	}
  1.1265 +
  1.1266 +	/* If we're adding an item, make sure the parent is valid, 
  1.1267 +	 * and that the parent is a category.
  1.1268 +	 */
  1.1269 +	topmenu_object_t *parent_obj = NULL;
  1.1270 +	topmenu_category_t *parent_cat = NULL;
  1.1271 +	if (type == TopMenuObject_Item)
  1.1272 +	{
  1.1273 +		/* Check parent index.  Note it will be >= 1 here. */
  1.1274 +		if (parent > m_Objects.size() || m_Objects[parent - 1]->is_free)
  1.1275 +		{
  1.1276 +			return 0;
  1.1277 +		}
  1.1278 +		parent_obj = m_Objects[parent - 1];
  1.1279 +
  1.1280 +		/* Find an equivalent pointer in the category array. */
  1.1281 +		for (size_t i = 0; i < m_Categories.size(); i++)
  1.1282 +		{
  1.1283 +			if (m_Categories[i]->obj == parent_obj)
  1.1284 +			{
  1.1285 +				parent_cat = m_Categories[i];
  1.1286 +				break;
  1.1287 +			}
  1.1288 +		}
  1.1289 +
  1.1290 +		/* If none was found, leave. */
  1.1291 +		if (parent_cat == NULL)
  1.1292 +		{
  1.1293 +			return 0;
  1.1294 +		}
  1.1295 +	}
  1.1296 +
  1.1297 +	/* Re-use an old object pointer if we can. */
  1.1298 +	topmenu_object_t *obj = NULL;
  1.1299 +	for (size_t i = 0; i < m_Objects.size(); i++)
  1.1300 +	{
  1.1301 +		if (m_Objects[i]->is_free == true)
  1.1302 +		{
  1.1303 +			obj = m_Objects[i];
  1.1304 +			break;
  1.1305 +		}
  1.1306 +	}
  1.1307 +
  1.1308 +	/* Otherwise, allocate a new one. */
  1.1309 +	if (obj == NULL)
  1.1310 +	{
  1.1311 +		obj = new topmenu_object_t;
  1.1312 +		obj->object_id = ((unsigned int)m_Objects.size()) + 1;
  1.1313 +		m_Objects.push_back(obj);
  1.1314 +	}
  1.1315 +
  1.1316 +	/* Initialize the object's properties. */
  1.1317 +	obj->callbacks = callbacks;
  1.1318 +	obj->flags = flags;
  1.1319 +	obj->owner = owner;
  1.1320 +	obj->type = type;
  1.1321 +	obj->is_free = false;
  1.1322 +	obj->parent = parent_obj;
  1.1323 +	strncopy(obj->name, name, sizeof(obj->name));
  1.1324 +	strncopy(obj->cmdname, cmdname ? cmdname : "", sizeof(obj->cmdname));
  1.1325 +	strncopy(obj->info, info_string ? info_string : "", sizeof(obj->info));
  1.1326 +
  1.1327 +	if (obj->type == TopMenuObject_Category)
  1.1328 +	{
  1.1329 +		/* Create a new category entry */
  1.1330 +		topmenu_category_t *cat = new topmenu_category_t;
  1.1331 +		cat->obj = obj;
  1.1332 +		cat->reorder = false;
  1.1333 +		cat->serial = 1;
  1.1334 +
  1.1335 +		/* Add it, then update our serial change number. */
  1.1336 +		m_Categories.push_back(cat);
  1.1337 +		m_SerialNo++;
  1.1338 +
  1.1339 +		/* Updating sorting info */
  1.1340 +		m_bCatsNeedResort = true;
  1.1341 +	}
  1.1342 +	else if (obj->type == TopMenuObject_Item)
  1.1343 +	{
  1.1344 +		/* Update the category, mark it as needing changes */
  1.1345 +		parent_cat->obj_list.push_back(obj);
  1.1346 +		parent_cat->reorder = true;
  1.1347 +		parent_cat->serial++;
  1.1348 +
  1.1349 +		/* If the category just went from 0 to 1 items, mark it as 
  1.1350 +		 * changed, so clients get the category drawn.
  1.1351 +		 */
  1.1352 +		if (parent_cat->obj_list.size() == 1)
  1.1353 +		{
  1.1354 +			m_SerialNo++;
  1.1355 +		}
  1.1356 +	}
  1.1357 +
  1.1358 +	m_ObjLookup.insert(name, obj);
  1.1359 +
  1.1360 +	return obj->object_id;
  1.1361 +}
  1.1362 +
  1.1363 +const char *TopMenu::GetObjectInfoString(unsigned int object_id)
  1.1364 +{
  1.1365 +	if (object_id == 0 
  1.1366 +		|| object_id > m_Objects.size() 
  1.1367 +		|| m_Objects[object_id - 1]->is_free)
  1.1368 +	{
  1.1369 +		return NULL;
  1.1370 +	}
  1.1371 +
  1.1372 +	topmenu_object_t *obj = m_Objects[object_id - 1];
  1.1373 +
  1.1374 +	return obj->info;
  1.1375 +}
  1.1376 +
  1.1377 +const char *TopMenu::GetObjectName(unsigned int object_id)
  1.1378 +{
  1.1379 +	if (object_id == 0 
  1.1380 +		|| object_id > m_Objects.size() 
  1.1381 +		|| m_Objects[object_id - 1]->is_free)
  1.1382 +	{
  1.1383 +		return NULL;
  1.1384 +	}
  1.1385 +
  1.1386 +	topmenu_object_t *obj = m_Objects[object_id - 1];
  1.1387 +
  1.1388 +	return obj->name;
  1.1389 +}
  1.1390 +
  1.1391 +void TopMenu::RemoveFromMenu(unsigned int object_id)
  1.1392 +{
  1.1393 +	if (object_id == 0 
  1.1394 +		|| object_id > m_Objects.size() 
  1.1395 +		|| m_Objects[object_id - 1]->is_free)
  1.1396 +	{
  1.1397 +		return;
  1.1398 +	}
  1.1399 +
  1.1400 +	topmenu_object_t *obj = m_Objects[object_id - 1];
  1.1401 +
  1.1402 +	m_ObjLookup.remove(obj->name);
  1.1403 +
  1.1404 +	if (obj->type == TopMenuObject_Category)
  1.1405 +	{
  1.1406 +		/* Find it in the category list. */
  1.1407 +		for (size_t i = 0; i < m_Categories.size(); i++)
  1.1408 +		{
  1.1409 +			if (m_Categories[i]->obj == obj)
  1.1410 +			{
  1.1411 +				/* Mark all children as removed + free.  Note we could
  1.1412 +				 * call into RemoveMenuItem() for this, but it'd be very 
  1.1413 +				 * inefficient!
  1.1414 +				 */
  1.1415 +				topmenu_category_t *cat = m_Categories[i];
  1.1416 +				for (size_t j = 0; j < m_Categories[i]->obj_list.size(); j++)
  1.1417 +				{
  1.1418 +					cat->obj_list[j]->callbacks->OnTopMenuObjectRemoved(this, cat->obj_list[j]->object_id);
  1.1419 +					cat->obj_list[j]->is_free = true;
  1.1420 +				}
  1.1421 +				
  1.1422 +				/* Remove the category from the list, then delete it. */
  1.1423 +				m_Categories.erase(m_Categories.iterAt(i));
  1.1424 +				delete cat;
  1.1425 +				break;
  1.1426 +			}
  1.1427 +		}
  1.1428 +
  1.1429 +		/* Update the root as changed. */
  1.1430 +		m_SerialNo++;
  1.1431 +		m_bCatsNeedResort = true;
  1.1432 +	}
  1.1433 +	else if (obj->type == TopMenuObject_Item)
  1.1434 +	{
  1.1435 +		/* Find the category this item is in. */
  1.1436 +		topmenu_category_t *parent_cat = NULL;
  1.1437 +		for (size_t i = 0; i < m_Categories.size(); i++)
  1.1438 +		{
  1.1439 +			if (m_Categories[i]->obj == obj->parent)
  1.1440 +			{
  1.1441 +				parent_cat = m_Categories[i];
  1.1442 +				break;
  1.1443 +			}
  1.1444 +		}
  1.1445 +
  1.1446 +		/* Erase it from the category's lists. */
  1.1447 +		if (parent_cat)
  1.1448 +		{
  1.1449 +			for (size_t i = 0; i < parent_cat->obj_list.size(); i++)
  1.1450 +			{
  1.1451 +				if (parent_cat->obj_list[i] == obj)
  1.1452 +				{
  1.1453 +					parent_cat->obj_list.erase(parent_cat->obj_list.iterAt(i));
  1.1454 +
  1.1455 +					/* If this category now has no items, mark root as changed 
  1.1456 +					 * so clients won't get the category drawn anymore.
  1.1457 +					 */
  1.1458 +					if (parent_cat->obj_list.size() == 0)
  1.1459 +					{
  1.1460 +						m_SerialNo++;
  1.1461 +					}
  1.1462 +					break;
  1.1463 +				}
  1.1464 +			}
  1.1465 +
  1.1466 +			/* Update the category as changed. */
  1.1467 +			parent_cat->reorder = true;
  1.1468 +			parent_cat->serial++;
  1.1469 +		}
  1.1470 +	}
  1.1471 +
  1.1472 +	/* The callbacks pointer is still valid, so fire away! */
  1.1473 +	obj->callbacks->OnTopMenuObjectRemoved(this, object_id);
  1.1474 +
  1.1475 +	/* Finally, mark the object as free. */
  1.1476 +	obj->is_free = true;
  1.1477 +}
  1.1478 +
  1.1479 +bool TopMenu::DisplayMenu(int client, unsigned int hold_time, TopMenuPosition position)
  1.1480 +{
  1.1481 +	if (m_clients == NULL)
  1.1482 +	{
  1.1483 +		return false;
  1.1484 +	}
  1.1485 +
  1.1486 +	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(client);
  1.1487 +	if (!pPlayer->IsInGame())
  1.1488 +	{
  1.1489 +		return false;
  1.1490 +	}
  1.1491 +
  1.1492 +	UpdateClientRoot(client, pPlayer);
  1.1493 +
  1.1494 +	topmenu_player_t *pClient = &m_clients[client];
  1.1495 +	if (pClient->root == NULL)
  1.1496 +	{
  1.1497 +		return false;
  1.1498 +	}
  1.1499 +
  1.1500 +	bool return_value = false;
  1.1501 +
  1.1502 +	if (position == TopMenuPosition_LastCategory && 
  1.1503 +		pClient->last_category < m_Categories.size())
  1.1504 +	{
  1.1505 +		return_value = DisplayCategory(client, pClient->last_category, hold_time, true);
  1.1506 +		if (!return_value)
  1.1507 +		{
  1.1508 +			return_value = pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
  1.1509 +		}
  1.1510 +	}
  1.1511 +	else if (position == TopMenuPosition_LastRoot)
  1.1512 +	{
  1.1513 +		pClient->root->DisplayAtItem(client, hold_time, pClient->last_root_pos);
  1.1514 +	}
  1.1515 +	else if (position == TopMenuPosition_Start)
  1.1516 +	{
  1.1517 +		pClient->last_position = 0;
  1.1518 +		pClient->last_category = 0;
  1.1519 +		return_value = pClient->root->Display(client, hold_time);
  1.1520 +	}
  1.1521 +
  1.1522 +	return return_value;
  1.1523 +}
  1.1524 +
  1.1525 +bool TopMenu::DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position)
  1.1526 +{
  1.1527 +	UpdateClientCategory(client, category);
  1.1528 +
  1.1529 +	topmenu_player_t *pClient = &m_clients[client];
  1.1530 +	if (category >= pClient->cat_count
  1.1531 +		|| pClient->cats[category].menu == NULL)
  1.1532 +	{
  1.1533 +		return false;
  1.1534 +	}
  1.1535 +
  1.1536 +	bool return_value = false;
  1.1537 +
  1.1538 +	topmenu_player_category_t *player_cat = &(pClient->cats[category]);
  1.1539 +
  1.1540 +	pClient->last_category = category;
  1.1541 +	if (last_position)
  1.1542 +	{
  1.1543 +		return_value = player_cat->menu->DisplayAtItem(client, hold_time, pClient->last_position);
  1.1544 +	}
  1.1545 +	else
  1.1546 +	{
  1.1547 +		return_value = player_cat->menu->Display(client, hold_time);
  1.1548 +	}
  1.1549 +
  1.1550 +	return return_value;
  1.1551 +}
  1.1552 +
  1.1553 +void TopMenu::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
  1.1554 +{
  1.1555 +	const char *item_name = menu->GetItemInfo(item, NULL);
  1.1556 +	if (!item_name)
  1.1557 +	{
  1.1558 +		return;
  1.1559 +	}
  1.1560 +
  1.1561 +	topmenu_object_t *obj;
  1.1562 +	topmenu_player_t *pClient = &m_clients[client];
  1.1563 +	topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
  1.1564 +	if (pObject == NULL)
  1.1565 +	{
  1.1566 +		return;
  1.1567 +	}
  1.1568 +
  1.1569 +	obj = *pObject;
  1.1570 +
  1.1571 +	/* We now have the object... what do we do with it? */
  1.1572 +	if (obj->type == TopMenuObject_Category)
  1.1573 +	{
  1.1574 +		/* If it's a category, the user wants to view it.. */
  1.1575 +		for (size_t i = 0; i < m_Categories.size(); i++)
  1.1576 +		{
  1.1577 +			if (m_Categories[i]->obj == obj)
  1.1578 +			{
  1.1579 +				pClient->last_root_pos = item_on_page;
  1.1580 +				if (!DisplayCategory(client, (unsigned int)i, MENU_TIME_FOREVER, false))
  1.1581 +				{
  1.1582 +					/* If we can't display the category, re-display the root menu */
  1.1583 +					DisplayMenu(client, MENU_TIME_FOREVER, TopMenuPosition_LastRoot);
  1.1584 +				}
  1.1585 +				break;
  1.1586 +			}
  1.1587 +		}
  1.1588 +	}
  1.1589 +	else
  1.1590 +	{
  1.1591 +		pClient->last_position = item_on_page;
  1.1592 +		
  1.1593 +		/* Re-check access in case this user had their credentials revoked */
  1.1594 +		if (obj->cmdname[0] != '\0' && !adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
  1.1595 +		{
  1.1596 +			DisplayMenu(client, 0, TopMenuPosition_LastCategory);
  1.1597 +			return;
  1.1598 +		}
  1.1599 +		
  1.1600 +		/* Pass the information on to the callback */
  1.1601 +		obj->callbacks->OnTopMenuSelectOption(this, client, obj->object_id);
  1.1602 +	}
  1.1603 +}
  1.1604 +
  1.1605 +void TopMenu::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
  1.1606 +{
  1.1607 +	const char *item_name = menu->GetItemInfo(item, NULL);
  1.1608 +	if (!item_name)
  1.1609 +	{
  1.1610 +		return;
  1.1611 +	}
  1.1612 +
  1.1613 +	topmenu_object_t *obj;
  1.1614 +	topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
  1.1615 +	if (pObject == NULL)
  1.1616 +	{
  1.1617 +		return;
  1.1618 +	}
  1.1619 +
  1.1620 +	obj = *pObject;
  1.1621 +
  1.1622 +	style = obj->callbacks->OnTopMenuDrawOption(this, client, obj->object_id);
  1.1623 +	if (style != ITEMDRAW_DEFAULT)
  1.1624 +	{
  1.1625 +		return;
  1.1626 +	}
  1.1627 +
  1.1628 +	if (obj->cmdname[0] == '\0')
  1.1629 +	{
  1.1630 +		return;
  1.1631 +	}
  1.1632 +
  1.1633 +	if (!adminsys->CheckAccess(client, obj->cmdname, obj->flags, false))
  1.1634 +	{
  1.1635 +		style = ITEMDRAW_IGNORE;
  1.1636 +	}
  1.1637 +}
  1.1638 +
  1.1639 +unsigned int TopMenu::OnMenuDisplayItem(IBaseMenu *menu,
  1.1640 +								int client,
  1.1641 +								IMenuPanel *panel,
  1.1642 +								unsigned int item,
  1.1643 +								const ItemDrawInfo &dr)
  1.1644 +{
  1.1645 +	const char *item_name = menu->GetItemInfo(item, NULL);
  1.1646 +	if (!item_name)
  1.1647 +	{
  1.1648 +		return 0;
  1.1649 +	}
  1.1650 +
  1.1651 +	topmenu_object_t *obj;
  1.1652 +	topmenu_object_t **pObject = m_ObjLookup.retrieve(item_name);
  1.1653 +	if (pObject == NULL)
  1.1654 +	{
  1.1655 +		return 0;
  1.1656 +	}
  1.1657 +
  1.1658 +	obj = *pObject;
  1.1659 +
  1.1660 +	/* Ask the object to render the text for this client */
  1.1661 +	char renderbuf[64];
  1.1662 +	obj->callbacks->OnTopMenuDisplayOption(this, client, obj->object_id, renderbuf, sizeof(renderbuf));
  1.1663 +
  1.1664 +	/* Build the new draw info */
  1.1665 +	ItemDrawInfo new_dr = dr;
  1.1666 +	new_dr.display = renderbuf;
  1.1667 +
  1.1668 +	/* Man I love the menu API.  Ask the panel to draw the item and give the position 
  1.1669 +	 * back to Core's renderer.  This way we don't have to worry about keeping the 
  1.1670 +	 * render buffer static!
  1.1671 +	 */
  1.1672 +	return panel->DrawItem(new_dr);
  1.1673 +}
  1.1674 +
  1.1675 +void TopMenu::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
  1.1676 +{
  1.1677 +	if (reason == MenuCancel_ExitBack)
  1.1678 +	{
  1.1679 +		/* If the client chose exit back, they were on a category menu, and we can
  1.1680 +		 * now display the root menu from the last known position.
  1.1681 +		 */
  1.1682 +		DisplayMenu(client, 0, TopMenuPosition_LastRoot);
  1.1683 +	}
  1.1684 +}
  1.1685 +
  1.1686 +void TopMenu::UpdateClientRoot(int client, IGamePlayer *pGamePlayer)
  1.1687 +{
  1.1688 +	topmenu_player_t *pClient = &m_clients[client];
  1.1689 +	IGamePlayer *pPlayer = pGamePlayer ? pGamePlayer : playerhelpers->GetGamePlayer(client);
  1.1690 +
  1.1691 +	/* Determine if an update is necessary */
  1.1692 +	bool is_update_needed = false;
  1.1693 +	if (pClient->menu_serial != m_SerialNo)
  1.1694 +	{
  1.1695 +		is_update_needed = true;
  1.1696 +	}
  1.1697 +	else if (pPlayer->GetUserId() != pClient->user_id)
  1.1698 +	{
  1.1699 +		is_update_needed = true;
  1.1700 +	}
  1.1701 +
  1.1702 +	/* If no update is needed at the root level, just leave now */
  1.1703 +	if (!is_update_needed)
  1.1704 +	{
  1.1705 +		return;
  1.1706 +	}
  1.1707 +
  1.1708 +	/* First we need to flush the cache... */
  1.1709 +	TearDownClient(pClient);
  1.1710 +
  1.1711 +	/* Now, rebuild the category list, but don't create menus */
  1.1712 +	if (m_Categories.size() == 0)
  1.1713 +	{
  1.1714 +		pClient->cat_count = 0;
  1.1715 +		pClient->cats = NULL;
  1.1716 +	}
  1.1717 +	else
  1.1718 +	{
  1.1719 +		pClient->cat_count = (unsigned int)m_Categories.size();
  1.1720 +		pClient->cats = new topmenu_player_category_t[pClient->cat_count];
  1.1721 +		memset(pClient->cats, 0, sizeof(topmenu_player_category_t) * pClient->cat_count);
  1.1722 +	}
  1.1723 +
  1.1724 +	/* Re-sort the root categories if needed */
  1.1725 +	SortCategoriesIfNeeded();
  1.1726 +
  1.1727 +	/* Build the root menu */
  1.1728 +	IBaseMenu *root_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
  1.1729 +
  1.1730 +	/* Add the sorted items */
  1.1731 +	for (size_t i = 0; i < m_SortedCats.size(); i++)
  1.1732 +	{
  1.1733 +		if (m_Categories[m_SortedCats[i]]->obj_list.size() == 0)
  1.1734 +		{
  1.1735 +			continue;
  1.1736 +		}
  1.1737 +		root_menu->AppendItem(m_Categories[m_SortedCats[i]]->obj->name, ItemDrawInfo(""));
  1.1738 +	}
  1.1739 +
  1.1740 +	/* Now we need to handle un-sorted items.  This is slightly trickier, as we need to 
  1.1741 +	 * pre-render each category name, and cache those names.  Phew!
  1.1742 +	 */
  1.1743 +	if (m_UnsortedCats.size())
  1.1744 +	{
  1.1745 +		obj_by_name_t *item_list = new obj_by_name_t[m_UnsortedCats.size()];
  1.1746 +		for (size_t i = 0; i < m_UnsortedCats.size(); i++)
  1.1747 +		{
  1.1748 +			obj_by_name_t *temp_obj = &item_list[i];
  1.1749 +			topmenu_object_t *obj = m_Categories[m_UnsortedCats[i]]->obj;
  1.1750 +			obj->callbacks->OnTopMenuDisplayOption(this,
  1.1751 +				client, 
  1.1752 +				obj->object_id,
  1.1753 +				temp_obj->name,
  1.1754 +				sizeof(temp_obj->name));
  1.1755 +			temp_obj->obj_index = m_UnsortedCats[i];
  1.1756 +		}
  1.1757 +
  1.1758 +		/* Sort our temp list */
  1.1759 +		qsort(item_list, m_UnsortedCats.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
  1.1760 +
  1.1761 +		/* Add the new sorted categories */
  1.1762 +		for (size_t i = 0; i < m_UnsortedCats.size(); i++)
  1.1763 +		{
  1.1764 +			if (m_Categories[item_list[i].obj_index]->obj_list.size() == 0)
  1.1765 +			{
  1.1766 +				continue;
  1.1767 +			}
  1.1768 +			root_menu->AppendItem(m_Categories[item_list[i].obj_index]->obj->name, ItemDrawInfo(""));
  1.1769 +		}
  1.1770 +
  1.1771 +		delete [] item_list;
  1.1772 +	}
  1.1773 +
  1.1774 +	/* Set the menu's title */
  1.1775 +	char renderbuf[128];
  1.1776 +	m_pTitle->OnTopMenuDisplayTitle(this, client, 0, renderbuf, sizeof(renderbuf));
  1.1777 +	root_menu->SetDefaultTitle(renderbuf);
  1.1778 +
  1.1779 +	/* The client is now fully updated */
  1.1780 +	pClient->root = root_menu;
  1.1781 +	pClient->user_id = pPlayer->GetUserId();
  1.1782 +	pClient->menu_serial = m_SerialNo;
  1.1783 +	pClient->last_position = 0;
  1.1784 +	pClient->last_category = 0;
  1.1785 +	pClient->last_root_pos = 0;
  1.1786 +}
  1.1787 +
  1.1788 +void TopMenu::UpdateClientCategory(int client, unsigned int category)
  1.1789 +{
  1.1790 +	/* Update the client's root menu just in case it needs it.  This 
  1.1791 +	 * will validate that we have both a valid client and a valid
  1.1792 +	 * category structure for that client.
  1.1793 +	 */
  1.1794 +	UpdateClientRoot(client);
  1.1795 +
  1.1796 +	/* Now it's guaranteed that our category tables will be usable */
  1.1797 +	topmenu_player_t *pClient = &m_clients[client];
  1.1798 +	topmenu_category_t *cat = m_Categories[category];
  1.1799 +	topmenu_player_category_t *player_cat = &(pClient->cats[category]);
  1.1800 +
  1.1801 +	/* Does the category actually need updating? */
  1.1802 +	if (player_cat->serial == cat->serial)
  1.1803 +	{
  1.1804 +		return;
  1.1805 +	}
  1.1806 +
  1.1807 +	/* Destroy any existing menu */
  1.1808 +	if (player_cat->menu)
  1.1809 +	{
  1.1810 +		player_cat->menu->Destroy();
  1.1811 +		player_cat->menu = NULL;
  1.1812 +	}
  1.1813 +
  1.1814 +	if (pClient->last_category == category)
  1.1815 +	{
  1.1816 +		pClient->last_position = 0;
  1.1817 +	}
  1.1818 +
  1.1819 +	IBaseMenu *cat_menu = menus->GetDefaultStyle()->CreateMenu(this, myself->GetIdentity());
  1.1820 +	
  1.1821 +	/* Categories get an "exit back" button */
  1.1822 +	cat_menu->SetMenuOptionFlags(cat_menu->GetMenuOptionFlags() | MENUFLAG_BUTTON_EXITBACK);
  1.1823 +
  1.1824 +	/* Re-sort the category if needed */
  1.1825 +	SortCategoryIfNeeded(category);
  1.1826 +
  1.1827 +	/* Build the menu with the sorted items first */
  1.1828 +	for (size_t i = 0; i < cat->sorted.size(); i++)
  1.1829 +	{
  1.1830 +		cat_menu->AppendItem(cat->sorted[i]->name, ItemDrawInfo(""));
  1.1831 +	}
  1.1832 +
  1.1833 +	/* Now handle unsorted items */
  1.1834 +	if (cat->unsorted.size())
  1.1835 +	{
  1.1836 +		/* Build a list of the item names */
  1.1837 +		obj_by_name_t *item_list = new obj_by_name_t[cat->unsorted.size()];
  1.1838 +		for (size_t i = 0; i < cat->unsorted.size(); i++)
  1.1839 +		{
  1.1840 +			obj_by_name_t *item = &item_list[i];
  1.1841 +			topmenu_object_t *obj = cat->unsorted[i];
  1.1842 +			obj->callbacks->OnTopMenuDisplayOption(this,
  1.1843 +				client,
  1.1844 +				obj->object_id,
  1.1845 +				item->name,
  1.1846 +				sizeof(item->name));
  1.1847 +			item->obj_index = (unsigned int)i;
  1.1848 +		}
  1.1849 +
  1.1850 +		/* Sort the names */
  1.1851 +		qsort(item_list, cat->unsorted.size(), sizeof(obj_by_name_t), _SortObjectNamesDescending);
  1.1852 +
  1.1853 +		/* Add to the menu */
  1.1854 +		for (size_t i = 0; i < cat->unsorted.size(); i++)
  1.1855 +		{
  1.1856 +			cat_menu->AppendItem(cat->unsorted[item_list[i].obj_index]->name, ItemDrawInfo(""));
  1.1857 +		}
  1.1858 +
  1.1859 +		delete [] item_list;
  1.1860 +	}
  1.1861 +
  1.1862 +	/* Set the menu's title */
  1.1863 +	char renderbuf[128];
  1.1864 +	cat->obj->callbacks->OnTopMenuDisplayTitle(this, 
  1.1865 +		client, 
  1.1866 +		cat->obj->object_id, 
  1.1867 +		renderbuf, 
  1.1868 +		sizeof(renderbuf));
  1.1869 +	cat_menu->SetDefaultTitle(renderbuf);
  1.1870 +
  1.1871 +	/* We're done! */
  1.1872 +	player_cat->menu = cat_menu;
  1.1873 +	player_cat->serial = cat->serial;
  1.1874 +}
  1.1875 +
  1.1876 +void TopMenu::SortCategoryIfNeeded(unsigned int category)
  1.1877 +{
  1.1878 +	topmenu_category_t *cat = m_Categories[category];
  1.1879 +	if (!cat->reorder)
  1.1880 +	{
  1.1881 +		return;
  1.1882 +	}
  1.1883 +
  1.1884 +	cat->sorted.clear();
  1.1885 +	cat->unsorted.clear();
  1.1886 +
  1.1887 +	if (cat->obj_list.size() == 0)
  1.1888 +	{
  1.1889 +		cat->reorder = false;
  1.1890 +		return;
  1.1891 +	}
  1.1892 +
  1.1893 +	CVector<unsigned int> to_sort;
  1.1894 +	for (size_t i = 0; i < cat->obj_list.size(); i++)
  1.1895 +	{
  1.1896 +		to_sort.push_back(i);
  1.1897 +	}
  1.1898 +
  1.1899 +	/* Find a matching category in the configs */
  1.1900 +	config_category_t *config_cat = NULL; 
  1.1901 +	for (size_t i = 0; i < m_Config.cats.size(); i++)
  1.1902 +	{
  1.1903 +		if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), cat->obj->name) == 0)
  1.1904 +		{
  1.1905 +			config_cat = m_Config.cats[i];
  1.1906 +			break;
  1.1907 +		}
  1.1908 +	}
  1.1909 +
  1.1910 +	/* If there is a matching category, build a pre-sorted item list */
  1.1911 +	if (config_cat != NULL)
  1.1912 +	{
  1.1913 +		/* Find matching objects in this category */
  1.1914 +		for (size_t i = 0; i < config_cat->commands.size(); i++)
  1.1915 +		{
  1.1916 +			const char *config_name = m_Config.strings.GetString(config_cat->commands[i]);
  1.1917 +			for (size_t j = 0; j < to_sort.size(); j++)
  1.1918 +			{
  1.1919 +				if (strcmp(config_name, cat->obj_list[to_sort[j]]->name) == 0)
  1.1920 +				{
  1.1921 +					/* Place in the final list, then remove from the temporary list */
  1.1922 +					cat->sorted.push_back(cat->obj_list[to_sort[j]]);
  1.1923 +					to_sort.erase(to_sort.iterAt(j));
  1.1924 +					break;
  1.1925 +				}
  1.1926 +			}
  1.1927 +		}
  1.1928 +	}
  1.1929 +
  1.1930 +	/* Push any remaining items onto the unsorted list */
  1.1931 +	for (size_t i = 0; i < to_sort.size(); i++)
  1.1932 +	{
  1.1933 +		cat->unsorted.push_back(cat->obj_list[to_sort[i]]);
  1.1934 +	}
  1.1935 +
  1.1936 +	cat->reorder = false;
  1.1937 +}
  1.1938 +
  1.1939 +void TopMenu::SortCategoriesIfNeeded()
  1.1940 +{
  1.1941 +	if (!m_bCatsNeedResort)
  1.1942 +	{
  1.1943 +		return;
  1.1944 +	}
  1.1945 +
  1.1946 +	/* Clear sort results */
  1.1947 +	m_SortedCats.clear();
  1.1948 +	m_UnsortedCats.clear();
  1.1949 +
  1.1950 +	if (m_Categories.size() == 0)
  1.1951 +	{
  1.1952 +		m_bCatsNeedResort = false;
  1.1953 +		return;
  1.1954 +	}
  1.1955 +
  1.1956 +	CVector<unsigned int> to_sort;
  1.1957 +	for (unsigned int i = 0; i < (unsigned int)m_Categories.size(); i++)
  1.1958 +	{
  1.1959 +		to_sort.push_back(i);
  1.1960 +	}
  1.1961 +
  1.1962 +	/* If we have any predefined categories, add them in as they appear. */
  1.1963 +	for (size_t i= 0; i < m_Config.cats.size(); i++)
  1.1964 +	{
  1.1965 +		/* Find this category and map it in if we can */
  1.1966 +		for (size_t j = 0; j < to_sort.size(); j++)
  1.1967 +		{
  1.1968 +			if (strcmp(m_Config.strings.GetString(m_Config.cats[i]->name), 
  1.1969 +					   m_Categories[to_sort[j]]->obj->name) == 0)
  1.1970 +			{
  1.1971 +				/* Add to the real list and remove from the temporary */
  1.1972 +				m_SortedCats.push_back(to_sort[j]);
  1.1973 +				to_sort.erase(to_sort.iterAt(j));
  1.1974 +				break;
  1.1975 +			}
  1.1976 +		}
  1.1977 +	}
  1.1978 +
  1.1979 +	/* Push any remaining items onto the unsorted list */
  1.1980 +	for (size_t i = 0; i < to_sort.size(); i++)
  1.1981 +	{
  1.1982 +		m_UnsortedCats.push_back(to_sort[i]);
  1.1983 +	}
  1.1984 +
  1.1985 +	m_bCatsNeedResort = false;
  1.1986 +}
  1.1987 +
  1.1988 +void TopMenu::CreatePlayers(int max_clients)
  1.1989 +{
  1.1990 +	m_max_clients = max_clients;
  1.1991 +	m_clients = (topmenu_player_t *)malloc(sizeof(topmenu_player_t) * (max_clients + 1));
  1.1992 +	memset(m_clients, 0, sizeof(topmenu_player_t) * (max_clients + 1));
  1.1993 +}
  1.1994 +
  1.1995 +void TopMenu::TearDownClient(topmenu_player_t *player)
  1.1996 +{
  1.1997 +	if (player->cats != NULL)
  1.1998 +	{
  1.1999 +		for (unsigned int i = 0; i < player->cat_count; i++)
  1.2000 +		{
  1.2001 +			topmenu_player_category_t *player_cat = &(player->cats[i]);
  1.2002 +			if (player_cat->menu != NULL)
  1.2003 +			{
  1.2004 +				player_cat->menu->Destroy();
  1.2005 +			}
  1.2006 +		}
  1.2007 +		delete [] player->cats;
  1.2008 +	}
  1.2009 +
  1.2010 +	if (player->root != NULL)
  1.2011 +	{
  1.2012 +		player->root->Destroy();
  1.2013 +	}
  1.2014 +
  1.2015 +	memset(player, 0, sizeof(topmenu_player_t));
  1.2016 +}
  1.2017 +
  1.2018 +bool TopMenu::LoadConfiguration(const char *file, char *error, size_t maxlength)
  1.2019 +{
  1.2020 +	SMCError err;
  1.2021 +	SMCStates states;
  1.2022 +
  1.2023 +	if ((err = textparsers->ParseFile_SMC(file, this, &states))
  1.2024 +		!= SMCError_Okay)
  1.2025 +	{
  1.2026 +		const char *err_string = textparsers->GetSMCErrorString(err);
  1.2027 +		if (!err_string)
  1.2028 +		{
  1.2029 +			err_string = "Unknown";
  1.2030 +		}
  1.2031 +
  1.2032 +		UTIL_Format(error, maxlength, "%s", err_string);
  1.2033 +
  1.2034 +		return false;
  1.2035 +	}
  1.2036 +
  1.2037 +	return true;
  1.2038 +}
  1.2039 +
  1.2040 +bool TopMenu::OnIdentityRemoval(IdentityToken_t *owner)
  1.2041 +{
  1.2042 +	/* First sweep the categories owned by us */
  1.2043 +	CVector<unsigned int> obj_list;
  1.2044 +	for (size_t i = 0; i < m_Categories.size(); i++)
  1.2045 +	{
  1.2046 +		if (m_Categories[i]->obj->owner == owner)
  1.2047 +		{
  1.2048 +			obj_list.push_back(m_Categories[i]->obj->object_id);
  1.2049 +		}
  1.2050 +	}
  1.2051 +
  1.2052 +	for (size_t i = 0; i < obj_list.size(); i++)
  1.2053 +	{
  1.2054 +		RemoveFromMenu(obj_list[i]);
  1.2055 +	}
  1.2056 +
  1.2057 +	/* Now we can look for actual items */
  1.2058 +	for (size_t i = 0; i < m_Objects.size(); i++)
  1.2059 +	{
  1.2060 +		if (m_Objects[i]->is_free)
  1.2061 +		{
  1.2062 +			continue;
  1.2063 +		}
  1.2064 +		if (m_Objects[i]->owner == owner)
  1.2065 +		{
  1.2066 +			assert(m_Objects[i]->type != TopMenuObject_Category);
  1.2067 +			RemoveFromMenu(m_Objects[i]->object_id);
  1.2068 +		}
  1.2069 +	}
  1.2070 +
  1.2071 +	return true;
  1.2072 +}
  1.2073 +
  1.2074 +#define PARSE_STATE_NONE		0
  1.2075 +#define PARSE_STATE_MAIN		1
  1.2076 +#define PARSE_STATE_CATEGORY	2
  1.2077 +unsigned int ignore_parse_level = 0;
  1.2078 +unsigned int current_parse_state = 0;
  1.2079 +config_category_t *cur_cat = NULL;
  1.2080 +
  1.2081 +void TopMenu::ReadSMC_ParseStart()
  1.2082 +{
  1.2083 +	current_parse_state = PARSE_STATE_NONE;
  1.2084 +	ignore_parse_level = 0;
  1.2085 +	cur_cat = NULL;
  1.2086 +
  1.2087 +	/* Reset the old config */
  1.2088 +	m_Config.strings.Reset();
  1.2089 +	for (size_t i = 0; i < m_Config.cats.size(); i++)
  1.2090 +	{
  1.2091 +		delete m_Config.cats[i];
  1.2092 +	}
  1.2093 +	m_Config.cats.clear();
  1.2094 +}
  1.2095 +
  1.2096 +SMCResult TopMenu::ReadSMC_NewSection(const SMCStates *states, const char *name)
  1.2097 +{
  1.2098 +	if (ignore_parse_level)
  1.2099 +	{
  1.2100 +		ignore_parse_level++;
  1.2101 +	}
  1.2102 +	else
  1.2103 +	{
  1.2104 +		if (current_parse_state == PARSE_STATE_NONE)
  1.2105 +		{
  1.2106 +			if (strcmp(name, "Menu") == 0)
  1.2107 +			{
  1.2108 +				current_parse_state = PARSE_STATE_MAIN;
  1.2109 +			}
  1.2110 +			else
  1.2111 +			{
  1.2112 +				ignore_parse_level = 1;
  1.2113 +			}
  1.2114 +		}
  1.2115 +		else if (current_parse_state == PARSE_STATE_MAIN)
  1.2116 +		{
  1.2117 +			cur_cat = new config_category_t;
  1.2118 +			cur_cat->name = m_Config.strings.AddString(name);
  1.2119 +			m_Config.cats.push_back(cur_cat);
  1.2120 +			current_parse_state = PARSE_STATE_CATEGORY;
  1.2121 +		}
  1.2122 +		else
  1.2123 +		{
  1.2124 +			ignore_parse_level = 1;
  1.2125 +		}
  1.2126 +	}
  1.2127 +
  1.2128 +	return SMCResult_Continue;
  1.2129 +}
  1.2130 +
  1.2131 +SMCResult TopMenu::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value)
  1.2132 +{
  1.2133 +	if (ignore_parse_level > 0
  1.2134 +		|| current_parse_state != PARSE_STATE_CATEGORY
  1.2135 +		|| cur_cat == NULL)
  1.2136 +	{
  1.2137 +		return SMCResult_Continue;
  1.2138 +	}
  1.2139 +
  1.2140 +	if (strcmp(key, "item") == 0)
  1.2141 +	{
  1.2142 +		cur_cat->commands.push_back(m_Config.strings.AddString(value));
  1.2143 +	}
  1.2144 +
  1.2145 +	return SMCResult_Continue;
  1.2146 +}
  1.2147 +
  1.2148 +SMCResult TopMenu::ReadSMC_LeavingSection(const SMCStates *states)
  1.2149 +{
  1.2150 +	if (ignore_parse_level)
  1.2151 +	{
  1.2152 +		ignore_parse_level--;
  1.2153 +	}
  1.2154 +	else
  1.2155 +	{
  1.2156 +		if (current_parse_state == PARSE_STATE_CATEGORY)
  1.2157 +		{
  1.2158 +			cur_cat = NULL;
  1.2159 +			current_parse_state = PARSE_STATE_MAIN;
  1.2160 +		}
  1.2161 +		else if (current_parse_state == PARSE_STATE_MAIN)
  1.2162 +		{
  1.2163 +			current_parse_state = PARSE_STATE_NONE;
  1.2164 +		}
  1.2165 +	}
  1.2166 +
  1.2167 +	return SMCResult_Continue;
  1.2168 +}
  1.2169 +
  1.2170 +unsigned int TopMenu::FindCategory(const char *name)
  1.2171 +{
  1.2172 +	topmenu_object_t **p_obj = m_ObjLookup.retrieve(name);
  1.2173 +	if (!p_obj)
  1.2174 +	{
  1.2175 +		return 0;
  1.2176 +	}
  1.2177 +
  1.2178 +	topmenu_object_t *obj = *p_obj;
  1.2179 +	if (obj->type != TopMenuObject_Category)
  1.2180 +	{
  1.2181 +		return 0;
  1.2182 +	}
  1.2183 +
  1.2184 +	return obj->object_id;
  1.2185 +}
  1.2186 +
  1.2187 +int _SortObjectNamesDescending(const void *ptr1, const void *ptr2)
  1.2188 +{
  1.2189 +	obj_by_name_t *obj1 = (obj_by_name_t *)ptr1;
  1.2190 +	obj_by_name_t *obj2 = (obj_by_name_t *)ptr2;
  1.2191 +	return strcmp(obj1->name, obj2->name);
  1.2192 +}
  1.2193 +
  1.2194 +unsigned int strncopy(char *dest, const char *src, size_t count)
  1.2195 +{
  1.2196 +	if (!count)
  1.2197 +	{
  1.2198 +		return 0;
  1.2199 +	}
  1.2200 +
  1.2201 +	char *start = dest;
  1.2202 +	while ((*src) && (--count))
  1.2203 +	{
  1.2204 +		*dest++ = *src++;
  1.2205 +	}
  1.2206 +	*dest = '\0';
  1.2207 +
  1.2208 +	return (dest - start);
  1.2209 +}
  1.2210 +
  1.2211 +size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
  1.2212 +{
  1.2213 +	va_list ap;
  1.2214 +	va_start(ap, fmt);
  1.2215 +	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1.2216 +	va_end(ap);
  1.2217 +
  1.2218 +	if (len >= maxlength)
  1.2219 +	{
  1.2220 +		buffer[maxlength - 1] = '\0';
  1.2221 +		return (maxlength - 1);
  1.2222 +	}
  1.2223 +	else
  1.2224 +	{
  1.2225 +		return len;
  1.2226 +	}
  1.2227 +}
     2.1 --- a/extensions/topmenus/TopMenu.h	Mon Nov 19 23:11:10 2007 +0000
     2.2 +++ b/extensions/topmenus/TopMenu.h	Tue Nov 20 02:22:26 2007 +0000
     2.3 @@ -1,178 +1,179 @@
     2.4 -/**
     2.5 - * vim: set ts=4 :
     2.6 - * =============================================================================
     2.7 - * SourceMod Sample Extension
     2.8 - * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     2.9 - * =============================================================================
    2.10 - *
    2.11 - * This program is free software; you can redistribute it and/or modify it under
    2.12 - * the terms of the GNU General Public License, version 3.0, as published by the
    2.13 - * Free Software Foundation.
    2.14 - * 
    2.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
    2.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    2.17 - * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    2.18 - * details.
    2.19 - *
    2.20 - * You should have received a copy of the GNU General Public License along with
    2.21 - * this program.  If not, see <http://www.gnu.org/licenses/>.
    2.22 - *
    2.23 - * As a special exception, AlliedModders LLC gives you permission to link the
    2.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
    2.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    2.26 - * by the Valve Corporation.  You must obey the GNU General Public License in
    2.27 - * all respects for all other code used.  Additionally, AlliedModders LLC grants
    2.28 - * this exception to all derivative works.  AlliedModders LLC defines further
    2.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    2.30 - * or <http://www.sourcemod.net/license.php>.
    2.31 - *
    2.32 - * Version: $Id$
    2.33 - */
    2.34 -
    2.35 -#ifndef _INCLUDE_SOURCEMOD_TOP_MENU_H_
    2.36 -#define _INCLUDE_SOURCEMOD_TOP_MENU_H_
    2.37 -
    2.38 -#include <sh_list.h>
    2.39 -#include <sh_vector.h>
    2.40 -#include <sm_trie_tpl.h>
    2.41 -#include <ITopMenus.h>
    2.42 -#include "smsdk_ext.h"
    2.43 -#include "sm_memtable.h"
    2.44 -
    2.45 -using namespace SourceHook;
    2.46 -using namespace SourceMod;
    2.47 -
    2.48 -struct config_category_t
    2.49 -{
    2.50 -	int name;
    2.51 -	CVector<int> commands;
    2.52 -};
    2.53 -
    2.54 -struct config_root_t
    2.55 -{
    2.56 -	config_root_t() : strings(1024)
    2.57 -	{
    2.58 -	}
    2.59 -	BaseStringTable strings;
    2.60 -	CVector<config_category_t *> cats;
    2.61 -};
    2.62 -
    2.63 -struct topmenu_object_t
    2.64 -{
    2.65 -	char name[64];						/** Name */
    2.66 -	char cmdname[64];					/** Command name */ 
    2.67 -	FlagBits flags;						/** Admin flags */
    2.68 -	ITopMenuObjectCallbacks *callbacks;	/** Callbacks */
    2.69 -	IdentityToken_t *owner;				/** Owner */
    2.70 -	unsigned int object_id;				/** Object ID */
    2.71 -	topmenu_object_t *parent;			/** Parent, if any */
    2.72 -	TopMenuObjectType type;				/** Object Type */
    2.73 -	bool is_free;						/** Free or not? */
    2.74 -	char info[255];						/** Info string */
    2.75 -};
    2.76 -
    2.77 -struct topmenu_category_t
    2.78 -{
    2.79 -	CVector<topmenu_object_t *> obj_list;	/** Full object list */
    2.80 -	CVector<topmenu_object_t *> sorted;		/** Sorted items */
    2.81 -	CVector<topmenu_object_t *> unsorted;	/** Unsorted items */
    2.82 -	topmenu_object_t *obj;					/** Bound object */
    2.83 -	unsigned int serial;					/** Serial number */
    2.84 -	bool reorder;							/** Whether ordering needs updating */
    2.85 -};
    2.86 -
    2.87 -struct topmenu_player_category_t
    2.88 -{
    2.89 -	IBaseMenu *menu;					/** menu pointer */
    2.90 -	unsigned int serial;				/** last known serial */
    2.91 -};
    2.92 -
    2.93 -struct topmenu_player_t
    2.94 -{
    2.95 -	int user_id;						/** userid on server */
    2.96 -	unsigned int menu_serial;			/** menu serial no */
    2.97 -	IBaseMenu *root;					/** root menu display */
    2.98 -	topmenu_player_category_t *cats;	/** category display */
    2.99 -	unsigned int cat_count;				/** number of categories */
   2.100 -	unsigned int last_category;			/** last category they selected */
   2.101 -	unsigned int last_position;			/** last position in that category */
   2.102 -	unsigned int last_root_pos;			/** last page in the root menu */
   2.103 -};
   2.104 -
   2.105 -class TopMenu : 
   2.106 -	public ITopMenu,
   2.107 -	public IMenuHandler,
   2.108 -	public ITextListener_SMC
   2.109 -{
   2.110 -	friend class TopMenuManager;
   2.111 -public:
   2.112 -	TopMenu(ITopMenuObjectCallbacks *callbacks);
   2.113 -	~TopMenu();
   2.114 -public: //ITopMenu
   2.115 -	virtual unsigned int AddToMenu(const char *name,
   2.116 -		TopMenuObjectType type,
   2.117 -		ITopMenuObjectCallbacks *callbacks,
   2.118 -		IdentityToken_t *owner,
   2.119 -		const char *cmdname,
   2.120 -		FlagBits flags,
   2.121 -		unsigned int parent);
   2.122 -	unsigned int AddToMenu2(const char *name,
   2.123 -		TopMenuObjectType type,
   2.124 -		ITopMenuObjectCallbacks *callbacks,
   2.125 -		IdentityToken_t *owner,
   2.126 -		const char *cmdname,
   2.127 -		FlagBits flags,
   2.128 -		unsigned int parent,
   2.129 -		const char *info_string);
   2.130 -	virtual void RemoveFromMenu(unsigned int object_id);
   2.131 -	virtual bool DisplayMenu(int client, 
   2.132 -		unsigned int hold_time, 
   2.133 -		TopMenuPosition position);
   2.134 -	virtual bool LoadConfiguration(const char *file, char *error, size_t maxlength);
   2.135 -	virtual unsigned int FindCategory(const char *name);
   2.136 -	const char *GetObjectInfoString(unsigned int object_id);
   2.137 -public: //IMenuHandler
   2.138 -	virtual void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
   2.139 -	virtual void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   2.140 -	virtual unsigned int OnMenuDisplayItem(IBaseMenu *menu, 
   2.141 -		int client, 
   2.142 -		IMenuPanel *panel,
   2.143 -		unsigned int item, 
   2.144 -		const ItemDrawInfo &dr);
   2.145 -	virtual void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
   2.146 -public: //ITextListener_SMC
   2.147 -	void ReadSMC_ParseStart();
   2.148 -	SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
   2.149 -	SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
   2.150 -	SMCResult ReadSMC_LeavingSection(const SMCStates *states);
   2.151 -private:
   2.152 -	void SortCategoriesIfNeeded();
   2.153 -	void SortCategoryIfNeeded(unsigned int category);
   2.154 -private:
   2.155 -	bool DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position);
   2.156 -	void CreatePlayers(int max_clients);
   2.157 -	void UpdateClientRoot(int client, IGamePlayer *pGamePlayer=NULL);
   2.158 -	void UpdateClientCategory(int client, unsigned int category);
   2.159 -	void TearDownClient(topmenu_player_t *player);
   2.160 -private:
   2.161 -	void OnClientConnected(int client);
   2.162 -	void OnClientDisconnected(int client);
   2.163 -	void OnServerActivated(int max_clients);
   2.164 -	bool OnIdentityRemoval(IdentityToken_t *owner);
   2.165 -private:
   2.166 -	config_root_t m_Config;					/* Configuration from file */
   2.167 -	topmenu_player_t *m_clients;			/* Client array */
   2.168 -	CVector<unsigned int> m_SortedCats;		/* Sorted categories */
   2.169 -	CVector<unsigned int> m_UnsortedCats;	/* Un-sorted categories */
   2.170 -	CVector<topmenu_category_t *> m_Categories; /* Category array */
   2.171 -	CVector<topmenu_object_t *> m_Objects;	/* Object array */
   2.172 -	KTrie<topmenu_object_t *> m_ObjLookup;	/* Object lookup trie */
   2.173 -	unsigned int m_SerialNo;				/* Serial number for updating */
   2.174 -	ITopMenuObjectCallbacks *m_pTitle;		/* Title callbacks */
   2.175 -	int m_max_clients;						/* Maximum number of clients */
   2.176 -	bool m_bCatsNeedResort;					/* True if categories need a resort */
   2.177 -};
   2.178 -
   2.179 -unsigned int strncopy(char *dest, const char *src, size_t count);
   2.180 -
   2.181 -#endif //_INCLUDE_SOURCEMOD_TOP_MENU_H_
   2.182 +/**
   2.183 + * vim: set ts=4 :
   2.184 + * =============================================================================
   2.185 + * SourceMod Sample Extension
   2.186 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
   2.187 + * =============================================================================
   2.188 + *
   2.189 + * This program is free software; you can redistribute it and/or modify it under
   2.190 + * the terms of the GNU General Public License, version 3.0, as published by the
   2.191 + * Free Software Foundation.
   2.192 + * 
   2.193 + * This program is distributed in the hope that it will be useful, but WITHOUT
   2.194 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   2.195 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
   2.196 + * details.
   2.197 + *
   2.198 + * You should have received a copy of the GNU General Public License along with
   2.199 + * this program.  If not, see <http://www.gnu.org/licenses/>.
   2.200 + *
   2.201 + * As a special exception, AlliedModders LLC gives you permission to link the
   2.202 + * code of this program (as well as its derivative works) to "Half-Life 2," the
   2.203 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
   2.204 + * by the Valve Corporation.  You must obey the GNU General Public License in
   2.205 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
   2.206 + * this exception to all derivative works.  AlliedModders LLC defines further
   2.207 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
   2.208 + * or <http://www.sourcemod.net/license.php>.
   2.209 + *
   2.210 + * Version: $Id$
   2.211 + */
   2.212 +
   2.213 +#ifndef _INCLUDE_SOURCEMOD_TOP_MENU_H_
   2.214 +#define _INCLUDE_SOURCEMOD_TOP_MENU_H_
   2.215 +
   2.216 +#include <sh_list.h>
   2.217 +#include <sh_vector.h>
   2.218 +#include <sm_trie_tpl.h>
   2.219 +#include <ITopMenus.h>
   2.220 +#include "smsdk_ext.h"
   2.221 +#include "sm_memtable.h"
   2.222 +
   2.223 +using namespace SourceHook;
   2.224 +using namespace SourceMod;
   2.225 +
   2.226 +struct config_category_t
   2.227 +{
   2.228 +	int name;
   2.229 +	CVector<int> commands;
   2.230 +};
   2.231 +
   2.232 +struct config_root_t
   2.233 +{
   2.234 +	config_root_t() : strings(1024)
   2.235 +	{
   2.236 +	}
   2.237 +	BaseStringTable strings;
   2.238 +	CVector<config_category_t *> cats;
   2.239 +};
   2.240 +
   2.241 +struct topmenu_object_t
   2.242 +{
   2.243 +	char name[64];						/** Name */
   2.244 +	char cmdname[64];					/** Command name */ 
   2.245 +	FlagBits flags;						/** Admin flags */
   2.246 +	ITopMenuObjectCallbacks *callbacks;	/** Callbacks */
   2.247 +	IdentityToken_t *owner;				/** Owner */
   2.248 +	unsigned int object_id;				/** Object ID */
   2.249 +	topmenu_object_t *parent;			/** Parent, if any */
   2.250 +	TopMenuObjectType type;				/** Object Type */
   2.251 +	bool is_free;						/** Free or not? */
   2.252 +	char info[255];						/** Info string */
   2.253 +};
   2.254 +
   2.255 +struct topmenu_category_t
   2.256 +{
   2.257 +	CVector<topmenu_object_t *> obj_list;	/** Full object list */
   2.258 +	CVector<topmenu_object_t *> sorted;		/** Sorted items */
   2.259 +	CVector<topmenu_object_t *> unsorted;	/** Unsorted items */
   2.260 +	topmenu_object_t *obj;					/** Bound object */
   2.261 +	unsigned int serial;					/** Serial number */
   2.262 +	bool reorder;							/** Whether ordering needs updating */
   2.263 +};
   2.264 +
   2.265 +struct topmenu_player_category_t
   2.266 +{
   2.267 +	IBaseMenu *menu;					/** menu pointer */
   2.268 +	unsigned int serial;				/** last known serial */
   2.269 +};
   2.270 +
   2.271 +struct topmenu_player_t
   2.272 +{
   2.273 +	int user_id;						/** userid on server */
   2.274 +	unsigned int menu_serial;			/** menu serial no */
   2.275 +	IBaseMenu *root;					/** root menu display */
   2.276 +	topmenu_player_category_t *cats;	/** category display */
   2.277 +	unsigned int cat_count;				/** number of categories */
   2.278 +	unsigned int last_category;			/** last category they selected */
   2.279 +	unsigned int last_position;			/** last position in that category */
   2.280 +	unsigned int last_root_pos;			/** last page in the root menu */
   2.281 +};
   2.282 +
   2.283 +class TopMenu : 
   2.284 +	public ITopMenu,
   2.285 +	public IMenuHandler,
   2.286 +	public ITextListener_SMC
   2.287 +{
   2.288 +	friend class TopMenuManager;
   2.289 +public:
   2.290 +	TopMenu(ITopMenuObjectCallbacks *callbacks);
   2.291 +	~TopMenu();
   2.292 +public: //ITopMenu
   2.293 +	virtual unsigned int AddToMenu(const char *name,
   2.294 +		TopMenuObjectType type,
   2.295 +		ITopMenuObjectCallbacks *callbacks,
   2.296 +		IdentityToken_t *owner,
   2.297 +		const char *cmdname,
   2.298 +		FlagBits flags,
   2.299 +		unsigned int parent);
   2.300 +	unsigned int AddToMenu2(const char *name,
   2.301 +		TopMenuObjectType type,
   2.302 +		ITopMenuObjectCallbacks *callbacks,
   2.303 +		IdentityToken_t *owner,
   2.304 +		const char *cmdname,
   2.305 +		FlagBits flags,
   2.306 +		unsigned int parent,
   2.307 +		const char *info_string);
   2.308 +	virtual void RemoveFromMenu(unsigned int object_id);
   2.309 +	virtual bool DisplayMenu(int client, 
   2.310 +		unsigned int hold_time, 
   2.311 +		TopMenuPosition position);
   2.312 +	virtual bool LoadConfiguration(const char *file, char *error, size_t maxlength);
   2.313 +	virtual unsigned int FindCategory(const char *name);
   2.314 +	const char *GetObjectInfoString(unsigned int object_id);
   2.315 +	const char *GetObjectName(unsigned int object_id);
   2.316 +public: //IMenuHandler
   2.317 +	virtual void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
   2.318 +	virtual void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   2.319 +	virtual unsigned int OnMenuDisplayItem(IBaseMenu *menu, 
   2.320 +		int client, 
   2.321 +		IMenuPanel *panel,
   2.322 +		unsigned int item, 
   2.323 +		const ItemDrawInfo &dr);
   2.324 +	virtual void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
   2.325 +public: //ITextListener_SMC
   2.326 +	void ReadSMC_ParseStart();
   2.327 +	SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name);
   2.328 +	SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value);
   2.329 +	SMCResult ReadSMC_LeavingSection(const SMCStates *states);
   2.330 +private:
   2.331 +	void SortCategoriesIfNeeded();
   2.332 +	void SortCategoryIfNeeded(unsigned int category);
   2.333 +private:
   2.334 +	bool DisplayCategory(int client, unsigned int category, unsigned int hold_time, bool last_position);
   2.335 +	void CreatePlayers(int max_clients);
   2.336 +	void UpdateClientRoot(int client, IGamePlayer *pGamePlayer=NULL);
   2.337 +	void UpdateClientCategory(int client, unsigned int category);
   2.338 +	void TearDownClient(topmenu_player_t *player);
   2.339 +private:
   2.340 +	void OnClientConnected(int client);
   2.341 +	void OnClientDisconnected(int client);
   2.342 +	void OnServerActivated(int max_clients);
   2.343 +	bool OnIdentityRemoval(IdentityToken_t *owner);
   2.344 +private:
   2.345 +	config_root_t m_Config;					/* Configuration from file */
   2.346 +	topmenu_player_t *m_clients;			/* Client array */
   2.347 +	CVector<unsigned int> m_SortedCats;		/* Sorted categories */
   2.348 +	CVector<unsigned int> m_UnsortedCats;	/* Un-sorted categories */
   2.349 +	CVector<topmenu_category_t *> m_Categories; /* Category array */
   2.350 +	CVector<topmenu_object_t *> m_Objects;	/* Object array */
   2.351 +	KTrie<topmenu_object_t *> m_ObjLookup;	/* Object lookup trie */
   2.352 +	unsigned int m_SerialNo;				/* Serial number for updating */
   2.353 +	ITopMenuObjectCallbacks *m_pTitle;		/* Title callbacks */
   2.354 +	int m_max_clients;						/* Maximum number of clients */
   2.355 +	bool m_bCatsNeedResort;					/* True if categories need a resort */
   2.356 +};
   2.357 +
   2.358 +unsigned int strncopy(char *dest, const char *src, size_t count);
   2.359 +
   2.360 +#endif //_INCLUDE_SOURCEMOD_TOP_MENU_H_
     3.1 --- a/extensions/topmenus/smn_topmenus.cpp	Mon Nov 19 23:11:10 2007 +0000
     3.2 +++ b/extensions/topmenus/smn_topmenus.cpp	Tue Nov 20 02:22:26 2007 +0000
     3.3 @@ -1,358 +1,383 @@
     3.4 -/**
     3.5 - * vim: set ts=4 :
     3.6 - * =============================================================================
     3.7 - * SourceMod Sample Extension
     3.8 - * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     3.9 - * =============================================================================
    3.10 - *
    3.11 - * This program is free software; you can redistribute it and/or modify it under
    3.12 - * the terms of the GNU General Public License, version 3.0, as published by the
    3.13 - * Free Software Foundation.
    3.14 - * 
    3.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
    3.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    3.17 - * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    3.18 - * details.
    3.19 - *
    3.20 - * You should have received a copy of the GNU General Public License along with
    3.21 - * this program.  If not, see <http://www.gnu.org/licenses/>.
    3.22 - *
    3.23 - * As a special exception, AlliedModders LLC gives you permission to link the
    3.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
    3.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    3.26 - * by the Valve Corporation.  You must obey the GNU General Public License in
    3.27 - * all respects for all other code used.  Additionally, AlliedModders LLC grants
    3.28 - * this exception to all derivative works.  AlliedModders LLC defines further
    3.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    3.30 - * or <http://www.sourcemod.net/license.php>.
    3.31 - *
    3.32 - * Version: $Id$
    3.33 - */
    3.34 -
    3.35 -#include "extension.h"
    3.36 -#include "TopMenuManager.h"
    3.37 -#include "TopMenu.h"
    3.38 -
    3.39 -HandleType_t hTopMenuType;
    3.40 -
    3.41 -class TopMenuHandle : public IHandleTypeDispatch
    3.42 -{
    3.43 -public: 
    3.44 -	void OnHandleDestroy(HandleType_t type, void *object)
    3.45 -	{
    3.46 -		ITopMenu *pTopMenu = (ITopMenu *)object;
    3.47 -		g_TopMenus.DestroyTopMenu(pTopMenu);
    3.48 -	}
    3.49 -} s_TopMenuHandle;
    3.50 -
    3.51 -void Initialize_Natives()
    3.52 -{
    3.53 -	hTopMenuType = handlesys->CreateType("ITopMenu",
    3.54 -		&s_TopMenuHandle,
    3.55 -		0,
    3.56 -		NULL,
    3.57 -		NULL,
    3.58 -		myself->GetIdentity(),
    3.59 -		NULL);
    3.60 -}
    3.61 -
    3.62 -void Shutdown_Natives()
    3.63 -{
    3.64 -	handlesys->RemoveType(hTopMenuType, myself->GetIdentity());
    3.65 -}
    3.66 -
    3.67 -enum TopMenuAction
    3.68 -{
    3.69 -	TopMenuAction_DisplayOption = 0,
    3.70 -	TopMenuAction_DisplayTitle = 1,
    3.71 -	TopMenuAction_SelectOption = 2,
    3.72 -	TopMenuAction_DrawOption = 3,
    3.73 -	TopMenuAction_RemoveObject = 4,
    3.74 -};
    3.75 -
    3.76 -class TopMenuCallbacks : public ITopMenuObjectCallbacks
    3.77 -{
    3.78 -public:
    3.79 -	TopMenuCallbacks(IPluginFunction *pFunction) : m_pFunction(pFunction)
    3.80 -	{		
    3.81 -	}
    3.82 -
    3.83 -	unsigned int OnTopMenuDrawOption(ITopMenu *menu,
    3.84 -		int client,
    3.85 -		unsigned int object_id)
    3.86 -	{
    3.87 -		char buffer[2] = {ITEMDRAW_DEFAULT, 0x0};
    3.88 -		m_pFunction->PushCell(m_hMenuHandle);
    3.89 -		m_pFunction->PushCell(TopMenuAction_DrawOption);
    3.90 -		m_pFunction->PushCell(object_id);
    3.91 -		m_pFunction->PushCell(client);
    3.92 -		m_pFunction->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
    3.93 -		m_pFunction->PushCell(sizeof(buffer));
    3.94 -		m_pFunction->Execute(NULL);
    3.95 -		return (unsigned int)buffer[0];
    3.96 -	}
    3.97 -
    3.98 -	void OnTopMenuDisplayOption(ITopMenu *menu,
    3.99 -		int client, 
   3.100 -		unsigned int object_id,
   3.101 -		char buffer[], 
   3.102 -		size_t maxlength)
   3.103 -	{
   3.104 -		m_pFunction->PushCell(m_hMenuHandle);
   3.105 -		m_pFunction->PushCell(TopMenuAction_DisplayOption);
   3.106 -		m_pFunction->PushCell(object_id);
   3.107 -		m_pFunction->PushCell(client);
   3.108 -		m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
   3.109 -		m_pFunction->PushCell(maxlength);
   3.110 -		m_pFunction->Execute(NULL);
   3.111 -	}
   3.112 -
   3.113 -	void OnTopMenuDisplayTitle(ITopMenu *menu,
   3.114 -		int client,
   3.115 -		unsigned int object_id,
   3.116 -		char buffer[],
   3.117 -		size_t maxlength)
   3.118 -	{
   3.119 -		m_pFunction->PushCell(m_hMenuHandle);
   3.120 -		m_pFunction->PushCell(TopMenuAction_DisplayTitle);
   3.121 -		m_pFunction->PushCell(object_id);
   3.122 -		m_pFunction->PushCell(client);
   3.123 -		m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
   3.124 -		m_pFunction->PushCell(maxlength);
   3.125 -		m_pFunction->Execute(NULL);
   3.126 -	}
   3.127 -
   3.128 -	void OnTopMenuSelectOption(ITopMenu *menu, 
   3.129 -		int client, 
   3.130 -		unsigned int object_id)
   3.131 -	{
   3.132 -		unsigned int old_reply = playerhelpers->SetReplyTo(SM_REPLY_CHAT);
   3.133 -		m_pFunction->PushCell(m_hMenuHandle);
   3.134 -		m_pFunction->PushCell(TopMenuAction_SelectOption);
   3.135 -		m_pFunction->PushCell(object_id);
   3.136 -		m_pFunction->PushCell(client);
   3.137 -		m_pFunction->PushString("");
   3.138 -		m_pFunction->PushCell(0);
   3.139 -		m_pFunction->Execute(NULL);
   3.140 -		playerhelpers->SetReplyTo(old_reply);
   3.141 -	}
   3.142 -
   3.143 -	void OnTopMenuObjectRemoved(ITopMenu *menu, unsigned int object_id)
   3.144 -	{
   3.145 -		m_pFunction->PushCell(m_hMenuHandle);
   3.146 -		m_pFunction->PushCell(TopMenuAction_RemoveObject);
   3.147 -		m_pFunction->PushCell(object_id);
   3.148 -		m_pFunction->PushCell(0);
   3.149 -		m_pFunction->PushString("");
   3.150 -		m_pFunction->PushCell(0);
   3.151 -		m_pFunction->Execute(NULL);
   3.152 -
   3.153 -		delete this;
   3.154 -	}
   3.155 -
   3.156 -	Handle_t m_hMenuHandle;
   3.157 -	IPluginFunction *m_pFunction;
   3.158 -};
   3.159 -
   3.160 -static cell_t CreateTopMenu(IPluginContext *pContext, const cell_t *params)
   3.161 -{
   3.162 -	IPluginFunction *func = pContext->GetFunctionById(params[1]);
   3.163 -	if (func == NULL)
   3.164 -	{
   3.165 -		return pContext	->ThrowNativeError("Invalid function specified");
   3.166 -	}
   3.167 -
   3.168 -	TopMenuCallbacks *cb = new TopMenuCallbacks(func);
   3.169 -
   3.170 -	ITopMenu *pMenu = g_TopMenus.CreateTopMenu(cb);
   3.171 -
   3.172 -	if (!pMenu)
   3.173 -	{
   3.174 -		delete cb;
   3.175 -		return BAD_HANDLE;
   3.176 -	}
   3.177 -
   3.178 -	Handle_t hndl = handlesys->CreateHandle(hTopMenuType, 
   3.179 -		pMenu,
   3.180 -		pContext->GetIdentity(),
   3.181 -		myself->GetIdentity(),
   3.182 -		NULL);
   3.183 -	if (hndl == 0)
   3.184 -	{
   3.185 -		g_TopMenus.DestroyTopMenu(pMenu);
   3.186 -		return BAD_HANDLE;
   3.187 -	}
   3.188 -
   3.189 -	cb->m_hMenuHandle = hndl;
   3.190 -
   3.191 -	return hndl;
   3.192 -}
   3.193 -
   3.194 -static cell_t LoadTopMenuConfig(IPluginContext *pContext, const cell_t *params)
   3.195 -{
   3.196 -	HandleError err;
   3.197 -	ITopMenu *pMenu;
   3.198 -	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.199 -
   3.200 -	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.201 -		!= HandleError_None)
   3.202 -	{
   3.203 -		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.204 -	}
   3.205 -
   3.206 -	char *file, *err_buf;
   3.207 -	pContext->LocalToString(params[2], &file);
   3.208 -	pContext->LocalToString(params[3], &err_buf);
   3.209 -
   3.210 -	char path[PLATFORM_MAX_PATH];
   3.211 -	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   3.212 -
   3.213 -	return pMenu->LoadConfiguration(path, err_buf, params[4]) ? 1 : 0;
   3.214 -}
   3.215 -
   3.216 -static cell_t AddToTopMenu(IPluginContext *pContext, const cell_t *params)
   3.217 -{
   3.218 -	HandleError err;
   3.219 -	ITopMenu *pMenu;
   3.220 -	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.221 -
   3.222 -	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.223 -		!= HandleError_None)
   3.224 -	{
   3.225 -		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.226 -	}
   3.227 -
   3.228 -	IPluginFunction *func = pContext->GetFunctionById(params[4]);
   3.229 -	if (func == NULL)
   3.230 -	{
   3.231 -		return pContext	->ThrowNativeError("Invalid function specified");
   3.232 -	}
   3.233 -
   3.234 -	TopMenuCallbacks *cb = new TopMenuCallbacks(func);
   3.235 -
   3.236 -	char *name, *cmdname, *info_string = NULL;
   3.237 -	pContext->LocalToString(params[2], &name);
   3.238 -	pContext->LocalToString(params[6], &cmdname);
   3.239 -
   3.240 -	if (params[0] >= 8)
   3.241 -	{
   3.242 -		pContext->LocalToString(params[8], &info_string);
   3.243 -	}
   3.244 -
   3.245 -	TopMenuObjectType obj_type = (TopMenuObjectType)params[3];
   3.246 -	
   3.247 -	unsigned int object_id;
   3.248 -	if ((object_id = pMenu->AddToMenu2(name,
   3.249 -		obj_type,
   3.250 -		cb,
   3.251 -		pContext->GetIdentity(),
   3.252 -		cmdname,
   3.253 -		params[7], 
   3.254 -		params[5],
   3.255 -		info_string)) == 0)
   3.256 -	{
   3.257 -		delete cb;
   3.258 -		return 0;
   3.259 -	}
   3.260 -
   3.261 -	cb->m_hMenuHandle = params[1];
   3.262 -
   3.263 -	return object_id;
   3.264 -}
   3.265 -
   3.266 -static cell_t RemoveFromTopMenu(IPluginContext *pContext, const cell_t *params)
   3.267 -{
   3.268 -	HandleError err;
   3.269 -	ITopMenu *pMenu;
   3.270 -	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.271 -
   3.272 -	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.273 -		!= HandleError_None)
   3.274 -	{
   3.275 -		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.276 -	}
   3.277 -
   3.278 -	pMenu->RemoveFromMenu(params[2]);
   3.279 -
   3.280 -	return 1;
   3.281 -}
   3.282 -
   3.283 -static cell_t FindTopMenuCategory(IPluginContext *pContext, const cell_t *params)
   3.284 -{
   3.285 -	HandleError err;
   3.286 -	ITopMenu *pMenu;
   3.287 -	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.288 -
   3.289 -	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.290 -		!= HandleError_None)
   3.291 -	{
   3.292 -		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.293 -	}
   3.294 -
   3.295 -	char *name;
   3.296 -	pContext->LocalToString(params[2], &name);
   3.297 -
   3.298 -	return pMenu->FindCategory(name);
   3.299 -}
   3.300 -
   3.301 -static cell_t DisplayTopMenu(IPluginContext *pContext, const cell_t *params)
   3.302 -{
   3.303 -	HandleError err;
   3.304 -	ITopMenu *pMenu;
   3.305 -	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.306 -
   3.307 -	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.308 -		!= HandleError_None)
   3.309 -	{
   3.310 -		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.311 -	}
   3.312 -
   3.313 -	int client = params[2];
   3.314 -	IGamePlayer *player = playerhelpers->GetGamePlayer(client);
   3.315 -	if (!player)
   3.316 -	{
   3.317 -		return pContext->ThrowNativeError("Invalid client index %d", client);
   3.318 -	}
   3.319 -	else if (!player->IsInGame())
   3.320 -	{
   3.321 -		return pContext->ThrowNativeError("Client %d is not in game", client);
   3.322 -	}
   3.323 -
   3.324 -	return pMenu->DisplayMenu(client, 0, (TopMenuPosition)params[3]);
   3.325 -}
   3.326 -
   3.327 -static cell_t GetTopMenuInfoString(IPluginContext *pContext, const cell_t *params)
   3.328 -{
   3.329 -	HandleError err;
   3.330 -	ITopMenu *pMenu;
   3.331 -	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.332 -
   3.333 -	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.334 -		!= HandleError_None)
   3.335 -	{
   3.336 -		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.337 -	}
   3.338 -
   3.339 -	const char *str;
   3.340 -	if ((str = pMenu->GetObjectInfoString(params[2])) == NULL)
   3.341 -	{
   3.342 -		return pContext->ThrowNativeError("Invalid menu object %d", params[2]);
   3.343 -	}
   3.344 -
   3.345 -	char *buffer;
   3.346 -	pContext->LocalToString(params[3], &buffer);
   3.347 -
   3.348 -	return strncopy(buffer, str, params[4]);
   3.349 -}
   3.350 -
   3.351 -sp_nativeinfo_t g_TopMenuNatives[] = 
   3.352 -{
   3.353 -	{"AddToTopMenu",			AddToTopMenu},
   3.354 -	{"CreateTopMenu",			CreateTopMenu},
   3.355 -	{"DisplayTopMenu",			DisplayTopMenu},
   3.356 -	{"LoadTopMenuConfig",		LoadTopMenuConfig},
   3.357 -	{"RemoveFromTopMenu",		RemoveFromTopMenu},
   3.358 -	{"FindTopMenuCategory",		FindTopMenuCategory},
   3.359 -	{"GetTopMenuInfoString",	GetTopMenuInfoString},
   3.360 -	{NULL,					NULL},
   3.361 -};
   3.362 +/**
   3.363 + * vim: set ts=4 :
   3.364 + * =============================================================================
   3.365 + * SourceMod Sample Extension
   3.366 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
   3.367 + * =============================================================================
   3.368 + *
   3.369 + * This program is free software; you can redistribute it and/or modify it under
   3.370 + * the terms of the GNU General Public License, version 3.0, as published by the
   3.371 + * Free Software Foundation.
   3.372 + * 
   3.373 + * This program is distributed in the hope that it will be useful, but WITHOUT
   3.374 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   3.375 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
   3.376 + * details.
   3.377 + *
   3.378 + * You should have received a copy of the GNU General Public License along with
   3.379 + * this program.  If not, see <http://www.gnu.org/licenses/>.
   3.380 + *
   3.381 + * As a special exception, AlliedModders LLC gives you permission to link the
   3.382 + * code of this program (as well as its derivative works) to "Half-Life 2," the
   3.383 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
   3.384 + * by the Valve Corporation.  You must obey the GNU General Public License in
   3.385 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
   3.386 + * this exception to all derivative works.  AlliedModders LLC defines further
   3.387 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
   3.388 + * or <http://www.sourcemod.net/license.php>.
   3.389 + *
   3.390 + * Version: $Id$
   3.391 + */
   3.392 +
   3.393 +#include "extension.h"
   3.394 +#include "TopMenuManager.h"
   3.395 +#include "TopMenu.h"
   3.396 +
   3.397 +HandleType_t hTopMenuType;
   3.398 +
   3.399 +class TopMenuHandle : public IHandleTypeDispatch
   3.400 +{
   3.401 +public: 
   3.402 +	void OnHandleDestroy(HandleType_t type, void *object)
   3.403 +	{
   3.404 +		ITopMenu *pTopMenu = (ITopMenu *)object;
   3.405 +		g_TopMenus.DestroyTopMenu(pTopMenu);
   3.406 +	}
   3.407 +} s_TopMenuHandle;
   3.408 +
   3.409 +void Initialize_Natives()
   3.410 +{
   3.411 +	hTopMenuType = handlesys->CreateType("ITopMenu",
   3.412 +		&s_TopMenuHandle,
   3.413 +		0,
   3.414 +		NULL,
   3.415 +		NULL,
   3.416 +		myself->GetIdentity(),
   3.417 +		NULL);
   3.418 +}
   3.419 +
   3.420 +void Shutdown_Natives()
   3.421 +{
   3.422 +	handlesys->RemoveType(hTopMenuType, myself->GetIdentity());
   3.423 +}
   3.424 +
   3.425 +enum TopMenuAction
   3.426 +{
   3.427 +	TopMenuAction_DisplayOption = 0,
   3.428 +	TopMenuAction_DisplayTitle = 1,
   3.429 +	TopMenuAction_SelectOption = 2,
   3.430 +	TopMenuAction_DrawOption = 3,
   3.431 +	TopMenuAction_RemoveObject = 4,
   3.432 +};
   3.433 +
   3.434 +class TopMenuCallbacks : public ITopMenuObjectCallbacks
   3.435 +{
   3.436 +public:
   3.437 +	TopMenuCallbacks(IPluginFunction *pFunction) : m_pFunction(pFunction)
   3.438 +	{		
   3.439 +	}
   3.440 +
   3.441 +	unsigned int OnTopMenuDrawOption(ITopMenu *menu,
   3.442 +		int client,
   3.443 +		unsigned int object_id)
   3.444 +	{
   3.445 +		char buffer[2] = {ITEMDRAW_DEFAULT, 0x0};
   3.446 +		m_pFunction->PushCell(m_hMenuHandle);
   3.447 +		m_pFunction->PushCell(TopMenuAction_DrawOption);
   3.448 +		m_pFunction->PushCell(object_id);
   3.449 +		m_pFunction->PushCell(client);
   3.450 +		m_pFunction->PushStringEx(buffer, sizeof(buffer), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
   3.451 +		m_pFunction->PushCell(sizeof(buffer));
   3.452 +		m_pFunction->Execute(NULL);
   3.453 +		return (unsigned int)buffer[0];
   3.454 +	}
   3.455 +
   3.456 +	void OnTopMenuDisplayOption(ITopMenu *menu,
   3.457 +		int client, 
   3.458 +		unsigned int object_id,
   3.459 +		char buffer[], 
   3.460 +		size_t maxlength)
   3.461 +	{
   3.462 +		m_pFunction->PushCell(m_hMenuHandle);
   3.463 +		m_pFunction->PushCell(TopMenuAction_DisplayOption);
   3.464 +		m_pFunction->PushCell(object_id);
   3.465 +		m_pFunction->PushCell(client);
   3.466 +		m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
   3.467 +		m_pFunction->PushCell(maxlength);
   3.468 +		m_pFunction->Execute(NULL);
   3.469 +	}
   3.470 +
   3.471 +	void OnTopMenuDisplayTitle(ITopMenu *menu,
   3.472 +		int client,
   3.473 +		unsigned int object_id,
   3.474 +		char buffer[],
   3.475 +		size_t maxlength)
   3.476 +	{
   3.477 +		m_pFunction->PushCell(m_hMenuHandle);
   3.478 +		m_pFunction->PushCell(TopMenuAction_DisplayTitle);
   3.479 +		m_pFunction->PushCell(object_id);
   3.480 +		m_pFunction->PushCell(client);
   3.481 +		m_pFunction->PushStringEx(buffer, maxlength, 0, SM_PARAM_COPYBACK);
   3.482 +		m_pFunction->PushCell(maxlength);
   3.483 +		m_pFunction->Execute(NULL);
   3.484 +	}
   3.485 +
   3.486 +	void OnTopMenuSelectOption(ITopMenu *menu, 
   3.487 +		int client, 
   3.488 +		unsigned int object_id)
   3.489 +	{
   3.490 +		unsigned int old_reply = playerhelpers->SetReplyTo(SM_REPLY_CHAT);
   3.491 +		m_pFunction->PushCell(m_hMenuHandle);
   3.492 +		m_pFunction->PushCell(TopMenuAction_SelectOption);
   3.493 +		m_pFunction->PushCell(object_id);
   3.494 +		m_pFunction->PushCell(client);
   3.495 +		m_pFunction->PushString("");
   3.496 +		m_pFunction->PushCell(0);
   3.497 +		m_pFunction->Execute(NULL);
   3.498 +		playerhelpers->SetReplyTo(old_reply);
   3.499 +	}
   3.500 +
   3.501 +	void OnTopMenuObjectRemoved(ITopMenu *menu, unsigned int object_id)
   3.502 +	{
   3.503 +		m_pFunction->PushCell(m_hMenuHandle);
   3.504 +		m_pFunction->PushCell(TopMenuAction_RemoveObject);
   3.505 +		m_pFunction->PushCell(object_id);
   3.506 +		m_pFunction->PushCell(0);
   3.507 +		m_pFunction->PushString("");
   3.508 +		m_pFunction->PushCell(0);
   3.509 +		m_pFunction->Execute(NULL);
   3.510 +
   3.511 +		delete this;
   3.512 +	}
   3.513 +
   3.514 +	Handle_t m_hMenuHandle;
   3.515 +	IPluginFunction *m_pFunction;
   3.516 +};
   3.517 +
   3.518 +static cell_t CreateTopMenu(IPluginContext *pContext, const cell_t *params)
   3.519 +{
   3.520 +	IPluginFunction *func = pContext->GetFunctionById(params[1]);
   3.521 +	if (func == NULL)
   3.522 +	{
   3.523 +		return pContext	->ThrowNativeError("Invalid function specified");
   3.524 +	}
   3.525 +
   3.526 +	TopMenuCallbacks *cb = new TopMenuCallbacks(func);
   3.527 +
   3.528 +	ITopMenu *pMenu = g_TopMenus.CreateTopMenu(cb);
   3.529 +
   3.530 +	if (!pMenu)
   3.531 +	{
   3.532 +		delete cb;
   3.533 +		return BAD_HANDLE;
   3.534 +	}
   3.535 +
   3.536 +	Handle_t hndl = handlesys->CreateHandle(hTopMenuType, 
   3.537 +		pMenu,
   3.538 +		pContext->GetIdentity(),
   3.539 +		myself->GetIdentity(),
   3.540 +		NULL);
   3.541 +	if (hndl == 0)
   3.542 +	{
   3.543 +		g_TopMenus.DestroyTopMenu(pMenu);
   3.544 +		return BAD_HANDLE;
   3.545 +	}
   3.546 +
   3.547 +	cb->m_hMenuHandle = hndl;
   3.548 +
   3.549 +	return hndl;
   3.550 +}
   3.551 +
   3.552 +static cell_t LoadTopMenuConfig(IPluginContext *pContext, const cell_t *params)
   3.553 +{
   3.554 +	HandleError err;
   3.555 +	ITopMenu *pMenu;
   3.556 +	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.557 +
   3.558 +	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.559 +		!= HandleError_None)
   3.560 +	{
   3.561 +		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.562 +	}
   3.563 +
   3.564 +	char *file, *err_buf;
   3.565 +	pContext->LocalToString(params[2], &file);
   3.566 +	pContext->LocalToString(params[3], &err_buf);
   3.567 +
   3.568 +	char path[PLATFORM_MAX_PATH];
   3.569 +	g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file);
   3.570 +
   3.571 +	return pMenu->LoadConfiguration(path, err_buf, params[4]) ? 1 : 0;
   3.572 +}
   3.573 +
   3.574 +static cell_t AddToTopMenu(IPluginContext *pContext, const cell_t *params)
   3.575 +{
   3.576 +	HandleError err;
   3.577 +	ITopMenu *pMenu;
   3.578 +	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.579 +
   3.580 +	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.581 +		!= HandleError_None)
   3.582 +	{
   3.583 +		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.584 +	}
   3.585 +
   3.586 +	IPluginFunction *func = pContext->GetFunctionById(params[4]);
   3.587 +	if (func == NULL)
   3.588 +	{
   3.589 +		return pContext	->ThrowNativeError("Invalid function specified");
   3.590 +	}
   3.591 +
   3.592 +	TopMenuCallbacks *cb = new TopMenuCallbacks(func);
   3.593 +
   3.594 +	char *name, *cmdname, *info_string = NULL;
   3.595 +	pContext->LocalToString(params[2], &name);
   3.596 +	pContext->LocalToString(params[6], &cmdname);
   3.597 +
   3.598 +	if (params[0] >= 8)
   3.599 +	{
   3.600 +		pContext->LocalToString(params[8], &info_string);
   3.601 +	}
   3.602 +
   3.603 +	TopMenuObjectType obj_type = (TopMenuObjectType)params[3];
   3.604 +	
   3.605 +	unsigned int object_id;
   3.606 +	if ((object_id = pMenu->AddToMenu2(name,
   3.607 +		obj_type,
   3.608 +		cb,
   3.609 +		pContext->GetIdentity(),
   3.610 +		cmdname,
   3.611 +		params[7], 
   3.612 +		params[5],
   3.613 +		info_string)) == 0)
   3.614 +	{
   3.615 +		delete cb;
   3.616 +		return 0;
   3.617 +	}
   3.618 +
   3.619 +	cb->m_hMenuHandle = params[1];
   3.620 +
   3.621 +	return object_id;
   3.622 +}
   3.623 +
   3.624 +static cell_t RemoveFromTopMenu(IPluginContext *pContext, const cell_t *params)
   3.625 +{
   3.626 +	HandleError err;
   3.627 +	ITopMenu *pMenu;
   3.628 +	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.629 +
   3.630 +	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.631 +		!= HandleError_None)
   3.632 +	{
   3.633 +		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.634 +	}
   3.635 +
   3.636 +	pMenu->RemoveFromMenu(params[2]);
   3.637 +
   3.638 +	return 1;
   3.639 +}
   3.640 +
   3.641 +static cell_t FindTopMenuCategory(IPluginContext *pContext, const cell_t *params)
   3.642 +{
   3.643 +	HandleError err;
   3.644 +	ITopMenu *pMenu;
   3.645 +	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.646 +
   3.647 +	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.648 +		!= HandleError_None)
   3.649 +	{
   3.650 +		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.651 +	}
   3.652 +
   3.653 +	char *name;
   3.654 +	pContext->LocalToString(params[2], &name);
   3.655 +
   3.656 +	return pMenu->FindCategory(name);
   3.657 +}
   3.658 +
   3.659 +static cell_t DisplayTopMenu(IPluginContext *pContext, const cell_t *params)
   3.660 +{
   3.661 +	HandleError err;
   3.662 +	ITopMenu *pMenu;
   3.663 +	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.664 +
   3.665 +	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.666 +		!= HandleError_None)
   3.667 +	{
   3.668 +		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.669 +	}
   3.670 +
   3.671 +	int client = params[2];
   3.672 +	IGamePlayer *player = playerhelpers->GetGamePlayer(client);
   3.673 +	if (!player)
   3.674 +	{
   3.675 +		return pContext->ThrowNativeError("Invalid client index %d", client);
   3.676 +	}
   3.677 +	else if (!player->IsInGame())
   3.678 +	{
   3.679 +		return pContext->ThrowNativeError("Client %d is not in game", client);
   3.680 +	}
   3.681 +
   3.682 +	return pMenu->DisplayMenu(client, 0, (TopMenuPosition)params[3]);
   3.683 +}
   3.684 +
   3.685 +static cell_t GetTopMenuInfoString(IPluginContext *pContext, const cell_t *params)
   3.686 +{
   3.687 +	HandleError err;
   3.688 +	ITopMenu *pMenu;
   3.689 +	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.690 +
   3.691 +	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.692 +		!= HandleError_None)
   3.693 +	{
   3.694 +		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.695 +	}
   3.696 +
   3.697 +	const char *str;
   3.698 +	if ((str = pMenu->GetObjectInfoString(params[2])) == NULL)
   3.699 +	{
   3.700 +		return pContext->ThrowNativeError("Invalid menu object %d", params[2]);
   3.701 +	}
   3.702 +
   3.703 +	char *buffer;
   3.704 +	pContext->LocalToString(params[3], &buffer);
   3.705 +
   3.706 +	return strncopy(buffer, str, params[4]);
   3.707 +}
   3.708 +
   3.709 +static cell_t GetTopMenuName(IPluginContext *pContext, const cell_t *params)
   3.710 +{
   3.711 +	HandleError err;
   3.712 +	ITopMenu *pMenu;
   3.713 +	HandleSecurity sec(pContext->GetIdentity(), myself->GetIdentity());
   3.714 +
   3.715 +	if ((err = handlesys->ReadHandle(params[1], hTopMenuType, &sec, (void **)&pMenu))
   3.716 +		!= HandleError_None)
   3.717 +	{
   3.718 +		return pContext->ThrowNativeError("Invalid Handle %x (error: %d)", params[1], err);
   3.719 +	}
   3.720 +
   3.721 +	const char *str;
   3.722 +	if ((str = pMenu->GetObjectName(params[2])) == NULL)
   3.723 +	{
   3.724 +		return pContext->ThrowNativeError("Invalid menu object %d", params[2]);
   3.725 +	}
   3.726 +
   3.727 +	char *buffer;
   3.728 +	pContext->LocalToString(params[3], &buffer);
   3.729 +
   3.730 +	return strncopy(buffer, str, params[4]);
   3.731 +}
   3.732 +
   3.733 +sp_nativeinfo_t g_TopMenuNatives[] = 
   3.734 +{
   3.735 +	{"AddToTopMenu",			AddToTopMenu},
   3.736 +	{"CreateTopMenu",			CreateTopMenu},
   3.737 +	{"DisplayTopMenu",			DisplayTopMenu},
   3.738 +	{"LoadTopMenuConfig",		LoadTopMenuConfig},
   3.739 +	{"RemoveFromTopMenu",		RemoveFromTopMenu},
   3.740 +	{"FindTopMenuCategory",		FindTopMenuCategory},
   3.741 +	{"GetTopMenuInfoString",	GetTopMenuInfoString},
   3.742 +	{"GetTopMenuName",			GetTopMenuName},
   3.743 +	{NULL,					NULL},
   3.744 +};
     4.1 --- a/plugins/include/topmenus.inc	Mon Nov 19 23:11:10 2007 +0000
     4.2 +++ b/plugins/include/topmenus.inc	Tue Nov 20 02:22:26 2007 +0000
     4.3 @@ -1,277 +1,290 @@
     4.4 -/**
     4.5 - * vim: set ts=4 :
     4.6 - * =============================================================================
     4.7 - * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4.8 - * =============================================================================
     4.9 - *
    4.10 - * This file is part of the SourceMod/SourcePawn SDK.
    4.11 - *
    4.12 - * This program is free software; you can redistribute it and/or modify it under
    4.13 - * the terms of the GNU General Public License, version 3.0, as published by the
    4.14 - * Free Software Foundation.
    4.15 - * 
    4.16 - * This program is distributed in the hope that it will be useful, but WITHOUT
    4.17 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    4.18 - * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    4.19 - * details.
    4.20 - *
    4.21 - * You should have received a copy of the GNU General Public License along with
    4.22 - * this program.  If not, see <http://www.gnu.org/licenses/>.
    4.23 - *
    4.24 - * As a special exception, AlliedModders LLC gives you permission to link the
    4.25 - * code of this program (as well as its derivative works) to "Half-Life 2," the
    4.26 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    4.27 - * by the Valve Corporation.  You must obey the GNU General Public License in
    4.28 - * all respects for all other code used.  Additionally, AlliedModders LLC grants
    4.29 - * this exception to all derivative works.  AlliedModders LLC defines further
    4.30 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    4.31 - * or <http://www.sourcemod.net/license.php>.
    4.32 - *
    4.33 - * Version: $Id$
    4.34 - */
    4.35 - 
    4.36 -#if defined _topmenus_included
    4.37 - #endinput
    4.38 -#endif
    4.39 -#define _topmenus_included
    4.40 -
    4.41 -#include <menus>
    4.42 -
    4.43 -/**
    4.44 - * Actions a top menu will take on an object.
    4.45 - */
    4.46 -enum TopMenuAction
    4.47 -{
    4.48 -	/**
    4.49 -	 * An option is being drawn for a menu (or for sorting purposes).
    4.50 -	 *
    4.51 -	 * INPUT : TopMenu Handle, object ID, client index.
    4.52 -	 * OUTPUT: Buffer for rendering, maxlength of buffer.
    4.53 -	 */
    4.54 -	TopMenuAction_DisplayOption = 0,
    4.55 -	
    4.56 -	/**
    4.57 -	 * The title of a menu is being drawn for a given object.
    4.58 -	 *
    4.59 -	 * Note: The Object ID will be INVALID_TOPMENUOBJECT if drawing the 
    4.60 -	 * root title.  Otherwise, the Object ID is a category.
    4.61 -	 *
    4.62 -	 * INPUT : TopMenu Handle, object ID, client index.
    4.63 -	 * OUTPUT: Buffer for rendering, maxlength of buffer.
    4.64 -	 */
    4.65 -	TopMenuAction_DisplayTitle = 1,
    4.66 -	
    4.67 -	/**
    4.68 -	 * A menu option has been selected.
    4.69 -	 *
    4.70 -	 * The Object ID will always be an item (not a category).
    4.71 -	 *
    4.72 -	 * INPUT : TopMenu Handle, object ID, client index.
    4.73 -	 */
    4.74 -	TopMenuAction_SelectOption = 2,
    4.75 -
    4.76 -	/**
    4.77 -	 * A menu option is being drawn and its flags can be overridden.
    4.78 -	 *
    4.79 -	 * INPUT : TopMenu Handle, object ID, client index.
    4.80 -	 * OUTPUT: The first byte of the 'buffer' string should be set 
    4.81 -	 *		   to the desired flags.  By default, it will contain
    4.82 -	 *		   ITEMDRAW_DEFAULT.
    4.83 -	 */
    4.84 -	 TopMenuAction_DrawOption = 3,
    4.85 -	 
    4.86 -	/**
    4.87 -	 * Called when an object is being removed from the menu.
    4.88 -	 * This can be used to clean up data stored in the info string.
    4.89 -	 *
    4.90 -	 * INPUT : TopMenu Handle, object ID.
    4.91 -	 */
    4.92 -	 TopMenuAction_RemoveObject = 4,
    4.93 -};
    4.94 -
    4.95 -/**
    4.96 - * Top menu object types.
    4.97 - */
    4.98 -enum TopMenuObjectType
    4.99 -{
   4.100 -	TopMenuObject_Category = 0,			/**< Category (sub-menu branching from root) */
   4.101 -	TopMenuObject_Item = 1				/**< Item on a sub-menu */
   4.102 -};
   4.103 -
   4.104 -/**
   4.105 - * Top menu starting positions for display.
   4.106 - */
   4.107 -enum TopMenuPosition
   4.108 -{
   4.109 -	TopMenuPosition_Start = 0,			/**< Start/root of the menu */
   4.110 -	TopMenuPosition_LastRoot = 1,		/**< Last position in the root menu */
   4.111 -	TopMenuPosition_LastCategory = 3,	/**< Last position in their last category */
   4.112 -};
   4.113 -
   4.114 -/**
   4.115 - * Top menu object tag for type checking.
   4.116 - */
   4.117 -enum TopMenuObject
   4.118 -{
   4.119 -	INVALID_TOPMENUOBJECT = 0,
   4.120 -}
   4.121 -
   4.122 -/**
   4.123 - * TopMenu callback prototype.
   4.124 - *
   4.125 - * @param topmenu			Handle to the TopMenu.
   4.126 - * @param action			TopMenuAction being performed.
   4.127 - * @param object_id			The object ID (if used).
   4.128 - * @param param				Extra parameter (if used).
   4.129 - * @param buffer			Output buffer (if used).
   4.130 - * @param maxlength			Output buffer (if used).
   4.131 - * @noreturn
   4.132 - */
   4.133 -functag TopMenuHandler public(Handle:topmenu, 
   4.134 -							  TopMenuAction:action,
   4.135 -							  TopMenuObject:object_id,
   4.136 -							  param,
   4.137 -							  String:buffer[],
   4.138 -							  maxlength);
   4.139 -
   4.140 -/**
   4.141 - * Creates a TopMenu.
   4.142 - *
   4.143 - * @param handler			Handler to use for drawing the root title.
   4.144 - * @return					A new TopMenu Handle, or INVALID_HANDLE on failure.
   4.145 - */						  
   4.146 -native Handle:CreateTopMenu(TopMenuHandler:handler);
   4.147 -
   4.148 -/**
   4.149 - * Re-sorts the items in a TopMenu via a configuration file.
   4.150 - *
   4.151 - * The format of the configuration file should be a Valve Key-Values 
   4.152 - * formatted file that SourceMod can parse.  There should be one root 
   4.153 - * section, and one sub-section for each category.  Each sub-section's 
   4.154 - * name should match the category name.
   4.155 - *
   4.156 - * Each sub-section may only contain key/value pairs in the form of:
   4.157 - * key: "item"
   4.158 - * value: Name of the item as passed to AddToTopMenu().
   4.159 - *
   4.160 - * The TopMenu will draw items in the order declared in the configuration 
   4.161 - * file.  If items do not appear in the configuration file, they are sorted 
   4.162 - * per-player based on how the handler function renders for that player.  
   4.163 - * These items appear after the configuration sorted items.
   4.164 - *
   4.165 - * @param topmenu			TopMenu Handle.
   4.166 - * @param file				File path.
   4.167 - * @param error				Error buffer.
   4.168 - * @param maxlength			Maximum size of the error buffer.
   4.169 - *							Error buffer will be filled with a 
   4.170 - *							zero-terminated string if false is 
   4.171 - *							returned.
   4.172 - * @return					True on success, false on failure.
   4.173 - * @error					Invalid TopMenu Handle.
   4.174 - */
   4.175 -native bool:LoadTopMenuConfig(Handle:topmenu, const String:file[], String:error[], maxlength);
   4.176 -
   4.177 -/**
   4.178 - * Adds an object to a TopMenu.
   4.179 - *
   4.180 - * @param topmenu			TopMenu Handle.
   4.181 - * @param name				Object name (MUST be unique).
   4.182 - * @param type				Object type.
   4.183 - * @param handler			Handler for object.
   4.184 - * @param cmdname			Command name (for access overrides).
   4.185 - * @param flags				Default access flags.
   4.186 - * @param parent			Parent object ID, or INVALID_TOPMENUOBJECT for none.
   4.187 - *							Items must have a category parent.
   4.188 - *							Categories must not have a parent.
   4.189 - * @param info_string		Arbitrary storage (max 255 bytes).
   4.190 - * @return					A new TopMenuObject ID, or INVALID_TOPMENUOBJECT on
   4.191 - *							failure.
   4.192 - * @error					Invalid TopMenu Handle.
   4.193 - */
   4.194 -native TopMenuObject:AddToTopMenu(Handle:topmenu,
   4.195 -								  const String:name[],
   4.196 -								  TopMenuObjectType:type,
   4.197 -								  TopMenuHandler:handler,
   4.198 -								  TopMenuObject:parent,
   4.199 -								  const String:cmdname[]="",
   4.200 -								  flags=0,
   4.201 -								  const String:info_string[]="");
   4.202 -								  
   4.203 -/**
   4.204 - * Retrieves the info string of a top menu item.
   4.205 - *
   4.206 - * @param topmenu			TopMenu Handle.
   4.207 - * @param object			TopMenuObject ID.
   4.208 - * @param buffer			Buffer to store info string.
   4.209 - * @param maxlength			Maximum size of info string.
   4.210 - * @return					Number of bytes written, not including the 
   4.211 - *							null terminator.
   4.212 - * @error					Invalid TopMenu Handle or TopMenuObject ID.
   4.213 - */
   4.214 -native GetTopMenuInfoString(Handle:topmenu, TopMenuObject:parent, String:buffer[], maxlength);
   4.215 -
   4.216 -/**
   4.217 - * Removes an object from a TopMenu.
   4.218 - *
   4.219 - * Plugins' objects are automatically removed all TopMenus when the given 
   4.220 - * plugin unloads or pauses.  In the case of unpausing, all items are restored.
   4.221 - *
   4.222 - * @param topmenu			TopMenu Handle.
   4.223 - * @param object			TopMenuObject ID.
   4.224 - * @noreturn
   4.225 - * @error					Invalid TopMenu Handle.
   4.226 - */
   4.227 -native RemoveFromTopMenu(Handle:topmenu, TopMenuObject:object);
   4.228 -
   4.229 -/**
   4.230 - * Displays a TopMenu to a client.
   4.231 - *
   4.232 - * @param topmenu			TopMenu Handle.
   4.233 - * @param client			Client index.
   4.234 - * @param position			Position to display from.
   4.235 - * @return					True on success, false on failure.
   4.236 - * @error					Invalid TopMenu Handle or client not in game.
   4.237 - */
   4.238 -native bool:DisplayTopMenu(Handle:topmenu, client, TopMenuPosition:position);
   4.239 -
   4.240 -/**
   4.241 - * Finds a category's object ID in a TopMenu.
   4.242 - *
   4.243 - * @param topmenu			TopMenu Handle.
   4.244 - * @param name				Object's unique name.
   4.245 - * @return					TopMenuObject ID on success, or 
   4.246 - *							INVALID_TOPMENUOBJECT on failure.
   4.247 - * @error					Invalid TopMenu Handle.
   4.248 - */
   4.249 -native TopMenuObject:FindTopMenuCategory(Handle:topmenu, const String:name[]);
   4.250 -
   4.251 -/**
   4.252 - * Do not edit below this line!
   4.253 - */
   4.254 -public Extension:__ext_topmenus = 
   4.255 -{
   4.256 -	name = "TopMenus",
   4.257 -	file = "topmenus.ext",
   4.258 -#if defined AUTOLOAD_EXTENSIONS
   4.259 -	autoload = 1,
   4.260 -#else
   4.261 -	autoload = 0,
   4.262 -#endif
   4.263 -#if defined REQUIRE_EXTENSIONS
   4.264 -	required = 1,
   4.265 -#else
   4.266 -	required = 0,
   4.267 -#endif
   4.268 -};
   4.269 -
   4.270 -#if !defined REQUIRE_EXTENSIONS
   4.271 -public __ext_topmenus_SetNTVOptional()
   4.272 -{
   4.273 -	MarkNativeAsOptional("CreateTopMenu");
   4.274 -	MarkNativeAsOptional("LoadTopMenuConfig");
   4.275 -	MarkNativeAsOptional("AddToTopMenu");
   4.276 -	MarkNativeAsOptional("RemoveFromTopMenu");
   4.277 -	MarkNativeAsOptional("DisplayTopMenu");
   4.278 -	MarkNativeAsOptional("FindTopMenuCategory");
   4.279 -}
   4.280 -#endif
   4.281 +/**
   4.282 + * vim: set ts=4 :
   4.283 + * =============================================================================
   4.284 + * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
   4.285 + * =============================================================================
   4.286 + *
   4.287 + * This file is part of the SourceMod/SourcePawn SDK.
   4.288 + *
   4.289 + * This program is free software; you can redistribute it and/or modify it under
   4.290 + * the terms of the GNU General Public License, version 3.0, as published by the
   4.291 + * Free Software Foundation.
   4.292 + * 
   4.293 + * This program is distributed in the hope that it will be useful, but WITHOUT
   4.294 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   4.295 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
   4.296 + * details.
   4.297 + *
   4.298 + * You should have received a copy of the GNU General Public License along with
   4.299 + * this program.  If not, see <http://www.gnu.org/licenses/>.
   4.300 + *
   4.301 + * As a special exception, AlliedModders LLC gives you permission to link the
   4.302 + * code of this program (as well as its derivative works) to "Half-Life 2," the
   4.303 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
   4.304 + * by the Valve Corporation.  You must obey the GNU General Public License in
   4.305 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
   4.306 + * this exception to all derivative works.  AlliedModders LLC defines further
   4.307 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
   4.308 + * or <http://www.sourcemod.net/license.php>.
   4.309 + *
   4.310 + * Version: $Id$
   4.311 + */
   4.312 + 
   4.313 +#if defined _topmenus_included
   4.314 + #endinput
   4.315 +#endif
   4.316 +#define _topmenus_included
   4.317 +
   4.318 +#include <menus>
   4.319 +
   4.320 +/**
   4.321 + * Actions a top menu will take on an object.
   4.322 + */
   4.323 +enum TopMenuAction
   4.324 +{
   4.325 +	/**
   4.326 +	 * An option is being drawn for a menu (or for sorting purposes).
   4.327 +	 *
   4.328 +	 * INPUT : TopMenu Handle, object ID, client index.
   4.329 +	 * OUTPUT: Buffer for rendering, maxlength of buffer.
   4.330 +	 */
   4.331 +	TopMenuAction_DisplayOption = 0,
   4.332 +	
   4.333 +	/**
   4.334 +	 * The title of a menu is being drawn for a given object.
   4.335 +	 *
   4.336 +	 * Note: The Object ID will be INVALID_TOPMENUOBJECT if drawing the 
   4.337 +	 * root title.  Otherwise, the Object ID is a category.
   4.338 +	 *
   4.339 +	 * INPUT : TopMenu Handle, object ID, client index.
   4.340 +	 * OUTPUT: Buffer for rendering, maxlength of buffer.
   4.341 +	 */
   4.342 +	TopMenuAction_DisplayTitle = 1,
   4.343 +	
   4.344 +	/**
   4.345 +	 * A menu option has been selected.
   4.346 +	 *
   4.347 +	 * The Object ID will always be an item (not a category).
   4.348 +	 *
   4.349 +	 * INPUT : TopMenu Handle, object ID, client index.
   4.350 +	 */
   4.351 +	TopMenuAction_SelectOption = 2,
   4.352 +
   4.353 +	/**
   4.354 +	 * A menu option is being drawn and its flags can be overridden.
   4.355 +	 *
   4.356 +	 * INPUT : TopMenu Handle, object ID, client index.
   4.357 +	 * OUTPUT: The first byte of the 'buffer' string should be set 
   4.358 +	 *		   to the desired flags.  By default, it will contain
   4.359 +	 *		   ITEMDRAW_DEFAULT.
   4.360 +	 */
   4.361 +	 TopMenuAction_DrawOption = 3,
   4.362 +	 
   4.363 +	/**
   4.364 +	 * Called when an object is being removed from the menu.
   4.365 +	 * This can be used to clean up data stored in the info string.
   4.366 +	 *
   4.367 +	 * INPUT : TopMenu Handle, object ID.
   4.368 +	 */
   4.369 +	 TopMenuAction_RemoveObject = 4,
   4.370 +};
   4.371 +
   4.372 +/**
   4.373 + * Top menu object types.
   4.374 + */
   4.375 +enum TopMenuObjectType
   4.376 +{
   4.377 +	TopMenuObject_Category = 0,			/**< Category (sub-menu branching from root) */
   4.378 +	TopMenuObject_Item = 1				/**< Item on a sub-menu */
   4.379 +};
   4.380 +
   4.381 +/**
   4.382 + * Top menu starting positions for display.
   4.383 + */
   4.384 +enum TopMenuPosition
   4.385 +{
   4.386 +	TopMenuPosition_Start = 0,			/**< Start/root of the menu */
   4.387 +	TopMenuPosition_LastRoot = 1,		/**< Last position in the root menu */
   4.388 +	TopMenuPosition_LastCategory = 3,	/**< Last position in their last category */
   4.389 +};
   4.390 +
   4.391 +/**
   4.392 + * Top menu object tag for type checking.
   4.393 + */
   4.394 +enum TopMenuObject
   4.395 +{
   4.396 +	INVALID_TOPMENUOBJECT = 0,
   4.397 +}
   4.398 +
   4.399 +/**
   4.400 + * TopMenu callback prototype.
   4.401 + *
   4.402 + * @param topmenu			Handle to the TopMenu.
   4.403 + * @param action			TopMenuAction being performed.
   4.404 + * @param object_id			The object ID (if used).
   4.405 + * @param param				Extra parameter (if used).
   4.406 + * @param buffer			Output buffer (if used).
   4.407 + * @param maxlength			Output buffer (if used).
   4.408 + * @noreturn
   4.409 + */
   4.410 +functag TopMenuHandler public(Handle:topmenu, 
   4.411 +							  TopMenuAction:action,
   4.412 +							  TopMenuObject:object_id,
   4.413 +							  param,
   4.414 +							  String:buffer[],
   4.415 +							  maxlength);
   4.416 +
   4.417 +/**
   4.418 + * Creates a TopMenu.
   4.419 + *
   4.420 + * @param handler			Handler to use for drawing the root title.
   4.421 + * @return					A new TopMenu Handle, or INVALID_HANDLE on failure.
   4.422 + */						  
   4.423 +native Handle:CreateTopMenu(TopMenuHandler:handler);
   4.424 +
   4.425 +/**
   4.426 + * Re-sorts the items in a TopMenu via a configuration file.
   4.427 + *
   4.428 + * The format of the configuration file should be a Valve Key-Values 
   4.429 + * formatted file that SourceMod can parse.  There should be one root 
   4.430 + * section, and one sub-section for each category.  Each sub-section's 
   4.431 + * name should match the category name.
   4.432 + *
   4.433 + * Each sub-section may only contain key/value pairs in the form of:
   4.434 + * key: "item"
   4.435 + * value: Name of the item as passed to AddToTopMenu().
   4.436 + *
   4.437 + * The TopMenu will draw items in the order declared in the configuration 
   4.438 + * file.  If items do not appear in the configuration file, they are sorted 
   4.439 + * per-player based on how the handler function renders for that player.  
   4.440 + * These items appear after the configuration sorted items.
   4.441 + *
   4.442 + * @param topmenu			TopMenu Handle.
   4.443 + * @param file				File path.
   4.444 + * @param error				Error buffer.
   4.445 + * @param maxlength			Maximum size of the error buffer.
   4.446 + *							Error buffer will be filled with a 
   4.447 + *							zero-terminated string if false is 
   4.448 + *							returned.
   4.449 + * @return					True on success, false on failure.
   4.450 + * @error					Invalid TopMenu Handle.
   4.451 + */
   4.452 +native bool:LoadTopMenuConfig(Handle:topmenu, const String:file[], String:error[], maxlength);
   4.453 +
   4.454 +/**
   4.455 + * Adds an object to a TopMenu.
   4.456 + *
   4.457 + * @param topmenu			TopMenu Handle.
   4.458 + * @param name				Object name (MUST be unique).
   4.459 + * @param type				Object type.
   4.460 + * @param handler			Handler for object.
   4.461 + * @param cmdname			Command name (for access overrides).
   4.462 + * @param flags				Default access flags.
   4.463 + * @param parent			Parent object ID, or INVALID_TOPMENUOBJECT for none.
   4.464 + *							Items must have a category parent.
   4.465 + *							Categories must not have a parent.
   4.466 + * @param info_string		Arbitrary storage (max 255 bytes).
   4.467 + * @return					A new TopMenuObject ID, or INVALID_TOPMENUOBJECT on
   4.468 + *							failure.
   4.469 + * @error					Invalid TopMenu Handle.
   4.470 + */
   4.471 +native TopMenuObject:AddToTopMenu(Handle:topmenu,
   4.472 +								  const String:name[],
   4.473 +								  TopMenuObjectType:type,
   4.474 +								  TopMenuHandler:handler,
   4.475 +								  TopMenuObject:parent,
   4.476 +								  const String:cmdname[]="",
   4.477 +								  flags=0,
   4.478 +								  const String:info_string[]="");
   4.479 +								  
   4.480 +/**
   4.481 + * Retrieves the info string of a top menu item.
   4.482 + *
   4.483 + * @param topmenu			TopMenu Handle.
   4.484 + * @param object			TopMenuObject ID.
   4.485 + * @param buffer			Buffer to store info string.
   4.486 + * @param maxlength			Maximum size of info string.
   4.487 + * @return					Number of bytes written, not including the 
   4.488 + *							null terminator.
   4.489 + * @error					Invalid TopMenu Handle or TopMenuObject ID.
   4.490 + */
   4.491 +native GetTopMenuInfoString(Handle:topmenu, TopMenuObject:parent, String:buffer[], maxlength);
   4.492 +
   4.493 +/**
   4.494 + * Retrieves the name string of a top menu item.
   4.495 + *
   4.496 + * @param topmenu			TopMenu Handle.
   4.497 + * @param object			TopMenuObject ID.
   4.498 + * @param buffer			Buffer to store info string.
   4.499 + * @param maxlength			Maximum size of info string.
   4.500 + * @return					Number of bytes written, not including the 
   4.501 + *							null terminator.
   4.502 + * @error					Invalid TopMenu Handle or TopMenuObject ID.
   4.503 + */
   4.504 +native GetTopMenuName(Handle:topmenu, TopMenuObject:parent, String:buffer[], maxlength);
   4.505 +
   4.506 +/**
   4.507 + * Removes an object from a TopMenu.
   4.508 + *
   4.509 + * Plugins' objects are automatically removed all TopMenus when the given 
   4.510 + * plugin unloads or pauses.  In the case of unpausing, all items are restored.
   4.511 + *
   4.512 + * @param topmenu			TopMenu Handle.
   4.513 + * @param object			TopMenuObject ID.
   4.514 + * @noreturn
   4.515 + * @error					Invalid TopMenu Handle.
   4.516 + */
   4.517 +native RemoveFromTopMenu(Handle:topmenu, TopMenuObject:object);
   4.518 +
   4.519 +/**
   4.520 + * Displays a TopMenu to a client.
   4.521 + *
   4.522 + * @param topmenu			TopMenu Handle.
   4.523 + * @param client			Client index.
   4.524 + * @param position			Position to display from.
   4.525 + * @return					True on success, false on failure.
   4.526 + * @error					Invalid TopMenu Handle or client not in game.
   4.527 + */
   4.528 +native bool:DisplayTopMenu(Handle:topmenu, client, TopMenuPosition:position);
   4.529 +
   4.530 +/**
   4.531 + * Finds a category's object ID in a TopMenu.
   4.532 + *
   4.533 + * @param topmenu			TopMenu Handle.
   4.534 + * @param name				Object's unique name.
   4.535 + * @return					TopMenuObject ID on success, or 
   4.536 + *							INVALID_TOPMENUOBJECT on failure.
   4.537 + * @error					Invalid TopMenu Handle.
   4.538 + */
   4.539 +native TopMenuObject:FindTopMenuCategory(Handle:topmenu, const String:name[]);
   4.540 +
   4.541 +/**
   4.542 + * Do not edit below this line!
   4.543 + */
   4.544 +public Extension:__ext_topmenus = 
   4.545 +{
   4.546 +	name = "TopMenus",
   4.547 +	file = "topmenus.ext",
   4.548 +#if defined AUTOLOAD_EXTENSIONS
   4.549 +	autoload = 1,
   4.550 +#else
   4.551 +	autoload = 0,
   4.552 +#endif
   4.553 +#if defined REQUIRE_EXTENSIONS
   4.554 +	required = 1,
   4.555 +#else
   4.556 +	required = 0,
   4.557 +#endif
   4.558 +};
   4.559 +
   4.560 +#if !defined REQUIRE_EXTENSIONS
   4.561 +public __ext_topmenus_SetNTVOptional()
   4.562 +{
   4.563 +	MarkNativeAsOptional("CreateTopMenu");
   4.564 +	MarkNativeAsOptional("LoadTopMenuConfig");
   4.565 +	MarkNativeAsOptional("AddToTopMenu");
   4.566 +	MarkNativeAsOptional("RemoveFromTopMenu");
   4.567 +	MarkNativeAsOptional("DisplayTopMenu");
   4.568 +	MarkNativeAsOptional("FindTopMenuCategory");
   4.569 +}
   4.570 +#endif