core/smn_menus.cpp
author David Anderson <dvander@alliedmods.net>
Mon May 14 04:46:06 2007 +0000 (2007-05-14)
changeset 790 9b1474d03470
child 791 37ef7934bc60
permissions -rw-r--r--
redid API yet again - IMenuHandler is now bound to an IBaseMenu instance
implemented MOST natives
removed broadcasting/voting handlers for the time being
fixed a crash bug in basecontext when i didn't obey the api
moved radio displays to a caching stack
     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 		IPluginContext *pContext = plugin->GetBaseContext();
   128 		for (size_t i = 0; i < m_PanelHandlers.size(); i++)
   129 		{
   130 			if (m_PanelHandlers[i]->m_pPlugin == plugin)
   131 			{
   132 				m_PanelHandlers[i]->m_pPlugin = NULL;
   133 				m_PanelHandlers[i]->m_pFunc = NULL;
   134 			}
   135 		}
   136 	}
   137 
   138 	inline HandleType_t GetPanelType()
   139 	{
   140 		return m_PanelType;
   141 	}
   142 
   143 	CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
   144 	{
   145 		CPanelHandler *handler;
   146 		if (m_FreePanelHandlers.empty())
   147 		{
   148 			handler = new CPanelHandler;
   149 			m_PanelHandlers.push_back(handler);
   150 		} else {
   151 			handler = m_FreePanelHandlers.front();
   152 			m_FreePanelHandlers.pop();
   153 		}
   154 		handler->m_pFunc = pFunction;
   155 		handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
   156 		return handler;
   157 	}
   158 
   159 	void FreePanelHandler(CPanelHandler *handler)
   160 	{
   161 		handler->m_pFunc = NULL;
   162 		handler->m_pPlugin = NULL;
   163 		m_FreePanelHandlers.push(handler);
   164 	}
   165 
   166 	CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
   167 	{
   168 		CMenuHandler *handler;
   169 		if (m_FreeMenuHandlers.empty())
   170 		{
   171 			handler = new CMenuHandler(pFunction, flags);
   172 		} else {
   173 			handler = m_FreeMenuHandlers.front();
   174 			m_FreeMenuHandlers.pop();
   175 			handler->m_pBasic = pFunction;
   176 			handler->m_Flags = flags;
   177 		}
   178 		return handler;
   179 	}
   180 
   181 	void FreeMenuHandler(CMenuHandler *handler)
   182 	{
   183 		m_FreeMenuHandlers.push(handler);
   184 	}
   185 
   186 private:
   187 	HandleType_t m_PanelType;
   188 	CStack<CPanelHandler *> m_FreePanelHandlers;
   189 	CStack<CMenuHandler *> m_FreeMenuHandlers;
   190 	CVector<CPanelHandler *> m_PanelHandlers;
   191 } g_MenuHelpers;
   192 
   193 /**
   194  * BASIC PANEL HANDLER WRAPPER
   195  */
   196 
   197 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   198 {
   199 	if (m_pFunc)
   200 	{
   201 		m_pFunc->PushCell(BAD_HANDLE);
   202 		m_pFunc->PushCell(MenuAction_Cancel);
   203 		m_pFunc->PushCell(client);
   204 		m_pFunc->PushCell(reason);
   205 		m_pFunc->Execute(NULL);
   206 	}
   207 	g_MenuHelpers.FreePanelHandler(this);
   208 }
   209 
   210 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   211 {
   212 	if (m_pFunc)
   213 	{
   214 		m_pFunc->PushCell(BAD_HANDLE);
   215 		m_pFunc->PushCell(MenuAction_Select);
   216 		m_pFunc->PushCell(client);
   217 		m_pFunc->PushCell(item);
   218 		m_pFunc->Execute(NULL);
   219 	}
   220 	g_MenuHelpers.FreePanelHandler(this);
   221 }
   222 
   223 /**
   224  * MENU HANDLER WRAPPER
   225  */
   226 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) : 
   227 	m_pBasic(pBasic), m_Flags(flags)
   228 {
   229 	/* :TODO: We can probably cache the handle ahead of time */
   230 }
   231 
   232 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
   233 {
   234 	if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
   235 	{
   236 		m_pBasic->PushCell(menu->GetHandle());
   237 		m_pBasic->PushCell(MenuAction_Start);
   238 		m_pBasic->PushCell(0);
   239 		m_pBasic->PushCell(0);
   240 		m_pBasic->Execute(NULL);
   241 	}
   242 }
   243 
   244 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
   245 {
   246 	if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
   247 	{
   248 		HandleSecurity sec;
   249 		sec.pIdentity = m_pBasic->GetParentContext()->GetIdentity();
   250 		sec.pOwner = g_pCoreIdent;
   251 	
   252 		HandleAccess access;
   253 		g_HandleSys.InitAccessDefaults(NULL, &access);
   254 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   255 
   256 		Handle_t hndl =  g_HandleSys.CreateHandleEx(g_MenuHelpers.GetPanelType(), panel, &sec, &access, NULL);
   257 
   258 		m_pBasic->PushCell(menu->GetHandle());
   259 		m_pBasic->PushCell(MenuAction_Display);
   260 		m_pBasic->PushCell(client);
   261 		m_pBasic->PushCell(hndl);
   262 		m_pBasic->Execute(NULL);
   263 
   264 		g_HandleSys.FreeHandle(hndl, &sec);
   265 	}
   266 }
   267 
   268 void CMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   269 {
   270 	m_pBasic->PushCell(menu->GetHandle());
   271 	m_pBasic->PushCell(MenuAction_Select);
   272 	m_pBasic->PushCell(client);
   273 	m_pBasic->PushCell(item);
   274 	m_pBasic->Execute(NULL);
   275 }
   276 
   277 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   278 {
   279 	m_pBasic->PushCell(menu->GetHandle());
   280 	m_pBasic->PushCell(MenuAction_Cancel);
   281 	m_pBasic->PushCell(client);
   282 	m_pBasic->PushCell(reason);
   283 	m_pBasic->Execute(NULL);
   284 }
   285 
   286 void CMenuHandler::OnMenuEnd(IBaseMenu *menu)
   287 {
   288 	m_pBasic->PushCell(menu->GetHandle());
   289 	m_pBasic->PushCell(MenuAction_End);
   290 	m_pBasic->PushCell(0);
   291 	m_pBasic->PushCell(0);
   292 	m_pBasic->Execute(NULL);
   293 }
   294 
   295 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
   296 {
   297 	g_MenuHelpers.FreeMenuHandler(this);
   298 }
   299 
   300 /**
   301  * INLINE FUNCTIONS FOR NATIVES
   302  */
   303 
   304 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
   305 {
   306 	return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
   307 }
   308 
   309 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
   310 {
   311 	HandleSecurity sec;
   312 	sec.pIdentity = g_pCoreIdent;
   313 	sec.pOwner = NULL;
   314 	return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
   315 }
   316 
   317 inline IMenuStyle *GetStyleFromCell(cell_t cell)
   318 {
   319 	enum MenuStyle
   320 	{
   321 		MenuStyle_Default = 0,		/**< The "default" menu style for the mod */
   322 		MenuStyle_Valve = 1,		/**< The Valve provided menu style (Used on HL2DM) */
   323 		MenuStyle_Radio = 2,		/**< The simpler menu style commonly used on CS:S */
   324 	};
   325 
   326 	if (cell == MenuStyle_Valve)
   327 	{
   328 		return &g_ValveMenuStyle;
   329 	} else if (cell == MenuStyle_Radio
   330 			   && g_RadioMenuStyle.IsSupported())
   331 	{
   332 		return &g_RadioMenuStyle;
   333 	}
   334 
   335 	return g_Menus.GetDefaultStyle();
   336 }
   337 
   338 /***********************************
   339  **** NATIVE DEFINITIONS ***********
   340  ***********************************/
   341 
   342 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
   343 {
   344 	IMenuStyle *style = g_Menus.GetDefaultStyle();
   345 	IPluginFunction *pFunction;
   346 
   347 	if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
   348 	{
   349 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   350 	}
   351 
   352 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
   353 	IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
   354 
   355 	Handle_t hndl = menu->GetHandle();
   356 	if (!hndl)
   357 	{
   358 		menu->Destroy();
   359 		return BAD_HANDLE;
   360 	}
   361 
   362 	return hndl;
   363 }
   364 
   365 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
   366 {
   367 	Handle_t hndl = (Handle_t)params[1];
   368 	HandleError err;
   369 	IBaseMenu *menu;
   370 
   371 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   372 	{
   373 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   374 	}
   375 
   376 	return menu->Display(params[2], params[3]) ? 1 : 0;
   377 }
   378 
   379 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
   380 {
   381 	Handle_t hndl = (Handle_t)params[1];
   382 	HandleError err;
   383 	IBaseMenu *menu;
   384 
   385 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   386 	{
   387 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   388 	}
   389 
   390 	char *info;
   391 	ItemDrawInfo dr;
   392 
   393 	pContext->LocalToString(params[2], &info);
   394 	pContext->LocalToString(params[3], (char **)&dr.display);
   395 	dr.style = params[4];
   396 
   397 	return menu->AppendItem(info, dr) ? 1 : 0;
   398 }
   399 
   400 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
   401 {
   402 	Handle_t hndl = (Handle_t)params[1];
   403 	HandleError err;
   404 	IBaseMenu *menu;
   405 
   406 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   407 	{
   408 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   409 	}
   410 
   411 	char *info;
   412 	ItemDrawInfo dr;
   413 
   414 	pContext->LocalToString(params[3], &info);
   415 	pContext->LocalToString(params[4], (char **)&dr.display);
   416 	dr.style = params[5];
   417 
   418 	return menu->InsertItem(params[2], info, dr) ? 1 : 0;
   419 }
   420 
   421 static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
   422 {
   423 	Handle_t hndl = (Handle_t)params[1];
   424 	HandleError err;
   425 	IBaseMenu *menu;
   426 
   427 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   428 	{
   429 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   430 	}
   431 
   432 	return menu->RemoveItem(params[2]) ? 1 : 0;
   433 }
   434 
   435 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
   436 {
   437 	Handle_t hndl = (Handle_t)params[1];
   438 	HandleError err;
   439 	IBaseMenu *menu;
   440 
   441 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   442 	{
   443 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   444 	}
   445 
   446 	menu->RemoveAllItems();
   447 
   448 	return 1;
   449 }
   450 
   451 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
   452 {
   453 	Handle_t hndl = (Handle_t)params[1];
   454 	HandleError err;
   455 	IBaseMenu *menu;
   456 
   457 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   458 	{
   459 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   460 	}
   461 
   462 	ItemDrawInfo dr;
   463 	const char *info;
   464 
   465 	if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
   466 	{
   467 		return 0;
   468 	}
   469 
   470 	pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
   471 	pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
   472 
   473 	cell_t *addr;
   474 	pContext->LocalToPhysAddr(params[5], &addr);
   475 	*addr = dr.style;
   476 
   477 	return 1;
   478 }
   479 
   480 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
   481 {
   482 	Handle_t hndl = (Handle_t)params[1];
   483 	HandleError err;
   484 	IBaseMenu *menu;
   485 
   486 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   487 	{
   488 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   489 	}
   490 
   491 	return menu->SetPagination(params[2]) ? 1 : 0;
   492 }
   493 
   494 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
   495 {
   496 	Handle_t hndl = (Handle_t)params[1];
   497 	HandleError err;
   498 	IBaseMenu *menu;
   499 
   500 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   501 	{
   502 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   503 	}
   504 
   505 	return menu->GetPagination();
   506 }
   507 
   508 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
   509 {
   510 	Handle_t hndl = (Handle_t)params[1];
   511 	HandleError err;
   512 	IBaseMenu *menu;
   513 
   514 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   515 	{
   516 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   517 	}
   518 
   519 	return menu->GetItemCount();
   520 }
   521 
   522 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
   523 {
   524 	Handle_t hndl = (Handle_t)params[1];
   525 	HandleError err;
   526 	IBaseMenu *menu;
   527 
   528 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   529 	{
   530 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   531 	}
   532 
   533 	char buffer[1024];
   534 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   535 
   536 	menu->SetDefaultTitle(buffer);
   537 
   538 	return 1;
   539 }
   540 
   541 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
   542 {
   543 	Handle_t hndl = (Handle_t)params[1];
   544 	HandleError err;
   545 	IBaseMenu *menu;
   546 
   547 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   548 	{
   549 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   550 	}
   551 
   552 	IMenuPanel *panel = menu->CreatePanel();
   553 	hndl = MakePanelHandle(panel, pContext);
   554 	if (!hndl)
   555 	{
   556 		panel->DeleteThis();
   557 	}
   558 
   559 	return hndl;
   560 }
   561 
   562 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   563 {
   564 	Handle_t hndl = (Handle_t)params[1];
   565 	HandleError err;
   566 	IBaseMenu *menu;
   567 
   568 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   569 	{
   570 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   571 	}
   572 
   573 	return menu->GetExitButton() ? 1 : 0;
   574 }
   575 
   576 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   577 {
   578 	Handle_t hndl = (Handle_t)params[1];
   579 	HandleError err;
   580 	IBaseMenu *menu;
   581 
   582 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   583 	{
   584 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   585 	}
   586 
   587 	return menu->SetExitButton(params[2] ? true : false) ? 1 : 0;
   588 }
   589 
   590 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
   591 {
   592 	Handle_t hndl = (Handle_t)params[1];
   593 	HandleError err;
   594 	IBaseMenu *menu;
   595 
   596 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   597 	{
   598 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   599 	}
   600 
   601 	menu->Cancel();
   602 
   603 	return 1;
   604 }
   605 
   606 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
   607 {
   608 	Handle_t hndl = (Handle_t)params[1];
   609 	HandleError err;
   610 	IBaseMenu *menu;
   611 
   612 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   613 	{
   614 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   615 	}
   616 
   617 	return menu->GetDrawStyle()->GetHandle();
   618 }
   619 
   620 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
   621 {
   622 	IMenuStyle *style = GetStyleFromCell(params[1]);
   623 	if (!style)
   624 	{
   625 		return BAD_HANDLE;
   626 	}
   627 
   628 	return style->GetHandle();
   629 }
   630 
   631 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
   632 {
   633 	Handle_t hndl = (Handle_t)params[1];
   634 	HandleError err;
   635 	IMenuStyle *style;
   636 
   637 	if (hndl != 0)
   638 	{
   639 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   640 		{
   641 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   642 		}
   643 	} else {
   644 		style = g_Menus.GetDefaultStyle();
   645 	}
   646 
   647 	IMenuPanel *panel = style->CreatePanel();
   648 
   649 	hndl = MakePanelHandle(panel, pContext);
   650 	if (!hndl)
   651 	{
   652 		panel->DeleteThis();
   653 		return BAD_HANDLE;
   654 	}
   655 
   656 	return hndl;
   657 }
   658 
   659 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
   660 {
   661 	Handle_t hndl = (Handle_t)params[1];
   662 	HandleError err;
   663 	IMenuStyle *style;
   664 
   665 	if (hndl != 0)
   666 	{
   667 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   668 		{
   669 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   670 		}
   671 	} else {
   672 		style = g_Menus.GetDefaultStyle();
   673 	}
   674 
   675 	IPluginFunction *pFunction;
   676 	if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
   677 	{
   678 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
   679 	}
   680 
   681 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
   682 
   683 	IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
   684 	hndl = pMenu->GetHandle();
   685 	if (!hndl)
   686 	{
   687 		pMenu->Destroy();
   688 		return BAD_HANDLE;
   689 	}
   690 
   691 	return hndl;
   692 }
   693 
   694 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
   695 {
   696 	Handle_t hndl = (Handle_t)params[2];
   697 	HandleError err;
   698 	IMenuStyle *style;
   699 
   700 	if (hndl != 0)
   701 	{
   702 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   703 		{
   704 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   705 		}
   706 	} else {
   707 		style = g_Menus.GetDefaultStyle();
   708 	}
   709 
   710 	return style->GetClientMenu(params[1], NULL);
   711 }
   712 
   713 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
   714 {
   715 	Handle_t hndl = (Handle_t)params[3];
   716 	HandleError err;
   717 	IMenuStyle *style;
   718 
   719 	if (hndl != 0)
   720 	{
   721 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   722 		{
   723 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   724 		}
   725 	} else {
   726 		style = g_Menus.GetDefaultStyle();
   727 	}
   728 
   729 	return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
   730 }
   731 
   732 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
   733 {
   734 	Handle_t hndl = (Handle_t)params[1];
   735 	HandleError err;
   736 	IMenuStyle *style;
   737 
   738 	if (hndl != 0)
   739 	{
   740 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   741 		{
   742 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   743 		}
   744 	} else {
   745 		style = g_Menus.GetDefaultStyle();
   746 	}
   747 
   748 	return style->GetMaxPageItems();
   749 }
   750 
   751 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
   752 {
   753 	Handle_t hndl = (Handle_t)params[1];
   754 	HandleError err;
   755 	IMenuPanel *panel;
   756 
   757 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   758 	{
   759 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   760 	}
   761 
   762 	return panel->GetParentStyle()->GetHandle();
   763 }
   764 
   765 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
   766 {
   767 	Handle_t hndl = (Handle_t)params[1];
   768 	HandleError err;
   769 	IMenuPanel *panel;
   770 
   771 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   772 	{
   773 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   774 	}
   775 
   776 	char *text;
   777 	pContext->LocalToString(params[2], &text);
   778 
   779 	panel->DrawTitle(text, params[3] ? true : false);
   780 
   781 	return 1;
   782 }
   783 
   784 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
   785 {
   786 	Handle_t hndl = (Handle_t)params[1];
   787 	HandleError err;
   788 	IMenuPanel *panel;
   789 
   790 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   791 	{
   792 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   793 	}
   794 
   795 	ItemDrawInfo dr;
   796 	pContext->LocalToString(params[2], (char **)&dr.display);
   797 	dr.style = params[3];
   798 
   799 	return panel->DrawItem(dr);
   800 }
   801 
   802 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
   803 {
   804 	Handle_t hndl = (Handle_t)params[1];
   805 	HandleError err;
   806 	IMenuPanel *panel;
   807 
   808 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   809 	{
   810 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   811 	}
   812 
   813 	char *text;
   814 	pContext->LocalToString(params[2], &text);
   815 
   816 	return panel->DrawRawLine(text) ? 1 : 0;
   817 }
   818 
   819 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
   820 {
   821 	Handle_t hndl = (Handle_t)params[1];
   822 	HandleError err;
   823 	IMenuPanel *panel;
   824 
   825 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   826 	{
   827 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   828 	}
   829 
   830 	return panel->CanDrawItem(params[2]);
   831 }
   832 
   833 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
   834 {
   835 	Handle_t hndl = (Handle_t)params[1];
   836 	HandleError err;
   837 	IMenuPanel *panel;
   838 
   839 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   840 	{
   841 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   842 	}
   843 
   844 	IPluginFunction *pFunction;
   845 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
   846 	{
   847 		return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
   848 	}
   849 
   850 	CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
   851 	if (!panel->SendDisplay(params[2], handler, params[4]))
   852 	{
   853 		g_MenuHelpers.FreePanelHandler(handler);
   854 	}
   855 
   856 	return 1;
   857 }
   858 
   859 REGISTER_NATIVES(menuNatives)
   860 {
   861 	{"AddMenuItem",				AddMenuItem},
   862 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
   863 	{"CancelClientMenu",		CancelClientMenu},
   864 	{"CancelMenu",				CancelMenu},
   865 	{"CreateMenu",				CreateMenu},
   866 	{"CreateMenuEx",			CreateMenuEx},
   867 	{"CreatePanel",				CreatePanel},
   868 	{"CreatePanelFromMenu",		CreatePanelFromMenu},\
   869 	{"DisplayMenu",				DisplayMenu},
   870 	{"DrawPanelItem",			DrawPanelItem},
   871 	{"DrawPanelText",			DrawPanelText},
   872 	{"GetClientMenu",			GetClientMenu},
   873 	{"GetMaxPageItems",			GetMaxPageItems},
   874 	{"GetMenuExitButton",		GetMenuExitButton},
   875 	{"GetMenuItem",				GetMenuItem},
   876 	{"GetMenuItemCount",		GetMenuItemCount},
   877 	{"GetMenuPagination",		GetMenuPagination},
   878 	{"GetMenuStyle",			GetMenuStyle},
   879 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
   880 	{"GetPanelStyle",			GetPanelStyle},
   881 	{"InsertMenuItem",			InsertMenuItem},
   882 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
   883 	{"RemoveMenuItem",			RemoveMenuItem},
   884 	{"SendPanelToClient",		SendPanelToClient},
   885 	{"SetMenuExitButton",		SetMenuExitButton},
   886 	{"SetMenuPagination",		SetMenuPagination},
   887 	{"SetMenuTitle",			SetMenuTitle},
   888 	{"SetPanelTitle",			SetPanelTitle},
   889 	{NULL,						NULL},
   890 };