Beispiel #1
0
/**
 * @brief Add a callback of a function into a node event. There can be more than on listener.
 * @param[in,out] node The node to add the listener to.
 * @param[in] property The property of the node to add the listener to.
 * @param[in] functionNode The node of the listener callback.
 */
void UI_AddListener (uiNode_t *node, const value_t *property, const uiNode_t *functionNode)
{
	uiAction_t *lastAction;
	uiAction_t *action;
	uiAction_t *value;

	if (node->dynamic) {
		Com_Printf("UI_AddListener: '%s' is a dynamic node. We can't listen it.\n", UI_GetPath(node));
		return;
	}
	if (functionNode->dynamic) {
		Com_Printf("UI_AddListener: '%s' is a dynamic node. It can't be a listener callback.\n", UI_GetPath(functionNode));
		return;
	}

	/* create the call action */
	action = (uiAction_t*) Mem_PoolAlloc(sizeof(*action), ui_sysPool, 0);
	value = (uiAction_t*) Mem_PoolAlloc(sizeof(*action), ui_sysPool, 0);
	value->d.terminal.d1.constString = Mem_PoolStrDup(UI_GetPath(functionNode), ui_sysPool, 0);
	value->next = NULL;
	action->type = EA_LISTENER;
	action->d.nonTerminal.left = value;
	/** @todo It is a hack, we should remove that */
	action->d.terminal.d2.constData = &functionNode->onClick;
	action->next = NULL;

	/* insert the action */
	lastAction = *(uiAction_t**)((char*)node + property->ofs);
	if (lastAction) {
		while (lastAction->next)
			lastAction = lastAction->next;
		lastAction->next = action;
	} else
		*(uiAction_t**)((char*)node + property->ofs) = action;
}
Beispiel #2
0
/**
 * @brief Add a callback of a function into a node event. There can be more than on listener.
 * @param[in,out] node The node to add the listener to.
 * @param[in] property The property of the node to add the listener to.
 * @param[in] functionNode The node of the listener callback.
 */
void UI_AddListener (uiNode_t *node, const value_t *property, const uiNode_t *functionNode)
{
	if (node->dynamic) {
		Com_Printf("UI_AddListener: '%s' is a dynamic node. We can't listen it.\n", UI_GetPath(node));
		return;
	}
	if (functionNode->dynamic) {
		Com_Printf("UI_AddListener: '%s' is a dynamic node. It can't be a listener callback.\n", UI_GetPath(functionNode));
		return;
	}

	/* create the call action */
	uiAction_t* const action = Mem_PoolAllocType(uiAction_t, ui_sysPool);
	uiAction_t* const value  = Mem_PoolAllocType(uiAction_t, ui_sysPool);
	value->d.terminal.d1.constString = Mem_PoolStrDup(UI_GetPath(functionNode), ui_sysPool, 0);
	value->next = NULL;
	action->type = EA_LISTENER;
	action->d.nonTerminal.left = value;
	/** @todo It is a hack, we should remove that */
	action->d.terminal.d2.constData = &functionNode->onClick;
	action->next = NULL;

	/* insert the action */
	uiAction_t** anchor = &Com_GetValue<uiAction_t*>(node, property);
	while (*anchor)
		anchor = &(*anchor)->next;
	*anchor = action;
}
Beispiel #3
0
/**
 * Parse a string according to a property type, and allocate a raw value to the static memory
 *
 * @param action Action to initialize
 * @param node Current node we are parsing, only used for error message
 * @param property Type of the value to parse, if nullptr the string is not stored as string
 * @param string String value to parse
 * @return True if the action is initialized
 * @todo remove node param and catch error where we call that function
 */
bool UI_InitRawActionValue (uiAction_t* action, uiNode_t* node, const value_t* property, const char* string)
{
	if (property == nullptr) {
		action->type = EA_VALUE_STRING;
		action->d.terminal.d1.data = UI_AllocStaticString(string, 0);
		action->d.terminal.d2.integer = 0;
		return true;
	}

	if (property->type == V_UI_SPRITEREF) {
		uiSprite_t* sprite = UI_GetSpriteByName(string);
		if (sprite == nullptr) {
			Com_Printf("UI_ParseSetAction: sprite '%s' not found (%s)\n", string, UI_GetPath(node));
			return false;
		}
		action->type = EA_VALUE_RAW;
		action->d.terminal.d1.data = sprite;
		action->d.terminal.d2.integer = property->type;
		return true;
	} else {
		const int baseType = property->type & V_UI_MASK;
		if (baseType != 0 && baseType != V_UI_CVAR) {
			Com_Printf("UI_ParseRawValue: setter for property '%s' (type %d, 0x%X) is not supported (%s)\n", property->string, property->type, property->type, UI_GetPath(node));
			return false;
		}
		ui_global.curadata = (byte*) Com_AlignPtr(ui_global.curadata, (valueTypes_t) (property->type & V_BASETYPEMASK));
		action->type = EA_VALUE_RAW;
		action->d.terminal.d1.data = ui_global.curadata;
		action->d.terminal.d2.integer = property->type;
		/** @todo we should hide use of ui_global.curadata */
		ui_global.curadata += Com_EParseValue(ui_global.curadata, string, (valueTypes_t) (property->type & V_BASETYPEMASK), 0, property->size);
		return true;
	}
}
Beispiel #4
0
static bool UI_ParseEventProperty (uiNode_t* node, const value_t* event, const char** text, const char** token, const char* errhead)
{
	/* add new actions to end of list */
	uiAction_t** action = &Com_GetValue<uiAction_t*>(node, event);
	for (; *action; action = &(*action)->next) {}

	/* get the action body */
	*token = Com_EParse(text, errhead, node->name);
	if (!*text)
		return false;

	if ((*token)[0] != '{') {
		Com_Printf("UI_ParseEventProperty: Event '%s' without body (%s)\n", event->string, UI_GetPath(node));
		return false;
	}

	*action = UI_ParseActionList(node, text, token);
	if (*action == nullptr)
		return false;

	/* block terminal already read */
	assert((*token)[0] == '}');

	return true;
}
Beispiel #5
0
/**
 * @brief Return a float from a node property
 * @param[in] node Requested node
 * @param[in] property Requested property
 * @return Return the float value of a property, else 0, if the type is not supported
 * @note If the type is not supported, a waring is reported to the console
 */
