UFO: Alien Invasion
Loading...
Searching...
No Matches
ui_actions.cpp
Go to the documentation of this file.
1
4
5/*
6Copyright (C) 2002-2025 UFO: Alien Invasion.
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17See the GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; if not, write to the Free Software
21Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23*/
24
25#include "ui_main.h"
26#include "ui_internal.h"
27#include "ui_parse.h"
28#include "ui_input.h"
29#include "ui_node.h"
30#include "ui_actions.h"
31#include "ui_lua.h"
33
34#include "../cl_language.h"
35
36typedef struct ui_typedActionToken_s {
37 const char* token;
38 int type;
39 int group;
41
47
48 /* 0x2x */
57
58 /* 0x3x */
64
65 /* 0x5x */
67
68 /* 'a'..'z' */
70 {"break", EA_BREAK, EA_ACTION},
71 {"call", EA_CALL, EA_ACTION},
72 {"cmd", EA_CMD, EA_ACTION},
73 {"delete", EA_DELETE, EA_ACTION},
74 {"elif", EA_ELIF, EA_ACTION},
75 {"else", EA_ELSE, EA_ACTION},
78 {"forchildin", EA_FORCHILDIN, EA_ACTION},
79 {"if", EA_IF, EA_ACTION},
83 {"while", EA_WHILE, EA_ACTION},
84
85 /* 0x7x */
87};
88
89static void UI_ExecuteActions(const uiAction_t* firstAction, uiCallContext_t* context);
90
96{
97 for (int i = 0; i < lengthof(actionTokens) - 1; i++) {
98 const char* a = actionTokens[i].token;
99 const char* b = actionTokens[i + 1].token;
100 if (strcmp(a, b) >= 0) {
101 Sys_Error("UI_CheckActionTokenTypeSanity: '%s' is before '%s'. actionList must be sorted by alphabet", a, b);
102 }
103 }
104}
105
113int UI_GetActionTokenType (const char* token, int group)
114{
115 unsigned char min = 0;
116 unsigned char max = lengthof(actionTokens);
117
118 while (min != max) {
119 const int mid = (min + max) >> 1;
120 const int diff = strcmp(actionTokens[mid].token, token);
121 assert(mid < max);
122 assert(mid >= min);
123
124 if (diff == 0) {
125 if (actionTokens[mid].group == group)
126 return actionTokens[mid].type;
127 else
128 return EA_NULL;
129 }
130
131 if (diff > 0)
132 max = mid;
133 else
134 min = mid + 1;
135 }
136 return EA_NULL;
137}
138
143static inline const char* UI_GenCommandReadProperty (const char* input, char* output, int outputSize)
144{
145 assert(input[0] == '<');
146 outputSize--;
147 input++;
148
149 while (outputSize && *input != '\0' && *input != ' ' && *input != '>') {
150 *output++ = *input++;
151 outputSize--;
152 }
153
154 if (input[0] != '>')
155 return nullptr;
156
157 output[0] = '\0';
158 return ++input;
159}
160
167{
168 if (context->useCmdParam)
169 return Cmd_Argc() - 1;
170 return context->paramNumber;
171}
172
179const char* UI_GetParam (const uiCallContext_t* context, int paramID)
180{
181 linkedList_t* current;
182 assert(paramID >= 1);
183
184 if (context->useCmdParam)
185 return Cmd_Argv(paramID);
186
187 if (paramID > context->paramNumber) {
188 Com_Printf("UI_GetParam: %i out of range\n", paramID);
189 return "";
190 }
191
192 current = context->params;
193 while (paramID > 1) {
194 current = current->next;
195 paramID--;
196 }
197
198 return (const char*) current->data;
199}
200
209const char* UI_GenInjectedString (const char* input, bool addNewLine, const uiCallContext_t* context)
210{
211 static char cmd[256];
212 int length = sizeof(cmd) - (addNewLine ? 2 : 1);
213 static char propertyName[256];
214 const char* cin = input;
215 char* cout = cmd;
216
217 while (length && cin[0] != '\0') {
218 if (cin[0] == '<') {
219 /* read propertyName between '<' and '>' */
220 const char* next = UI_GenCommandReadProperty(cin, propertyName, sizeof(propertyName));
221 if (next) {
222 /* cvar injection */
223 if (char const* const rest = Q_strstart(propertyName, "cvar:")) {
224 cvar_t const* const cvar = Cvar_Get(rest);
225 const int l = snprintf(cout, length, "%s", cvar->string);
226 cout += l;
227 cin = next;
228 length -= l;
229 continue;
230 } else if (char const* const path = Q_strstart(propertyName, "node:")) {
231 uiNode_t* node;
232 const value_t* property;
233 const char* string;
234 int l;
235 UI_ReadNodePath(path, context->source, nullptr, &node, &property);
236 if (!node) {
237 Com_Printf("UI_GenInjectedString: Node '%s' wasn't found; '' returned\n", path);
238#ifdef DEBUG
239 Com_Printf("UI_GenInjectedString: Path relative to '%s'\n", UI_GetPath(context->source));
240#endif
241 string = "";
242 } else if (!property) {
243 Com_Printf("UI_GenInjectedString: Property '%s' wasn't found; '' returned\n", path);
244 string = "";
245 } else {
246 string = UI_GetStringFromNodeProperty(node, property);
247 if (string == nullptr) {
248 Com_Printf("UI_GenInjectedString: String getter for '%s' property do not exists; '' injected\n", path);
249 string = "";
250 }
251 }
252
253 l = snprintf(cout, length, "%s", string);
254 cout += l;
255 cin = next;
256 length -= l;
257 continue;
258 /* source path injection */
259 } else if (char const* const command = Q_strstart(propertyName, "path:")) {
260 if (context->source) {
261 const uiNode_t* node = nullptr;
262 if (Q_streq(command, "root"))
263 node = context->source->root;
264 else if (Q_streq(command, "this"))
265 node = context->source;
266 else if (Q_streq(command, "parent"))
267 node = context->source->parent;
268 else
269 Com_Printf("UI_GenCommand: Command '%s' for path injection unknown\n", command);
270
271 if (node) {
272 const int l = snprintf(cout, length, "%s", UI_GetPath(node));
273 cout += l;
274 cin = next;
275 length -= l;
276 continue;
277 }
278 }
279
280 /* no prefix */
281 } else {
282 /* source property injection */
283 if (context->source) {
284 /* find property definition */
285 const value_t* property = UI_GetPropertyFromBehaviour(context->source->behaviour, propertyName);
286 if (property) {
287 const char* value;
288 int l;
289 /* inject the property value */
290 value = UI_GetStringFromNodeProperty(context->source, property);
291 if (value == nullptr)
292 value = "";
293 l = snprintf(cout, length, "%s", value);
294 cout += l;
295 cin = next;
296 length -= l;
297 continue;
298 }
299 }
300
301 /* param injection */
302 if (UI_GetParamNumber(context) != 0) {
303 int arg;
304 const int checked = sscanf(propertyName, "%d", &arg);
305 if (checked == 1 && arg >= 1 && arg <= UI_GetParamNumber(context)) {
306 const int l = snprintf(cout, length, "%s", UI_GetParam(context, arg));
307 cout += l;
308 cin = next;
309 length -= l;
310 continue;
311 }
312 }
313 }
314 }
315 }
316 *cout++ = *cin++;
317 length--;
318 }
319
320 /* is buffer too small? */
321 assert(cin[0] == '\0');
322
323 if (addNewLine)
324 *cout++ = '\n';
325
326 *cout++ = '\0';
327
328 /* copy the result into a free va slot */
329 return va("%s", cmd);
330}
331
342static void UI_NodeSetPropertyFromActionValue (uiNode_t* node, const value_t* property, const uiCallContext_t* context, uiAction_t* value)
343{
344 /* @todo we can use a new EA_VALUE type to flag already parsed values, we dont need to do it again and again */
345 /* pre compute value if possible */
346 if (value->type == EA_VALUE_STRING) {
347 const char* string = value->d.terminal.d1.constString;
348 if ((property->type & V_UI_MASK) == V_UI_CVAR && Q_strstart(string, "*cvar:")) {
349 Com_GetValue<void*>(node, property) = value->d.terminal.d1.data;
350 } else {
352 UI_InitRawActionValue(value, node, property, string);
353 }
354 }
355
356 /* decode RAW value */
357 if (value->type == EA_VALUE_RAW) {
358 const void* rawValue = value->d.terminal.d1.constData;
359 const int rawType = value->d.terminal.d2.integer;
360 UI_NodeSetPropertyFromRAW(node, property, rawValue, rawType);
361 }
362 /* else it is an expression */
363 else {
365 const char* string = UI_GetStringFromExpression(value, context);
366 UI_NodeSetProperty(node, property, string);
367 }
368}
369
370static inline void UI_ExecuteSetAction (const uiAction_t* action, const uiCallContext_t* context)
371{
372 const char* path;
373 uiNode_t* node;
374 const value_t* property;
375 const uiAction_t* left;
376 uiAction_t* right;
377
378 left = action->d.nonTerminal.left;
379 if (left == nullptr) {
380 Com_Printf("UI_ExecuteSetAction: Action without left operand skipped.\n");
381 return;
382 }
383
384 right = action->d.nonTerminal.right;
385 if (right == nullptr) {
386 Com_Printf("UI_ExecuteSetAction: Action without right operand skipped.\n");
387 return;
388 }
389
391 const char* cvarName;
392
393 if (left->type == EA_VALUE_CVARNAME)
394 cvarName = left->d.terminal.d1.constString;
395 else
396 cvarName = UI_GenInjectedString(left->d.terminal.d1.constString, false, context);
397
398 const char* textValue = CL_Translate(UI_GetStringFromExpression(right, context));
399 Cvar_ForceSet(cvarName, textValue);
400 return;
401 }
402
403 /* search the node */
404 if (left->type == EA_VALUE_PATHPROPERTY)
405 path = left->d.terminal.d1.constString;
407 path = UI_GenInjectedString(left->d.terminal.d1.constString, false, context);
408 else
409 Com_Error(ERR_FATAL, "UI_ExecuteSetAction: Property setter with wrong type '%d'", left->type);
410
411 /* context->tagNode holds reference to child in iteration loop if applicable */
412 UI_ReadNodePath(path, context->source, context->tagNode, &node, &property);
413 if (!node) {
414 Com_Printf("UI_ExecuteSetAction: node \"%s\" doesn't exist (source: %s)\n", path, UI_GetPath(context->source));
415 return;
416 }
417 if (!property) {
418 Com_Printf("UI_ExecuteSetAction: property \"%s\" doesn't exist (source: %s)\n", path, UI_GetPath(context->source));
419 return;
420 }
421
422 UI_NodeSetPropertyFromActionValue(node, property, context, right);
423}
424
425static inline void UI_ExecuteCallAction (const uiAction_t* action, const uiCallContext_t* context)
426{
427 uiNode_t* callNode = nullptr;
428 uiAction_t* param;
429 uiAction_t* left = action->d.nonTerminal.left;
430 uiCallContext_t newContext;
431 const value_t* callProperty = nullptr;
432 value_t luaMethod;
433 const char* path = left->d.terminal.d1.constString;
434
435 // clear luaMethod structure before using it
436 memset(&luaMethod, 0, sizeof(luaMethod));
437
438 if (left->type == EA_VALUE_PATHPROPERTY || left->type == EA_VALUE_PATHNODE)
439 path = left->d.terminal.d1.constString;
441 path = UI_GenInjectedString(left->d.terminal.d1.constString, false, context);
442
443 UI_ReadNodePath(path, context->source, context->tagNode, &callNode, &callProperty, &luaMethod);
444 if ((callNode == nullptr) && (!luaMethod.type)) {
445 Com_Printf("UI_ExecuteCallAction: Node from path \"%s\" not found (relative to \"%s\").\n", path, UI_GetPath(context->source));
446 return;
447 }
448 if (callProperty != nullptr && callProperty->type != V_UI_ACTION && callProperty->type != V_UI_NODEMETHOD && callProperty->type != V_UI_NODEMETHOD_LUA) {
449 Com_Printf("UI_ExecuteCallAction: Call operand %d unsupported. (%s)\n", callProperty->type, UI_GetPath(callNode));
450 return;
451 }
452
453 newContext.source = callNode;
454 newContext.params = nullptr;
455 newContext.paramNumber = 0;
456 newContext.varNumber = 0;
457 newContext.varPosition = context->varPosition + context->varNumber;
458 newContext.breakLoop = false;
459
460 if (action->type == EA_LISTENER) {
461 newContext.useCmdParam = context->useCmdParam;
462
463 if (!newContext.useCmdParam) {
464 linkedList_t* p = context->params;
465 while (p) {
466 const char* value = (char*) p->data;
467 LIST_AddString(&newContext.params, value);
468 newContext.paramNumber++;
469 p = p->next;
470 }
471 }
472 } else {
473 newContext.useCmdParam = false;
474
475 param = action->d.nonTerminal.right;
476 while (param) {
477 const char* value;
478 value = UI_GetStringFromExpression(param, context);
479 LIST_AddString(&newContext.params, value);
480 newContext.paramNumber++;
481 param = param->next;
482 }
483 }
484
485 if (luaMethod.type == V_UI_NODEMETHOD_LUA) {
486 UI_ExecuteLuaMethod(callNode, luaMethod.ofs, newContext.params, newContext.paramNumber);
487 Mem_Free(const_cast<char*>(luaMethod.string));
488 }
489 else if (callProperty == nullptr || callProperty->type == V_UI_ACTION) {
490 uiAction_t const* const actionsRef = callProperty ? Com_GetValue<uiAction_t*>(callNode, callProperty) : callNode->onClick;
491 if (actionsRef)
492 UI_ExecuteActions(actionsRef, &newContext);
493 if (callNode->lua_onClick != LUA_NOREF) {
494 if (newContext.useCmdParam)
495 UI_ExecuteLuaConFunc(callNode, callNode->lua_onClick);
496 else
497 UI_ExecuteLuaMethod(callNode, callNode->lua_onClick, newContext.params, newContext.paramNumber);
498 }
499 }
500 else if (callProperty->type == V_UI_NODEMETHOD) {
501 uiNodeMethod_t func = (uiNodeMethod_t) callProperty->ofs;
502 func(callNode, &newContext);
503 }
504 else {
505 /* unreachable, already checked few line before */
506 assert(false);
507 }
508
509 LIST_Delete(&newContext.params);
510}
511
517uiValue_t* UI_GetVariable (const uiCallContext_t* context, int relativeVarId)
518{
519 const int varId = context->varPosition + relativeVarId;
520 assert(relativeVarId >= 0);
521 assert(relativeVarId < context->varNumber);
522 return &(ui_global.variableStack[varId]);
523}
524
525static void UI_ReleaseVariable (uiValue_t* variable)
526{
528 switch (variable->type) {
529 case EA_VALUE_STRING:
531 break;
532 case EA_VALUE_FLOAT:
533 case EA_VALUE_NODE:
534 case EA_VALUE_CVAR:
535 /* nothing */
536 break;
537 default:
538 Com_Error(ERR_FATAL, "UI_ReleaseVariable: Variable type \"%d\" unsupported", variable->type);
539 }
540
541 /* bug safe, but useless */
542 OBJZERO(*variable);
543}
544
550static void UI_ExecuteAction (const uiAction_t* action, uiCallContext_t* context)
551{
552 switch (action->type) {
553 case EA_NULL:
554 /* do nothing */
555 break;
556
557 case EA_CMD:
558 /* execute a command */
559 if (action->d.terminal.d1.constString)
560 Cbuf_AddText("%s\n", UI_GenInjectedString(action->d.terminal.d1.constString, true, context));
561 break;
562
563 case EA_CALL:
564 case EA_LISTENER:
565 UI_ExecuteCallAction(action, context);
566 break;
567
568 case EA_POPVARS:
569 {
570 const int number = action->d.terminal.d1.integer;
571 assert(number <= context->varNumber);
572 for (int i = 0; i < number; i++) {
573 const int varId = context->varPosition + context->varNumber - i - 1;
574 UI_ReleaseVariable(&(ui_global.variableStack[varId]));
575 }
576 context->varNumber -= number;
577 }
578 break;
579
580 case EA_PUSHVARS:
581#ifdef DEBUG
582 /* check sanity */
584#endif
585 context->varNumber += action->d.terminal.d1.integer;
586 if (context->varNumber >= UI_MAX_VARIABLESTACK)
587 Com_Error(ERR_FATAL, "UI_ExecuteAction: Variable stack full. UI_MAX_VARIABLESTACK hit.");
588 break;
589
590 case EA_ASSIGN:
591 UI_ExecuteSetAction(action, context);
592 break;
593
594 case EA_DELETE:
595 {
596 const char* cvarname = action->d.nonTerminal.left->d.terminal.d1.constString;
597 Cvar_Delete(cvarname);
598 break;
599 }
600
601 case EA_WHILE:
602 {
603 int loop = 0;
604 while (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) {
605 UI_ExecuteActions(action->d.nonTerminal.right, context);
606 if (context->breakLoop) {
607 context->breakLoop = false;
608 break;
609 }
610 if (loop > 1000) {
611 Com_Printf("UI_ExecuteAction: Infinite loop. Force breaking 'while'\n");
612 break;
613 }
614 loop++;
615 }
616 break;
617 }
618
619 /*
620 expected usage in .ufo scripts:
621
622 forchildin ( *node:someNode ) {
623 ...
624 *node:child <-- reference the child being iterated
625 ...
626 }
627 */
628 case EA_FORCHILDIN:
629 {
630 uiNode_t* root;
631 root = UI_GetNodeFromExpression(action->d.nonTerminal.left, context, nullptr);
632 if (!root) {
633 break;
634 }
635
636 int loop = 0;
637 for (uiNode_t* node = root->firstChild; node; node = node->next, loop++) {
638 /* associate the child node with the call context so it can be referenced inside the
639 script block */
640 context->tagNode = node;
641 /* execute the script block inside the forchildin loop */
642 UI_ExecuteActions(action->d.nonTerminal.right, context);
643 /* clean the tag node */
644 context->tagNode = nullptr;
645 if (context->breakLoop) {
646 context->breakLoop = false;
647 break;
648 }
649 if (loop > 1000) {
650 Com_Printf("UI_ExecuteAction: Infinite loop. Force breaking 'forchildin'\n");
651 break;
652 }
653 }
654 break;
655 }
656
657 case EA_BREAK:
658 {
659 /* flag to break, the caller must check this flag before processing the next action in the list */
660 Com_Printf("BREAK: break statement found\n");
661 context->breakLoop = true;
662 break;
663 }
664
665 case EA_IF:
666 if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) {
667 UI_ExecuteActions(action->d.nonTerminal.right, context);
668 return;
669 }
670 action = action->next;
671 while (action && action->type == EA_ELIF) {
672 if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) {
673 UI_ExecuteActions(action->d.nonTerminal.right, context);
674 return;
675 }
676 action = action->next;
677 }
678 if (action && action->type == EA_ELSE) {
679 UI_ExecuteActions(action->d.nonTerminal.right, context);
680 }
681 break;
682
686 case EA_ELSE:
687 case EA_ELIF:
688 /* previous EA_IF execute this action */
689 break;
690
691 default:
692 Com_Error(ERR_FATAL, "UI_ExecuteAction: Unknown action type %i", action->type);
693 }
694}
695
701static void UI_ExecuteActions (const uiAction_t* firstAction, uiCallContext_t* context)
702{
703 static int callnumber = 0;
704 if (callnumber++ > 20) {
705 Com_Printf("UI_ExecuteActions: Break possible infinite recursion, source [%s]\n", UI_GetPath(context->source));
706 return;
707 }
708 for (const uiAction_t* action = firstAction; action && !context->breakLoop; action = action->next) {
709 UI_ExecuteAction(action, context);
710 }
711 callnumber--;
712}
713
717void UI_ExecuteConFuncActions (uiNode_t* source, const uiAction_t* firstAction)
718{
719 uiCallContext_t context;
720 OBJZERO(context);
721 context.source = source;
722 context.useCmdParam = true;
723 UI_ExecuteActions(firstAction, &context);
724}
725
726void UI_ExecuteEventActions (uiNode_t* source, const uiAction_t* firstAction)
727{
728 uiCallContext_t context;
729 OBJZERO(context);
730 context.source = source;
731 context.useCmdParam = false;
732 UI_ExecuteActions(firstAction, &context);
733}
734
735void UI_ExecuteEventActionsEx (uiNode_t* source, const uiAction_t* firstAction, linkedList_t* params)
736{
737 uiCallContext_t context;
738 OBJZERO(context);
739 context.source = source;
740 context.useCmdParam = false;
741 context.params = params;
742 context.paramNumber = LIST_Count(params);
743 UI_ExecuteActions(firstAction, &context);
744}
745
751bool UI_IsInjectedString (const char* string)
752{
753 const char* c = string;
754 assert(string);
755 while (*c != '\0') {
756 if (*c == '<') {
757 const char* d = c + 1;
758 if (*d != '>') {
759 while (*d) {
760 if (*d == '>')
761 return true;
762 if (*d == ' ' || *d == '\t' || *d == '\n' || *d == '\r')
763 break;
764 d++;
765 }
766 }
767 }
768 c++;
769 }
770 return false;
771}
772
779{
780 /* skip const string */
781 if ((uintptr_t)ui_global.adata <= (uintptr_t)pointer && (uintptr_t)pointer < (uintptr_t)ui_global.adata + (uintptr_t)ui_global.adataize)
782 return;
783
784 /* skip pointer out of ui_dynStringPool */
786 return;
787
789}
790
797{
799 action->type = EA_CMD;
800 action->d.terminal.d1.constString = command;
801 return action;
802}
803
814void UI_PoolAllocAction (uiAction_t** action, int type, const void* data)
815{
816 if (*action)
817 Com_Error(ERR_FATAL, "There is already an action assigned");
819 (*action)->type = type;
820 switch (type) {
821 case EA_CMD:
822 (*action)->d.terminal.d1.constString = Mem_PoolStrDup((const char*)data, ui_sysPool, 0);
823 break;
824 default:
825 Com_Error(ERR_FATAL, "Action type %i is not yet implemented", type);
826 }
827}
828
835void UI_AddListener (uiNode_t* node, const value_t* property, const uiNode_t* functionNode)
836{
837 /* all nodes are now created in a dynamic way, so this check is removed */
838 /*
839 if (node->dynamic) {
840 Com_Printf("UI_AddListener: '%s' is a dynamic node. We can't listen it.\n", UI_GetPath(node));
841 return;
842 }
843 if (functionNode->dynamic) {
844 Com_Printf("UI_AddListener: '%s' is a dynamic node. It can't be a listener callback.\n", UI_GetPath(functionNode));
845 return;
846 }
847 */
848
849 /* create the call action */
852 value->d.terminal.d1.constString = Mem_PoolStrDup(UI_GetPath(functionNode), ui_sysPool, 0);
853 value->next = nullptr;
854 action->type = EA_LISTENER;
855 action->d.nonTerminal.left = value;
857 action->d.terminal.d2.constData = &functionNode->onClick;
858 action->next = nullptr;
859
860 /* insert the action */
861 uiAction_t** anchor = &Com_GetValue<uiAction_t*>(node, property);
862 while (*anchor)
863 anchor = &(*anchor)->next;
864 *anchor = action;
865}
866
870static void UI_AddListener_f (void)
871{
872 uiNode_t* node;
873 uiNode_t* function;
874 const value_t* property;
875
876 if (Cmd_Argc() != 3) {
877 Com_Printf("Usage: %s <pathnode@event> <pathnode>\n", Cmd_Argv(0));
878 return;
879 }
880
881 UI_ReadNodePath(Cmd_Argv(1), nullptr, nullptr, &node, &property);
882 if (node == nullptr) {
883 Com_Printf("UI_AddListener_f: '%s' node not found.\n", Cmd_Argv(1));
884 return;
885 }
886 if (property == nullptr || property->type != V_UI_ACTION) {
887 Com_Printf("UI_AddListener_f: '%s' property not found, or is not an event.\n", Cmd_Argv(1));
888 return;
889 }
890
891 function = UI_GetNodeByPath(Cmd_Argv(2));
892 if (function == nullptr) {
893 Com_Printf("UI_AddListener_f: '%s' node not found.\n", Cmd_Argv(2));
894 return;
895 }
896
897 UI_AddListener(node, property, function);
898}
899
906void UI_RemoveListener (uiNode_t* node, const value_t* property, uiNode_t* functionNode)
907{
908 void* data;
909
910 /* data we must remove */
911 data = (void*) &functionNode->onClick;
912
913 /* remove the action */
914 uiAction_t*& action = Com_GetValue<uiAction_t*>(node, property);
915 if (uiAction_t* lastAction = action) {
916 uiAction_t* tmp = nullptr;
917 if (lastAction->type == EA_LISTENER && lastAction->d.terminal.d2.constData == data) {
918 tmp = lastAction;
919 action = lastAction->next;
920 } else {
921 while (lastAction->next) {
922 if (lastAction->next->type == EA_LISTENER && lastAction->next->d.terminal.d2.constData == data)
923 break;
924 lastAction = lastAction->next;
925 }
926 if (lastAction->next) {
927 tmp = lastAction->next;
928 lastAction->next = lastAction->next->next;
929 }
930 }
931 if (tmp) {
932 uiAction_t* value = tmp->d.nonTerminal.left;
933 Mem_Free(value->d.terminal.d1.data);
934 Mem_Free(value);
935 Mem_Free(tmp);
936 }
937 else
938 Com_Printf("UI_RemoveListener_f: '%s' into '%s' not found.\n", Cmd_Argv(2), Cmd_Argv(1));
939 }
940}
941
945static void UI_RemoveListener_f (void)
946{
947 uiNode_t* node;
948 uiNode_t* function;
949 const value_t* property;
950
951 if (Cmd_Argc() != 3) {
952 Com_Printf("Usage: %s <pathnode@event> <pathnode>\n", Cmd_Argv(0));
953 return;
954 }
955
956 UI_ReadNodePath(Cmd_Argv(1), nullptr, nullptr, &node, &property);
957 if (node == nullptr) {
958 Com_Printf("UI_RemoveListener_f: '%s' node not found.\n", Cmd_Argv(1));
959 return;
960 }
961 if (property == nullptr || property->type != V_UI_ACTION) {
962 Com_Printf("UI_RemoveListener_f: '%s' property not found, or is not an event.\n", Cmd_Argv(1));
963 return;
964 }
965
966 function = UI_GetNodeByPath(Cmd_Argv(2));
967 if (function == nullptr) {
968 Com_Printf("UI_RemoveListener_f: '%s' node not found.\n", Cmd_Argv(2));
969 return;
970 }
971
972 UI_RemoveListener(node, property, function);
973}
974
975static void UI_CvarChangeListener (const char* cvarName, const char* oldValue, const char* newValue, void* data)
976{
977 linkedList_t* list = static_cast<linkedList_t*>(data);
978 LIST_Foreach(list, char const, confunc) {
979 UI_ExecuteConfunc("%s %s %s", confunc, oldValue, newValue);
980 }
981}
982
983static void UI_AddCvarListener_f (void)
984{
985 const char* cvarName;
986 const char* confuncName;
988
989 if (Cmd_Argc() != 3) {
990 Com_Printf("Usage: %s <cvar> <confunc>\n", Cmd_Argv(0));
991 return;
992 }
993
994 cvarName = Cmd_Argv(1);
995 confuncName = Cmd_Argv(2);
997 if (LIST_ContainsString(static_cast<linkedList_t*>(l->data), confuncName) == nullptr)
998 LIST_AddString(reinterpret_cast<linkedList_t**>(&l->data), confuncName);
999}
1000
1001static void UI_RemoveCvarListener_f (void)
1002{
1003 const char* cvarName;
1004 const char* confuncName;
1005 cvar_t* var;
1006
1007 if (Cmd_Argc() != 3) {
1008 Com_Printf("Usage: %s <cvar> <confunc>\n", Cmd_Argv(0));
1009 return;
1010 }
1011
1012 cvarName = Cmd_Argv(1);
1013 confuncName = Cmd_Argv(2);
1014
1015 var = Cvar_FindVar(cvarName);
1016 if (var == nullptr)
1017 return;
1018
1020 while (l) {
1021 if (l->exec == UI_CvarChangeListener) {
1022 linkedList_t* entry = const_cast<linkedList_t*>(LIST_ContainsString(static_cast<linkedList_t*>(l->data), confuncName));
1023 if (entry != nullptr)
1024 LIST_RemoveEntry(reinterpret_cast<linkedList_t**>(&l->data), entry);
1025 if (LIST_IsEmpty(static_cast<linkedList_t*>(l->data))) {
1027 }
1028 return;
1029 }
1030 l = l->next;
1031 }
1032}
1033
1035{
1037 Cmd_AddCommand("ui_addcvarlistener", UI_AddCvarListener_f, "Add a confunc to a cvar change event");
1038 Cmd_AddCommand("ui_removecvarlistener", UI_RemoveCvarListener_f, "Remove a confunc from a cvar change event");
1040 Cmd_AddCommand("ui_addlistener", UI_AddListener_f, "Add a function into a node event");
1041 Cmd_AddCommand("ui_removelistener", UI_RemoveListener_f, "Remove a function from a node event");
1042}
const char * CL_Translate(const char *t)
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition cmd.cpp:516
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition cmd.cpp:505
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition cmd.cpp:744
void Cbuf_AddText(const char *format,...)
Adds command text at the end of the buffer.
Definition cmd.cpp:126
void Com_Error(int code, const char *fmt,...)
Definition common.cpp:459
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define ERR_FATAL
Definition common.h:210
cvar_t * Cvar_ForceSet(const char *varName, const char *value)
Will set the variable even if NOSET or LATCH.
Definition cvar.cpp:604
cvarChangeListener_t * Cvar_RegisterChangeListener(const char *varName, cvarChangeListenerFunc_t listenerFunc)
Registers a listener that is executed each time a cvar changed its value.
Definition cvar.cpp:446
void Cvar_UnRegisterChangeListener(const char *varName, cvarChangeListenerFunc_t listenerFunc)
Unregisters a cvar change listener.
Definition cvar.cpp:487
bool Cvar_Delete(const char *varName)
Function to remove the cvar and free the space.
Definition cvar.cpp:279
cvar_t * Cvar_FindVar(const char *varName)
Searches for a cvar given by parameter.
Definition cvar.cpp:106
cvar_t * Cvar_Get(const char *var_name, const char *var_value, int flags, const char *desc)
Init or return a cvar.
Definition cvar.cpp:342
void Sys_Error(const char *error,...)
Definition g_main.cpp:421
void LIST_AddString(linkedList_t **listDest, const char *data)
Adds an string to a new or to an already existing linked list. The string is copied here.
Definition list.cpp:139
void LIST_Delete(linkedList_t **list)
Definition list.cpp:195
bool LIST_RemoveEntry(linkedList_t **list, linkedList_t *entry)
Removes one entry from the linked list.
Definition list.cpp:172
int LIST_Count(const linkedList_t *list)
Definition list.cpp:344
bool LIST_IsEmpty(const linkedList_t *list)
Checks whether the given list is empty.
Definition list.cpp:335
const linkedList_t * LIST_ContainsString(const linkedList_t *list, const char *string)
Searches for the first occurrence of a given string.
Definition list.cpp:73
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition list.h:41
bool _Mem_AllocatedInPool(memPool_t *pool, const void *pointer)
Definition mem.cpp:486
#define Mem_Free(ptr)
Definition mem.h:35
#define Mem_PoolStrDup(in, pool, tagNum)
Definition mem.h:50
#define Mem_PoolAllocType(type, pool)
Definition mem.h:43
QGL_EXTERN GLsizei const GLvoid * data
Definition r_gl.h:89
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLint GLenum GLboolean GLsizei const GLvoid * pointer
Definition r_gl.h:94
T & Com_GetValue(void *const object, value_t const *const value)
Definition scripts.h:174
#define Q_streq(a, b)
Definition shared.h:136
#define OBJZERO(obj)
Definition shared.h:178
#define lengthof(x)
Definition shared.h:105
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition shared.cpp:587
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition shared.cpp:410
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
char * string
Definition cvar.h:73
cvarChangeListener_t * changeListener
Definition cvar.h:83
struct cvarChangeListener_s * next
Definition cvar.h:63
cvarChangeListenerFunc_t exec
Definition cvar.h:62
void * data
Definition list.h:31
linkedList_t * next
Definition list.h:32
Atomic element to store UI scripts The parser use this atom to translate script action into many tree...
Definition ui_actions.h:144
short type
Define the type of the element, it can be a command, an operator, or a value.
Definition ui_actions.h:149
uiTerminalActionData_t d1
Definition ui_actions.h:175
struct uiAction_t::@020173205255351022052355137266041366256354050344::@226100300324067003344030331332225043077332072062 terminal
Stores a terminal action (a value, which must be a leaf in the tree).
struct uiAction_s * next
Next element in the action list.
Definition ui_actions.h:183
struct uiAction_t::@020173205255351022052355137266041366256354050344::@051207210304220322206103241251135377014006203150 nonTerminal
Stores a none terminal action (a command or an operator).
uiTerminalActionData_t d2
Definition ui_actions.h:176
struct uiAction_s * right
Definition ui_actions.h:166
union uiAction_t::@020173205255351022052355137266041366256354050344 d
Stores data about the action.
struct uiAction_s * left
Definition ui_actions.h:165
Contain the context of the calling of a function.
Definition ui_actions.h:208
linkedList_t * params
Definition ui_actions.h:215
uiNode_t * source
Definition ui_actions.h:210
uiNode_t * tagNode
Definition ui_actions.h:212
Atomic structure used to define most of the UI.
Definition ui_nodes.h:80
uiNode_t * firstChild
Definition ui_nodes.h:89
uiNode_t * next
Definition ui_nodes.h:91
uiBehaviour_t * behaviour
Definition ui_nodes.h:83
uiNode_t * parent
Definition ui_nodes.h:92
LUA_EVENT lua_onClick
Definition ui_nodes.h:148
struct uiAction_s * onClick
Definition ui_nodes.h:135
uiNode_t * root
Definition ui_nodes.h:93
Type for uiAction_t It also contain type about type (for example EA_BINARYOPERATOR).
Definition ui_actions.h:194
char * string
Definition ui_actions.h:199
uiActionType_t type
Definition ui_actions.h:195
union uiValue_t::@240370141006122110255260007133317070215100126152 value
size_t ofs
Definition scripts.h:170
const char * string
Definition scripts.h:168
valueTypes_t type
Definition scripts.h:169
static void UI_CheckActionTokenTypeSanity(void)
Check if the action token list is sorted by alphabet, else dichotomic search can't work.
void UI_ExecuteEventActionsEx(uiNode_t *source, const uiAction_t *firstAction, linkedList_t *params)
void UI_ExecuteEventActions(uiNode_t *source, const uiAction_t *firstAction)
const char * UI_GetParam(const uiCallContext_t *context, int paramID)
static void UI_AddListener_f(void)
add a call of a function into a node event
void UI_InitActions(void)
int UI_GetActionTokenType(const char *token, int group)
return an action type from a token, and a group
void UI_PoolAllocAction(uiAction_t **action, int type, const void *data)
Set a new action to a uiAction_t pointer.
void UI_RemoveListener(uiNode_t *node, const value_t *property, uiNode_t *functionNode)
Remove a function callback from a node event. There can be more than on listener.
static void UI_ExecuteSetAction(const uiAction_t *action, const uiCallContext_t *context)
void UI_AddListener(uiNode_t *node, const value_t *property, const uiNode_t *functionNode)
Add a callback of a function into a node event. There can be more than on listener.
static void UI_CvarChangeListener(const char *cvarName, const char *oldValue, const char *newValue, void *data)
static void UI_ExecuteActions(const uiAction_t *firstAction, uiCallContext_t *context)
Execute a tree of actions from a source.
uiAction_t * UI_AllocStaticCommandAction(const char *command)
Allocate and initialize a command action.
static const char * UI_GenCommandReadProperty(const char *input, char *output, int outputSize)
read a property name from an input buffer to an output
bool UI_IsInjectedString(const char *string)
Test if a string use an injection syntax.
static void UI_AddCvarListener_f(void)
static void UI_RemoveCvarListener_f(void)
static void UI_ReleaseVariable(uiValue_t *variable)
static void UI_NodeSetPropertyFromActionValue(uiNode_t *node, const value_t *property, const uiCallContext_t *context, uiAction_t *value)
uiValue_t * UI_GetVariable(const uiCallContext_t *context, int relativeVarId)
Return a variable from the context.
static void UI_ExecuteCallAction(const uiAction_t *action, const uiCallContext_t *context)
void UI_ExecuteConFuncActions(uiNode_t *source, const uiAction_t *firstAction)
allow to inject command param into cmd of confunc command
static void UI_ExecuteAction(const uiAction_t *action, uiCallContext_t *context)
Execute an action from a source.
static const ui_typedActionToken_t actionTokens[]
List of typed token.
int UI_GetParamNumber(const uiCallContext_t *context)
void UI_FreeStringProperty(void *pointer)
Free a string property if it is allocated into ui_dynStringPool.
const char * UI_GenInjectedString(const char *input, bool addNewLine, const uiCallContext_t *context)
Replace injection identifiers (e.g. <eventParam>) by a value.
static void UI_RemoveListener_f(void)
Remove a function callback from a node event.
@ EA_OPERATOR_LT
Definition ui_actions.h:71
@ EA_CMD
Definition ui_actions.h:44
@ EA_OPERATOR_STR_NE
Definition ui_actions.h:85
@ EA_VALUE_PATHPROPERTY_WITHINJECTION
Definition ui_actions.h:104
@ EA_OPERATOR_XOR
Definition ui_actions.h:62
@ EA_VALUE_STRING
Definition ui_actions.h:95
@ EA_OPERATOR_DIV
Definition ui_actions.h:79
@ EA_OPERATOR_ADD
Definition ui_actions.h:76
@ EA_VALUE_PATHNODE
Definition ui_actions.h:101
@ EA_ELSE
Definition ui_actions.h:48
@ EA_VALUE_FLOAT
Definition ui_actions.h:97
@ EA_CALL
Definition ui_actions.h:45
@ EA_LISTENER
Definition ui_actions.h:52
@ EA_OPERATOR_GT
Definition ui_actions.h:70
@ EA_OPERATOR_GE
Definition ui_actions.h:69
@ EA_OPERATOR_OR
Definition ui_actions.h:61
@ EA_BINARYOPERATOR
Definition ui_actions.h:36
@ EA_OPERATOR_NOT
Definition ui_actions.h:63
@ EA_ASSIGN
Definition ui_actions.h:46
@ EA_ELIF
Definition ui_actions.h:49
@ EA_OPERATOR_SUB
Definition ui_actions.h:77
@ EA_OPERATOR_AND
Definition ui_actions.h:60
@ EA_VALUE_CVARNAME
Definition ui_actions.h:99
@ EA_DELETE
Definition ui_actions.h:51
@ EA_FORCHILDIN
Definition ui_actions.h:55
@ EA_POPVARS
Definition ui_actions.h:54
@ EA_OPERATOR_MOD
Definition ui_actions.h:80
@ EA_VALUE_RAW
Definition ui_actions.h:98
@ EA_OPERATOR_LE
Definition ui_actions.h:68
@ EA_VALUE_PATHPROPERTY
Definition ui_actions.h:103
@ EA_OPERATOR_STR_EQ
Definition ui_actions.h:84
@ EA_OPERATOR_EXISTS
Definition ui_actions.h:89
@ EA_VALUE_CVARNAME_WITHINJECTION
Definition ui_actions.h:100
@ EA_NULL
Definition ui_actions.h:34
@ EA_PUSHVARS
Definition ui_actions.h:53
@ EA_OPERATOR_MUL
Definition ui_actions.h:78
@ EA_BREAK
Definition ui_actions.h:56
@ EA_OPERATOR_NE
Definition ui_actions.h:72
@ EA_IF
Definition ui_actions.h:47
@ EA_WHILE
Definition ui_actions.h:50
@ EA_VALUE_NODE
Definition ui_actions.h:108
@ EA_VALUE_CVAR
Definition ui_actions.h:107
@ EA_OPERATOR_EQ
Definition ui_actions.h:67
@ EA_ACTION
Definition ui_actions.h:43
@ EA_UNARYOPERATOR
Definition ui_actions.h:37
@ EA_VALUE_PATHNODE_WITHINJECTION
Definition ui_actions.h:102
const value_t * UI_GetPropertyFromBehaviour(const uiBehaviour_t *behaviour, const char *name)
Get a property from a behaviour or his inheritance It use a dichotomic search.
void(* uiNodeMethod_t)(uiNode_t *node, const struct uiCallContext_s *context)
Signature of a function to bind a node method.
bool UI_GetBooleanFromExpression(uiAction_t *expression, const uiCallContext_t *context)
Check if an expression is true.
const char * UI_GetStringFromExpression(uiAction_t *expression, const uiCallContext_t *context)
uiNode_t * UI_GetNodeFromExpression(uiAction_t *expression, const uiCallContext_t *context, const value_t **property)
Get a node and a property from an expression.
Internal data use by the UI package.
#define UI_MAX_VARIABLESTACK
Definition ui_internal.h:33
memPool_t * ui_sysPool
Definition ui_main.cpp:42
uiGlobal_t ui_global
Definition ui_main.cpp:38
bool UI_ExecuteLuaMethod(uiNode_t *node, LUA_FUNCTION fcn, linkedList_t *params, int nparams)
Executes a lua based method defined on the behaviour class of a node.
Definition ui_lua.cpp:295
bool UI_ExecuteLuaConFunc(uiNode_t *node, LUA_FUNCTION fcn)
Executes a lua based confunc node.
Definition ui_lua.cpp:323
Basic lua initialization for the ui.
void UI_ExecuteConfunc(const char *fmt,...)
Executes confunc - just to identify those confuncs in the code - in this frame.
Definition ui_main.cpp:110
const char * UI_GetStringFromNodeProperty(const uiNode_t *node, const value_t *property)
Return a string from a node property.
Definition ui_node.cpp:990
void UI_NodeSetPropertyFromRAW(uiNode_t *node, const value_t *property, const void *rawValue, int rawType)
Definition ui_node.cpp:873
bool UI_NodeSetProperty(uiNode_t *node, const value_t *property, const char *value)
Set node property.
Definition ui_node.cpp:902
C interface to allow to access to cpp node code.
memPool_t * ui_dynStringPool
Definition ui_main.cpp:40
uiNode_t * UI_GetNodeByPath(const char *path)
Return a node by a path name (names with dot separation) It is a simplification facade over UI_ReadNo...
Definition ui_nodes.cpp:313
const char * UI_GetPath(const uiNode_t *node)
Return a path from a window to a node.
Definition ui_nodes.cpp:174
void UI_ReadNodePath(const char *path, const uiNode_t *relativeNode, const uiNode_t *iterationNode, uiNode_t **resultNode, const value_t **resultProperty, value_t *luaMethod)
Read a path and return every we can use (node and property).
Definition ui_nodes.cpp:219
bool UI_InitRawActionValue(uiAction_t *action, uiNode_t *node, const value_t *property, const char *string)
Definition ui_parse.cpp:238
uiAction_t * UI_AllocStaticAction(void)
Allocate an action.
Definition ui_parse.cpp:221
#define V_UI_NODEMETHOD
Definition ui_parse.h:61
#define V_UI_NODEMETHOD_LUA
Definition ui_parse.h:62
#define V_UI_ACTION
Definition ui_parse.h:54
#define V_UI_CVAR
Definition ui_parse.h:59
#define V_UI_MASK
Definition ui_parse.h:51
const char * constString
Definition ui_actions.h:127
const void * constData
Definition ui_actions.h:129