00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "ui_main.h"
00026 #include "ui_internal.h"
00027 #include "ui_nodes.h"
00028 #include "ui_parse.h"
00029 #include "ui_input.h"
00030
00031 #include "node/ui_node_abstractnode.h"
00032 #include "node/ui_node_abstractscrollbar.h"
00033 #include "node/ui_node_abstractoption.h"
00034 #include "node/ui_node_abstractvalue.h"
00035 #include "node/ui_node_bar.h"
00036 #include "node/ui_node_base.h"
00037 #include "node/ui_node_baseinventory.h"
00038 #include "node/ui_node_button.h"
00039 #include "node/ui_node_checkbox.h"
00040 #include "node/ui_node_controls.h"
00041 #include "node/ui_node_video.h"
00042 #include "node/ui_node_container.h"
00043 #include "node/ui_node_custombutton.h"
00044 #include "node/ui_node_editor.h"
00045 #include "node/ui_node_ekg.h"
00046 #include "node/ui_node_image.h"
00047 #include "node/ui_node_item.h"
00048 #include "node/ui_node_linechart.h"
00049 #include "node/ui_node_map.h"
00050 #include "node/ui_node_material_editor.h"
00051 #include "node/ui_node_messagelist.h"
00052 #include "node/ui_node_model.h"
00053 #include "node/ui_node_option.h"
00054 #include "node/ui_node_optionlist.h"
00055 #include "node/ui_node_optiontree.h"
00056 #include "node/ui_node_panel.h"
00057 #include "node/ui_node_radar.h"
00058 #include "node/ui_node_radiobutton.h"
00059 #include "node/ui_node_rows.h"
00060 #include "node/ui_node_selectbox.h"
00061 #include "node/ui_node_sequence.h"
00062 #include "node/ui_node_string.h"
00063 #include "node/ui_node_special.h"
00064 #include "node/ui_node_spinner.h"
00065 #include "node/ui_node_tab.h"
00066 #include "node/ui_node_tbar.h"
00067 #include "node/ui_node_text.h"
00068 #include "node/ui_node_text2.h"
00069 #include "node/ui_node_textlist.h"
00070 #include "node/ui_node_textentry.h"
00071 #include "node/ui_node_keybinding.h"
00072 #include "node/ui_node_todo.h"
00073 #include "node/ui_node_vscrollbar.h"
00074 #include "node/ui_node_zone.h"
00075
00076 typedef void (*registerFunction_t)(uiBehaviour_t *node);
00077
00082 static const registerFunction_t registerFunctions[] = {
00083 UI_RegisterNullNode,
00084 UI_RegisterAbstractBaseNode,
00085 UI_RegisterAbstractNode,
00086 UI_RegisterAbstractOptionNode,
00087 UI_RegisterAbstractScrollableNode,
00088 UI_RegisterAbstractScrollbarNode,
00089 UI_RegisterAbstractValueNode,
00090 UI_RegisterBarNode,
00091 UI_RegisterBaseInventoryNode,
00092 UI_RegisterBaseLayoutNode,
00093 UI_RegisterBaseMapNode,
00094 UI_RegisterButtonNode,
00095 UI_RegisterCheckBoxNode,
00096 UI_RegisterConFuncNode,
00097 UI_RegisterContainerNode,
00098 UI_RegisterControlsNode,
00099 UI_RegisterCustomButtonNode,
00100 UI_RegisterCvarFuncNode,
00101 UI_RegisterEditorNode,
00102 UI_RegisterEKGNode,
00103 UI_RegisterFuncNode,
00104 UI_RegisterImageNode,
00105 UI_RegisterItemNode,
00106 UI_RegisterKeyBindingNode,
00107 UI_RegisterLineChartNode,
00108 UI_RegisterMapNode,
00109 UI_RegisterMaterialEditorNode,
00110 UI_RegisterMessageListNode,
00111 UI_RegisterModelNode,
00112 UI_RegisterOptionNode,
00113 UI_RegisterOptionListNode,
00114 UI_RegisterOptionTreeNode,
00115 UI_RegisterPanelNode,
00116 UI_RegisterRadarNode,
00117 UI_RegisterRadioButtonNode,
00118 UI_RegisterRowsNode,
00119 UI_RegisterSelectBoxNode,
00120 UI_RegisterSequenceNode,
00121 UI_RegisterSpecialNode,
00122 UI_RegisterSpinnerNode,
00123 UI_RegisterStringNode,
00124 UI_RegisterTabNode,
00125 UI_RegisterTBarNode,
00126 UI_RegisterTextNode,
00127 UI_RegisterText2Node,
00128 UI_RegisterTextEntryNode,
00129 UI_RegisterTextListNode,
00130 UI_RegisterTodoNode,
00131 UI_RegisterVideoNode,
00132 UI_RegisterVScrollbarNode,
00133 UI_RegisterWindowNode,
00134 UI_RegisterZoneNode
00135 };
00136 #define NUMBER_OF_BEHAVIOURS lengthof(registerFunctions)
00137
00141 static uiBehaviour_t nodeBehaviourList[NUMBER_OF_BEHAVIOURS];
00142
00149 const value_t *UI_GetPropertyFromBehaviour (const uiBehaviour_t *behaviour, const char* name)
00150 {
00151 for (; behaviour; behaviour = behaviour->super) {
00152 const value_t *result;
00153 if (behaviour->properties == NULL)
00154 continue;
00155 result = UI_FindPropertyByName(behaviour->properties, name);
00156 if (result)
00157 return result;
00158 }
00159 return NULL;
00160 }
00161
00167 qboolean UI_CheckVisibility (uiNode_t *node)
00168 {
00169 uiCallContext_t context;
00170 if (!node->visibilityCondition)
00171 return qtrue;
00172 context.source = node;
00173 context.useCmdParam = qfalse;
00174 return UI_GetBooleanFromExpression(node->visibilityCondition, &context);
00175 }
00176
00182 const char* UI_GetPath (const uiNode_t* node)
00183 {
00184 static char result[MAX_VAR];
00185 const uiNode_t* nodes[8];
00186 int i = 0;
00187
00188 while (node) {
00189 assert(i < 8);
00190 nodes[i] = node;
00191 node = node->parent;
00192 i++;
00193 }
00194
00196 result[0] = '\0';
00197 while (i) {
00198 i--;
00199 Q_strcat(result, nodes[i]->name, sizeof(result));
00200 if (i > 0)
00201 Q_strcat(result, ".", sizeof(result));
00202 }
00203
00204 return result;
00205 }
00206
00222 void UI_ReadNodePath (const char* path, const uiNode_t *relativeNode, uiNode_t **resultNode, const value_t **resultProperty)
00223 {
00224 char name[MAX_VAR];
00225 uiNode_t* node = NULL;
00226 const char* nextName;
00227 char nextCommand = '^';
00228
00229 *resultNode = NULL;
00230 if (resultProperty)
00231 *resultProperty = NULL;
00232
00233 nextName = path;
00234 while (nextName && nextName[0] != '\0') {
00235 const char* begin = nextName;
00236 char command = nextCommand;
00237 nextName = strpbrk(begin, ".@#");
00238 if (!nextName) {
00239 Q_strncpyz(name, begin, sizeof(name));
00240 nextCommand = '\0';
00241 } else {
00242 assert(nextName - begin + 1 <= sizeof(name));
00243 Q_strncpyz(name, begin, nextName - begin + 1);
00244 nextCommand = *nextName;
00245 nextName++;
00246 }
00247
00248 switch (command) {
00249 case '^':
00250 if (!strcmp(name, "this")) {
00251 if (relativeNode == NULL)
00252 return;
00254 node = *(uiNode_t**) ((void*)&relativeNode);
00255 } else if (!strcmp(name, "parent")) {
00256 if (relativeNode == NULL)
00257 return;
00258 node = relativeNode->parent;
00259 } else if (!strcmp(name, "root")) {
00260 if (relativeNode == NULL)
00261 return;
00262 node = relativeNode->root;
00263 } else
00264 node = UI_GetWindow(name);
00265 break;
00266 case '.':
00267 if (!strcmp(name, "parent"))
00268 node = node->parent;
00269 else if (!strcmp(name, "root"))
00270 node = node->root;
00271 else
00272 node = UI_GetNode(node, name);
00273 break;
00274 case '#':
00276 assert(node->behaviour == ui_windowBehaviour);
00277 node = UI_WindowNodeGetIndexedChild(node, name);
00278 break;
00279 case '@':
00280 assert(nextCommand == '\0');
00281 *resultProperty = UI_GetPropertyFromBehaviour(node->behaviour, name);
00282 *resultNode = node;
00283 return;
00284 }
00285
00286 if (!node)
00287 return;
00288 }
00289
00290 *resultNode = node;
00291 return;
00292 }
00293
00304 uiNode_t* UI_GetNodeByPath (const char* path)
00305 {
00306 uiNode_t* node = NULL;
00307 const value_t *property;
00308 UI_ReadNodePath(path, NULL, &node, &property);
00310 return node;
00311 }
00312
00321 static uiNode_t* UI_AllocNodeWithoutNew (const char* name, const char* type, qboolean isDynamic)
00322 {
00323 uiNode_t* node;
00324 uiBehaviour_t *behaviour;
00325 int nodeSize;
00326
00327 behaviour = UI_GetNodeBehaviour(type);
00328 if (behaviour == NULL)
00329 Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behaviour '%s' doesn't exist", type);
00330
00331 nodeSize = sizeof(*node) + behaviour->extraDataSize;
00332
00333 if (!isDynamic) {
00334 if (ui_global.curadata + nodeSize > ui_global.adata + ui_global.adataize)
00335 Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: No more memory to allocate a new node");
00336 node = (uiNode_t*) ui_global.curadata;
00338 ui_global.curadata = ALIGN_PTR(ui_global.curadata, 8);
00339 ui_global.curadata += nodeSize;
00340 ui_global.numNodes++;
00341 memset(node, 0, nodeSize);
00342 } else {
00343 node = (uiNode_t*)Mem_PoolAlloc(nodeSize, ui_dynPool, 0);
00344 memset(node, 0, nodeSize);
00345 node->dynamic = qtrue;
00346 }
00347
00348 node->behaviour = behaviour;
00349 #ifdef DEBUG
00350 node->behaviour->count++;
00351 #endif
00352 if (node->behaviour->isAbstract)
00353 Com_Error(ERR_FATAL, "UI_AllocNodeWithoutNew: Node behavior '%s' is abstract. We can't instantiate it.", type);
00354
00355 if (name != NULL) {
00356 Q_strncpyz(node->name, name, sizeof(node->name));
00357 if (strlen(node->name) != strlen(name))
00358 Com_Printf("UI_AllocNodeWithoutNew: Node name \"%s\" truncated. New name is \"%s\"\n", name, node->name);
00359 }
00360
00361
00362 if (node->behaviour->loading)
00363 node->behaviour->loading(node);
00364
00365 return node;
00366 }
00367
00376 uiNode_t* UI_AllocNode (const char* name, const char* type, qboolean isDynamic)
00377 {
00378 uiNode_t* node = UI_AllocNodeWithoutNew (name, type, isDynamic);
00379
00380
00381 if (node->dynamic && node->behaviour->new)
00382 node->behaviour->new(node);
00383
00384 return node;
00385 }
00386
00394 static uiNode_t *UI_GetNodeInTreeAtPosition (uiNode_t *node, int rx, int ry)
00395 {
00396 uiNode_t *find;
00397 int i;
00398
00399 if (node->invis || node->behaviour->isVirtual || !UI_CheckVisibility(node))
00400 return NULL;
00401
00402
00403 rx -= node->pos[0];
00404 ry -= node->pos[1];
00405
00406
00407 if (rx < 0 || ry < 0 || rx >= node->size[0] || ry >= node->size[1])
00408 return NULL;
00409
00411 find = NULL;
00412 if (node->firstChild) {
00413 uiNode_t *child;
00414 vec2_t clientPosition = {0, 0};
00415
00416 if (node->behaviour->getClientPosition)
00417 node->behaviour->getClientPosition(node, clientPosition);
00418
00419 rx -= clientPosition[0];
00420 ry -= clientPosition[1];
00421
00422 for (child = node->firstChild; child; child = child->next) {
00423 uiNode_t *tmp;
00424 tmp = UI_GetNodeInTreeAtPosition(child, rx, ry);
00425 if (tmp)
00426 find = tmp;
00427 }
00428
00429 rx += clientPosition[0];
00430 ry += clientPosition[1];
00431 }
00432 if (find)
00433 return find;
00434
00435
00436 if (UI_DebugMode() != 2) {
00437
00438 if (node->ghost)
00439 return NULL;
00440
00441
00442 for (i = 0; i < node->excludeRectNum; i++) {
00443 if (rx >= node->excludeRect[i].pos[0]
00444 && rx < node->excludeRect[i].pos[0] + node->excludeRect[i].size[0]
00445 && ry >= node->excludeRect[i].pos[1]
00446 && ry < node->excludeRect[i].pos[1] + node->excludeRect[i].size[1])
00447 return NULL;
00448 }
00449 }
00450
00451
00452 return node;
00453 }
00454
00458 uiNode_t *UI_GetNodeAtPosition (int x, int y)
00459 {
00460 int pos;
00461
00462
00463 for (pos = ui_global.windowStackPos - 1; pos >= 0; pos--) {
00464 uiNode_t *window = ui_global.windowStack[pos];
00465 uiNode_t *find;
00466
00467
00468 UI_Validate(window);
00469
00470 find = UI_GetNodeInTreeAtPosition(window, x, y);
00471 if (find)
00472 return find;
00473
00474
00475 if (UI_WindowIsDropDown(window))
00476 break;
00477 if (UI_WindowIsModal(window))
00478 break;
00479 if (UI_WindowIsFullScreen(window))
00480 break;
00481 }
00482
00483 return NULL;
00484 }
00485
00492 uiBehaviour_t* UI_GetNodeBehaviour (const char* name)
00493 {
00494 unsigned char min = 0;
00495 unsigned char max = NUMBER_OF_BEHAVIOURS;
00496
00497 while (min != max) {
00498 const int mid = (min + max) >> 1;
00499 const char diff = strcmp(nodeBehaviourList[mid].name, name);
00500 assert(mid < max);
00501 assert(mid >= min);
00502
00503 if (diff == 0)
00504 return &nodeBehaviourList[mid];
00505
00506 if (diff > 0)
00507 max = mid;
00508 else
00509 min = mid + 1;
00510 }
00511
00512 return NULL;
00513 }
00514
00515 uiBehaviour_t* UI_GetNodeBehaviourByIndex (int index)
00516 {
00517 return &nodeBehaviourList[index];
00518 }
00519
00520 int UI_GetNodeBehaviourCount (void)
00521 {
00522 return NUMBER_OF_BEHAVIOURS;
00523 }
00524
00529 void UI_DeleteAllChild (uiNode_t* node)
00530 {
00531 uiNode_t *child;
00532 child = node->firstChild;
00533 while (child) {
00534 uiNode_t *next = child->next;
00535 UI_DeleteNode(child);
00536 child = next;
00537 }
00538 }
00539
00544 void UI_DeleteNode (uiNode_t* node)
00545 {
00546 uiBehaviour_t *behaviour;
00547
00548 if (!node->dynamic)
00549 return;
00550
00551 UI_DeleteAllChild(node);
00552 if (node->firstChild != NULL) {
00553 Com_Printf("UI_DeleteNode: Node '%s' contain static nodes. We can't delete it.", UI_GetPath(node));
00554 return;
00555 }
00556
00557 if (node->parent)
00558 UI_RemoveNode(node->parent, node);
00559
00560
00561 for (behaviour = node->behaviour; behaviour; behaviour = behaviour->super) {
00562 const value_t *property = behaviour->properties;
00563 if (property == NULL)
00564 continue;
00565 while (property->string != NULL) {
00566 if ((property->type & V_UI_MASK) == V_UI_CVAR) {
00567 void *mem = ((byte *) node + property->ofs);
00568 if (*(void**)mem != NULL) {
00569 UI_FreeStringProperty(*(void**)mem);
00570 *(void**)mem = NULL;
00571 }
00572 }
00573
00576 property++;
00577 }
00578 }
00579
00580 if (node->behaviour->delete)
00581 node->behaviour->delete(node);
00582 }
00583
00594 uiNode_t* UI_CloneNode (const uiNode_t* node, uiNode_t *newWindow, qboolean recursive, const char *newName, qboolean isDynamic)
00595 {
00596 uiNode_t* newNode = UI_AllocNodeWithoutNew(NULL, node->behaviour->name, isDynamic);
00597
00598
00599 memcpy(newNode, node, sizeof(*node) + node->behaviour->extraDataSize);
00600 newNode->dynamic = isDynamic;
00601
00602
00603 if (newName != NULL) {
00604 Q_strncpyz(newNode->name, newName, sizeof(newNode->name));
00605 if (strlen(newNode->name) != strlen(newName))
00606 Com_Printf("UI_CloneNode: Node name \"%s\" truncated. New name is \"%s\"\n", newName, newNode->name);
00607 }
00608
00609
00610 if (node->root == node && newWindow == NULL)
00611 newWindow = newNode;
00612 newNode->root = newWindow;
00613 newNode->parent = NULL;
00614 newNode->firstChild = NULL;
00615 newNode->lastChild = NULL;
00616 newNode->next = NULL;
00617 newNode->super = *(uiNode_t**) ((void*)&node);
00618
00619
00620 if (recursive) {
00621 uiNode_t* childNode;
00622 for (childNode = node->firstChild; childNode; childNode = childNode->next) {
00623 uiNode_t* newChildNode = UI_CloneNode(childNode, newWindow, recursive, NULL, isDynamic);
00624 UI_AppendNode(newNode, newChildNode);
00625 }
00626 }
00627
00628
00629 if (newNode->dynamic && newNode->behaviour->new)
00630 newNode->behaviour->new(newNode);
00631
00632 newNode->behaviour->clone(node, newNode);
00633
00634 return newNode;
00635 }
00636
00638 static const int virtualFunctions[] = {
00639 offsetof(uiBehaviour_t, draw),
00640 offsetof(uiBehaviour_t, drawTooltip),
00641 offsetof(uiBehaviour_t, leftClick),
00642 offsetof(uiBehaviour_t, rightClick),
00643 offsetof(uiBehaviour_t, middleClick),
00644 offsetof(uiBehaviour_t, mouseWheel),
00645 offsetof(uiBehaviour_t, mouseMove),
00646 offsetof(uiBehaviour_t, mouseDown),
00647 offsetof(uiBehaviour_t, mouseUp),
00648 offsetof(uiBehaviour_t, capturedMouseMove),
00649 offsetof(uiBehaviour_t, loading),
00650 offsetof(uiBehaviour_t, loaded),
00651 offsetof(uiBehaviour_t, init),
00652 offsetof(uiBehaviour_t, close),
00653 offsetof(uiBehaviour_t, clone),
00654 offsetof(uiBehaviour_t, new),
00655 offsetof(uiBehaviour_t, delete),
00656 offsetof(uiBehaviour_t, activate),
00657 offsetof(uiBehaviour_t, doLayout),
00658 offsetof(uiBehaviour_t, dndEnter),
00659 offsetof(uiBehaviour_t, dndMove),
00660 offsetof(uiBehaviour_t, dndLeave),
00661 offsetof(uiBehaviour_t, dndDrop),
00662 offsetof(uiBehaviour_t, dndFinished),
00663 offsetof(uiBehaviour_t, focusGained),
00664 offsetof(uiBehaviour_t, focusLost),
00665 offsetof(uiBehaviour_t, extraDataSize),
00666 offsetof(uiBehaviour_t, sizeChanged),
00667 offsetof(uiBehaviour_t, propertyChanged),
00668 offsetof(uiBehaviour_t, getClientPosition),
00669 -1
00670 };
00671
00676 static void UI_InitializeNodeBehaviour (uiBehaviour_t* behaviour)
00677 {
00678 if (behaviour->isInitialized)
00679 return;
00680
00682
00683 if (behaviour->properties) {
00684 int num = 0;
00685 const value_t* current = behaviour->properties;
00686 while (current->string != NULL) {
00687 num++;
00688 current++;
00689 }
00690 behaviour->propertyCount = num;
00691 }
00692
00693
00694 if (behaviour->extends == NULL && strcmp(behaviour->name, "abstractnode") != 0) {
00695 behaviour->extends = "abstractnode";
00696 }
00697
00698 if (behaviour->extends) {
00699 int i = 0;
00700 behaviour->super = UI_GetNodeBehaviour(behaviour->extends);
00701 UI_InitializeNodeBehaviour(behaviour->super);
00702
00703 while (qtrue) {
00704 const size_t pos = virtualFunctions[i];
00705 uintptr_t superFunc;
00706 uintptr_t func;
00707 if (pos == -1)
00708 break;
00709
00710
00711 superFunc = *(uintptr_t*)((byte*)behaviour->super + pos);
00712 func = *(uintptr_t*)((byte*)behaviour + pos);
00713 if (func == 0 && superFunc != 0)
00714 *(uintptr_t*)((byte*)behaviour + pos) = superFunc;
00715
00716 i++;
00717 }
00718 }
00719
00720
00721 if (behaviour->super && behaviour->properties) {
00722 const value_t* property = behaviour->properties;
00723 while (property->string != NULL) {
00724 const value_t *p = UI_GetPropertyFromBehaviour(behaviour->super, property->string);
00725 #if 0
00726 const uiBehaviour_t *b = UI_GetNodeBehaviour(current->string);
00727 #endif
00728 if (p != NULL)
00729 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' overwrite another property", property->string, behaviour->name);
00730 #if 0
00731 if (b != NULL)
00732 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' use the name of an existing node behaviour", property->string, behaviour->name);
00733 #endif
00734 property++;
00735 }
00736 }
00737
00738
00739 if (behaviour->properties) {
00740 const int size = sizeof(uiNode_t) + behaviour->extraDataSize;
00741 const value_t* property = behaviour->properties;
00742 while (property->string != NULL) {
00743 if (property->type != V_UI_NODEMETHOD && property->ofs + property->size > size)
00744 Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' is outside the node memory. The C code need a fix.", property->string, behaviour->name);
00745 property++;
00746 }
00747 }
00748
00749 behaviour->isInitialized = qtrue;
00750 }
00751
00752 void UI_InitNodes (void)
00753 {
00754 int i = 0;
00755 uiBehaviour_t *current = nodeBehaviourList;
00756
00757
00758 for (i = 0; i < NUMBER_OF_BEHAVIOURS; i++) {
00759 registerFunctions[i](current);
00760 current++;
00761 }
00762
00763
00764 current = nodeBehaviourList;
00765 assert(current);
00766 for (i = 0; i < NUMBER_OF_BEHAVIOURS - 1; i++) {
00767 const uiBehaviour_t *a = current;
00768 const uiBehaviour_t *b = current + 1;
00769 assert(b);
00770 if (strcmp(a->name, b->name) >= 0) {
00771 #ifdef DEBUG
00772 Com_Error(ERR_FATAL, "UI_InitNodes: '%s' is before '%s'. Please order node behaviour registrations by name", a->name, b->name);
00773 #else
00774 Com_Error(ERR_FATAL, "UI_InitNodes: Error: '%s' is before '%s'", a->name, b->name);
00775 #endif
00776 }
00777 current++;
00778 }
00779
00780
00781 current = nodeBehaviourList;
00782 for (i = 0; i < NUMBER_OF_BEHAVIOURS; i++) {
00783 UI_InitializeNodeBehaviour(current);
00784 current++;
00785 }
00786 }