float UI_GetFloatFromNodeProperty (const uiNode_t* node, const value_t* property)
{
	assert(node);

	if (property->type == V_FLOAT) {
		return Com_GetValue<float>(node, property);
	} else if ((property->type & V_UI_MASK) == V_UI_CVAR) {
		void* const b = Com_GetValue<void*>(node, property);
		if (char const* const cvarName = Q_strstart((char const*)b, "*cvar:")) {
			const cvar_t* cvar = Cvar_Get(cvarName, "", 0, "UI script cvar property");
			return cvar->value;
		} else if (property->type == V_CVAR_OR_FLOAT) {
			return *(const float*) b;
		} else if (property->type == V_CVAR_OR_STRING) {
			return atof((const char*)b);
		}
	} else if (property->type == V_INT) {
		return Com_GetValue<int>(node, property);
	} else if (property->type == V_BOOL) {
		return Com_GetValue<bool>(node, property);
	} else {
#ifdef DEBUG
		Com_Printf("UI_GetFloatFromNodeProperty: Unimplemented float getter for property '%s@%s'. If it should return a float, request it.\n", UI_GetPath(node), property->string);
#else
		Com_Printf("UI_GetFloatFromNodeProperty: Property '%s@%s' can't return a float\n", UI_GetPath(node), property->string);
#endif
	}

	return 0;
}
Beispiel #6
0
/**
 * @brief Returns the absolute position of a node in the screen.
 * Screen position is not used for the node rendering cause we use OpenGL
 * translations. But this function is need for some R_functions methodes.
 * @param[in] node Context node
 * @param[out] pos Absolute position into the screen
 */
void UI_GetNodeScreenPos (const uiNode_t* node, vec2_t pos)
{
	assert(node);
	assert(pos);

	/* if we request the position of a non drawable node, there is a problem */
	if (node->behaviour->isVirtual)
		Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' doesn't have a position", node->name);

	Vector2Set(pos, 0, 0);
	while (node) {
#ifdef DEBUG
		if (node->box.pos[0] != (int)node->box.pos[0] || node->box.pos[1] != (int)node->box.pos[1])
			Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' position %f,%f is not integer", UI_GetPath(node), node->box.pos[0], node->box.pos[1]);
#endif
		pos[0] += node->box.pos[0];
		pos[1] += node->box.pos[1];
		node = node->parent;

		if (node && UI_Node_IsScrollableContainer(node)) {
			vec2_t clientPosition = {0, 0};
			UI_Node_GetClientPosition(node, clientPosition);
			pos[0] += clientPosition[0];
			pos[1] += clientPosition[1];
		}
	}
}
Beispiel #7
0
const char* UI_Node_GetText (uiNode_t* node) {
	#ifdef DEBUG
	if (node->text == nullptr) {
		Com_Printf("warning: requesting uninitialized node->text from [%s]\n", UI_GetPath(node));
	}
	#endif // DEBUG
	return (node->text? node->text : "");
}
Beispiel #8
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);
}
Beispiel #9
0
/**
 * @sa UI_ParseNodeProperties
 * @brief parse all sequencial properties into a block
 * @note allow to use an extra block
 * @code
 * foobehaviour foonode {
 *   { properties }
 *   // the function stop reading here
 *   nodes
 * }
 * foobehaviour foonode {
 *   properties
 *   // the function stop reading here
 *   nodes
 * }
 * @endcode
 */
static bool UI_ParseNodeProperties (uiNode_t* node, const char** text, const char** token)
{
	const char* errhead = "UI_ParseNodeProperties: unexpected end of file (node";
	bool nextTokenAlreadyRead = false;

	if ((*token)[0] != '{')
		nextTokenAlreadyRead = true;

	do {
		const value_t* val;
		int result;

		/* get new token */
		if (!nextTokenAlreadyRead) {
			*token = Com_EParse(text, errhead, node->name);
			if (!*text)
				return false;
		} else {
			nextTokenAlreadyRead = false;
		}

		/* is finished */
		if ((*token)[0] == '}')
			break;

		/* find the property */
		val = UI_GetPropertyFromBehaviour(node->behaviour, *token);
		if (!val) {
			/* unknown token, print message and continue */
			Com_Printf("UI_ParseNodeProperties: unknown property \"%s\", node ignored (node %s)\n",
					*token, UI_GetPath(node));
			return false;
		}

		/* get parameter values */
		result = UI_ParseProperty(node, val, node->name, text, token);
		if (!result) {
			Com_Printf("UI_ParseNodeProperties: Problem with parsing of node property '%s@%s'. See upper\n",
					UI_GetPath(node), val->string);
			return false;
		}
	} while (*text);

	return true;
}
Beispiel #10
0
/**
 * @brief Execute a tree of actions from a source
 * @param[in] context Context node
 * @param[in] firstAction Action to execute
 */
