00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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"
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
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
00144 if (UI_WindowIsFullScreen(node)) {
00145
00146 if (pos[1] != 0)
00147 UI_DrawFill(0, 0, viddef.virtualWidth, pos[1], anamorphicBorder);
00148
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
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
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
00167 image = UI_GetReferenceString(node, node->image);
00168 if (image)
00169 UI_DrawPanel(pos, node->size, image, 0, 0, windowTemplate);
00170
00171
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
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
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
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
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
00217 ui_windowBehaviour->super->doLayout(node);
00218 }
00219
00224 static void UI_WindowNodeInit (uiNode_t *node)
00225 {
00226 uiNode_t *child;
00227
00228
00229 EXTRADATA(node).lastTime = CL_Milliseconds();
00230
00231
00232 for (child = node->firstChild; child; child = child->next) {
00233 if (child->behaviour->init) {
00234 child->behaviour->init(child);
00235 }
00236 }
00237
00238
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
00254 for (child = node->firstChild; child; child = child->next) {
00255 if (child->behaviour->close) {
00256 child->behaviour->close(child);
00257 }
00258 }
00259
00260
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
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
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
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
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368 {"noticepos", V_POS, UI_EXTRADATA_OFFSETOF(windowExtraData_t, noticePos), MEMBER_SIZEOF(windowExtraData_t, noticePos)},
00369
00370 {"dragbutton", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, dragButton), MEMBER_SIZEOF(windowExtraData_t, dragButton)},
00371
00372 {"closebutton", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, closeButton), MEMBER_SIZEOF(windowExtraData_t, closeButton)},
00373
00374 {"modal", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, modal), MEMBER_SIZEOF(windowExtraData_t, modal)},
00375
00376 {"dropdown", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, dropdown), MEMBER_SIZEOF(windowExtraData_t, dropdown)},
00377
00378 {"preventtypingescape", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, preventTypingEscape), MEMBER_SIZEOF(windowExtraData_t, preventTypingEscape)},
00379
00380 {"fill", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, fill), MEMBER_SIZEOF(windowExtraData_t, fill)},
00381
00382
00383
00384 {"starlayout", V_BOOL, UI_EXTRADATA_OFFSETOF(windowExtraData_t, starLayout), MEMBER_SIZEOF(windowExtraData_t, starLayout)},
00385
00386
00387
00388
00389
00390 {"timeout", V_INT,UI_EXTRADATA_OFFSETOF(windowExtraData_t, timeOut), MEMBER_SIZEOF(windowExtraData_t, timeOut)},
00391
00392
00393 {"oninit", V_UI_ACTION, UI_EXTRADATA_OFFSETOF(windowExtraData_t, onInit), MEMBER_SIZEOF(windowExtraData_t, onInit)},
00394
00395 {"onclose", V_UI_ACTION, UI_EXTRADATA_OFFSETOF(windowExtraData_t, onClose), MEMBER_SIZEOF(windowExtraData_t, onClose)},
00396
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 }