void UI_ExecuteEventActions (uiNode_t* source, const uiAction_t* firstAction) { uiCallContext_t context; OBJZERO(context); context.source = source; context.useCmdParam = false; UI_ExecuteActions(firstAction, &context); }
void UI_ExecuteEventActionsEx (uiNode_t* source, const uiAction_t* firstAction, linkedList_t *params) { uiCallContext_t context; OBJZERO(context); context.source = source; context.useCmdParam = false; context.params = params; context.paramNumber = LIST_Count(params); UI_ExecuteActions(firstAction, &context); }
/** * @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 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); }