static void UI_ExecuteActions (const uiAction_t* firstAction, uiCallContext_t* context)
{
	static int callnumber = 0;
	if (callnumber++ > 20) {
		Com_Printf("UI_ExecuteActions: Break possible infinite recursion, source [%s]\n", UI_GetPath(context->source));
		return;
	}
	for (const uiAction_t* action = firstAction; action && !context->breakLoop; action = action->next) {
		UI_ExecuteAction(action, context);
	}
	callnumber--;
}
Beispiel #11
0
void uiTextNode::onLoaded (uiNode_t* node)
{
	int lineheight = EXTRADATA(node).lineHeight;
	/* auto compute lineheight */
	/* we don't overwrite EXTRADATA(node).lineHeight, because "0" is dynamically replaced by font height on draw function */
	if (lineheight == 0) {
		/* the font is used */
		const char* font = UI_GetFontFromNode(node);
		lineheight = UI_FontGetHeight(font);
	}

	/* auto compute rows (super.viewSizeY) */
	if (EXTRADATA(node).super.scrollY.viewSize == 0) {
		if (node->box.size[1] != 0 && lineheight != 0) {
			EXTRADATA(node).super.scrollY.viewSize = node->box.size[1] / lineheight;
		} else {
			EXTRADATA(node).super.scrollY.viewSize = 1;
			Com_Printf("UI_TextNodeLoaded: node '%s' has no rows value\n", UI_GetPath(node));
		}
	}

	/* auto compute height */
	if (node->box.size[1] == 0) {
		node->box.size[1] = EXTRADATA(node).super.scrollY.viewSize * lineheight;
	}

	/* is text slot exists */
	if (EXTRADATA(node).dataID >= UI_MAX_DATAID)
		Com_Error(ERR_DROP, "Error in node %s - max shared data id num exceeded (num: %i, max: %i)", UI_GetPath(node), EXTRADATA(node).dataID, UI_MAX_DATAID);

#ifdef DEBUG
	if (EXTRADATA(node).super.scrollY.viewSize != (int)(node->box.size[1] / lineheight)) {
		Com_Printf("UI_TextNodeLoaded: rows value (%i) of node '%s' differs from size (%.0f) and format (%i) values\n",
			EXTRADATA(node).super.scrollY.viewSize, UI_GetPath(node), node->box.size[1], lineheight);
	}
#endif

	if (node->text == nullptr && EXTRADATA(node).dataID == TEXT_NULL)
		Com_Printf("UI_TextNodeLoaded: 'textid' property of node '%s' is not set\n", UI_GetPath(node));
}
Beispiel #12
0
/**
 * Delete the node and remove it from his parent
 * @param node The node we want to delete
 */
void UI_DeleteNode (uiNode_t* node)
{
	if (!node->dynamic) {
		Com_Printf("UI_DeleteNode: Can't delete the static node '%s'\n", UI_GetPath(node));
		return;
	}

	UI_BeforeDeletingNode(node);

	UI_DeleteAllChild(node);
	if (node->firstChild != nullptr) {
		Com_Printf("UI_DeleteNode: Node '%s' contain static nodes. We can't delete it.\n", UI_GetPath(node));
		return;
	}

	if (node->parent)
		UI_RemoveNode(node->parent, node);

	/* delete all allocated properties */
	for (uiBehaviour_t* behaviour = node->behaviour; behaviour; behaviour = behaviour->super) {
		const value_t** property = behaviour->localProperties;
		if (property == nullptr)
			continue;
		while (*property) {
			if (((*property)->type & V_UI_MASK) == V_UI_CVAR) {
				if (void*& mem = Com_GetValue<void*>(node, *property)) {
					UI_FreeStringProperty(mem);
					mem = 0;
				}
			}

			/** @todo We must delete all EA_LISTENER too */

			property++;
		}
	}

	UI_Node_DeleteNode(node);
}
Beispiel #13
0
/**
 * @brief Callback every time the parent window is opened (pushed into the active window stack)
 */
static void UI_CvarListenerNodeBind (uiNode_t* node)
{
    cvarChangeListener_t* l;
    l = Cvar_RegisterChangeListener(node->name, UI_CvarListenerNodeCallback);
    if (l == nullptr) {
        Com_Printf("Node %s is not binded: cvar %s not found\n", UI_GetPath(node), node->name);
        return;
    }

    if (LIST_GetPointer(static_cast<linkedList_t*>(l->data), node) == nullptr) {
        LIST_AddPointer(reinterpret_cast<linkedList_t**>(&l->data), node);
    }
}
Beispiel #14
0
/**
 * Delete the node and remove it from his parent
 * @param node The node we want to delete
 */
