UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ui_actions.cpp
Go to the documentation of this file.
1 
5 /*
6 Copyright (C) 2002-2020 UFO: Alien Invasion.
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 See the GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, 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 
36 typedef 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 
89 static 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 
113 int 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 
143 static 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 
166 int UI_GetParamNumber (const uiCallContext_t* context)
167 {
168  if (context->useCmdParam)
169  return Cmd_Argc() - 1;
170  return context->paramNumber;
171 }
172 
179 const 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 
209 const 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 
342 static 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 
370 static 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 
390  if (left->type == EA_VALUE_CVARNAME || left->type == EA_VALUE_CVARNAME_WITHINJECTION) {
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;
406  else if (left->type == EA_VALUE_PATHPROPERTY_WITHINJECTION)
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 
425 static 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 
517 uiValue_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 
525 static 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 
550 static 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;
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 
701 static 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 
717 void 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 
726 void 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 
735 void 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 
751 bool 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 */
785  if (!_Mem_AllocatedInPool(ui_dynStringPool, pointer))
786  return;
787 
788  Mem_Free(pointer);
789 }
790 
797 {
798  uiAction_t* action = UI_AllocStaticAction();
799  action->type = EA_CMD;
800  action->d.terminal.d1.constString = command;
801  return action;
802 }
803 
814 void 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 
835 void 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 
870 static 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 
906 void 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 
945 static 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 
975 static 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 
983 static 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 
1001 static 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 
1034 void UI_InitActions (void)
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 }
void UI_InitActions(void)
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition: cmd.cpp:516
void Cmd_AddCommand(const char *cmdName, xcommand_t function, const char *desc)
Add a new command to the script interface.
Definition: cmd.cpp:744
bool UI_IsInjectedString(const char *string)
Test if a string use an injection syntax.
Definition: ui_actions.cpp:751
static const char * UI_GenCommandReadProperty(const char *input, char *output, int outputSize)
read a property name from an input buffer to an output
Definition: ui_actions.cpp:143
uiAction_t * UI_AllocStaticAction(void)
Allocate an action.
Definition: ui_parse.cpp:221
struct uiAction_s::@14::@16 terminal
Stores a terminal action (a value, which must be a leaf in the tree)
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
bool UI_ExecuteLuaConFunc(uiNode_t *node, LUA_FUNCTION fcn)
Executes a lua based confunc node.
Definition: ui_lua.cpp:323
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
const char * token
Definition: ui_actions.cpp:37
bool UI_NodeSetProperty(uiNode_t *node, const value_t *property, const char *value)
Set node property.
Definition: ui_node.cpp:890
void UI_PoolAllocAction(uiAction_t **action, int type, const void *data)
Set a new action to a uiAction_t pointer.
Definition: ui_actions.cpp:814
uiNode_t * parent
Definition: ui_nodes.h:92
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_GetBooleanFromExpression(uiAction_t *expression, const uiCallContext_t *context)
Check if an expression is true.
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
static void UI_CvarChangeListener(const char *cvarName, const char *oldValue, const char *newValue, void *data)
Definition: ui_actions.cpp:975
void Cvar_UnRegisterChangeListener(const char *varName, cvarChangeListenerFunc_t listenerFunc)
Unregisters a cvar change listener.
Definition: cvar.cpp:487
uiNode_t * next
Definition: ui_nodes.h:91
uiActionType_t type
Definition: ui_actions.h:195
Basic lua initialization for the ui.
uiGlobal_t ui_global
Definition: ui_main.cpp:38
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
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
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition: cvar.h:71
struct ui_typedActionToken_s ui_typedActionToken_t
QGL_EXTERN GLint GLenum GLboolean GLsizei const GLvoid * pointer
Definition: r_gl.h:94
void * data
Definition: list.h:31
#define V_UI_NODEMETHOD
Definition: ui_parse.h:61
static void UI_ExecuteAction(const uiAction_t *action, uiCallContext_t *context)
Execute an action from a source.
Definition: ui_actions.cpp:550
uiBehaviour_t * behaviour
Definition: ui_nodes.h:83
void Cbuf_AddText(const char *format,...)
Adds command text at the end of the buffer.
Definition: cmd.cpp:126
uiValue_t * UI_GetVariable(const uiCallContext_t *context, int relativeVarId)
Return a variable from the context.
Definition: ui_actions.cpp:517
uiAction_t * UI_AllocStaticCommandAction(const char *command)
Allocate and initialize a command action.
Definition: ui_actions.cpp:796
#define UI_MAX_VARIABLESTACK
Definition: ui_internal.h:33
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
valueTypes_t type
Definition: scripts.h:170
static void UI_AddListener_f(void)
add a call of a function into a node event
Definition: ui_actions.cpp:870
void LIST_Delete(linkedList_t **list)
Definition: list.cpp:195
union uiAction_s::@14 d
Stores data about the action.
struct uiAction_s * onClick
Definition: ui_nodes.h:135
void UI_FreeStringProperty(void *pointer)
Free a string property if it is allocated into ui_dynStringPool.
Definition: ui_actions.cpp:778
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.
#define ERR_FATAL
Definition: common.h:210
int LIST_Count(const linkedList_t *list)
Definition: list.cpp:344
Internal data use by the UI package.
C interface to allow to access to cpp node code.
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
static void UI_CheckActionTokenTypeSanity(void)
Check if the action token list is sorted by alphabet, else dichotomic search can't work...
Definition: ui_actions.cpp:95
char * string
Definition: ui_actions.h:199
cvarChangeListenerFunc_t exec
Definition: cvar.h:62
uiNode_t * root
Definition: ui_nodes.h:93
struct uiAction_s::@14::@15 nonTerminal
Stores a none terminal action (a command or an operator)
bool _Mem_AllocatedInPool(memPool_t *pool, const void *pointer)
Definition: mem.cpp:482
int UI_GetActionTokenType(const char *token, int group)
return an action type from a token, and a group
Definition: ui_actions.cpp:113
static void UI_RemoveListener_f(void)
Remove a function callback from a node event.
Definition: ui_actions.cpp:945
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 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
const char * UI_GetStringFromExpression(uiAction_t *expression, const uiCallContext_t *context)
#define V_UI_CVAR
Definition: ui_parse.h:59
#define OBJZERO(obj)
Definition: shared.h:178
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition: r_gl.h:110
memPool_t * ui_dynStringPool
Definition: ui_main.cpp:40
static const ui_typedActionToken_t actionTokens[]
List of typed token.
Definition: ui_actions.cpp:46
static void UI_ExecuteCallAction(const uiAction_t *action, const uiCallContext_t *context)
Definition: ui_actions.cpp:425
static void UI_NodeSetPropertyFromActionValue(uiNode_t *node, const value_t *property, const uiCallContext_t *context, uiAction_t *value)
Definition: ui_actions.cpp:342
void UI_ExecuteEventActionsEx(uiNode_t *source, const uiAction_t *firstAction, linkedList_t *params)
Definition: ui_actions.cpp:735
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
uiNode_t * UI_GetNodeFromExpression(uiAction_t *expression, const uiCallContext_t *context, const value_t **property)
Get a node and a property from an expression.
const char * UI_GetPath(const uiNode_t *node)
Return a path from a window to a node.
Definition: ui_nodes.cpp:174
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition: shared.cpp:587
uiNode_t * source
Definition: ui_actions.h:210
static void UI_AddCvarListener_f(void)
Definition: ui_actions.cpp:983
void UI_ExecuteEventActions(uiNode_t *source, const uiAction_t *firstAction)
Definition: ui_actions.cpp:726
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.
Definition: ui_actions.cpp:835
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
linkedList_t * params
Definition: ui_actions.h:215
Atomic structure used to define most of the UI.
Definition: ui_nodes.h:80
int adataize
Definition: ui_internal.h:75
const char * string
Definition: scripts.h:169
bool LIST_RemoveEntry(linkedList_t **list, linkedList_t *entry)
Removes one entry from the linked list.
Definition: list.cpp:172
static void UI_ExecuteSetAction(const uiAction_t *action, const uiCallContext_t *context)
Definition: ui_actions.cpp:370
uiNode_t * tagNode
Definition: ui_actions.h:212
bool Cvar_Delete(const char *varName)
Function to remove the cvar and free the space.
Definition: cvar.cpp:279
Contain the context of the calling of a function.
Definition: ui_actions.h:208
size_t ofs
Definition: scripts.h:171
void UI_NodeSetPropertyFromRAW(uiNode_t *node, const value_t *property, const void *rawValue, int rawType)
Definition: ui_node.cpp:861
int UI_GetParamNumber(const uiCallContext_t *context)
Definition: ui_actions.cpp:166
void UI_ExecuteConFuncActions(uiNode_t *source, const uiAction_t *firstAction)
allow to inject command param into cmd of confunc command
Definition: ui_actions.cpp:717
const char * UI_GetParam(const uiCallContext_t *context, int paramID)
Definition: ui_actions.cpp:179
const char * UI_GenInjectedString(const char *input, bool addNewLine, const uiCallContext_t *context)
Replace injection identifiers (e.g. ) by a value.
Definition: ui_actions.cpp:209
#define V_UI_NODEMETHOD_LUA
Definition: ui_parse.h:62
cvarChangeListener_t * changeListener
Definition: cvar.h:83
QGL_EXTERN GLint i
Definition: r_gl.h:113
const char * CL_Translate(const char *t)
union uiValue_s::@17 value
char * string
Definition: cvar.h:73
uiValue_t variableStack[UI_MAX_VARIABLESTACK]
Local var for script function.
Definition: ui_internal.h:64
bool UI_InitRawActionValue(uiAction_t *action, uiNode_t *node, const value_t *property, const char *string)
Definition: ui_parse.cpp:238
#define Mem_Free(ptr)
Definition: mem.h:35
#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
struct uiAction_s * next
Next element in the action list.
Definition: ui_actions.h:183
linkedList_t * next
Definition: list.h:32
Type for uiAction_t It also contain type about type (for example EA_BINARYOPERATOR) ...
Definition: ui_actions.h:194
byte * adata
Definition: ui_internal.h:74
cvar_t * Cvar_ForceSet(const char *varName, const char *value)
Will set the variable even if NOSET or LATCH.
Definition: cvar.cpp:604
#define lengthof(x)
Definition: shared.h:105
uiNode_t * firstChild
Definition: ui_nodes.h:89
memPool_t * ui_sysPool
Definition: ui_main.cpp:42
GLsizei const GLvoid * data
Definition: r_gl.h:152
#define V_UI_ACTION
Definition: ui_parse.h:54
struct cvarChangeListener_s * next
Definition: cvar.h:63
#define Q_streq(a, b)
Definition: shared.h:136
#define Mem_PoolStrDup(in, pool, tagNum)
Definition: mem.h:50
static void UI_ReleaseVariable(uiValue_t *variable)
Definition: ui_actions.cpp:525
void(* uiNodeMethod_t)(uiNode_t *node, const struct uiCallContext_s *context)
Signature of a function to bind a node method.
Definition: ui_behaviour.h:67
const char * UI_GetStringFromNodeProperty(const uiNode_t *node, const value_t *property)
Return a string from a node property.
Definition: ui_node.cpp:978
bool LIST_IsEmpty(const linkedList_t *list)
Checks whether the given list is empty.
Definition: list.cpp:335
#define Mem_PoolAllocType(type, pool)
Definition: mem.h:43
#define V_UI_MASK
Definition: ui_parse.h:51
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
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. ...
Definition: ui_actions.cpp:906
cvar_t * Cvar_FindVar(const char *varName)
Searches for a cvar given by parameter.
Definition: cvar.cpp:106
short type
Define the type of the element, it can be a command, an operator, or a value.
Definition: ui_actions.h:149
Atomic element to store UI scripts The parser use this atom to translate script action into many tree...
Definition: ui_actions.h:144
static void UI_RemoveCvarListener_f(void)
static void UI_ExecuteActions(const uiAction_t *firstAction, uiCallContext_t *context)
Execute a tree of actions from a source.
Definition: ui_actions.cpp:701
void UI_ExecuteConfunc(const char *fmt,...)
Executes confunc - just to identify those confuncs in the code - in this frame.
Definition: ui_main.cpp:110