From ddc1f6b0e3c9690822508ade7a7db8dc035287f6 Mon Sep 17 00:00:00 2001 From: faded <> Date: Tue, 28 Jan 2003 17:28:23 +0000 Subject: [PATCH] added a new menuing system based on xul-like files --- xmlmenu.c | 2101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ xmlmenu.h | 120 +++ 2 files changed, 2221 insertions(+) create mode 100644 xmlmenu.c create mode 100644 xmlmenu.h diff --git a/xmlmenu.c b/xmlmenu.c new file mode 100644 index 0000000..1a339d1 --- /dev/null +++ b/xmlmenu.c @@ -0,0 +1,2101 @@ +/* +Copyright (C) 2003 Tenebrae Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + + +/* + -DC- + a pretty inefficient way of implementing XUL elements ;) + most of the attributes aren't supported + +*/ + +#include "quakedef.h" + +#ifdef _WIN32 +#include "winquake.h" +#endif + + +#define MAX_MLABEL 128 + + +cvar_t m_debug = {"m_debug","0"}; + + +typedef struct xmlimagedata_s +{ + char src[MAX_OSPATH]; +} xmlimagedata_t; + + +typedef struct xmlbuttondata_s +{ + int disabled:1; + int isdown:1; + char label[MAX_MLABEL]; + char command[255]; +} xmlbuttondata_t; + + +typedef struct xmlcheckboxdata_s +{ + int disabled:1; + int checked:1; + char label[MAX_MLABEL]; +} xmlcheckboxdata_t; + + +typedef struct xmllabeldata_s +{ + char text[MAX_MLABEL]; +} xmllabeldata_t; + +typedef struct xmlsliderdata_s +{ + int range; + int cursor; + char label[MAX_MLABEL]; +} xmlsliderdata_t; + +typedef struct xmlradiodata_s +{ + int disabled:1; + int selected:1; + char label[MAX_MLABEL]; +} xmlradiodata_t; + +typedef struct xmlradiogroupdata_s +{ + int disabled:1; + char label[MAX_MLABEL]; +} xmlradiogroupdata_t; + + +typedef struct xmlmenudata_s +{ +} xmlmenudata_t; + +/* + xml window data + */ + +typedef struct xmlwindowdata_s +{ +} xmlwindowdata_t; + +typedef struct qwindow_s { + char file[MAX_OSPATH]; + qwidget_t *widget; + struct qwindow_s *stack; // visible window stack + struct qwindow_s *next; // available window list +} qwindow_t; + + +xmlhandler_t widgethandlers[] = +{ + { + (const xmlChar *)"box", + { + 0, // debug + 0, // focusable + 0, // orient = horizontal + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlBox, // Load + NULL, // Draw + NULL, // Focus + M_XmlBoxKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + 0 + }, + {(const xmlChar *)"hbox", + { + 0, // debug + 0, // focusable + 0, // orient = horizontal + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlBox, // Load + NULL, // Draw + NULL, // Focus + M_XmlBoxKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + 0 + }, + { + (const xmlChar *)"vbox", + { + 0, // debug + 0, // focusable + 1, // orient = vertical + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlBox, // Load + NULL, // Draw + NULL, // Focus + M_XmlBoxKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + 0 + }, + { + (const xmlChar *)"label", + { + 0, // debug + 0, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlLabel, // Load + M_DrawXmlLabel, // Draw + NULL, // Focus + NULL, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmllabeldata_t) + }, + { + (const xmlChar *)"image", + { + 0, // debug + 0, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlImage, // Load + M_DrawXmlImage, // Draw + NULL, // Focus + NULL, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmlimagedata_t) + }, + { + (const xmlChar *)"button", + { + 0, // debug + 1, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlButton, // Load + M_DrawXmlButton, // Draw + NULL, // Focus + M_XmlButtonKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmlbuttondata_t) + }, + { + (const xmlChar *)"menuitem", + { + 0, // debug + 1, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlButton, // Load + M_DrawXmlButton, // Draw + NULL, // Focus + M_XmlButtonKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmlbuttondata_t) + }, + { + (const xmlChar *)"checkbox", + { + 0, // debug + 1, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlCheckBox, // Load + M_DrawXmlCheckBox, // Draw + NULL, // Focus + M_XmlCheckBoxKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmlcheckboxdata_t) + }, + { + (const xmlChar *)"radio", + { + 0, // debug + 1, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlRadio, // Load + M_DrawXmlRadio, // Draw + NULL, // Focus + M_XmlRadioKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmlradiodata_t) + }, + { + (const xmlChar *)"radiogroup", + { + 0, // debug + 1, // focusable + 1, // orient = vertical + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlRadioGroup, // Load + M_DrawXmlRadioGroup, // Draw + NULL, // Focus + M_XmlRadioGroupKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmlradiogroupdata_t) + }, + { + (const xmlChar *)"slider", + { + 0, // debug + 1, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlSlider, // Load + M_DrawXmlSlider, // Draw + NULL, // Focus + M_XmlSliderKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(xmlsliderdata_t) + }, + { + (const xmlChar *)"window", + { + 0, // debug + 0, // focusable + 0, // orient + a_stretch, // align + p_start, // pack + "", // name + "", // id + 0, // xpos + 0, // ypos + {0,0}, // width + {0,0}, // height + 0, // num_children + NULL, // tag + NULL, // parent +// NULL, // root + NULL, // previous + NULL, // next + NULL, // children + NULL, // rchildren + // functions + M_LoadXmlWindow, // Load + NULL, // Draw + NULL, // Focus + M_XmlWindowKey, // HandleKey + NULL, // onMouseOver + NULL, // onMouseDown + NULL, // onMouseUp + NULL // data + }, + sizeof(qwindow_t) + }, + NULL +}; + +xmlChar *xmlalign[] = { + "start", + "center", + "end", + "baseline", + "stretch" +}; + +xmlChar *xmlpack[] = { + "start", + "center", + "end" +}; + +xmlChar *xmlorient[] = { + "horizontal", + "vertical" +}; + +xmlChar *xmlbool[] = { + "false", + "true" +}; + +/* -DC- + * perhaps we could use xml menus to define hud ? + * qwidget_t *hud_window; + */ + +void M_DrawXmlNestedWidget (qwidget_t *widget, int xpos, int ypos); + +qwindow_t *windows = NULL; + + +qwindow_t *visible_window; +qwidget_t *focused; + + + +// ------------------------------------------------------------------------------------- + +/* -DC- useless for now + + +typedef struct qwiterator_s { + qwidget_t *w; + void *(next_f)(struct qwiterator_s *self); +} qwiterator_t; + +void iterator_next (qwiterator_t *self) +{ + qwidget_t *w = self->w; + if (w->next) { + self->w = w->next; + else + self->w = NULL; +} + +void iterator_prev (qwiterator_t *self) +{ + qwidget_t *w = self->w; + if (w->previous) + self->w = w->previous; + else + self->w = NULL; +} + +void treeiterator_next (qwiterator_t *self) +{ + qwidget_t *w = self->w; + if (w->children) + self->w = w->children; + else if (!w->next) + while (w->parent && !w->next) + w = w->parent; + self->w = w->next; +} + +void treeiterator_prev (qwiterator_t *self) +{ + qwidget_t *w = self->w; + if (w->rchildren) + self->w = w->rchildren; + else if (!w->previous) + while (w->parent && !w->previous) + w = w->parent; + self->w = w->previous; +} + +*/ + +// ------------------------------------------------------------------------------------- + +qboolean m_entersound; // play after drawing a frame, so caching + // won't disrupt the sound +qboolean m_recursiveDraw; + +int m_return_state; +qboolean m_return_onerror; +char m_return_reason [32]; + + +extern cvar_t con_spiral; + +enum {m_none, m_menu } m_state; + + + +// ------------------------------------------------------------------------------------- + +void M_StartGame_f (void) +{ + if (sv.active) { + if (!SCR_ModalMessage("Are you sure you want to\nstart a new game?\n")) + return; + Cbuf_AddText ("disconnect\n"); + } + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map start\n"); +} + + + +/* -DC- + FIXME : + change Host_Quit_f in host_cmd to M_Quit_f code +*/ +void M_Quit_f (void) +{ + CL_Disconnect (); + Host_ShutdownServer(false); + Sys_Quit (); +} + + +void M_OpenWindow_f (void) +{ + qwindow_t *w = windows; + char *name; + if (Cmd_Argc() != 2) + { + Con_Printf ("openwindow : bring up the window called 'name'\n"); + return; + } + name = Cmd_Argv(1); + while (w){ + if (!strncmp (w->widget->id, name, sizeof(w->widget->id))) + break; + w = w->next; + } + if (!w) { + Con_Printf ("openwindow : no such window '%s'\n", name); + return; + } + if (visible_window != w){ + w->stack = visible_window; + visible_window = w; + focused = w->widget; + M_CycleFocusNext (); + } + key_dest = key_menu; + m_state = m_menu; +} + +void M_CloseWindow_f (void) +{ + if (visible_window != NULL){ + visible_window = visible_window->stack; + } + if (visible_window == NULL) + key_dest = key_game; + else { + focused = visible_window->widget; + M_CycleFocusNext (); + } + +} + +void M_WindowList_f (void) +{ + qwindow_t *w = windows; + while (w){ + Con_Printf ("%s\n", w->widget->id); + w = w->next; + } + +} + +void M_Init (void) +{ + + Cmd_AddCommand ("startgame", M_StartGame_f); + Cmd_AddCommand ("openwindow", M_OpenWindow_f); + Cmd_AddCommand ("closewindow", M_CloseWindow_f); + Cmd_AddCommand ("windowlist", M_WindowList_f); + Cvar_RegisterVariable (&m_debug); + // let's load the whole stuff now + COM_FindAllExt ("menu", "xul", M_LoadXmlWindowFile); + +} + +/* +======== +M_Menu_Main_f +M_Menu_Quit_f + +both functions are used in another part of the code (console.c) so I implemented them as stub to +the console commands they are bound to originally to concentrate the menus change in one file. +So basically here we're just expecting that some other part of the game will have defined these commands/aliases/whatever +(xmlmenu should make them as aliases : check out M_LoadXmlWindow). +======== +*/ +void M_Menu_Main_f (void) +{ + Cbuf_AddText ("menu_main\n"); +} + +void M_Menu_Quit_f (void) +{ + M_Quit_f (); +} + + +/* +======== +M_Keydown + +recursive function set : this one is at top level, and calls the focused widget key handler +======== +*/ +void M_Keydown (int key) +{ + if (focused && focused->HandleKey) + M_XmlElementKey (focused,key); + else + M_XmlElementKey (visible_window->widget, key); + // current window get the key +} + +// ------------------------------------------------------------------------------------- + +void M_Print (int cx, int cy, char *str) +{ + while (*str) + { + Draw_Character (cx, cy, (*str)+128); + str++; + cx += 8; + } +} + +void M_DrawTransPic (int x, int y, qpic_t *pic) +{ + Draw_TransPic (x + ((vid.width - 320)>>1), y, pic); +} + +void M_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x + ((vid.width - 320)>>1), y, pic); +} + +byte identityTable[256]; +byte translationTable[256]; + +void M_BuildTranslationTable(int top, int bottom) +{ + int j; + byte *dest, *source; + + for (j = 0; j < 256; j++) + identityTable[j] = j; + dest = translationTable; + source = identityTable; + memcpy (dest, source, 256); + + if (top < 128) // the artists made some backwards ranges. sigh. + memcpy (dest + TOP_RANGE, source + top, 16); + else + for (j=0 ; j<16 ; j++) + dest[TOP_RANGE+j] = source[top+15-j]; + + if (bottom < 128) + memcpy (dest + BOTTOM_RANGE, source + bottom, 16); + else + for (j=0 ; j<16 ; j++) + dest[BOTTOM_RANGE+j] = source[bottom+15-j]; +} + +void M_DrawTransPicTranslate (int x, int y, qpic_t *pic) +{ + Draw_TransPicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable); +} + + +void M_DrawTextBox (int x, int y, int width, int lines) +{ + qpic_t *p; + int cx, cy; + int n; + + // draw left side + cx = x; + cy = y; + p = Draw_CachePic ("gfx/box_tl.lmp"); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_ml.lmp"); + for (n = 0; n < lines; n++) + { + cy += 8; + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bl.lmp"); + M_DrawTransPic (cx, cy+8, p); + + // draw middle + cx += 8; + while (width > 0) + { + cy = y; + p = Draw_CachePic ("gfx/box_tm.lmp"); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mm.lmp"); + for (n = 0; n < lines; n++) + { + cy += 8; + if (n == 1) + p = Draw_CachePic ("gfx/box_mm2.lmp"); + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_bm.lmp"); + M_DrawTransPic (cx, cy+8, p); + width -= 2; + cx += 16; + } + + // draw right side + cy = y; + p = Draw_CachePic ("gfx/box_tr.lmp"); + M_DrawTransPic (cx, cy, p); + p = Draw_CachePic ("gfx/box_mr.lmp"); + for (n = 0; n < lines; n++) + { + cy += 8; + M_DrawTransPic (cx, cy, p); + } + p = Draw_CachePic ("gfx/box_br.lmp"); + M_DrawTransPic (cx, cy+8, p); +} + +void M_ToggleMenu_f (void) +{ + m_entersound = true; + + if (key_dest == key_menu) + { + // if not main menu + { + M_Menu_Main_f (); + return; + } + key_dest = key_game; + m_state = m_none; + return; + } + if (key_dest == key_console) + { + Con_ToggleConsole_f (); + } + else + { + M_Menu_Main_f (); + } +} + +// ------------------------------------------------------------------------------------- + +void M_XmlButtonCommand (qwidget_t *self) +{ + xmlbuttondata_t *data = self->data; + Cbuf_AddText (data->command); + Cbuf_AddText ("\n"); +} + +// ------------------------------------------------------------------------------------- + + +qwidget_t *M_NextXmlElement (qwidget_t *w) +{ + if (w->children) + return w->children; + if (w->next) + return w->next; + while (w->parent && !w->next) + w = w->parent; + if (!w->next) + return visible_window->widget; + return w->next; +} + +qwidget_t *M_PreviousXmlElement (qwidget_t *w) +{ + if (w->rchildren) + return w->rchildren; + if (w->previous) + return w->previous; + while (w->parent && !w->previous) + w = w->parent; + if (!w->next) + return visible_window->widget; + return w->previous; +} + +void M_CycleFocus (qwidget_t *(*next_f)(qwidget_t *)) +{ + qwidget_t *w = focused; + do { + w = next_f (w); + } while ((w != focused) && !(w->focusable)); + focused = w; +} + +// ------------------------------------------------------------------------------------- + +qboolean M_XmlBoxKey (qwidget_t *self, int k) +{ + if (!self->children) + return false; + + switch (k) + { + case K_UPARROW: + S_LocalSound ("misc/menu1.wav"); + M_CycleFocusPrevious (); + break; + + case K_DOWNARROW: + case K_TAB: + S_LocalSound ("misc/menu1.wav"); + M_CycleFocusNext (); + break; + case K_ESCAPE: + // pop up previous menu + S_LocalSound ("misc/menu2.wav"); + M_CloseWindow_f (); + break; + default: + return false; + } + return true; +} + +qboolean M_XmlCheckBoxKey (qwidget_t *self, int k) +{ + xmlcheckboxdata_t *data = self->data; + switch (k) + { + case K_ENTER: + // (un)check the checkbox + S_LocalSound ("misc/menu2.wav"); + data->checked = !data->checked; + Cvar_SetValue (self->name, data->checked); + break; + default: + return false; + } + return true; +} + + +qboolean M_XmlButtonKey (qwidget_t *self, int k) +{ + switch (k) + { + case K_ENTER: + S_LocalSound ("misc/menu2.wav"); + M_XmlButtonCommand (self); + break; + default: + return false; + } + return true; +} + +qboolean M_XmlRadioKey (qwidget_t *self, int k) +{ + switch (k) + { + case K_ENTER: + // select the radio button + break; + default: + return false; + } + return true; +} + +qboolean M_XmlRadioGroupKey (qwidget_t *self, int k) +{ + return M_XmlBoxKey (self,k); +} + + +qboolean M_XmlSliderKey (qwidget_t *self, int k) +{ + xmlsliderdata_t *data = self->data; + switch (k) + { + case K_LEFTARROW: + // reduce cvar value + S_LocalSound ("misc/menu3.wav"); + + break; + case K_RIGHTARROW: + // augment cvar value + S_LocalSound ("misc/menu3.wav"); + break; + default: + return false; + } + return true; +} + +qboolean M_XmlWindowKey (qwidget_t *self, int k) +{ + return M_XmlBoxKey (self,k); +} + +qboolean M_XmlMenuKey (qwidget_t *self, int k) +{ + return M_XmlBoxKey (self,k); +} + +qboolean M_XmlElementKey (qwidget_t *widget, int k) +{ + // start from the current focused widget + // and propagate the key up the parents tree + // until it has been consumed + while (widget){ + if (widget->HandleKey) + if (widget->HandleKey (widget, k)) + return true; + widget = widget->parent; + } + return false; +} + +// ------------------------------------------------------------------------------------- + +/* +================ +Draw_StringN +================ +*/ +int Draw_StringN (int x, int y, char *str, int len) +{ + while (*str && len) + { + Draw_Character (x, y, (*str)+128); + str++; + x += 8; + len--; + } + return x; +} + + +void M_Draw (void) +{ + + if (m_state == m_none || key_dest != key_menu) + return; + + if (!m_recursiveDraw) + { + scr_copyeverything = 1; + + if (scr_con_current) + { + if (con_spiral.value) //Console Spiral - Eradicator + Draw_SpiralConsoleBackground (vid.height); + else + Draw_ConsoleBackground (vid.height); + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); + } + else + Draw_FadeScreen (); + + scr_fullupdate = 0; + } + else + { + m_recursiveDraw = false; + } + + // draw current window + M_DrawXmlNestedWidget (visible_window->widget, 0, 0); + + if (m_entersound) + { + S_LocalSound ("misc/menu2.wav"); + m_entersound = false; + } + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); +} + + +void M_DrawXmlButton (qwidget_t *self, int x, int y) +{ + xmlbuttondata_t *data = self->data; + Draw_StringN (x, y, data->label, sizeof(data->label)); +} + +void M_DrawXmlImage (qwidget_t *self, int x, int y) +{ + xmlimagedata_t *data = self->data; + Draw_TransPicFilled (x, y, x+self->width.absolute, y+self->height.absolute, Draw_CachePic (data->src)); +} + +void M_DrawXmlLabel (qwidget_t *self, int x, int y) +{ + xmllabeldata_t *data = self->data; + Draw_StringN (x, y, data->text, sizeof(data->text)); +} + +void M_DrawXmlBox (qwidget_t *self, int x, int y) +{ + // nothing to do + +} + +void M_DrawXmlRadio (qwidget_t *self, int x, int y) +{ + xmlradiodata_t *data = self->data; + qboolean on = data->selected; + Draw_StringN (x, y, data->label, sizeof(data->label)); + if (on) + M_Print (x, y, "(o)"); + else + M_Print (x, y, "( )"); +} + +void M_DrawXmlRadioGroup (qwidget_t *self, int x, int y) +{ + // nothing to do +} + +// almost pasted from menu.c + +#define SLIDER_RANGE 10 + +void M_DrawXmlSlider (qwidget_t *self, int x, int y) +{ + int i; + xmlsliderdata_t *data = self->data; + int range = data->range; + + x += 8; + + if (range < 0) + range = 0; + if (range > 1) + range = 1; + + Draw_Character (x-8, y, 128); + for (i=0 ; idata; + qboolean on = data->checked; + + x = Draw_StringN (x, y, data->label, sizeof(data->label)); + + if (on) + M_Print (x, y, "[x]"); + else + M_Print (x, y, "[ ]"); +} + +void M_DrawXmlWindow (qwidget_t *self,int x, int y) +{ + qwindow_t *data = self->data; + // draw a border ? +} + +/* -DC- + FIXME : doesn't work at all + So this function is a little hack to display widget outline + just for debugging purpose + -> There is certainly a (better) way to do this + cause I don't know much about OpenGL (shame on me ^^;) +*/ + +void M_DrawFocus (qwidget_t *self, int x, int y){ + //Draw_TransPicFilled (x, y, x+self->width.absolute, y+self->height.absolute, Draw_CachePic ("menu/focus.png")); +} + + +void M_DrawOutlines (qwidget_t *self, int x, int y){ + int xs = x + self->width.absolute; + int ys = x + self->height.absolute; + GLint color[4]; + GLint shademodel; + GL_DisableMultitexture (); + glGetIntegerv(GL_CURRENT_COLOR, color); + glColor4b(1,1,1,1); + //glGetIntegerv(GL_SHADE_MODEL, &shademodel); + //glShadeModel (GL_FLAT); + glBegin (GL_LINE_STRIP); + glVertex2f (x, y); + glVertex2f (xs, y); + glVertex2f (xs, ys); + glVertex2f (x, ys); + glEnd (); + //glShadeModel (shademodel); + glColor4iv(color); + GL_EnableMultitexture (); +} + +/* -DC- + instead of having a function handling recursive drawing of widget + we could perhaps put this code in widget rendering code directly + -> more flexible if some widget need to do stuff in between children + rendering + -> but code duplication + + */ + +void M_DrawXmlNestedWidget (qwidget_t *widget, int xpos, int ypos) +{ + qwidget_t *w; + + + if (widget->Draw){ + widget->Draw (widget, xpos, ypos); + } + if (widget == focused){ + M_DrawFocus (widget, xpos, ypos); + } + if (m_debug.value) { + // draw outlines + M_DrawOutlines (widget, xpos, ypos); + } + if (widget->children == NULL) + return; + // vertical orientation + if (widget->orient) { + int wwidth; + //ypos += widget->yoffset; + switch (widget->pack){ + case p_start: + for (w = widget->children; w != NULL; w = w->next){ + M_DrawXmlNestedWidget (w, xpos, ypos); + ypos += w->height.absolute; + } + break; + case p_center: + for (w = widget->children; w != NULL; w = w->next){ + wwidth = widget->width.absolute - w->width.absolute; + M_DrawXmlNestedWidget (w, xpos+wwidth/2, ypos); + ypos += w->height.absolute; + } + break; + case p_end: + for (w = widget->children; w != NULL; w = w->next){ + wwidth = widget->width.absolute - w->width.absolute; + M_DrawXmlNestedWidget (w, xpos+wwidth, ypos); + ypos += w->height.absolute; + } + break; + } + } + else { + int wheight; + //xpos += widget->xoffset; + switch (widget->pack){ + case p_start: + for (w = widget->children; w != NULL; w = w->next){ + M_DrawXmlNestedWidget (w, xpos, ypos); + xpos += w->width.absolute; + } + break; + case p_center: + for (w = widget->children; w != NULL; w = w->next){ + wheight = - w->height.absolute + widget->height.absolute; + M_DrawXmlNestedWidget (w, xpos, ypos+wheight/2); + xpos += w->width.absolute; + } + break; + case p_end: + for (w = widget->children; w != NULL; w = w->next){ + wheight = - w->height.absolute + widget->height.absolute; + M_DrawXmlNestedWidget (w, xpos, ypos+wheight); + xpos += w->width.absolute; + } + break; + } + } +} + + +// ------------------------------------------------------------------------------------- + +int M_ReadXmlPropAsInt (xmlNodePtr node, char *name, int defvalue) +{ + int ret; + int p; + xmlChar *value; + value = xmlGetProp (node, name); + if (value){ + ret = atoi (value); + xmlFree (value); + } else + ret = defvalue; + return ret; +} + +int M_ReadXmlPropAsFloat (xmlNodePtr node, char *name, float defvalue) +{ + float ret; + xmlChar *value; + value = xmlGetProp (node, name); + if (value) { + ret = atof (value); + xmlFree (value); + } else + ret = defvalue; + return ret; +} + +char *M_ReadXmlPropAsString (xmlNodePtr node, char *name, char *ret, int size) +{ + xmlChar *value; + value = xmlGetProp (node, name); + if (value){ + strncpy (ret, (char *)value, size); + xmlFree (value); + } else + *ret='\0'; + + return ret; +} + +int M_CompareXmlProp (xmlNodePtr node, char *name, xmlChar **str, int count) +{ + int ret; + xmlChar *value; + value = xmlGetProp (node, name); + if (!value) + return -1; + for (ret = 0; ret < count; ret++){ + if (strcmp ((char *)value, (char *)str[ret]) == 0){ + xmlFree (value); + return ret; + } + } + xmlFree (value); + return -1; +} + +void M_ReadXmlDim (xmlNodePtr node, char *name, xmldim_t *dim) +{ + char *p; + xmlChar *value; + value = xmlGetProp (node, name); + if (value) { + p = strchr (value,'%'); + if (p){ + *p = '\0'; + dim->ratio = atof (value) / 100; + *p = '%'; + dim->absolute = -1; + } + else { + dim->absolute = atoi (value); + dim->ratio = -1; + } + } + else { + dim->absolute = -1; + dim->ratio = -1; + } +} + +// ------------------------------------------------------------------------------------- + +void *M_AllocateMem (int size, void *template) +{ + // FIXME : allocate it in Cache ? + void *ret = Hunk_AllocName (size,"xmlmenu"); + if (template) + memcpy (ret, template, size); + else + memset (ret,0,size); + return ret; +} + + +void M_RemoveXmlWidget (qwidget_t *owner, qwidget_t *child) +{ + if (child->parent == owner) { + if (child->previous) + child->previous->next = child->next; + else + owner->children = child->next; + if (child->next) + child->next->previous = child->previous; + else + owner->rchildren = child->previous; + owner->num_children--; + child->next = child->previous = NULL; + } +} + +void M_InsertXmlWidget (qwidget_t *owner, qwidget_t *child) +{ + + child->parent = owner; + owner->num_children++; + if (owner->rchildren) { + qwidget_t *w = owner->rchildren; + child->next = NULL; + owner->rchildren = child; + w->next = child; + child->previous = w; + } + else { + owner->children = child; + owner->rchildren = child; + } +} + + +// ------------------------------------------------------------------------------------- + +/* +================ +M_BuildHardcodedMenu + +Build a default menu set +FIXME : should make something here and add a call in case no menu have been loaded + (perhaps hardcode the original quake menu) +================ +*/ + +void M_BuildHardcodedMenus () +{ + // check legacy aliases (menu_main etc...) +} + +// ------------------------------------------------------------------------------------- + + +/* +================ +M_CalculateWidgetDim + +evaluate widgets size and offsets recursively +The goal is to avoid recalculating everything for each frame +================ +*/ + +void M_CalculateWidgetDim (qwidget_t *root) +{ + // we don't want unecessary data on the stack -> so let's make them static + qwidget_t *w; + static int num; // undimensioned widget counter + static int width; // remaining width + static int height; // and height + + if (root->children == NULL) + return; + + num = 0; + //Con_DPrintf("%s (%d,%d)\n",root->tag,root->width.absolute,root->height.absolute); + width = root->width.absolute; + height = root->height.absolute; + + if (root->orient){ // vertical orientation + for (w = root->children; w != NULL; w = w->next){ + if (w->height.ratio != -1) + w->height.absolute = (root->height.absolute * w->height.ratio); + if (w->height.absolute != -1) + height -= w->height.absolute; + else num++; + if (w->width.ratio != -1) + w->width.absolute = (root->width.absolute * w->width.ratio); + if (w->width.absolute == -1) + w->width.absolute = root->width.absolute; + } + if (num) { + // widget without defined dimension take a part of the remaining space + for (w = root->children; w != NULL; w = w->next) + if (w->height.absolute == -1) + w->height.absolute = height / num; + } else { + // check for remaining space and set x offset according to the align properties + if (width) + // if orient == vertical -> align changes x offset + switch (root->align) { + case a_start: + root->xoffset = 0; + break; + case a_center: + root->xoffset = width / 2; + break; + case a_end: + root->xoffset = width; + break; + case a_baseline: + // not supported for vertical boxes + root->xoffset = 0; + break; + case a_stretch: + // search for first flexible child and resize it + // FIXME : support flex attribute + root->xoffset = 0; + break; + } + } + + } + else { // horizontal orientation + for (w = root->children; w != NULL; w = w->next){ + if (w->width.ratio != -1) + w->width.absolute = (root->width.absolute * w->width.ratio); + if (w->width.absolute != -1) + width -= w->width.absolute; + else num++; + if (w->height.ratio != -1) + w->height.absolute = (height * w->height.ratio); + if (w->height.absolute == -1) + w->height.absolute = height; + } + if (num) { + // widget without defined dimension take a part of the remaining space + for (w = root->children; w != NULL; w = w->next) + if (w->width.absolute == -1) + w->width.absolute = width / num; + } else { + // check for remaining space and set y offset according to the align properties + if (height) + // if orient == horizontal -> align changes y offset + switch (root->align) { + case a_start: + root->yoffset = 0; + break; + case a_center: + root->yoffset = height / 2; + break; + case a_end: + root->yoffset = height; + break; + case a_baseline: + // FIXME: here we should look for label + // in children and align them accordingly + // but it's getting complicated + root->yoffset = 0; + break; + case a_stretch: + // search for first flexible child and resize it + // FIXME : support flex attribute + root->yoffset = 0; + break; + } + } + } + // let's do it for the children now + for (w = root->children; w != NULL; w = w->next) + M_CalculateWidgetDim(w); +} + +// ------------------------------------------------------------------------------------- + +/* +================ +M_LoadXml* + +Load a Xml Element definition from the provided libxml tree pointer +================ +*/ + + +void M_LoadXmlBox (qwidget_t *widget, xmlNodePtr node) +{ + + /* + * --- supported attributes : + * --- unsupported : + */ + + +} + +void M_LoadXmlLabel (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + control = string : xul element id + disabled = bool + value = string : label text + + * --- unsupported : + + accesskey = string + + */ + xmllabeldata_t *data = widget->data; + M_ReadXmlPropAsString (node, "value", data->text, sizeof (data->text)); +} + +void M_LoadXmlImage (qwidget_t *widget, xmlNodePtr node) +{ + + /* + * --- supported attributes : + + src = URL + + * --- unsupported : + + onerror = callback + onload = callback + validate = {always|never} : load image from cache + + */ + xmlimagedata_t *data = widget->data; + + M_ReadXmlPropAsString (node, "src", data->src, sizeof (data->src)); +} + + +void M_LoadXmlButton (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + command = string + disabled = bool + label = string + + * --- unsupported : + + image = URL + accesskey = string : shortcut key + autoCheck = bool ? + checked = bool ? + checkState = bool ? + crop = {start|end|center|none} : how the text is cropped when too large + dlgType = {accept|cancel|help|disclosure} + group = int + open = ? + tabindex = int : tab order + type = {checkbox|menu|menu-button|radio} + value = string ? : user attribute + + */ + xmlbuttondata_t *data = widget->data; + int temp; + + M_ReadXmlPropAsString (node, "command", data->command, sizeof(data->command)); + M_ReadXmlPropAsString (node, "label", data->label, sizeof(data->label)); + temp = M_CompareXmlProp (node, "disabled", xmlbool, 2); + if (temp != -1) + data->disabled = temp; + +} + +void M_LoadXmlCheckBox (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + disabled = bool + label = string + + * --- unsupported : + + accesskey = string : shortcut key + autoCheck = bool ? + checked = bool ? + checkState = bool ? + crop = {start|end|center|none} : how the text is cropped when too large + dlgType = {accept|cancel|help|disclosure} + group = int + open = ? + tabindex = int : tab order + type = {checkbox|menu|menu-button|radio} + value = string ? : user attribute + */ + xmlcheckboxdata_t *data = widget->data; + int temp; + + M_ReadXmlPropAsString (node, "label", data->label, sizeof (data->label)); + temp = M_CompareXmlProp (node, "disabled", xmlbool, 2); + if (temp != -1) + data->disabled = temp; + +} + +void M_LoadXmlRadio (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + disabled = bool + label = string + selected = bool + + * --- unsupported : + + accesskey = string : shortcut key + autoCheck = bool ? + checked = bool ? + checkState = bool ? + crop = {start|end|center|none} : how the text is cropped when too large + focused = bool + group = int + open = ? + tabindex = int : tab order + type = {checkbox|menu|menu-button|radio} + value = string ? : user attribute + */ + xmlradiodata_t *data = widget->data; + int temp; + + M_ReadXmlPropAsString (node, "label", data->label, sizeof (data->label)); + + temp = M_CompareXmlProp (node, "disabled", xmlbool, 2); + if (temp != -1) + data->disabled = temp; + + temp = M_CompareXmlProp (node, "selected", xmlbool, 2); + if (temp != -1) + data->selected = temp; + +} + +void M_LoadXmlRadioGroup (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + disabled = bool + + * --- unsupported : + + value = string ? : user attribute + */ + xmlradiogroupdata_t *data = widget->data; + int temp; + temp = M_CompareXmlProp (node, "disabled", xmlbool, 2); + if (temp != -1) + data->disabled = temp; +} + +void M_LoadXmlSlider (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + curpos = [0,maxpos] : cursor position + maxpos = int : maximum cursor value + pageincrement = int + + * --- unsupported : + + increment = int : + + */ + xmlsliderdata_t *data = widget->data; + + data->range = M_ReadXmlPropAsInt (node, "maxpos", 0); + // FIXME : shoud property override cvar value ? + data->cursor = M_ReadXmlPropAsInt (node, "curpos", Cvar_VariableValue (widget->name)); +} + + +void M_LoadXmlMenu (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + * --- unsupported : + + screenX = int : window x position (persistent ?) + screenY = int : window y position (persistent ?) + sizemode = {maximized|minimized|normal} : window size state + title = string + windowtype = string + */ + +} + +void M_LoadXmlWindow (qwidget_t *widget, xmlNodePtr node) +{ + /* + * --- supported attributes : + + * --- unsupported : + + screenX = int : window x position (persistent ?) + screenY = int : window y position (persistent ?) + sizemode = {maximized|minimized|normal} : window size state + title = string + windowtype = string + */ + qwidget_t *w; + qwindow_t *data = widget->data; + char command[255]; + data->widget = widget; + + // check window size + if (widget->width.ratio != -1) + widget->width.absolute = widget->width.ratio * vid.width; + if (widget->height.ratio != -1) + widget->height.absolute = widget->height.ratio * vid.height; + + if (widget->width.absolute == -1) + widget->width.absolute = vid.width; + if (widget->height.absolute == -1) + widget->height.absolute = vid.height; + + // add newly created window to the list + data->next = windows; + windows = data; + + // register alias for quake compat + strcpy (command, "alias menu_\0"); + strcat (command, widget->id); // no more that 32 char in widget->id + strcat (command, " \"openwindow "); // + strcat (command, widget->id); // no more that 32 char in widget->id + strcat (command, "\"\n"); // so it should fit in command + + Cbuf_AddText (command); + + // we have a fully loaded window here + M_CalculateWidgetDim (widget); + + /* + // set root on all sub-widget + w = widget; + while (w){ + w->root = widget; + w = M_NextXmlElement (w); + } + */ + +} + + +qwidget_t *M_LoadXmlElement (xmlNodePtr root) +{ + /* + * --- supported attributes : + + align = {start|center|end|baseline|stretch} + pack = {start|center|end} + debug = bool : draw a border around it and all children ? + height = int + width = int + id = string : the name/command of the object + orient = {horizontal|vertical} + + * --- unsupported : + + + allowevents + allownegativeassertions + class + coalesceduplicatearcs + collapsed + container + containment + context + contextmenu + datasources + dir + empty + equalsize = {always|never} : This attribute can be used to make the children of the element equal in size. + flags = {dont-test-empty|dont-build-content} + flex + flexgroup + hidden + insertafter + insertbefore + left + maxheight + maxwidth + menu + minheight + minwidth + observes + ordinal + pack = {start|center|end} + persist + popup + position + ref + removeelement + statustext + style + template + tooltip + tooltiptext + top + uri + */ + + xmlNodePtr node; + qwidget_t *ret,*sub; + xmlhandler_t *handler; + int temp; + static int sp; + + // we don't want anonymous tags + if (!root->name) + return NULL; + // first read/initialize specifics attributes + handler = widgethandlers; + + while (handler->tag) { + if (!xmlStrcmp (root->name, handler->tag)){ + break; + } + handler++; + } + + if (!handler->tag) + return NULL; + + ret = M_AllocateMem (sizeof(qwidget_t), &(handler->template)); + + if (handler->datasize) + ret->data = M_AllocateMem (handler->datasize, NULL); + ret->tag = (char *)handler->tag; + + // then read general attributes from the file + + M_ReadXmlDim (root, "width", &ret->width); + M_ReadXmlDim (root, "height", &ret->height); + ret->debug = (M_ReadXmlPropAsInt (root, "debug", 0) != 0); + M_ReadXmlPropAsString (root, "id", ret->id, sizeof(ret->id)); + M_ReadXmlPropAsString (root, "name", ret->name, sizeof(ret->name)); + temp = M_CompareXmlProp (root, "orient", xmlorient, 2); + if (temp != -1) + ret->orient = temp; + + temp = M_CompareXmlProp (root, "align", xmlalign, 5); + if (temp != -1) + ret->align = temp; + temp = M_CompareXmlProp (root, "pack", xmlpack, 3); + if (temp != -1) + ret->pack = temp; + +#if 0 + for (temp=0;temptag, ret->align, ret->pack, ret->width.absolute,ret->height.absolute); + sp++; +#endif + /* load all the subnodes */ + node = root->xmlChildrenNode; + while ( node != NULL ){ + // comment nodes have node->name == NULL + if (!xmlIsBlankNode (node) && (node->type != XML_COMMENT_NODE)){ + sub = M_LoadXmlElement (node); + if (sub) + M_InsertXmlWidget (ret, sub); + } + node = node->next; + } +#if 0 + sp--; +#endif + // finally init the private data + ret->Load (ret, root); + + return ret; +} + +/* +================ +M_LoadXmlMenu + +Load a Menu(Window) definition from a file +================ +*/ + +void M_LoadXmlWindowFile (const char *filename) +{ +#if 0 + // best method : lower resource need + // unfinished ^^; + int res, size; + xmlParserCtxtPtr ctxt; + FILE *f=NULL; + + COM_FOpenFile(filename, &f); + // here set the elements callbacks + + if (f != NULL) { + int res, size = 1024; + char chars[1024]; + xmlParserCtxtPtr ctxt; + + res = fread (chars, 1, 4, f); + if (res > 0) { + ctxt = xmlCreatePushParserCtxt (NULL, NULL, + chars, res, filename); + while ((res = fread (chars, 1, size, f)) > 0) { + xmlParseChunk (ctxt, chars, res, 0); + } + xmlParseChunk (ctxt, chars, 0, 1); + doc = ctxt->myDoc; + xmlFreeParserCtxt (ctxt); + } + } + +#else + // easier method : memory heavier + + char *buffer; + xmlDocPtr doc; + xmlNodePtr node; + int h, len; + + Con_DPrintf ("XML : loading %s document started \n",filename); + + // yuck - dirty filesize check + len = COM_OpenFile (filename, &h); + if (h == -1) { + Con_Printf ("Warning : %s document not found\n",filename); + return; + } + COM_CloseFile (h); + + buffer = COM_LoadTempFile (filename); + + doc = xmlParseMemory (buffer, len); + if (doc == NULL) { + Con_Printf ("Warning : %s document not found\n",filename); + return; + } + node = xmlDocGetRootElement (doc); + if (node == NULL) { + Con_Printf ("Warning : %s empty document\n",filename); + xmlFreeDoc (doc); + return; + } + + if (!xmlIsBlankNode (node) && (node->name)){ + if (!xmlStrcmp (node->name, (const xmlChar *)"window")) { + M_LoadXmlElement (node); + } + else { + Con_Printf ("Warning : %s document root isn't a window\n",filename); + } + } + + xmlFreeDoc (doc); + +#endif + Con_DPrintf ("XML : loading %s document ended \n",filename); +} + +// ------------------------------------------------------------------------------------- diff --git a/xmlmenu.h b/xmlmenu.h new file mode 100644 index 0000000..46cdcd4 --- /dev/null +++ b/xmlmenu.h @@ -0,0 +1,120 @@ +/* +Copyright (C) 2003 Tenebrae Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef _XMLMENU_H +#define _XMLMENU_H + +#include "libxml/parser.h" + + +typedef struct xmldim_s +{ + float ratio; + int absolute; +} xmldim_t; + +typedef enum {a_start=0,a_center=1,a_end=2,a_baseline=3,a_stretch=4} xmlalign_t; +typedef enum {p_start=0,p_center=1,p_end=2} xmlpack_t; + +typedef struct qwidget_s +{ + int debug:1; + int focusable:1; + int orient:1; + xmlalign_t align; + xmlpack_t pack; + char name[32]; + char id[32]; + int xoffset; + int yoffset; + xmldim_t width; + xmldim_t height; + int num_children; + char *tag; + struct qwidget_s *parent; // bounding widget + //struct qwidget_s *root; // root widget + struct qwidget_s *previous; // next sibling + struct qwidget_s *next; // next sibling + struct qwidget_s *children; // bounded widget list + struct qwidget_s *rchildren; // bounded widget list (reverse order) + // functions + void (*Load) (struct qwidget_s *self,xmlNodePtr node); + void (*Draw) (struct qwidget_s *self, int x, int y); + void (*Focus) (struct qwidget_s *self); + qboolean (*HandleKey) (struct qwidget_s *self, int key); + void (*onMouseOver) (struct qwidget_s *self); + void (*onMouseDown) (struct qwidget_s *self); + void (*onMouseUp) (struct qwidget_s *self); + void *data; +} qwidget_t; + +void M_LoadXmlBox (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlVBox (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlLabel (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlImage (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlButton (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlCheckBox (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlMenu (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlSlider (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlRadio (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlRadioGroup (qwidget_t *widget, xmlNodePtr node); +void M_LoadXmlWindow (qwidget_t *widget, xmlNodePtr node); + +void M_LoadXmlWindowFile (const char *filename); + +void M_XmlButtonCommand (qwidget_t *self); + + +qwidget_t *M_NextXmlElement (qwidget_t *w); +qwidget_t *M_PreviousXmlElement (qwidget_t *w); + +void M_CycleFocus (qwidget_t *(*next_f)(qwidget_t *)); + +#define M_CycleFocusNext() M_CycleFocus (M_NextXmlElement) +#define M_CycleFocusPrevious() M_CycleFocus (M_PreviousXmlElement) + + +qboolean M_XmlBoxKey (qwidget_t *self, int k); +qboolean M_XmlCheckBoxKey (qwidget_t *self, int k); +qboolean M_XmlButtonKey (qwidget_t *self, int k); +qboolean M_XmlRadioKey (qwidget_t *self, int k); +qboolean M_XmlRadioGroupKey (qwidget_t *self, int k); +qboolean M_XmlSliderKey (qwidget_t *self, int k); +qboolean M_XmlWindowKey (qwidget_t *self, int k); +qboolean M_XmlMenuKey (qwidget_t *self, int k); +qboolean M_XmlElementKey (qwidget_t *self, int k); + +void M_DrawXmlButton (qwidget_t *self, int x, int y); +void M_DrawXmlImage (qwidget_t *self, int x, int y); +void M_DrawXmlLabel (qwidget_t *self, int x, int y); +void M_DrawXmlBox (qwidget_t *self, int x, int y); +void M_DrawXmlSlider (qwidget_t *self, int x, int y); +void M_DrawXmlCheckBox (qwidget_t *self,int x, int y); +void M_DrawXmlRadio (qwidget_t *self,int x, int y); +void M_DrawXmlRadioGroup (qwidget_t *self,int x, int y); +void M_DrawXmlWindow (qwidget_t *self,int x, int y); + +typedef struct xmlhandlers_s { + const xmlChar *tag; + qwidget_t template; + int datasize; +} xmlhandler_t; + +#endif