void UI_DeleteNode (uiNode_t* node)
{
	uiBehaviour_t *behaviour;

	if (!node->dynamic)
		return;

	UI_BeforeDeletingNode(node);

	UI_DeleteAllChild(node);
	if (node->firstChild != NULL) {
		Com_Printf("UI_DeleteNode: Node '%s' contain static nodes. We can't delete it.", UI_GetPath(node));
		return;
	}

	if (node->parent)
		UI_RemoveNode(node->parent, node);

	/* delete all allocated properties */
	for (behaviour = node->behaviour; behaviour; behaviour = behaviour->super) {
		const value_t *property = behaviour->properties;
		if (property == NULL)
			continue;
		while (property->string != NULL) {
			if ((property->type & V_UI_MASK) == V_UI_CVAR) {
				void *mem = ((byte *) node + property->ofs);
				if (*(void**)mem != NULL) {
					UI_FreeStringProperty(*(void**)mem);
					*(void**)mem = NULL;
				}
			}

			/** @todo We must delete all EA_LISTENER too */

			property++;
		}
	}

	if (node->behaviour->deleteNode)
		node->behaviour->deleteNode(node);
}
Beispiel #15
0
/**
 * @brief Handled after the end of the load of the node from the script (all data and/or child are set)
 */
void uiImageNode::onLoaded (uiNode_t* node)
{
	/* update the size when its possible */
	if (Vector2Empty(node->box.size)) {
		if (EXTRADATA(node).texl[0] != 0 || EXTRADATA(node).texh[0]) {
			node->box.size[0] = EXTRADATA(node).texh[0] - EXTRADATA(node).texl[0];
			node->box.size[1] = EXTRADATA(node).texh[1] - EXTRADATA(node).texl[1];
		} else if (node->image) {
			const image_t* image = UI_LoadImage(node->image);
			if (image) {
				node->box.size[0] = image->width;
				node->box.size[1] = image->height;
			}
		}
	}
#ifdef DEBUG
	if (Vector2Empty(node->box.size)) {
		if (node->onClick || node->onRightClick || node->onMouseEnter || node->onMouseLeave || node->onWheelUp || node->onWheelDown || node->onWheel || node->onMiddleClick) {
			Com_DPrintf(DEBUG_CLIENT, "Node '%s' is an active image without size\n", UI_GetPath(node));
		}
	}
#endif
}
/**
 * @brief set a node property from the command line
 * @todo Unify path syntaxe to allow to create a common autocompletion
 */
static void UI_NodeSetProperty_f (void)
{
	uiNode_t* node;
	const value_t* property;

	if (Cmd_Argc() != 4) {
		Com_Printf("Usage: %s <nodepath> <prop> <value>\n", Cmd_Argv(0));
		return;
	}

	node = UI_GetNodeByPath(Cmd_Argv(1));
	if (!node) {
		Com_Printf("UI_NodeSetProperty_f: Node '%s' not found\n", Cmd_Argv(1));
		return;
	}

	property = UI_GetPropertyFromBehaviour(node->behaviour, Cmd_Argv(2));
	if (!property) {
		Com_Printf("Property '%s@%s' doesn't exist\n", UI_GetPath(node), Cmd_Argv(2));
		return;
	}

	UI_NodeSetProperty(node, property, Cmd_Argv(3));
}
Beispiel #17
0
static bool UI_ParseExcludeRect (uiNode_t* node, const char** text, const char** token, const char* errhead)
{
	uiExcludeRect_t rect;
	uiExcludeRect_t* newRect;

	/* get parameters */
	*token = Com_EParse(text, errhead, node->name);
	if (!*text)
		return false;
	if ((*token)[0] != '{') {
		Com_Printf("UI_ParseExcludeRect: node with bad excluderect ignored (node \"%s\")\n", UI_GetPath(node));
		return true;
	}

	do {
		*token = Com_EParse(text, errhead, node->name);
		if (!*text)
			return false;
		/** @todo move it into a property array */
		if (Q_streq(*token, "pos")) {
			*token = Com_EParse(text, errhead, node->name);
			if (!*text)
				return false;
			Com_EParseValue(&rect, *token, V_POS, offsetof(uiExcludeRect_t, pos), sizeof(vec2_t));
		} else if (Q_streq(*token, "size")) {
			*token = Com_EParse(text, errhead, node->name);
			if (!*text)
				return false;
			Com_EParseValue(&rect, *token, V_POS, offsetof(uiExcludeRect_t, size), sizeof(vec2_t));
		}
	} while ((*token)[0] != '}');

	newRect = (uiExcludeRect_t*) UI_AllocHunkMemory(sizeof(*newRect), STRUCT_MEMORY_ALIGN, false);
	if (newRect == nullptr) {
		Com_Printf("UI_ParseExcludeRect: ui hunk memory exceeded.");
		return false;
	}

	/* move data to final memory and link to node */
	*newRect = rect;
	newRect->next = node->firstExcludeRect;
	node->firstExcludeRect = newRect;
	return true;
}
Beispiel #18
0
/**
 * @brief Parse actions and return action list
 * @return The first element from a list of action
 * @sa ea_t
 * @todo need cleanup, compute action out of the final memory; reduce number of var
 */
