/** * @brief display value of a node property from the command line */ static void UI_NodeGetProperty_f (void) { uiNode_t* node; const value_t* property; const char* sValue; float fValue; if (Cmd_Argc() != 2) { Com_Printf("Usage: %s <nodepath@prop>\n", Cmd_Argv(0)); return; } UI_ReadNodePath(Cmd_Argv(1), nullptr, &node, &property); if (node == nullptr) { Com_Printf("UI_NodeGetProperty_f: Node from path '%s' doesn't exist\n", Cmd_Argv(1)); return; } if (property == nullptr) { Com_Printf("UI_NodeGetProperty_f: Property from path '%s' doesn't exist\n", Cmd_Argv(1)); return; } /* check string value */ sValue = UI_GetStringFromNodeProperty(node, property); if (sValue) { Com_Printf("\"%s\" is \"%s\"\n", Cmd_Argv(1), sValue); return; } /* check float value */ fValue = UI_GetFloatFromNodeProperty(node, property); Com_Printf("\"%s\" is \"%f\"\n", Cmd_Argv(1), fValue); }
/** * @brief Remove a function callback from a node event */ static void UI_RemoveListener_f (void) { uiNode_t *node; uiNode_t *function; const value_t *property; if (Cmd_Argc() != 3) { Com_Printf("Usage: %s <pathnode@event> <pathnode>\n", Cmd_Argv(0)); return; } UI_ReadNodePath(Cmd_Argv(1), NULL, &node, &property); if (node == NULL) { Com_Printf("UI_RemoveListener_f: '%s' node not found.\n", Cmd_Argv(1)); return; } if (property == NULL || property->type != V_UI_ACTION) { Com_Printf("UI_RemoveListener_f: '%s' property not found, or is not an event.\n", Cmd_Argv(1)); return; } function = UI_GetNodeByPath(Cmd_Argv(2)); if (function == NULL) { Com_Printf("UI_RemoveListener_f: '%s' node not found.\n", Cmd_Argv(2)); return; } UI_RemoveListener(node, property, function); }
/** * @brief Return a node by a path name (names with dot separation) * It is a simplification facade over UI_ReadNodePath * @return The requested node, else NULL if not found * @code * // get keylist node from options_keys node from options window * node = UI_GetNodeByPath("options.options_keys.keylist"); * @sa UI_ReadNodePath * @endcode */ uiNode_t* UI_GetNodeByPath (const char* path) { uiNode_t* node = NULL; const value_t *property; UI_ReadNodePath(path, NULL, &node, &property); /** @todo FIXME warning if it return a peroperty */ return node; }
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 Return a node by a path name (names with dot separation) * It is a simplification facade over UI_ReadNodePath * @return The requested node, else nullptr if not found * @code * // get keylist node from options_keys node from options window * node = UI_GetNodeByPath("options.options_keys.keylist"); * @sa UI_ReadNodePath * @endcode */ uiNode_t* UI_GetNodeByPath (const char* path) { uiNode_t* node = nullptr; const value_t* property = nullptr; UI_ReadNodePath(path, nullptr, nullptr, &node, &property); if (property) { Com_Printf("WARNING: Search for node using path [%s] returns property [%s]\n", path, property->string); } return node; }
/** * @brief Set a binding from a key to a node to active. * * This command create a relation between a key and a node. * * The relation is stored to the node (to display the shortcut into tooltip) * and to parent window of the node (to faster search of all available shortcuts). * * The storage on the node is not a list, then if there is more than one shortcut * to a node we can't display all shortcut to tooltip, but the binding will still * work. * * If the parent window is inherited, the binding is dup to others extend * windows and the relation is flagged as "inherited". * * @param path Path to a node, or a node mathod * @param key The key number to use (see for example the K_* names are matched up.) * @param inherited True if this binding is inherited from another binding. * * @todo check: only one binding per nodes * @todo check: key per window must be unique * @todo check: key used into UI_KeyPressed can't be used */ static void UI_SetKeyBindingEx (const char* path, int key, const char* description, bool inherited) { uiNode_t *node; uiKeyBinding_t *binding; const value_t *property = NULL; int windowId; char newPath[256]; UI_ReadNodePath(path, NULL, &node, &property); if (node == NULL) { Com_Printf("UI_SetKeyBinding: node \"%s\" not found.\n", path); return; } if (property != NULL && property->type != V_UI_NODEMETHOD) Com_Error(ERR_FATAL, "UI_SetKeyBinding: Only node and method are supported. Property @%s not found in path \"%s\".", property->string, path); /* init and link the keybinding */ binding = UI_AllocStaticKeyBinding(); binding->node = node; binding->property = property; binding->key = key; binding->inherited = inherited; node->key = binding; if (Q_strnull(description)) Com_Printf("Warning: Empty description for UI keybinding: %s (%s)\n", path, Key_KeynumToString(key)); else binding->description = Mem_PoolStrDup(description, ui_dynPool, 0);; UI_WindowNodeRegisterKeyBinding(node->root, binding); /* search and update windows extend node->root */ for (windowId = 0; windowId < ui_global.numWindows; windowId++) { uiNode_t *window = ui_global.windows[windowId]; /* skip window which are not direct extends of the main window */ if (window->super != node->root) continue; /* create a new patch from the new windows */ newPath[0] = '\0'; Q_strcat(newPath, window->name, sizeof(newPath)); Q_strcat(newPath, path + strlen(node->root->name), sizeof(newPath)); UI_SetKeyBindingEx(newPath, key, description, true); } }
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 Replace injection identifiers (e.g. <eventParam>) by a value * @note The injection identifier can be every node value - e.g. <image> or <width>. * It's also possible to do something like * @code * cmd "set someCvar <min>/<max>" * @endcode */ const char* UI_GenInjectedString (const char* input, bool addNewLine, const uiCallContext_t *context) { static char cmd[256]; int length = sizeof(cmd) - (addNewLine ? 2 : 1); static char propertyName[256]; const char *cin = input; char *cout = cmd; while (length && cin[0] != '\0') { if (cin[0] == '<') { /* read propertyName between '<' and '>' */ const char *next = UI_GenCommandReadProperty(cin, propertyName, sizeof(propertyName)); if (next) { /* cvar injection */ if (char const* const rest = Q_strstart(propertyName, "cvar:")) { cvar_t const* const cvar = Cvar_Get(rest); const int l = snprintf(cout, length, "%s", cvar->string); cout += l; cin = next; length -= l; continue; } else if (char const* const path = Q_strstart(propertyName, "node:")) { uiNode_t *node; const value_t *property; const char* string; int l; UI_ReadNodePath(path, context->source, &node, &property); if (!node) { Com_Printf("UI_GenInjectedString: Node '%s' wasn't found; '' returned\n", path); #ifdef DEBUG Com_Printf("UI_GenInjectedString: Path relative to '%s'\n", UI_GetPath(context->source)); #endif string = ""; } else if (!property) { Com_Printf("UI_GenInjectedString: Property '%s' wasn't found; '' returned\n", path); string = ""; } else { string = UI_GetStringFromNodeProperty(node, property); if (string == NULL) { Com_Printf("UI_GenInjectedString: String getter for '%s' property do not exists; '' injected\n", path); string = ""; } } l = snprintf(cout, length, "%s", string); cout += l; cin = next; length -= l; continue; /* source path injection */ } else if (char const* const command = Q_strstart(propertyName, "path:")) { if (context->source) { const uiNode_t *node = NULL; if (Q_streq(command, "root")) node = context->source->root; else if (Q_streq(command, "this")) node = context->source; else if (Q_streq(command, "parent")) node = context->source->parent; else Com_Printf("UI_GenCommand: Command '%s' for path injection unknown\n", command); if (node) { const int l = snprintf(cout, length, "%s", UI_GetPath(node)); cout += l; cin = next; length -= l; continue; } } /* no prefix */ } else { /* source property injection */ if (context->source) { /* find property definition */ const value_t *property = UI_GetPropertyFromBehaviour(context->source->behaviour, propertyName); if (property) { const char* value; int l; /* inject the property value */ value = UI_GetStringFromNodeProperty(context->source, property); if (value == NULL) value = ""; l = snprintf(cout, length, "%s", value); cout += l; cin = next; length -= l; continue; } } /* param injection */ if (UI_GetParamNumber(context) != 0) { int arg; const int checked = sscanf(propertyName, "%d", &arg); if (checked == 1 && arg >= 1 && arg <= UI_GetParamNumber(context)) { const int l = snprintf(cout, length, "%s", UI_GetParam(context, arg)); cout += l; cin = next; length -= l; continue; } } } } } *cout++ = *cin++; length--; } /* is buffer too small? */ assert(cin[0] == '\0'); if (addNewLine) *cout++ = '\n'; *cout++ = '\0'; /* copy the result into a free va slot */ return va("%s", cmd); }
/** * @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; }
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); }