core/smn_menus.cpp
author Scott Ehlert <ds@alliedmods.net>
Mon May 14 21:29:17 2007 +0000 (2007-05-14)
changeset 794 5947b0ee60bb
parent 791 37ef7934bc60
child 819 a2f2353049b9
permissions -rw-r--r--
Implemented amb286 - .smx extension no longer required with "sm plugins load"
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include "sm_globals.h"
    16 #include <sh_stack.h>
    17 #include "MenuManager.h"
    18 #include "MenuStyle_Valve.h"
    19 #include "MenuStyle_Radio.h"
    20 #include "HandleSys.h"
    21 #include "PluginSys.h"
    22 
    23 #if defined CreateMenu
    24 #undef CreateMenu
    25 #endif
    26 
    27 /**
    28  * And God said, "let there be menus," and behold, there were menus.  
    29  * God saw the menus and they were good.  And the evening and the morning
    30  * were the third day.
    31  */
    32 
    33 enum MenuAction
    34 {
    35 	MenuAction_Start = (1<<0),		/**< A menu has been started (nothing passed) */
    36 	MenuAction_Display = (1<<1),	/**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
    37 	MenuAction_Select = (1<<2),		/**< An item was selected (param1=client, param2=item) */
    38 	MenuAction_Cancel = (1<<3),		/**< The menu was cancelled (param1=client, param2=item) */
    39 	MenuAction_End = (1<<4),		/**< A menu's display/selection cycle is complete (nothing passed). */
    40 	MenuAction_VoteEnd = (1<<5),	/**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
    41 };
    42 
    43 class CPanelHandler : public IMenuHandler
    44 {
    45 	friend class MenuNativeHelpers;
    46 public:
    47 	CPanelHandler()
    48 	{
    49 	}
    50 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    51 	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
    52 private:
    53 	IPluginFunction *m_pFunc;
    54 	IPlugin *m_pPlugin;
    55 };
    56 
    57 class CMenuHandler : public IMenuHandler
    58 {
    59 	friend class MenuNativeHelpers;
    60 public:
    61 	CMenuHandler(IPluginFunction *pBasic, int flags);
    62 public:
    63 	void OnMenuStart(IBaseMenu *menu);
    64 	void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
    65 	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
    66 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    67 	void OnMenuEnd(IBaseMenu *menu);
    68 	void OnMenuDestroy(IBaseMenu *menu);
    69 #if 0
    70 	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
    71 	void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
    72 #endif
    73 private:
    74 	IPluginFunction *m_pBasic;
    75 	int m_Flags;
    76 };
    77 
    78 /**
    79  * GLOBAL CLASS FOR HELPERS
    80  */
    81 
    82 class MenuNativeHelpers : 
    83 	public SMGlobalClass,
    84 	public IHandleTypeDispatch,
    85 	public IPluginsListener
    86 {
    87 public:
    88 	virtual void OnSourceModAllInitialized()
    89 	{
    90 		m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
    91 		g_PluginSys.AddPluginsListener(this);
    92 	}
    93 
    94 	virtual void OnSourceModShutdown()
    95 	{
    96 		g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
    97 
    98 		while (!m_FreePanelHandlers.empty())
    99 		{
   100 			delete m_FreePanelHandlers.front();
   101 			m_FreePanelHandlers.pop();
   102 		}
   103 
   104 		while (!m_FreeMenuHandlers.empty())
   105 		{
   106 			delete m_FreeMenuHandlers.front();
   107 			m_FreeMenuHandlers.pop();
   108 		}
   109 	}
   110 
   111 	virtual void OnHandleDestroy(HandleType_t type, void *object)
   112 	{
   113 		IMenuPanel *panel = (IMenuPanel *)object;
   114 		panel->DeleteThis();
   115 	}
   116 
   117 	/**
   118 	 *   It is extremely important that unloaded plugins don't crash.
   119 	 * Thus, if a plugin unloads, we run through every handler we have.
   120 	 * This means we do almost no runtime work for keeping track of
   121 	 * our panel handlers (we don't have to store a list of the running 
   122 	 * ones), but when push comes to shove, we have to scan them all
   123 	 * in case any of them are active.
   124 	 */
   125 	virtual void OnPluginUnloaded(IPlugin *plugin)
   126 	{
   127 		for (size_t i = 0; i < m_PanelHandlers.size(); i++)
   128 		{
   129 			if (m_PanelHandlers[i]->m_pPlugin == plugin)
   130 			{
   131 				m_PanelHandlers[i]->m_pPlugin = NULL;
   132 				m_PanelHandlers[i]->m_pFunc = NULL;
   133 			}
   134 		}
   135 	}
   136 
   137 	inline HandleType_t GetPanelType()
   138 	{
   139 		return m_PanelType;
   140 	}
   141 
   142 	CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
   143 	{
   144 		CPanelHandler *handler;
   145 		if (m_FreePanelHandlers.empty())
   146 		{
   147 			handler = new CPanelHandler;
   148 			m_PanelHandlers.push_back(handler);
   149 		} else {
   150 			handler = m_FreePanelHandlers.front();
   151 			m_FreePanelHandlers.pop();
   152 		}
   153 		handler->m_pFunc = pFunction;
   154 		handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
   155 		return handler;
   156 	}
   157 
   158 	void FreePanelHandler(CPanelHandler *handler)
   159 	{
   160 		handler->m_pFunc = NULL;
   161 		handler->m_pPlugin = NULL;
   162 		m_FreePanelHandlers.push(handler);
   163 	}
   164 
   165 	CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
   166 	{
   167 		CMenuHandler *handler;
   168 		if (m_FreeMenuHandlers.empty())
   169 		{
   170 			handler = new CMenuHandler(pFunction, flags);
   171 		} else {
   172 			handler = m_FreeMenuHandlers.front();
   173 			m_FreeMenuHandlers.pop();
   174 			handler->m_pBasic = pFunction;
   175 			handler->m_Flags = flags;
   176 		}
   177 		return handler;
   178 	}
   179 
   180 	void FreeMenuHandler(CMenuHandler *handler)
   181 	{
   182 		m_FreeMenuHandlers.push(handler);
   183 	}
   184 
   185 private:
   186 	HandleType_t m_PanelType;
   187 	CStack<CPanelHandler *> m_FreePanelHandlers;
   188 	CStack<CMenuHandler *> m_FreeMenuHandlers;
   189 	CVector<CPanelHandler *> m_PanelHandlers;
   190 } g_MenuHelpers;
   191 
   192 /**
   193  * BASIC PANEL HANDLER WRAPPER
   194  */
   195 
   196 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   197 {
   198 	if (m_pFunc)
   199 	{
   200 		m_pFunc->PushCell(BAD_HANDLE);
   201 		m_pFunc->PushCell(MenuAction_Cancel);
   202 		m_pFunc->PushCell(client);
   203 		m_pFunc->PushCell(reason);
   204 		m_pFunc->Execute(NULL);
   205 	}
   206 	g_MenuHelpers.FreePanelHandler(this);
   207 }
   208 
   209 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   210 {
   211 	if (m_pFunc)
   212 	{
   213 		m_pFunc->PushCell(BAD_HANDLE);
   214 		m_pFunc->PushCell(MenuAction_Select);
   215 		m_pFunc->PushCell(client);
   216 		m_pFunc->PushCell(item);
   217 		m_pFunc->Execute(NULL);
   218 	}
   219 	g_MenuHelpers.FreePanelHandler(this);
   220 }
   221 
   222 /**
   223  * MENU HANDLER WRAPPER
   224  */
   225 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) : 
   226 	m_pBasic(pBasic), m_Flags(flags)
   227 {
   228 	/* :TODO: We can probably cache the handle ahead of time */
   229 }
   230 
   231 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
   232 {
   233 	if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
   234 	{
   235 		m_pBasic->PushCell(menu->GetHandle());
   236 		m_pBasic->PushCell(MenuAction_Start);
   237 		m_pBasic->PushCell(0);
   238 		m_pBasic->PushCell(0);
   239 		m_pBasic->Execute(NULL);
   240 	}
   241 }
   242 
   243 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
   244 {
   245 	if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
   246 	{
   247 		HandleSecurity sec;
   248 		sec.pIdentity = m_pBasic->GetParentContext()->GetIdentity();
   249 		sec.pOwner = g_pCoreIdent;
   250 	
   251 		HandleAccess access;
   252 		g_HandleSys.InitAccessDefaults(NULL, &access);
   253 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   254 
   255 		Handle_t hndl =  g_HandleSys.CreateHandleEx(g_MenuHelpers.GetPanelType(), panel, &sec, &access, NULL);
   256 
   257 		m_pBasic->PushCell(menu->GetHandle());
   258 		m_pBasic->PushCell(MenuAction_Display);
   259 		m_pBasic->PushCell(client);
   260 		m_pBasic->PushCell(hndl);
   261 		m_pBasic->Execute(NULL);
   262 
   263 		g_HandleSys.FreeHandle(hndl, &sec);
   264 	}
   265 }
   266 
   267 void CMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   268 {
   269 	m_pBasic->PushCell(menu->GetHandle());
   270 	m_pBasic->PushCell(MenuAction_Select);
   271 	m_pBasic->PushCell(client);
   272 	m_pBasic->PushCell(item);
   273 	m_pBasic->Execute(NULL);
   274 }
   275 
   276 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   277 {
   278 	m_pBasic->PushCell(menu->GetHandle());
   279 	m_pBasic->PushCell(MenuAction_Cancel);
   280 	m_pBasic->PushCell(client);
   281 	m_pBasic->PushCell(reason);
   282 	m_pBasic->Execute(NULL);
   283 }
   284 
   285 void CMenuHandler::OnMenuEnd(IBaseMenu *menu)
   286 {
   287 	m_pBasic->PushCell(menu->GetHandle());
   288 	m_pBasic->PushCell(MenuAction_End);
   289 	m_pBasic->PushCell(0);
   290 	m_pBasic->PushCell(0);
   291 	m_pBasic->Execute(NULL);
   292 }
   293 
   294 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
   295 {
   296 	g_MenuHelpers.FreeMenuHandler(this);
   297 }
   298 
   299 /**
   300  * INLINE FUNCTIONS FOR NATIVES
   301  */
   302 
   303 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
   304 {
   305 	return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
   306 }
   307 
   308 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
   309 {
   310 	HandleSecurity sec;
   311 	sec.pIdentity = g_pCoreIdent;
   312 	sec.pOwner = NULL;
   313 	return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
   314 }
   315 
   316 inline IMenuStyle *GetStyleFromCell(cell_t cell)
   317 {
   318 	enum MenuStyle
   319 	{
   320 		MenuStyle_Default = 0,		/**< The "default" menu style for the mod */
   321 		MenuStyle_Valve = 1,		/**< The Valve provided menu style (Used on HL2DM) */
   322 		MenuStyle_Radio = 2,		/**< The simpler menu style commonly used on CS:S */
   323 	};
   324 
   325 	if (cell == MenuStyle_Valve)
   326 	{
   327 		return &g_ValveMenuStyle;
   328 	} else if (cell == MenuStyle_Radio
   329 			   && g_RadioMenuStyle.IsSupported())
   330 	{
   331 		return &g_RadioMenuStyle;
   332 	}
   333 
   334 	return g_Menus.GetDefaultStyle();
   335 }
   336 
   337 /***********************************
   338  **** NATIVE DEFINITIONS ***********
   339  ***********************************/
   340 
   341 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
   342 {
   343 	IMenuStyle *style = g_Menus.GetDefaultStyle();
   344 	IPluginFunction *pFunction;
   345 
   346 	if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
   347 	{
   348 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   349 	}
   350 
   351 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
   352 	IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
   353 
   354 	Handle_t hndl = menu->GetHandle();
   355 	if (!hndl)
   356 	{
   357 		menu->Destroy();
   358 		return BAD_HANDLE;
   359 	}
   360 
   361 	return hndl;
   362 }
   363 
   364 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
   365 {
   366 	Handle_t hndl = (Handle_t)params[1];
   367 	HandleError err;
   368 	IBaseMenu *menu;
   369 
   370 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   371 	{
   372 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   373 	}
   374 
   375 	return menu->Display(params[2], params[3]) ? 1 : 0;
   376 }
   377 
   378 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
   379 {
   380 	Handle_t hndl = (Handle_t)params[1];
   381 	HandleError err;
   382 	IBaseMenu *menu;
   383 
   384 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   385 	{
   386 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   387 	}
   388 
   389 	char *info;
   390 	ItemDrawInfo dr;
   391 
   392 	pContext->LocalToString(params[2], &info);
   393 	pContext->LocalToString(params[3], (char **)&dr.display);
   394 	dr.style = params[4];
   395 
   396 	return menu->AppendItem(info, dr) ? 1 : 0;
   397 }
   398 
   399 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
   400 {
   401 	Handle_t hndl = (Handle_t)params[1];
   402 	HandleError err;
   403 	IBaseMenu *menu;
   404 
   405 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   406 	{
   407 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   408 	}
   409 
   410 	char *info;
   411 	ItemDrawInfo dr;
   412 
   413 	pContext->LocalToString(params[3], &info);
   414 	pContext->LocalToString(params[4], (char **)&dr.display);
   415 	dr.style = params[5];
   416 
   417 	return menu->InsertItem(params[2], info, dr) ? 1 : 0;
   418 }
   419 
   420 static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
   421 {
   422 	Handle_t hndl = (Handle_t)params[1];
   423 	HandleError err;
   424 	IBaseMenu *menu;
   425 
   426 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   427 	{
   428 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   429 	}
   430 
   431 	return menu->RemoveItem(params[2]) ? 1 : 0;
   432 }
   433 
   434 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
   435 {
   436 	Handle_t hndl = (Handle_t)params[1];
   437 	HandleError err;
   438 	IBaseMenu *menu;
   439 
   440 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   441 	{
   442 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   443 	}
   444 
   445 	menu->RemoveAllItems();
   446 
   447 	return 1;
   448 }
   449 
   450 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
   451 {
   452 	Handle_t hndl = (Handle_t)params[1];
   453 	HandleError err;
   454 	IBaseMenu *menu;
   455 
   456 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   457 	{
   458 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   459 	}
   460 
   461 	ItemDrawInfo dr;
   462 	const char *info;
   463 
   464 	if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
   465 	{
   466 		return 0;
   467 	}
   468 
   469 	pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
   470 	pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
   471 
   472 	cell_t *addr;
   473 	pContext->LocalToPhysAddr(params[5], &addr);
   474 	*addr = dr.style;
   475 
   476 	return 1;
   477 }
   478 
   479 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
   480 {
   481 	Handle_t hndl = (Handle_t)params[1];
   482 	HandleError err;
   483 	IBaseMenu *menu;
   484 
   485 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   486 	{
   487 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   488 	}
   489 
   490 	return menu->SetPagination(params[2]) ? 1 : 0;
   491 }
   492 
   493 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
   494 {
   495 	Handle_t hndl = (Handle_t)params[1];
   496 	HandleError err;
   497 	IBaseMenu *menu;
   498 
   499 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   500 	{
   501 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   502 	}
   503 
   504 	return menu->GetPagination();
   505 }
   506 
   507 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
   508 {
   509 	Handle_t hndl = (Handle_t)params[1];
   510 	HandleError err;
   511 	IBaseMenu *menu;
   512 
   513 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   514 	{
   515 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   516 	}
   517 
   518 	return menu->GetItemCount();
   519 }
   520 
   521 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
   522 {
   523 	Handle_t hndl = (Handle_t)params[1];
   524 	HandleError err;
   525 	IBaseMenu *menu;
   526 
   527 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   528 	{
   529 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   530 	}
   531 
   532 	char buffer[1024];
   533 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   534 
   535 	menu->SetDefaultTitle(buffer);
   536 
   537 	return 1;
   538 }
   539 
   540 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
   541 {
   542 	Handle_t hndl = (Handle_t)params[1];
   543 	HandleError err;
   544 	IBaseMenu *menu;
   545 
   546 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   547 	{
   548 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   549 	}
   550 
   551 	IMenuPanel *panel = menu->CreatePanel();
   552 	hndl = MakePanelHandle(panel, pContext);
   553 	if (!hndl)
   554 	{
   555 		panel->DeleteThis();
   556 	}
   557 
   558 	return hndl;
   559 }
   560 
   561 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   562 {
   563 	Handle_t hndl = (Handle_t)params[1];
   564 	HandleError err;
   565 	IBaseMenu *menu;
   566 
   567 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   568 	{
   569 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   570 	}
   571 
   572 	return menu->GetExitButton() ? 1 : 0;
   573 }
   574 
   575 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   576 {
   577 	Handle_t hndl = (Handle_t)params[1];
   578 	HandleError err;
   579 	IBaseMenu *menu;
   580 
   581 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   582 	{
   583 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   584 	}
   585 
   586 	return menu->SetExitButton(params[2] ? true : false) ? 1 : 0;
   587 }
   588 
   589 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
   590 {
   591 	Handle_t hndl = (Handle_t)params[1];
   592 	HandleError err;
   593 	IBaseMenu *menu;
   594 
   595 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   596 	{
   597 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   598 	}
   599 
   600 	menu->Cancel();
   601 
   602 	return 1;
   603 }
   604 
   605 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
   606 {
   607 	Handle_t hndl = (Handle_t)params[1];
   608 	HandleError err;
   609 	IBaseMenu *menu;
   610 
   611 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   612 	{
   613 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   614 	}
   615 
   616 	return menu->GetDrawStyle()->GetHandle();
   617 }
   618 
   619 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
   620 {
   621 	IMenuStyle *style = GetStyleFromCell(params[1]);
   622 	if (!style)
   623 	{
   624 		return BAD_HANDLE;
   625 	}
   626 
   627 	return style->GetHandle();
   628 }
   629 
   630 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
   631 {
   632 	Handle_t hndl = (Handle_t)params[1];
   633 	HandleError err;
   634 	IMenuStyle *style;
   635 
   636 	if (hndl != 0)
   637 	{
   638 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   639 		{
   640 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   641 		}
   642 	} else {
   643 		style = g_Menus.GetDefaultStyle();
   644 	}
   645 
   646 	IMenuPanel *panel = style->CreatePanel();
   647 
   648 	hndl = MakePanelHandle(panel, pContext);
   649 	if (!hndl)
   650 	{
   651 		panel->DeleteThis();
   652 		return BAD_HANDLE;
   653 	}
   654 
   655 	return hndl;
   656 }
   657 
   658 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
   659 {
   660 	Handle_t hndl = (Handle_t)params[1];
   661 	HandleError err;
   662 	IMenuStyle *style;
   663 
   664 	if (hndl != 0)
   665 	{
   666 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   667 		{
   668 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   669 		}
   670 	} else {
   671 		style = g_Menus.GetDefaultStyle();
   672 	}
   673 
   674 	IPluginFunction *pFunction;
   675 	if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
   676 	{
   677 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
   678 	}
   679 
   680 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
   681 
   682 	IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
   683 	hndl = pMenu->GetHandle();
   684 	if (!hndl)
   685 	{
   686 		pMenu->Destroy();
   687 		return BAD_HANDLE;
   688 	}
   689 
   690 	return hndl;
   691 }
   692 
   693 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
   694 {
   695 	Handle_t hndl = (Handle_t)params[2];
   696 	HandleError err;
   697 	IMenuStyle *style;
   698 
   699 	if (hndl != 0)
   700 	{
   701 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   702 		{
   703 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   704 		}
   705 	} else {
   706 		style = g_Menus.GetDefaultStyle();
   707 	}
   708 
   709 	return style->GetClientMenu(params[1], NULL);
   710 }
   711 
   712 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
   713 {
   714 	Handle_t hndl = (Handle_t)params[3];
   715 	HandleError err;
   716 	IMenuStyle *style;
   717 
   718 	if (hndl != 0)
   719 	{
   720 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   721 		{
   722 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   723 		}
   724 	} else {
   725 		style = g_Menus.GetDefaultStyle();
   726 	}
   727 
   728 	return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
   729 }
   730 
   731 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
   732 {
   733 	Handle_t hndl = (Handle_t)params[1];
   734 	HandleError err;
   735 	IMenuStyle *style;
   736 
   737 	if (hndl != 0)
   738 	{
   739 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   740 		{
   741 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   742 		}
   743 	} else {
   744 		style = g_Menus.GetDefaultStyle();
   745 	}
   746 
   747 	return style->GetMaxPageItems();
   748 }
   749 
   750 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
   751 {
   752 	Handle_t hndl = (Handle_t)params[1];
   753 	HandleError err;
   754 	IMenuPanel *panel;
   755 
   756 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   757 	{
   758 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   759 	}
   760 
   761 	return panel->GetParentStyle()->GetHandle();
   762 }
   763 
   764 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
   765 {
   766 	Handle_t hndl = (Handle_t)params[1];
   767 	HandleError err;
   768 	IMenuPanel *panel;
   769 
   770 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   771 	{
   772 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   773 	}
   774 
   775 	char *text;
   776 	pContext->LocalToString(params[2], &text);
   777 
   778 	panel->DrawTitle(text, params[3] ? true : false);
   779 
   780 	return 1;
   781 }
   782 
   783 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
   784 {
   785 	Handle_t hndl = (Handle_t)params[1];
   786 	HandleError err;
   787 	IMenuPanel *panel;
   788 
   789 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   790 	{
   791 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   792 	}
   793 
   794 	ItemDrawInfo dr;
   795 	pContext->LocalToString(params[2], (char **)&dr.display);
   796 	dr.style = params[3];
   797 
   798 	return panel->DrawItem(dr);
   799 }
   800 
   801 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
   802 {
   803 	Handle_t hndl = (Handle_t)params[1];
   804 	HandleError err;
   805 	IMenuPanel *panel;
   806 
   807 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   808 	{
   809 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   810 	}
   811 
   812 	char *text;
   813 	pContext->LocalToString(params[2], &text);
   814 
   815 	return panel->DrawRawLine(text) ? 1 : 0;
   816 }
   817 
   818 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
   819 {
   820 	Handle_t hndl = (Handle_t)params[1];
   821 	HandleError err;
   822 	IMenuPanel *panel;
   823 
   824 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   825 	{
   826 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   827 	}
   828 
   829 	return panel->CanDrawItem(params[2]);
   830 }
   831 
   832 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
   833 {
   834 	Handle_t hndl = (Handle_t)params[1];
   835 	HandleError err;
   836 	IMenuPanel *panel;
   837 
   838 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   839 	{
   840 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   841 	}
   842 
   843 	IPluginFunction *pFunction;
   844 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
   845 	{
   846 		return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
   847 	}
   848 
   849 	CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
   850 	if (!panel->SendDisplay(params[2], handler, params[4]))
   851 	{
   852 		g_MenuHelpers.FreePanelHandler(handler);
   853 	}
   854 
   855 	return 1;
   856 }
   857 
   858 REGISTER_NATIVES(menuNatives)
   859 {
   860 	{"AddMenuItem",				AddMenuItem},
   861 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
   862 	{"CancelClientMenu",		CancelClientMenu},
   863 	{"CancelMenu",				CancelMenu},
   864 	{"CreateMenu",				CreateMenu},
   865 	{"CreateMenuEx",			CreateMenuEx},
   866 	{"CreatePanel",				CreatePanel},
   867 	{"CreatePanelFromMenu",		CreatePanelFromMenu},\
   868 	{"DisplayMenu",				DisplayMenu},
   869 	{"DrawPanelItem",			DrawPanelItem},
   870 	{"DrawPanelText",			DrawPanelText},
   871 	{"GetClientMenu",			GetClientMenu},
   872 	{"GetMaxPageItems",			GetMaxPageItems},
   873 	{"GetMenuExitButton",		GetMenuExitButton},
   874 	{"GetMenuItem",				GetMenuItem},
   875 	{"GetMenuItemCount",		GetMenuItemCount},
   876 	{"GetMenuPagination",		GetMenuPagination},
   877 	{"GetMenuStyle",			GetMenuStyle},
   878 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
   879 	{"GetPanelStyle",			GetPanelStyle},
   880 	{"InsertMenuItem",			InsertMenuItem},
   881 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
   882 	{"RemoveMenuItem",			RemoveMenuItem},
   883 	{"SendPanelToClient",		SendPanelToClient},
   884 	{"SetMenuExitButton",		SetMenuExitButton},
   885 	{"SetMenuPagination",		SetMenuPagination},
   886 	{"SetMenuTitle",			SetMenuTitle},
   887 	{"SetPanelTitle",			SetPanelTitle},
   888 	{NULL,						NULL},
   889 };