static uiAction_t* UI_ParseActionList (uiNode_t* node, const char** text, const char** token)
{
	const char* errhead = "UI_ParseActionList: unexpected end of file (in event)";
	uiAction_t* firstAction;
	uiAction_t* lastAction;
	uiAction_t* action;

	lastAction = nullptr;
	firstAction = nullptr;

	/* prevent bad position */
	if ((*token)[0] != '{') {
		Com_Printf("UI_ParseActionList: token \"{\" expected, but \"%s\" found (in event) (node: %s)\n", *token, UI_GetPath(node));
		return nullptr;
	}

	while (true) {
		bool result;
		int type = EA_NULL;

		/* get new token */
		*token = Com_EParse(text, errhead, nullptr);
		if (!*token)
			return nullptr;

		if ((*token)[0] == '}')
			break;

		type = UI_GetActionTokenType(*token, EA_ACTION);
		/* setter form */
		if (type == EA_NULL && (*token)[0] == '*')
			type = EA_ASSIGN;

		/* unknown, we break the parsing */
		if (type == EA_NULL) {
			Com_Printf("UI_ParseActionList: unknown token \"%s\" ignored (in event) (node: %s)\n", *token, UI_GetPath(node));
			return nullptr;
		}

		/* add the action */
		action = UI_AllocStaticAction();
		/** @todo better to append the action after initialization */
		if (lastAction)
			lastAction->next = action;
		if (!firstAction)
			firstAction = action;
		action->type = type;

		/* decode action */
		switch (action->type) {
		case EA_CMD:
			/* get parameter values */
			*token = Com_EParse(text, errhead, nullptr);
			if (!*text)
				return nullptr;

			/* get the value */
			action->d.terminal.d1.constString = UI_AllocStaticString(*token, 0);
			break;

		case EA_ASSIGN:
			result = UI_ParseSetAction(node, action, text, token, errhead);
			if (!result)
				return nullptr;
			break;

		case EA_CALL:
			result = UI_ParseCallAction(node, action, text, token, errhead);
			if (!result)
				return nullptr;
			break;

		case EA_DELETE:
			{
				uiAction_t* expression;
				expression = UI_ParseExpression(text);
				if (expression == nullptr)
					return nullptr;

				if (expression->type != EA_VALUE_CVARNAME) {
					Com_Printf("UI_ParseActionList: \"delete\" keyword only support cvarname (node: %s)\n", UI_GetPath(node));
					return nullptr;
				}

				action->d.nonTerminal.left = expression;
				break;
			}

		case EA_ELIF:
			/* check previous action */
			if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
				Com_Printf("UI_ParseActionList: 'elif' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
				return nullptr;
			}
			/* then it execute EA_IF, no break */
		case EA_WHILE:
		case EA_IF:
			{
				uiAction_t* expression;

				/* get the condition */
				expression = UI_ParseExpression(text);
				if (expression == nullptr)
					return nullptr;
				action->d.nonTerminal.left = expression;

				/* get the action block */
				*token = Com_EParse(text, errhead, nullptr);
				if (!*text)
					return nullptr;
				action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
				if (action->d.nonTerminal.right == nullptr) {
					switch (action->type) {
					case EA_IF:
						Com_Printf("UI_ParseActionList: block expected after \"if\" (node: %s)\n", UI_GetPath(node));
						break;
					case EA_ELIF:
						Com_Printf("UI_ParseActionList: block expected after \"elif\" (node: %s)\n", UI_GetPath(node));
						break;
					case EA_WHILE:
						Com_Printf("UI_ParseActionList: block expected after \"while\" (node: %s)\n", UI_GetPath(node));
						break;
					default:
						Com_Printf("UI_ParseActionList: cannot determine statement type (node: %s)\n", UI_GetPath(node));
					}
					return nullptr;
				}
				break;
			}

		case EA_FORCHILDIN:
			{
				uiAction_t* expression;

				/* get the node */
				expression = UI_ParseExpression(text);
				if (expression == nullptr) {
					return nullptr;
				}
				action->d.nonTerminal.left = expression;

				/* check the type */
				type = action->d.nonTerminal.left->type;
				if (type != EA_VALUE_PATHNODE && type != EA_VALUE_PATHNODE_WITHINJECTION) {
					Com_Printf("UI_ParseActionList: Node property expected. Type '%x' found\n", type);
					return nullptr;
				}

				/* get the action block */
				*token = Com_EParse(text, errhead, nullptr);
				if (!*text)
					return nullptr;
				action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
				if (action->d.nonTerminal.right == nullptr) {
					Com_Printf("UI_ParseActionList: block expected after \"forchildin\" (node: %s)\n", UI_GetPath(node));
					return nullptr;
				}
				break;
			}

		case EA_ELSE:
			/* check previous action */
			if (!lastAction || (lastAction->type != EA_IF && lastAction->type != EA_ELIF)) {
				Com_Printf("UI_ParseActionList: 'else' must be set after an 'if' or an 'elif' (node: %s)\n", UI_GetPath(node));
				return nullptr;
			}

			/* get the action block */
			*token = Com_EParse(text, errhead, nullptr);
			if (!*text)
				return nullptr;
			action->d.nonTerminal.left = nullptr;
			action->d.nonTerminal.right = UI_ParseActionList(node, text, token);
			if (action->d.nonTerminal.right == nullptr) {
				Com_Printf("UI_ParseActionList: block expected after \"else\" (node: %s)\n", UI_GetPath(node));
				return nullptr;
			}
			break;

		default:
			assert(false);
		}

		/* step */
		lastAction = action;
	}

	assert((*token)[0] == '}');

	/* return non nullptr value */
	if (firstAction == nullptr) {
		firstAction = UI_AllocStaticAction();
	}

	return firstAction;
}
Beispiel #19
0
/**
 * @brief Parser for call command
 */
