Пример #1
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);
}
Пример #2
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(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);
	}
}
Пример #3
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);
}
Пример #4
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);
	}
}
Пример #5
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;
}
Пример #6
0
/**
 * @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 "";
}
Пример #7
0
/**
 * @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;
}
Пример #8
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);
    }
}
Пример #9
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);
}