ui_node_window.c

Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018 See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 
00024 */
00025 
00026 #include "../ui_main.h"
00027 #include "../ui_parse.h"
00028 #include "../ui_font.h"
00029 #include "../ui_nodes.h"
00030 #include "../ui_internal.h"
00031 #include "../ui_render.h"
00032 #include "ui_node_window.h"
00033 #include "ui_node_panel.h"
00034 #include "ui_node_abstractnode.h"
00035 
00036 #include "../../client.h" /* gettext _() */
00037 
00038 #define EXTRADATA_TYPE windowExtraData_t
00039 #define EXTRADATA(node) UI_EXTRADATA(node, EXTRADATA_TYPE)
00040 #define EXTRADATACONST(node) UI_EXTRADATACONST(node, EXTRADATA_TYPE)
00041 
00042 /* constants defining all tile of the texture */
00043 
00044 #define LEFT_WIDTH 20
00045 #define MID_WIDTH 1
00046 #define RIGHT_WIDTH 19
00047 
00048 #define TOP_HEIGHT 46
00049 #define MID_HEIGHT 1
00050 #define BOTTOM_HEIGHT 19
00051 
00052 #define MARGE 3
00053 
00054 const uiBehaviour_t const *ui_windowBehaviour;
00055 
00056 static const int CONTROLS_IMAGE_DIMENSIONS = 17;
00057 static const int CONTROLS_PADDING = 22;
00058 static const int CONTROLS_SPACING = 5;
00059 
00060 static const int windowTemplate[] = {
00061     LEFT_WIDTH, MID_WIDTH, RIGHT_WIDTH,
00062     TOP_HEIGHT, MID_HEIGHT, BOTTOM_HEIGHT,
00063     MARGE
00064 };
00065 
00066 static const vec4_t modalBackground = {0, 0, 0, 0.6};
00067 static const vec4_t anamorphicBorder = {0, 0, 0, 1};
00068 
00073 uiNode_t* UI_WindowNodeGetIndexedChild (uiNode_t* const node, const char* childName)
00074 {
00075     node_index_t *a;
00076     unsigned int hash;
00077 
00078     hash = Com_HashKey(childName, INDEXEDCHILD_HASH_SIZE);
00079     for (a = EXTRADATA(node).index_hash[hash]; a; a = a->hash_next) {
00080         if (!strcmp(childName, a->node->name)) {
00081             return a->node;
00082         }
00083     }
00084     return NULL;
00085 }
00086 
00090 qboolean UI_WindowNodeAddIndexedNode (uiNode_t* const node, uiNode_t* const child)
00091 {
00092     node_index_t *a;
00093     unsigned int hash;
00094 
00095     hash = Com_HashKey(child->name, INDEXEDCHILD_HASH_SIZE);
00096     for (a = EXTRADATA(node).index_hash[hash]; a; a = a->hash_next) {
00097         if (!strcmp(child->name, a->node->name)) {
00099             break;
00100         }
00101     }
00102 
00103     if (!a) {
00104         a = Mem_PoolAlloc(sizeof(*a), ui_sysPool, 0);
00105         a->next = EXTRADATA(node).index;
00106         a->hash_next = EXTRADATA(node).index_hash[hash];
00107         EXTRADATA(node).index_hash[hash] = a;
00108         EXTRADATA(node).index = a;
00109     }
00110 
00111     return qfalse;
00112 }
00113 
00117 qboolean UI_WindowNodeRemoveIndexedNode (uiNode_t* const node, uiNode_t* const child)
00118 {
00120     return qfalse;
00121 }
00122 
00126 qboolean UI_WindowIsFullScreen (const uiNode_t* const node)
00127 {
00128     assert(UI_NodeInstanceOf(node, "window"));
00129     return EXTRADATACONST(node).isFullScreen;
00130 }
00131 
00132 static void UI_WindowNodeDraw (uiNode_t *node)
00133 {
00134     const char* image;
00135     const char* text;
00136     vec2_t pos;
00137     const char *font = UI_GetFontFromNode(node);
00138 
00139     UI_GetNodeAbsPos(node, pos);
00140 
00141     /* black border for anamorphic mode */
00144     if (UI_WindowIsFullScreen(node)) {
00145         /* top */
00146         if (pos[1] != 0)
00147             UI_DrawFill(0, 0, viddef.virtualWidth, pos[1], anamorphicBorder);
00148         /* left-right */
00149         if (pos[0] != 0)
00150             UI_DrawFill(0, pos[1], pos[0], node->size[1], anamorphicBorder);
00151         if (pos[0] + node->size[0] < viddef.virtualWidth) {
00152             const int width = viddef.virtualWidth - (pos[0] + node->size[0]);
00153             UI_DrawFill(viddef.virtualWidth - width, pos[1], width, node->size[1], anamorphicBorder);
00154         }
00155         /* bottom */
00156         if (pos[1] + node->size[1] < viddef.virtualHeight) {
00157             const int height = viddef.virtualHeight - (pos[1] + node->size[1]);
00158             UI_DrawFill(0, viddef.virtualHeight - height, viddef.virtualWidth, height, anamorphicBorder);
00159         }
00160     }
00161 
00162     /* darker background if last window is a modal */
00163     if (EXTRADATA(node).modal && ui_global.windowStack[ui_global.windowStackPos - 1] == node)
00164         UI_DrawFill(0, 0, viddef.virtualWidth, viddef.virtualHeight, modalBackground);
00165 
00166     /* draw the background */
00167     image = UI_GetReferenceString(node, node->image);
00168     if (image)
00169         UI_DrawPanel(pos, node->size, image, 0, 0, windowTemplate);
00170 
00171     /* draw the title */
00172     text = UI_GetReferenceString(node, node->text);
00173     if (text)
00174         UI_DrawStringInBox(font, ALIGN_CC, pos[0] + node->padding, pos[1] + node->padding, node->size[0] - node->padding - node->padding, TOP_HEIGHT + 10 - node->padding - node->padding, text, LONGLINES_PRETTYCHOP);
00175 
00176     /* embedded timer */
00177     if (EXTRADATA(node).onTimeOut && EXTRADATA(node).timeOut) {
00178         if (EXTRADATA(node).lastTime == 0)
00179             EXTRADATA(node).lastTime = CL_Milliseconds();
00180         if (EXTRADATA(node).lastTime + EXTRADATA(node).timeOut < CL_Milliseconds()) {
00181             /* allow to reset timeOut on the event, and restart it, with an uptodate lastTime */
00182             EXTRADATA(node).lastTime = 0;
00183             Com_DPrintf(DEBUG_CLIENT, "UI_WindowNodeDraw: timeout for node '%s'\n", node->name);
00184             UI_ExecuteEventActions(node, EXTRADATA(node).onTimeOut);
00185         }
00186     }
00187 }
00188 
00189 static void UI_WindowNodeDoLayout (uiNode_t *node)
00190 {
00191     if (!node->invalidated)
00192         return;
00193 
00194     /* use a the space */
00195     if (EXTRADATA(node).fill) {
00196         if (node->size[0] != viddef.virtualWidth) {
00197             node->size[0] = viddef.virtualWidth;
00198         }
00199         if (node->size[1] != viddef.virtualHeight) {
00200             node->size[1] = viddef.virtualHeight;
00201         }
00202     }
00203 
00204     /* move fullscreen window on the center of the screen */
00205     if (UI_WindowIsFullScreen(node)) {
00206         node->pos[0] = (int) ((viddef.virtualWidth - node->size[0]) / 2);
00207         node->pos[1] = (int) ((viddef.virtualHeight - node->size[1]) / 2);
00208     }
00209 
00212     if (EXTRADATA(node).starLayout) {
00213         UI_StarLayout(node);
00214     }
00215 
00216     /* super */
00217     ui_windowBehaviour->super->doLayout(node);
00218 }
00219 
00224 static void UI_WindowNodeInit (uiNode_t *node)
00225 {
00226     uiNode_t *child;
00227 
00228     /* init the embeded timer */
00229     EXTRADATA(node).lastTime = CL_Milliseconds();
00230 
00231     /* init child */
00232     for (child = node->firstChild; child; child = child->next) {
00233         if (child->behaviour->init) {
00234             child->behaviour->init(child);
00235         }
00236     }
00237 
00238     /* script callback */
00239     if (EXTRADATA(node).onInit)
00240         UI_ExecuteEventActions(node, EXTRADATA(node).onInit);
00241 
00242     UI_Invalidate(node);
00243 }
00244 
00249 static void UI_WindowNodeClose (uiNode_t *node)
00250 {
00251     uiNode_t *child;
00252 
00253     /* close child */
00254     for (child = node->firstChild; child; child = child->next) {
00255         if (child->behaviour->close) {
00256             child->behaviour->close(child);
00257         }
00258     }
00259 
00260     /* script callback */
00261     if (EXTRADATA(node).onClose)
00262         UI_ExecuteEventActions(node, EXTRADATA(node).onClose);
00263 }
00264 
00268 static void UI_WindowNodeLoading (uiNode_t *node)
00269 {
00270     node->size[0] = VID_NORM_WIDTH;
00271     node->size[1] = VID_NORM_HEIGHT;
00272     node->font = "f_big";
00273     node->padding = 5;
00274 }
00275 
00276 void UI_WindowNodeSetRenderNode (uiNode_t *node, uiNode_t *renderNode)
00277 {
00278     if (!UI_NodeInstanceOf(node, "window")) {
00279         Com_Printf("UI_WindowNodeSetRenderNode: '%s' node is not an 'window'.\n", UI_GetPath(node));
00280         return;
00281     }
00282 
00283     if (EXTRADATA(node).renderNode) {
00284         Com_Printf("UI_WindowNodeSetRenderNode: second render node ignored (\"%s\")\n", UI_GetPath(renderNode));
00285         return;
00286     }
00287 
00288     EXTRADATA(node).renderNode = renderNode;
00289 }
00290 
00294 static void UI_WindowNodeLoaded (uiNode_t *node)
00295 {
00296     static char* closeCommand = "mn_close <path:root>;";
00297 
00298     /* create a drag zone, if it is requested */
00299     if (EXTRADATA(node).dragButton) {
00300         uiNode_t *control = UI_AllocNode("move_window_button", "controls", node->dynamic);
00301         control->root = node;
00302         control->image = NULL;
00304         control->size[0] = node->size[0];
00305         control->size[1] = TOP_HEIGHT;
00306         control->pos[0] = 0;
00307         control->pos[1] = 0;
00308         control->tooltip = _("Drag to move window");
00309         UI_AppendNode(node, control);
00310     }
00311 
00312     /* create a close button, if it is requested */
00313     if (EXTRADATA(node).closeButton) {
00314         uiNode_t *button = UI_AllocNode("close_window_button", "image", node->dynamic);
00315         const int positionFromRight = CONTROLS_PADDING;
00316         button->root = node;
00317         button->image = "icons/system_close";
00319         button->size[0] = CONTROLS_IMAGE_DIMENSIONS;
00320         button->size[1] = CONTROLS_IMAGE_DIMENSIONS;
00321         button->pos[0] = node->size[0] - positionFromRight - button->size[0];
00322         button->pos[1] = CONTROLS_PADDING;
00323         button->tooltip = _("Close the window");
00324         button->onClick = UI_AllocStaticCommandAction(closeCommand);
00325         UI_AppendNode(node, button);
00326     }
00327 
00328     EXTRADATA(node).isFullScreen = node->size[0] == VID_NORM_WIDTH
00329             && node->size[1] == VID_NORM_HEIGHT;
00330 
00331     if (EXTRADATA(node).starLayout)
00332         UI_Invalidate(node);
00333 
00334 #ifdef DEBUG
00335     if (node->size[0] < LEFT_WIDTH + MID_WIDTH + RIGHT_WIDTH || node->size[1] < TOP_HEIGHT + MID_HEIGHT + BOTTOM_HEIGHT)
00336         Com_DPrintf(DEBUG_CLIENT, "Node '%s' too small. It can create graphical bugs\n", node->name);
00337 #endif
00338 }
00339 
00340 static void UI_WindowNodeClone (const uiNode_t *source, uiNode_t *clone)
00341 {
00343     if (EXTRADATACONST(source).renderNode != NULL) {
00344         Com_Printf("UI_WindowNodeClone: Do not inherite window using a render node. Render node ignored (\"%s\")\n", UI_GetPath(clone));
00345         EXTRADATA(clone).renderNode = NULL;
00346     }
00347 
00348     /* clean up index */
00349     EXTRADATA(clone).index = NULL;
00350     memset(EXTRADATA(clone).index_hash, 0, sizeof(EXTRADATA(clone).index_hash[0]) * INDEXEDCHILD_HASH_SIZE);
00351 }
00352 
00356 static const value_t windowNodeProperties[] = {
00357     /* @override image
00358      * Texture to use. The texture is a cut of 9 portions
00359      * (left, middle, right � top, middle, bottom). Between all this elements,
00360      * we use a margin of 3 pixels (purple mark into the sample).
00361      * Graphically we see only a 1 pixel margin, because, for a problem of
00362      * lossy compression of texture it's not nice to have a pure transparent
00363      * pixel near the last colored one, when we cut or stretch textures.
00364      * @image html http://ufoai.ninex.info/wiki/images/Popup_alpha_tile.png
00365      */
00366 
00367     /* In windows where notify messages appear (like e.g. the video options window when you have to restart the game until the settings take effects) you can define the position of those messages with this option. */
00368     {"noticepos", V_POS, UI_EXTRADATA_OFFSETOF(windowExtraData_t, noticePos), MEMBER_SIZEOF(windowExtraData_t, noticePos)},
00369     /* Create subnode allowing to move the window when we click on the header. Updating this attribute at the runtime will change nothing. */
00370     {"dragbutton", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, dragButton), MEMBER_SIZEOF(windowExtraData_t, dragButton)},
00371     /* Add a button on the top right the window to close it. Updating this attribute at the runtime will change nothing. */
00372     {"closebutton", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, closeButton), MEMBER_SIZEOF(windowExtraData_t, closeButton)},
00373     /* If true, the user can't select something outside the modal window. He must first close the window. */
00374     {"modal", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, modal), MEMBER_SIZEOF(windowExtraData_t, modal)},
00375     /* If true, if the user click outside the window, it will close it. */
00376     {"dropdown", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, dropdown), MEMBER_SIZEOF(windowExtraData_t, dropdown)},
00377     /* If true, the user can't use ''ESC'' key to close the window. */
00378     {"preventtypingescape", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, preventTypingEscape), MEMBER_SIZEOF(windowExtraData_t, preventTypingEscape)},
00379     /* If true, the window is filled according to the widescreen. */
00380     {"fill", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, fill), MEMBER_SIZEOF(windowExtraData_t, fill)},
00381     /* If true, when the window size change, the window content position is updated according to the "star" layout.
00382      * @todo Need more documentation.
00383      */
00384     {"starlayout", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, starLayout), MEMBER_SIZEOF(windowExtraData_t, starLayout)},
00385 
00386     /* This property control milliseconds between each calls of <code>onEvent</code>.
00387      * If value is 0 (the default value) nothing is called. We can change the
00388      * value at the runtime.
00389      */
00390     {"timeout", V_INT,UI_EXTRADATA_OFFSETOF(windowExtraData_t, timeOut), MEMBER_SIZEOF(windowExtraData_t, timeOut)},
00391 
00392     /* Called when the window is puched into the active window stack. */
00393     {"oninit", V_UI_ACTION, UI_EXTRADATA_OFFSETOF(windowExtraData_t, onInit), MEMBER_SIZEOF(windowExtraData_t, onInit)},
00394     /* Called when the window is removed from the active window stack. */
00395     {"onclose", V_UI_ACTION, UI_EXTRADATA_OFFSETOF(windowExtraData_t, onClose), MEMBER_SIZEOF(windowExtraData_t, onClose)},
00396     /* Called periodically. See <code>timeout</code>. */
00397     {"onevent", V_UI_ACTION, UI_EXTRADATA_OFFSETOF(windowExtraData_t, onTimeOut), MEMBER_SIZEOF(windowExtraData_t, onTimeOut)},
00398 
00399     {NULL, V_NULL, 0, 0}
00400 };
00401 
00407 vec_t *UI_WindowNodeGetNoticePosition(struct uiNode_s *node)
00408 {
00409     if (EXTRADATA(node).noticePos[0] == 0 && EXTRADATA(node).noticePos[1] == 0)
00410         return NULL;
00411     return EXTRADATA(node).noticePos;
00412 }
00413 
00419 qboolean UI_WindowIsDropDown(const struct uiNode_s* const node)
00420 {
00421     return EXTRADATACONST(node).dropdown;
00422 }
00423 
00429 qboolean UI_WindowIsModal(const struct uiNode_s* const node)
00430 {
00431     return EXTRADATACONST(node).modal;
00432 }
00433 
00441 void UI_WindowNodeRegisterKeyBinding (uiNode_t* node, uiKeyBinding_t *binding)
00442 {
00443     assert(UI_NodeInstanceOf(node, "window"));
00444     binding->next = EXTRADATA(node).keyList;
00445     EXTRADATA(node).keyList = binding;
00446 }
00447 
00448 const uiKeyBinding_t *binding;
00449 
00456 uiKeyBinding_t *UI_WindowNodeGetKeyBinding (const struct uiNode_s* const node, unsigned int key)
00457 {
00458     uiKeyBinding_t *binding = EXTRADATACONST(node).keyList;
00459     assert(UI_NodeInstanceOf(node, "window"));
00460     while (binding) {
00461         if (binding->key == key)
00462             break;
00463         binding = binding->next;
00464     }
00465     return binding;
00466 }
00467 
00468 void UI_RegisterWindowNode (uiBehaviour_t *behaviour)
00469 {
00470     ui_windowBehaviour = behaviour;
00471     behaviour->name = "window";
00472     behaviour->loading = UI_WindowNodeLoading;
00473     behaviour->loaded = UI_WindowNodeLoaded;
00474     behaviour->init = UI_WindowNodeInit;
00475     behaviour->close = UI_WindowNodeClose;
00476     behaviour->draw = UI_WindowNodeDraw;
00477     behaviour->doLayout = UI_WindowNodeDoLayout;
00478     behaviour->clone = UI_WindowNodeClone;
00479     behaviour->properties = windowNodeProperties;
00480     behaviour->extraDataSize = sizeof(EXTRADATA_TYPE);
00481 }

Generated by  doxygen 1.6.2