static bool UI_ParseCallAction (uiNode_t* node, uiAction_t* action, const char** text, const char** token, const char* errhead)
{
	uiAction_t* expression;
	uiAction_t* lastParam = nullptr;
	int paramID = 0;
	expression = UI_ParseExpression(text);
	if (expression == nullptr)
		return false;

	if (expression->type != EA_VALUE_PATHNODE_WITHINJECTION && expression->type != EA_VALUE_PATHNODE && expression->type != EA_VALUE_PATHPROPERTY && expression->type != EA_VALUE_PATHPROPERTY_WITHINJECTION) {
		Com_Printf("UI_ParseCallAction: \"call\" keyword only support pathnode and pathproperty (node: %s)\n", UI_GetPath(node));
		return false;
	}

	action->d.nonTerminal.left = expression;

	/* check parameters */
	*token = Com_EParse(text, errhead, nullptr);
	if ((*token)[0] == '\0')
		return false;

	/* there is no parameters */
	if (!Q_streq(*token, "(")) {
		Com_UnParseLastToken();
		return true;
	}

	/* read parameters */
	do {
		uiAction_t* param;
		paramID++;

		/* parameter */
		param = UI_ParseExpression(text);
		if (param == nullptr) {
			Com_Printf("UI_ParseCallAction: problem with the %i parameter\n", paramID);
			return false;
		}
		if (lastParam == nullptr)
			action->d.nonTerminal.right = param;
		else
			lastParam->next = param;
		lastParam = param;

		/* separator */
		*token = Com_EParse(text, errhead, nullptr);
		if (!*token)
			return false;
		if (!Q_streq(*token, ",")) {
			if (Q_streq(*token, ")"))
				break;
			Com_UnParseLastToken();
			Com_Printf("UI_ParseCallAction: Invalidate end of 'call' after param %i\n", paramID);
			return false;
		}
	} while(true);

	return true;
}
Beispiel #20
0
/**
 * @brief Parser for setter command
 */
static bool UI_ParseSetAction (uiNode_t* node, uiAction_t* action, const char** text, const char** token, const char* errhead)
{
	const value_t* property;
	int type;
	uiAction_t* localAction;

	assert((*token)[0] == '*');

	Com_UnParseLastToken();
	action->d.nonTerminal.left = UI_ParseExpression(text);

	type = action->d.nonTerminal.left->type;
	if (type != EA_VALUE_CVARNAME && type != EA_VALUE_CVARNAME_WITHINJECTION
		&& type != EA_VALUE_PATHPROPERTY && type != EA_VALUE_PATHPROPERTY_WITHINJECTION) {
		Com_Printf("UI_ParseSetAction: Cvar or Node property expected. Type '%i' found\n", type);
		return false;
	}

	/* must use "equal" char between name and value */
	*token = Com_EParse(text, errhead, nullptr);
	if (!*text)
		return false;
	if (!Q_streq(*token, "=")) {
		Com_Printf("UI_ParseSetAction: Assign sign '=' expected between variable and value. '%s' found in node %s.\n", *token, UI_GetPath(node));
		return false;
	}

	/* get the value */
	if (type == EA_VALUE_CVARNAME || type == EA_VALUE_CVARNAME_WITHINJECTION) {
		action->d.nonTerminal.right = UI_ParseExpression(text);
		return true;
	}

	property = (const value_t*) action->d.nonTerminal.left->d.terminal.d2.data;

	*token = Com_EParse(text, errhead, nullptr);
	if (!*text)
		return false;

	if (Q_streq(*token, "{")) {
		uiAction_t* actionList;

		if (property != nullptr && property->type != V_UI_ACTION) {
			Com_Printf("UI_ParseSetAction: Property %s@%s do not expect code block.\n", UI_GetPath(node), property->string);
			return false;
		}

		actionList = UI_ParseActionList(node, text, token);
		if (actionList == nullptr)
			return false;

		localAction = UI_AllocStaticAction();
		localAction->type = EA_VALUE_RAW;
		localAction->d.terminal.d1.data = actionList;
		localAction->d.terminal.d2.integer = V_UI_ACTION;
		action->d.nonTerminal.right = localAction;

		return true;
	}

	if (Q_streq(*token, "(")) {
		Com_UnParseLastToken();
		action->d.nonTerminal.right = UI_ParseExpression(text);
		return true;
	}

	/* @todo everything should come from UI_ParseExpression */

	if (UI_IsInjectedString(*token)) {
		localAction = UI_AllocStaticAction();
		localAction->type = EA_VALUE_STRING_WITHINJECTION;
		localAction->d.terminal.d1.data = UI_AllocStaticString(*token, 0);
		action->d.nonTerminal.right = localAction;
		return true;
	}

	localAction = UI_AllocStaticAction();
	UI_InitRawActionValue(localAction, node, property, *token);
	action->d.nonTerminal.right = localAction;
	return true;
}
const char *UI_AbstractOptionGetCurrentValue (uiNode_t * node)
{
	/* no cvar given? */
	if (!EXTRADATA(node).cvar || !*EXTRADATA(node).cvar) {
		Com_Printf("UI_AbstractOptionGetCurrentValue: node '%s' doesn't have a valid cvar assigned\n", UI_GetPath(node));
		return NULL;
	}

	/* not a cvar? */
	if (!Q_strstart(EXTRADATA(node).cvar, "*cvar:"))
		return NULL;

	return UI_GetReferenceString(node, EXTRADATA(node).cvar);
}
Beispiel #22
0
static void UI_VScrollbarNodeLoaded (uiNode_t *node)
{
#ifdef DEBUG
	if (node->size[1] - (ELEMENT_HEIGHT * 4) < 0)
		Com_DPrintf(DEBUG_CLIENT, "Node '%s' too small. It can create graphical glitches\n", UI_GetPath(node));
#endif
}
Beispiel #23
0
/**
 * @brief Call after the script initialized the node
 */
