/**
 * @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);
}
Example #2
0
/**
 * @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);
}
Example #3
0
/**
 * @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;
}
Example #4
0
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);
}
Example #5
0
/**
 * @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;
}
Example #6
0
/**
 * @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);
	}
}
Example #7
0
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);
}
Example #8
0
/**
 * @brief Replace injection identifiers (e.g. &lt;eventParam&gt;) by a value
 * @note The injection identifier can be every node value - e.g. &lt;image&gt; or &lt;width&gt;.
 * It's also possible to do something like
 * @code
 * cmd "set someCvar &lt;min&gt;/&lt;max&gt;"
 * @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);
}
Example #9
0
/**
 * @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);
	}
}
Example #10
0
/**
 * @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;
}
Example #11
0
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);
}