/** * Apply an action value to a node property. If the tuple property/value allow it, the function * pre compute the value and update the action value to speed up the next call. * @param node Node to edit * @param property Property of the node to edit * @param value Action value containing the value to set to the node property * @param context Call context of the script * @todo refactoring it to remove "context", we should only call that function when the action * value is a leaf (then a value, and not an expression) */ static void UI_NodeSetPropertyFromActionValue (uiNode_t *node, const value_t *property, const uiCallContext_t *context, uiAction_t* value) { /* @todo we can use a new EA_VALUE type to flag already parsed values, we dont need to do it again and again */ /* pre compute value if possible */ if (value->type == EA_VALUE_STRING) { const char* string = value->d.terminal.d1.constString; if ((property->type & V_UI_MASK) == V_UI_CVAR && Q_strstart(string, "*cvar:")) { Com_GetValue<void*>(node, property) = value->d.terminal.d1.data; } else { /** @todo here we must catch error in a better way, and using cvar for error code to create unittest automations */ UI_InitRawActionValue(value, node, property, string); } } /* decode RAW value */ if (value->type == EA_VALUE_RAW) { const void *rawValue = value->d.terminal.d1.constData; const int rawType = value->d.terminal.d2.integer; UI_NodeSetPropertyFromRAW(node, property, rawValue, rawType); } /* else it is an expression */ else { /** @todo we should improve if when the prop is a boolean/int/float */ const char* string = UI_GetStringFromExpression(value, context); UI_NodeSetProperty(node, property, string); } }
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); }
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); } }
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); }