static void UI_ConFuncNodeLoaded (uiNode_t *node)
{
	/* register confunc non inherited */
	if (node->super == NULL) {
		/* don't add a callback twice */
		if (!Cmd_Exists(node->name)) {
			Cmd_AddCommand(node->name, UI_ConfuncCommand_f, "Confunc callback");
			Cmd_AddUserdata(node->name, node);
		} else {
			Com_Printf("UI_ParseNodeBody: Command name for confunc '%s' already registered\n", UI_GetPath(node));
		}
	} else {
		uiNode_t *dummy;

		/* convert a confunc to an "inherited" confunc if it is possible */
		if (Cmd_Exists(node->name)) {
			if (UI_ConFuncIsVirtual(node))
				return;
		}

		dummy = UI_AllocNode(node->name, "confunc", qfalse);
		Cmd_AddCommand(node->name, UI_ConfuncCommand_f, "Inherited confunc callback");
		Cmd_AddUserdata(dummy->name, dummy);
	}
}
Beispiel #24
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;
}
void uiLineChartNode::draw (uiNode_t *node)
{
	lineStrip_t *lineStrip;
	const int dataId = EXTRADATA(node).dataId;
	vec3_t pos;

	if (dataId == 0)
		return;

	if (ui_global.sharedData[dataId].type != UI_SHARED_LINESTRIP) {
		Com_Printf("UI_LineStripNodeDraw: Node '%s' have use an invalide dataId type. LineStrip expected. dataId invalidated\n", UI_GetPath(node));
		EXTRADATA(node).dataId = 0;
		return;
	}

	UI_GetNodeAbsPos(node, pos);
	pos[2] = 0;

	UI_Transform(pos, nullptr, nullptr);

	/* Draw axes */
	if (EXTRADATA(node).displayAxes) {
		int axes[6];
		axes[0] = 0;
		axes[1] = 0;
		axes[2] = 0;
		axes[3] = (int) node->box.size[1];
		axes[4] = (int) node->box.size[0];
		axes[5] = (int) node->box.size[1];
		R_Color(EXTRADATA(node).axesColor);
		R_DrawLineStrip(3, axes);
	}

	/* Draw all linestrips. */
	lineStrip = ui_global.sharedData[dataId].data.lineStrip;
	for (; lineStrip; lineStrip = lineStrip->next) {
		/* Draw this line if it's valid. */
		if (lineStrip->pointList && lineStrip->numPoints > 0) {
			R_Color(lineStrip->color);
			R_DrawLineStrip(lineStrip->numPoints, lineStrip->pointList);
		}
	}
	R_Color(nullptr);

	UI_Transform(nullptr, nullptr, nullptr);
}
Beispiel #26
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);
}
Beispiel #27
0
/**
 * @brief Read a node body
 * @note Node header already read, we are over the node name, or '{'
 * @code
 * Allowed syntax
 * { properties }
 * OR
 * { nodes }
 * OR
 * { { properties } nodes }
 * @endcode
 */
