static inline void UI_ExecuteSetAction (const uiAction_t* action, const uiCallContext_t *context) { const char* path; uiNode_t *node; const value_t *property; const uiAction_t *left; uiAction_t *right; left = action->d.nonTerminal.left; if (left == NULL) { Com_Printf("UI_ExecuteSetAction: Action without left operand skipped.\n"); return; } right = action->d.nonTerminal.right; if (right == NULL) { Com_Printf("UI_ExecuteSetAction: Action without right operand skipped.\n"); return; } if (left->type == EA_VALUE_CVARNAME || left->type == EA_VALUE_CVARNAME_WITHINJECTION) { const char *cvarName; const char* textValue; if (left->type == EA_VALUE_CVARNAME) cvarName = left->d.terminal.d1.constString; else cvarName = UI_GenInjectedString(left->d.terminal.d1.constString, false, context); textValue = UI_GetStringFromExpression(right, context); if (textValue[0] == '_') textValue = _(textValue + 1); Cvar_ForceSet(cvarName, textValue); return; } /* search the node */ if (left->type == EA_VALUE_PATHPROPERTY) path = left->d.terminal.d1.constString; else if (left->type == EA_VALUE_PATHPROPERTY_WITHINJECTION) path = UI_GenInjectedString(left->d.terminal.d1.constString, false, context); else Com_Error(ERR_FATAL, "UI_ExecuteSetAction: Property setter with wrong type '%d'", left->type); UI_ReadNodePath(path, context->source, &node, &property); if (!node) { Com_Printf("UI_ExecuteSetAction: node \"%s\" doesn't exist (source: %s)\n", path, UI_GetPath(context->source)); return; } if (!property) { Com_Printf("UI_ExecuteSetAction: property \"%s\" doesn't exist (source: %s)\n", path, UI_GetPath(context->source)); return; } UI_NodeSetPropertyFromActionValue(node, property, context, right); }
/** * @brief Execute an action from a source * @param[in] context Context node * @param[in] action Action to execute */ static void UI_ExecuteAction (const uiAction_t* action, uiCallContext_t *context) { switch (action->type) { case EA_NULL: /* do nothing */ break; case EA_CMD: /* execute a command */ if (action->d.terminal.d1.constString) Cbuf_AddText(UI_GenInjectedString(action->d.terminal.d1.constString, true, context)); break; case EA_CALL: case EA_LISTENER: UI_ExecuteCallAction(action, context); break; case EA_POPVARS: { int i; const int number = action->d.terminal.d1.integer; assert(number <= context->varNumber); for (i = 0; i < number; i++) { const int varId = context->varPosition + context->varNumber - i - 1; UI_ReleaseVariable(&(ui_global.variableStack[varId])); } context->varNumber -= number; } break; case EA_PUSHVARS: #ifdef DEBUG /* check sanity */ /** @todo check var slots should be empty */ #endif context->varNumber += action->d.terminal.d1.integer; if (context->varNumber >= UI_MAX_VARIABLESTACK) Com_Error(ERR_FATAL, "UI_ExecuteAction: Variable stack full. UI_MAX_VARIABLESTACK hit."); break; case EA_ASSIGN: UI_ExecuteSetAction(action, context); break; case EA_DELETE: { const char* cvarname = action->d.nonTerminal.left->d.terminal.d1.constString; Cvar_Delete(cvarname); break; } case EA_WHILE: { int loop = 0; while (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) { UI_ExecuteActions(action->d.nonTerminal.right, context); if (loop > 1000) { Com_Printf("UI_ExecuteAction: Infinite loop. Force breaking 'while'\n"); break; } loop++; } break; } case EA_IF: if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) { UI_ExecuteActions(action->d.nonTerminal.right, context); return; } action = action->next; while (action && action->type == EA_ELIF) { if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) { UI_ExecuteActions(action->d.nonTerminal.right, context); return; } action = action->next; } if (action && action->type == EA_ELSE) { UI_ExecuteActions(action->d.nonTerminal.right, context); } break; /** @todo Skipping actions like that is a bad way. the function should return the next action, * or we should move IF,ELSE,ELIF... in a IF block and not interleave it with default actions */ case EA_ELSE: case EA_ELIF: /* previous EA_IF execute this action */ break; default: Com_Error(ERR_FATAL, "UI_ExecuteAction: Unknown action type %i", action->type); } }
static inline void UI_ExecuteCallAction (const uiAction_t* action, const uiCallContext_t *context) { uiNode_t* callNode = NULL; uiAction_t* param; uiAction_t* left = action->d.nonTerminal.left; uiCallContext_t newContext; const value_t* callProperty = NULL; const char* path = left->d.terminal.d1.constString; if (left->type == EA_VALUE_PATHPROPERTY || left->type == EA_VALUE_PATHNODE) path = left->d.terminal.d1.constString; else if (left->type == EA_VALUE_PATHPROPERTY_WITHINJECTION || left->type == EA_VALUE_PATHNODE_WITHINJECTION) path = UI_GenInjectedString(left->d.terminal.d1.constString, false, context); UI_ReadNodePath(path, context->source, &callNode, &callProperty); if (callNode == NULL) { Com_Printf("UI_ExecuteCallAction: Node from path \"%s\" not found (relative to \"%s\").\n", path, UI_GetPath(context->source)); return; } if (callProperty != NULL && callProperty->type != V_UI_ACTION && callProperty->type != V_UI_NODEMETHOD) { Com_Printf("UI_ExecuteCallAction: Call operand %d unsupported. (%s)\n", callProperty->type, UI_GetPath(callNode)); return; } newContext.source = callNode; newContext.params = NULL; newContext.paramNumber = 0; newContext.varNumber = 0; newContext.varPosition = context->varPosition + context->varNumber; if (action->type == EA_LISTENER) { newContext.useCmdParam = context->useCmdParam; if (!newContext.useCmdParam) { linkedList_t *p = context->params; while (p) { const char* value = (char*) p->data; LIST_AddString(&newContext.params, value); newContext.paramNumber++; p = p->next; } } } else { newContext.useCmdParam = false; param = action->d.nonTerminal.right; while (param) { const char* value; value = UI_GetStringFromExpression(param, context); LIST_AddString(&newContext.params, value); newContext.paramNumber++; param = param->next; } } if (callProperty == NULL || callProperty->type == V_UI_ACTION) { uiAction_t const* const actionsRef = callProperty ? Com_GetValue<uiAction_t*>(callNode, callProperty) : callNode->onClick; UI_ExecuteActions(actionsRef, &newContext); } else if (callProperty->type == V_UI_NODEMETHOD) { uiNodeMethod_t func = (uiNodeMethod_t) callProperty->ofs; func(callNode, &newContext); } else { /* unreachable, already checked few line before */ assert(false); } LIST_Delete(&newContext.params); }
/** * @brief Check if an expression is true * @return True if the expression is true */ bool UI_GetBooleanFromExpression (uiAction_t *expression, const uiCallContext_t *context) { if (expression == nullptr) return false; switch (expression->type & EA_HIGHT_MASK) { case EA_VALUE: return UI_GetFloatFromExpression(expression, context) != 0; case EA_OPERATOR_BOOLEAN2BOOLEAN: { #define VALUE1 UI_GetBooleanFromExpression(expression->d.nonTerminal.left, context) #define VALUE2 UI_GetBooleanFromExpression(expression->d.nonTerminal.right, context) switch (expression->type) { case EA_OPERATOR_AND: return VALUE1 && VALUE2; case EA_OPERATOR_OR: return VALUE1 || VALUE2; case EA_OPERATOR_XOR: return VALUE1 ^ VALUE2; case EA_OPERATOR_NOT: return !VALUE1; default: Com_Error(ERR_FATAL, "UI_GetBooleanFromExpression: (BOOL2BOOL) Invalid expression type"); } } case EA_OPERATOR_FLOAT2BOOLEAN: { const float value1 = UI_GetFloatFromExpression(expression->d.nonTerminal.left, context); const float value2 = UI_GetFloatFromExpression(expression->d.nonTerminal.right, context); switch (expression->type) { case EA_OPERATOR_EQ: return value1 == value2; case EA_OPERATOR_LE: return value1 <= value2; case EA_OPERATOR_GE: return value1 >= value2; case EA_OPERATOR_GT: return value1 > value2; case EA_OPERATOR_LT: return value1 < value2; case EA_OPERATOR_NE: return value1 != value2; default: Com_Error(ERR_FATAL, "UI_GetBooleanFromExpression: (FLOAT2BOOL) Invalid expression type"); } } case EA_OPERATOR_UNARY: switch (expression->type) { case EA_OPERATOR_EXISTS: { const uiAction_t *e = expression->d.nonTerminal.left; const char* name; assert(e); name = e->d.terminal.d1.constString; switch (e->type) { case EA_VALUE_CVARNAME_WITHINJECTION: name = UI_GenInjectedString(name, false, context); case EA_VALUE_CVARNAME: return Cvar_FindVar(name) != nullptr; case EA_VALUE_PATHNODE_WITHINJECTION: name = UI_GenInjectedString(name, false, context); case EA_VALUE_PATHNODE: { uiNode_t* node = nullptr; const value_t *property; UI_ReadNodePath(name, context->source, &node, &property); return node != nullptr; } default: return false; } } case EA_OPERATOR_PATHPROPERTYFROM: return UI_GetFloatFromExpression(expression, context) != 0; default: Com_Error(ERR_FATAL, "UI_GetBooleanFromExpression: (EA_OPERATOR_UNARY) Invalid expression type %i", expression->type); } case EA_OPERATOR_STRING2BOOLEAN: { const char* value1 = UI_GetStringFromExpression(expression->d.nonTerminal.left, context); const char* value2 = UI_GetStringFromExpression(expression->d.nonTerminal.right, context); switch (expression->type) { case EA_OPERATOR_STR_EQ: return Q_streq(value1, value2); case EA_OPERATOR_STR_NE: return !Q_streq(value1, value2); default: Com_Error(ERR_FATAL, "UI_GetBooleanFromExpression: (STRING2BOOL) Invalid expression type"); } } default: Com_Error(ERR_FATAL, "UI_GetBooleanFromExpression: Unsupported expression type: %i", expression->type); } }
/** * @brief Get a node and a property from an expression * @param expression Expression tree to analyse * @param context Call context * @param[out] property A node property * @return A node (else nullptr, if no node found) and a property (else nullptr if no/wrong property defined) */ uiNode_t* UI_GetNodeFromExpression (uiAction_t *expression, const uiCallContext_t *context, const value_t **property) { if (property != nullptr) *property = nullptr; switch (expression->type & EA_HIGHT_MASK) { case EA_VALUE: switch (expression->type) { case EA_VALUE_VAR: { uiValue_t *variable = UI_GetVariable(context, expression->d.terminal.d1.integer); switch (variable->type) { case EA_VALUE_NODE: return variable->value.node; default: break; } } break; case EA_VALUE_PATHNODE: case EA_VALUE_PATHNODE_WITHINJECTION: { uiNode_t *node; const value_t *propertyTmp; const char *path = expression->d.terminal.d1.constString; if (expression->type == EA_VALUE_PATHNODE_WITHINJECTION) path = UI_GenInjectedString(path, false, context); UI_ReadNodePath(path, context->source, &node, &propertyTmp); if (!node) { Com_Printf("UI_GetNodeFromExpression: Node '%s' wasn't found; nullptr returned\n", path); return nullptr; } if (propertyTmp != nullptr) Com_Printf("UI_GetNodeFromExpression: No property expected, but path '%s' contain a property. Property ignored.\n", path); return node; } case EA_VALUE_PATHPROPERTY: case EA_VALUE_PATHPROPERTY_WITHINJECTION: { uiNode_t *node; const value_t *propertyTmp; const char *path = expression->d.terminal.d1.constString; if (expression->type == EA_VALUE_PATHPROPERTY_WITHINJECTION) path = UI_GenInjectedString(path, false, context); UI_ReadNodePath(path, context->source, &node, &propertyTmp); if (!node) { Com_Printf("UI_GetNodeFromExpression: Node '%s' wasn't found; nullptr returned\n", path); return nullptr; } if (property == nullptr) { if (propertyTmp != nullptr) Com_Printf("UI_GetNodeFromExpression: No property expected, but path '%s' contain a property. Property ignored.\n", path); } else { *property = propertyTmp; } return node; } case EA_VALUE_THIS: return context->source; case EA_VALUE_PARENT: return context->source->parent; case EA_VALUE_WINDOW: return context->source->root; default: break; } case EA_OPERATOR_UNARY: switch (expression->type) { case EA_OPERATOR_PATHPROPERTYFROM: { uiNode_t *relativeTo = UI_GetNodeFromExpression(expression->d.nonTerminal.left, context, nullptr); uiNode_t *node; const value_t *propertyTmp; const char* path = expression->d.terminal.d2.constString; UI_ReadNodePath(path, relativeTo, &node, &propertyTmp); if (!node) { Com_Printf("UI_GetNodeFromExpression: Path '%s' from node '%s' found no node; nullptr returned\n", path, UI_GetPath(relativeTo)); return nullptr; } if (property == nullptr) { if (propertyTmp != nullptr) Com_Printf("UI_GetNodeFromExpression: No property expected, but path '%s' from node '%s' found no node; nullptr returned\n", path, UI_GetPath(relativeTo)); } else { *property = propertyTmp; } return node; } default: break; } default: break; } return nullptr; }
/** * @return A string, else an empty string * @todo this should not work very well, because too much va are used * then we should locally cache values, or manage a temporary string structure */ const char* UI_GetStringFromExpression (uiAction_t *expression, const uiCallContext_t *context) { switch (expression->type & EA_HIGHT_MASK) { case EA_VALUE: switch (expression->type) { case EA_VALUE_VAR: { uiValue_t *variable = UI_GetVariable(context, expression->d.terminal.d1.integer); switch (variable->type) { case EA_VALUE_STRING: if (variable->value.string == nullptr) { Com_Printf("UI_GetStringFromExpression: String variable not initialized. Empty string returned"); return ""; } return variable->value.string; case EA_VALUE_FLOAT: { const float number = variable->value.number; const int integer = number; /** @todo should we add a delta? */ if (number == integer) return va("%i", integer); else return va("%f", number); } case EA_VALUE_CVAR: { cvar_t *cvar = variable->value.cvar; if (cvar == nullptr) { Com_Printf("UI_GetStringFromExpression: Cvar variable not initialized. Empty string returned"); return ""; } return cvar->string; } default: Com_Printf("UI_GetStringFromExpression: Unsupported variable type: %i. Empty string returned", variable->type); return ""; } } case EA_VALUE_STRING: case EA_VALUE_STRING_WITHINJECTION: { const char* string = expression->d.terminal.d1.constString; if (expression->type == EA_VALUE_STRING_WITHINJECTION) string = UI_GenInjectedString(string, false, context); return string; } case EA_VALUE_FLOAT: { const float number = expression->d.terminal.d1.number; const int integer = number; /** @todo should we add a delta? */ if (number == integer) return va("%i", integer); else return va("%f", number); } case EA_VALUE_CVARNAME: case EA_VALUE_CVARNAME_WITHINJECTION: { cvar_t *cvar = nullptr; const char *cvarName = expression->d.terminal.d1.constString; if (expression->type == EA_VALUE_CVARNAME_WITHINJECTION) cvarName = UI_GenInjectedString(cvarName, false, context); cvar = Cvar_Get(cvarName, "", 0, "Cvar from UI script expression"); return cvar->string; } case EA_VALUE_PATHPROPERTY: case EA_VALUE_PATHPROPERTY_WITHINJECTION: { uiNode_t *node; const value_t *property; const char* string; node = UI_GetNodeFromExpression(expression, context, &property); if (!node) { Com_Printf("UI_GetStringFromExpression: Node wasn't found; Empty string returned\n"); return ""; } if (!property) { Com_Printf("UI_GetStringFromExpression: Property wasn't found; Empty string returned\n"); return ""; } string = UI_GetStringFromNodeProperty(node, property); if (string == nullptr) { Com_Printf("UI_GetStringFromExpression: String getter for '%s@%s' property do not exists; '' returned\n", UI_Node_GetWidgetName(node), property->string); return ""; } return string; } break; case EA_VALUE_PARAM: return UI_GetParam(context, expression->d.terminal.d1.integer); case EA_VALUE_PARAMCOUNT: return va("%i", UI_GetParamNumber(context)); } break; case EA_OPERATOR_UNARY: switch (expression->type) { case EA_OPERATOR_PATHPROPERTYFROM: { uiNode_t *node; const value_t *property; node = UI_GetNodeFromExpression(expression, context, &property); return UI_GetStringFromNodeProperty(node, property); } default: Com_Error(ERR_FATAL, "UI_GetFloatFromExpression: (EA_OPERATOR_UNARY) Invalid expression type %i", expression->type); } case EA_OPERATOR_BOOLEAN2BOOLEAN: case EA_OPERATOR_FLOAT2BOOLEAN: case EA_OPERATOR_STRING2BOOLEAN: { const bool v = UI_GetBooleanFromExpression(expression, context); return (v)?"1":"0"; } case EA_OPERATOR_FLOAT2FLOAT: { const float number = UI_GetFloatFromExpression(expression, context); const int integer = number; /** @todo should we add a delta? */ if (number == integer) return va("%i", integer); else return va("%f", number); } } Com_Printf("UI_GetStringFromExpression: Unsupported expression type: %i", expression->type); return ""; }
/** * @return A float value, else 0 */ float UI_GetFloatFromExpression (uiAction_t *expression, const uiCallContext_t *context) { switch (expression->type & EA_HIGHT_MASK) { case EA_VALUE: switch (expression->type) { case EA_VALUE_VAR: { uiValue_t *variable = UI_GetVariable(context, expression->d.terminal.d1.integer); switch (variable->type) { case EA_VALUE_STRING: if (variable->value.string == nullptr) { Com_Printf("UI_GetFloatFromExpression: String variable not initialized. '0' returned"); return 0; } return atof(variable->value.string); case EA_VALUE_FLOAT: return variable->value.number; case EA_VALUE_CVAR: { cvar_t *cvar = variable->value.cvar; if (cvar == nullptr) { Com_Printf("UI_GetFloatFromExpression: Cvar variable not initialized. '0' returned"); return 0; } return cvar->value; } default: Com_Printf("UI_GetFloatFromExpression: Unsupported variable type: %i. '0' returned", variable->type); return 0; } } case EA_VALUE_STRING: case EA_VALUE_STRING_WITHINJECTION: { const char* string = expression->d.terminal.d1.constString; if (expression->type == EA_VALUE_STRING_WITHINJECTION) string = UI_GenInjectedString(string, false, context); return atof(string); } case EA_VALUE_FLOAT: return expression->d.terminal.d1.number; case EA_VALUE_CVARNAME: case EA_VALUE_CVARNAME_WITHINJECTION: { cvar_t *cvar = nullptr; const char *cvarName = expression->d.terminal.d1.constString; if (expression->type == EA_VALUE_CVARNAME_WITHINJECTION) cvarName = UI_GenInjectedString(cvarName, false, context); cvar = Cvar_Get(cvarName, "", 0, "Cvar from UI script expression"); return cvar->value; } case EA_VALUE_PATHPROPERTY: case EA_VALUE_PATHPROPERTY_WITHINJECTION: { uiNode_t *node; const value_t *property; node = UI_GetNodeFromExpression(expression, context, &property); if (!node) { Com_Printf("UI_GetFloatFromParam: Node wasn't found; '0'\n"); return 0; } if (!property) { Com_Printf("UI_GetFloatFromParam: Property wasn't found; '0' returned\n"); return 0; } return UI_GetFloatFromNodeProperty(node, property); } case EA_VALUE_PARAM: { const int paramId = expression->d.terminal.d1.integer; const char *string = UI_GetParam(context, paramId); if (string[0] == '\0') { Com_Printf("UI_GetFloatFromParam: Param '%i' is out of range, or empty; '0' returned\n", paramId); return 0; } return atof(string); } case EA_VALUE_PARAMCOUNT: return UI_GetParamNumber(context); } break; case EA_OPERATOR_FLOAT2FLOAT: { const float value1 = UI_GetFloatFromExpression(expression->d.nonTerminal.left, context); const float value2 = UI_GetFloatFromExpression(expression->d.nonTerminal.right, context); switch (expression->type) { case EA_OPERATOR_ADD: return value1 + value2; case EA_OPERATOR_SUB: return value1 - value2; case EA_OPERATOR_MUL: return value1 * value2; case EA_OPERATOR_DIV: if (value2 == 0) { Com_Printf("UI_GetFloatFromExpression: Div by 0. '0' returned"); return 0; } else return value1 / value2; case EA_OPERATOR_MOD: { const int v1 = value1; const int v2 = value2; /** @todo do we have some check to do? */ return v1 % v2; } } } break; case EA_OPERATOR_UNARY: switch (expression->type) { case EA_OPERATOR_PATHPROPERTYFROM: { uiNode_t *node; const value_t *property; node = UI_GetNodeFromExpression(expression, context, &property); return UI_GetFloatFromNodeProperty(node, property); } default: Com_Error(ERR_FATAL, "UI_GetFloatFromExpression: (EA_OPERATOR_UNARY) Invalid expression type %i", expression->type); } } Com_Printf("UI_GetFloatFromExpression: Unsupported expression type: %i. '0' returned", expression->type); return 0; }
/** * @brief Execute an action from a source * @param[in] context Context node * @param[in] action Action to execute */ static void UI_ExecuteAction (const uiAction_t* action, uiCallContext_t* context) { switch (action->type) { case EA_NULL: /* do nothing */ break; case EA_CMD: /* execute a command */ if (action->d.terminal.d1.constString) Cbuf_AddText("%s\n", UI_GenInjectedString(action->d.terminal.d1.constString, true, context)); break; case EA_CALL: case EA_LISTENER: UI_ExecuteCallAction(action, context); break; case EA_POPVARS: { const int number = action->d.terminal.d1.integer; assert(number <= context->varNumber); for (int i = 0; i < number; i++) { const int varId = context->varPosition + context->varNumber - i - 1; UI_ReleaseVariable(&(ui_global.variableStack[varId])); } context->varNumber -= number; } break; case EA_PUSHVARS: #ifdef DEBUG /* check sanity */ /** @todo check var slots should be empty */ #endif context->varNumber += action->d.terminal.d1.integer; if (context->varNumber >= UI_MAX_VARIABLESTACK) Com_Error(ERR_FATAL, "UI_ExecuteAction: Variable stack full. UI_MAX_VARIABLESTACK hit."); break; case EA_ASSIGN: UI_ExecuteSetAction(action, context); break; case EA_DELETE: { const char* cvarname = action->d.nonTerminal.left->d.terminal.d1.constString; Cvar_Delete(cvarname); break; } case EA_WHILE: { int loop = 0; while (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) { UI_ExecuteActions(action->d.nonTerminal.right, context); if (context->breakLoop) { context->breakLoop = false; break; } if (loop > 1000) { Com_Printf("UI_ExecuteAction: Infinite loop. Force breaking 'while'\n"); break; } loop++; } break; } /* expected usage in .ufo scripts: forchildin ( *node:someNode ) { ... *node:child <-- reference the child being iterated ... } */ case EA_FORCHILDIN: { uiNode_t* root; root = UI_GetNodeFromExpression(action->d.nonTerminal.left, context, nullptr); if (!root) { break; } int loop = 0; for (uiNode_t* node = root->firstChild; node; node = node->next, loop++) { /* associate the child node with the call context so it can be referenced inside the script block */ context->tagNode = node; /* execute the script block inside the forchildin loop */ UI_ExecuteActions(action->d.nonTerminal.right, context); /* clean the tag node */ context->tagNode = nullptr; if (context->breakLoop) { context->breakLoop = false; break; } if (loop > 1000) { Com_Printf("UI_ExecuteAction: Infinite loop. Force breaking 'forchildin'\n"); break; } } break; } case EA_BREAK: { /* flag to break, the caller must check this flag before processing the next action in the list */ Com_Printf("BREAK: break statement found\n"); context->breakLoop = true; break; } case EA_IF: if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) { UI_ExecuteActions(action->d.nonTerminal.right, context); return; } action = action->next; while (action && action->type == EA_ELIF) { if (UI_GetBooleanFromExpression(action->d.nonTerminal.left, context)) { UI_ExecuteActions(action->d.nonTerminal.right, context); return; } action = action->next; } if (action && action->type == EA_ELSE) { UI_ExecuteActions(action->d.nonTerminal.right, context); } break; /** @todo Skipping actions like that is a bad way. the function should return the next action, * or we should move IF, ELSE, ELIF... in a IF block and not interleave it with default actions */ case EA_ELSE: case EA_ELIF: /* previous EA_IF execute this action */ break; default: Com_Error(ERR_FATAL, "UI_ExecuteAction: Unknown action type %i", action->type); } }
static inline void UI_ExecuteCallAction (const uiAction_t* action, const uiCallContext_t* context) { uiNode_t* callNode = nullptr; uiAction_t* param; uiAction_t* left = action->d.nonTerminal.left; uiCallContext_t newContext; const value_t* callProperty = nullptr; value_t luaMethod; const char* path = left->d.terminal.d1.constString; // clear luaMethod structure before using it memset(&luaMethod, 0, sizeof(luaMethod)); if (left->type == EA_VALUE_PATHPROPERTY || left->type == EA_VALUE_PATHNODE) path = left->d.terminal.d1.constString; else if (left->type == EA_VALUE_PATHPROPERTY_WITHINJECTION || left->type == EA_VALUE_PATHNODE_WITHINJECTION) path = UI_GenInjectedString(left->d.terminal.d1.constString, false, context); UI_ReadNodePath(path, context->source, context->tagNode, &callNode, &callProperty, &luaMethod); if ((callNode == nullptr) && (!luaMethod.type)) { Com_Printf("UI_ExecuteCallAction: Node from path \"%s\" not found (relative to \"%s\").\n", path, UI_GetPath(context->source)); return; } if (callProperty != nullptr && callProperty->type != V_UI_ACTION && callProperty->type != V_UI_NODEMETHOD && callProperty->type != V_UI_NODEMETHOD_LUA) { Com_Printf("UI_ExecuteCallAction: Call operand %d unsupported. (%s)\n", callProperty->type, UI_GetPath(callNode)); return; } newContext.source = callNode; newContext.params = nullptr; newContext.paramNumber = 0; newContext.varNumber = 0; newContext.varPosition = context->varPosition + context->varNumber; newContext.breakLoop = false; if (action->type == EA_LISTENER) { newContext.useCmdParam = context->useCmdParam; if (!newContext.useCmdParam) { linkedList_t* p = context->params; while (p) { const char* value = (char*) p->data; LIST_AddString(&newContext.params, value); newContext.paramNumber++; p = p->next; } } } else { newContext.useCmdParam = false; param = action->d.nonTerminal.right; while (param) { const char* value; value = UI_GetStringFromExpression(param, context); LIST_AddString(&newContext.params, value); newContext.paramNumber++; param = param->next; } } if (luaMethod.type == V_UI_NODEMETHOD_LUA) { UI_ExecuteLuaMethod(callNode, luaMethod.ofs, newContext.params, newContext.paramNumber); Mem_Free(const_cast<char*>(luaMethod.string)); } else if (callProperty == nullptr || callProperty->type == V_UI_ACTION) { uiAction_t const* const actionsRef = callProperty ? Com_GetValue<uiAction_t*>(callNode, callProperty) : callNode->onClick; if (actionsRef) UI_ExecuteActions(actionsRef, &newContext); if (callNode->lua_onClick != LUA_NOREF) UI_ExecuteLuaMethod(callNode, callNode->lua_onClick, newContext.params, newContext.paramNumber); } else if (callProperty->type == V_UI_NODEMETHOD) { uiNodeMethod_t func = (uiNodeMethod_t) callProperty->ofs; func(callNode, &newContext); } else { /* unreachable, already checked few line before */ assert(false); } LIST_Delete(&newContext.params); }