static bool UI_ParseNodeBody (uiNode_t* node, const char** text, const char** token, const char* errhead)
{
	bool result = true;

	if ((*token)[0] != '{') {
		/* read the body block start */
		*token = Com_EParse(text, errhead, node->name);
		if (!*text)
			return false;
		if ((*token)[0] != '{') {
			Com_Printf("UI_ParseNodeBody: node doesn't have body, token '%s' read (node \"%s\")\n", *token, UI_GetPath(node));
			ui_global.numNodes--;
			return false;
		}
	}

	/* functions are a special case */
	if (UI_Node_IsFunction(node)) {
		result = UI_ParseFunction(node, text, token);
	} else {

		/* check the content */
		*token = Com_EParse(text, errhead, node->name);
		if (!*text)
			return false;

		if ((*token)[0] == '{') {
			/* we have a special block for properties */
			result = UI_ParseNodeProperties(node, text, token);
			if (!result)
				return false;

			/* move token over the next node behaviour */
			*token = Com_EParse(text, errhead, node->name);
			if (!*text)
				return false;

			/* and then read all nodes */
			while ((*token)[0] != '}') {
				uiNode_t* newNode = UI_ParseNode(node, text, token, errhead);
				if (!newNode)
					return false;

				*token = Com_EParse(text, errhead, node->name);
				if (*text == nullptr)
					return false;
			}
		} else if (UI_GetPropertyFromBehaviour(node->behaviour, *token)) {
			/* we should have a block with properties only */
			result = UI_ParseNodeProperties(node, text, token);
		} else {
			/* we should have a block with nodes only */
			while ((*token)[0] != '}') {
				uiNode_t* newNode = UI_ParseNode(node, text, token, errhead);
				if (!newNode)
					return false;

				*token = Com_EParse(text, errhead, node->name);
				if (*text == nullptr)
					return false;
			}
		}
	}
	if (!result) {
		Com_Printf("UI_ParseNodeBody: node with bad body ignored (node \"%s\")\n", UI_GetPath(node));
		ui_global.numNodes--;
		return false;
	}

	/* already check on UI_ParseNodeProperties */
	assert((*token)[0] == '}');
	return true;
}
Beispiel #28
0
/**
 * @brief parse a node
 * @sa UI_ParseNodeProperties
 * @todo we can think about merging UI_ParseNodeProperties here
 * @note first token already read
 * @note dont read more than the need token (last right token is '}' of end of node)
 */
static uiNode_t* UI_ParseNode (uiNode_t* parent, const char** text, const char** token, const char* errhead)
{
	uiNode_t* node = nullptr;
	uiBehaviour_t* behaviour;
	uiNode_t* component = nullptr;

	/* get the behaviour */
	behaviour = UI_GetNodeBehaviour(*token);
	if (!behaviour) {
		component = UI_GetComponent(*token);
	}
	if (behaviour == nullptr && component == nullptr) {
		Com_Printf("UI_ParseNode: node behaviour/component '%s' doesn't exist (%s)\n", *token, UI_GetPath(parent));
		return nullptr;
	}

	/* get the name */
	*token = Com_EParse(text, errhead, "");
	if (!*text)
		return nullptr;
	if (!UI_TokenIsName(*token, Com_GetType(text) == TT_QUOTED_WORD)) {
		Com_Printf("UI_ParseNode: \"%s\" is not a well formed node name ([a-zA-Z_][a-zA-Z0-9_]*)\n", *token);
		return nullptr;
	}
	if (UI_TokenIsReserved(*token)) {
		Com_Printf("UI_ParseNode: \"%s\" is a reserved token, we can't call a node with it\n", *token);
		return nullptr;
	}

	/* test if node already exists */
	/* Already existing node should only come from inherited node, we should not have 2 definitions of the same node into the same window. */
	if (parent)
		node = UI_GetNode(parent, *token);

	/* reuse a node */
	if (node) {
		const uiBehaviour_t* test = (behaviour != nullptr) ? behaviour : (component != nullptr) ? component->behaviour : nullptr;
		if (node->behaviour != test) {
			Com_Printf("UI_ParseNode: we can't change node type (node \"%s\")\n", UI_GetPath(node));
			return nullptr;
		}
		Com_DPrintf(DEBUG_CLIENT, "... over-riding node %s\n", UI_GetPath(node));

	/* else initialize a component */
	} else if (component) {
		node = UI_CloneNode(component, nullptr, true, *token, false);
		if (parent) {
			if (parent->root)
				UI_UpdateRoot(node, parent->root);
			UI_AppendNode(parent, node);
		}

	/* else initialize a new node */
	} else {
		node = UI_AllocNode(*token, behaviour->name, false);
		node->parent = parent;
		if (parent)
			node->root = parent->root;
		/** @todo move it into caller */
		if (parent)
			UI_AppendNode(parent, node);
	}

	/* get body */
	const bool result = UI_ParseNodeBody(node, text, token, errhead);
	if (!result)
		return nullptr;

	/* validate properties */
	UI_Node_Loaded(node);

	return node;
}
Beispiel #29
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);
}
Beispiel #30
0
/** called when the window is pushed
 * check cvar then, reduce runtime check
 * @todo move cvar check to AbstractOption
 */
void uiTabNode::onWindowOpened (uiNode_t *node, linkedList_t *params)
{
	/* no cvar given? */
	if (!(EXTRADATA(node).cvar))
		return;

	/* not a cvar? */
	char const* const cvarName = Q_strstart(EXTRADATA(node).cvar, "*cvar:");
	if (!cvarName) {
		/* normalize */
		Com_Printf("UI_TabNodeInit: node '%s' doesn't have a valid cvar assigned (\"%s\" read)\n", UI_GetPath(node), EXTRADATA(node).cvar);
		EXTRADATA(node).cvar = NULL;
		return;
	}

	/* cvar do not exists? */
	if (Cvar_FindVar(cvarName) == NULL) {
		/* search default value, if possible */
		uiNode_t* option = node->firstChild;
		assert(option->behaviour == ui_optionBehaviour);
		Cvar_ForceSet(cvarName, OPTIONEXTRADATA(option).value);
	}
}