Пример #1
0
static void UI_AbstractNodeCallCreateChild (uiNode_t* node, const uiCallContext_t* context)
{
	uiNode_t* child;
	uiNode_t* component;
	const char* name;
	const char* type;

	if (UI_GetParamNumber(context) != 2) {
		Com_Printf("UI_AbstractNodeCallCreateChild: Invalid number of parameters\n");
		return;
	}

	name = UI_GetParam(context, 1);
	type = UI_GetParam(context, 2);

	uiNode_t* existingNode = UI_GetNode(node, name);
	if (existingNode != nullptr) {
		Com_Printf("UI_AbstractNodeCallCreateChild: Node with name '%s' already exists\n", name);
	}

	component = UI_GetComponent(type);
	if (component) {
		child = UI_CloneNode(component, node->root, true, name, true);
	} else {
		child = UI_AllocNode(name, type, true);
	}

	if (child == nullptr) {
		Com_Printf("UI_AbstractNodeCallCreateChild: Impossible to create the node\n");
		return;
	}

	UI_AppendNode(node, child);
}
Пример #2
0
/**
 * @brief Recursive searches for a child node by name in the entire subtree.
 * @return A uiNode_t* or nullptr if not found.
 */
uiNode_t* UI_FindNode(const uiNode_t* node, const char* name) {
	/* search current level */
	uiNode_t* result = UI_GetNode(node, name);
	if (!result) {
		/* iterate child nodes and search next level */
		for(uiNode_t* current = node->firstChild; current; current = current->next) {
			result = UI_FindNode(current, name);
			if (result) break;
		}
	}
	return result;
}
Пример #3
0
/**
 * @brief Generates a popup that contains a list of selectable choices.
 * @param[in] title Title of the popup.
 * @param[in] headline First line of text written in the popup.
 * @param[in] entries List of the selectables choices.
 * @param[in] clickAction Action to perform when one clicked on the popup.
 */
uiNode_t *UI_PopupList (const char *title, const char *headline, linkedList_t* entries, const char *clickAction)
{
	uiNode_t* window;
	uiNode_t* listNode;

	Cvar_Set("ui_sys_popup_title", title);
	UI_RegisterText(TEXT_POPUP_INFO, headline);

	/* make sure, that we are using the linked list */
	UI_ResetData(TEXT_LIST);
	UI_RegisterLinkedListText(TEXT_LIST, entries);

	window = UI_GetWindow(POPUPLIST_WINDOW_NAME);
	if (!window)
		Com_Error(ERR_FATAL, "Could not get "POPUPLIST_WINDOW_NAME" window");
	listNode = UI_GetNode(window, POPUPLIST_NODE_NAME);
	if (!listNode)
		Com_Error(ERR_FATAL, "Could not get "POPUPLIST_NODE_NAME" node in "POPUPLIST_WINDOW_NAME" window");

	/* free previous actions */
	if (listNode->onClick) {
		assert(listNode->onClick->d.terminal.d1.data);
		Mem_Free(listNode->onClick->d.terminal.d1.data);
		Mem_Free(listNode->onClick);
		listNode->onClick = NULL;
	}

	if (clickAction) {
		UI_PoolAllocAction(&listNode->onClick, EA_CMD, clickAction);
	} else {
		listNode->onClick = NULL;
	}

	if (!UI_IsWindowOnStack(window->name))
		UI_PushWindow(window->name, NULL, NULL);
	return listNode;
}
Пример #4
0
/**
 * @brief Set string and action button.
 * @param[in] window window where buttons are modified.
 * @param[in] button Name of the node of the button.
 * @param[in] clickAction Action to perform when button is clicked.
 * @note clickAction may be NULL if button is not needed.
 */
static void UI_SetOneButton (uiNode_t* window, const char *button, const char *clickAction)
{
	uiNode_t* buttonNode;

	buttonNode = UI_GetNode(window, button);
	if (!buttonNode)
		Com_Error(ERR_FATAL, "Could not get %s node in %s window", button, window->name);

	/* free previous actions */
	if (buttonNode->onClick) {
		assert(buttonNode->onClick->d.terminal.d1.data);
		Mem_Free(buttonNode->onClick->d.terminal.d1.data);
		Mem_Free(buttonNode->onClick);
		buttonNode->onClick = NULL;
	}

	if (clickAction) {
		UI_PoolAllocAction(&buttonNode->onClick, EA_CMD, clickAction);
		buttonNode->invis = qfalse;
	} else {
		buttonNode->onClick = NULL;
		buttonNode->invis = qtrue;
	}
}
Пример #5
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;
}
Пример #6
0
/**
 * @brief Read a path and return every we can use (node and property)
 * @details The path token must be a window name, and then node child.
 * Reserved token 'root' and 'parent' can be used to navigate.
 * If relativeNode is set, the path can start with reserved token
 * 'this', 'root' and 'parent' (relative to this node).
 * The function can return a node property by using a '\@',
 * the path 'foo\@pos' will return the window foo and the property 'pos'
 * from the 'window' behaviour.
 * @param[in] path Path to read. Contain a node location with dot seprator and a facultative property
 * @param[in] relativeNode relative node where the path start. It allow to use facultative command to start the path (this, parent, root).
 * @param[out] resultNode Node found. Else NULL.
 * @param[out] resultProperty Property found. Else NULL.
 * TODO Speed up, evilly used function, use strncmp instead of using buffer copy (name[MAX_VAR])
 */
void UI_ReadNodePath (const char* path, const uiNode_t *relativeNode, uiNode_t **resultNode, const value_t **resultProperty)
{
	char name[MAX_VAR];
	uiNode_t* node = NULL;
	const char* nextName;
	char nextCommand = '^';

	*resultNode = NULL;
	if (resultProperty)
		*resultProperty = NULL;

	nextName = path;
	while (nextName && nextName[0] != '\0') {
		const char* begin = nextName;
		char command = nextCommand;
		nextName = strpbrk(begin, ".@#");
		if (!nextName) {
			Q_strncpyz(name, begin, sizeof(name));
			nextCommand = '\0';
		} else {
			assert(nextName - begin + 1 <= sizeof(name));
			Q_strncpyz(name, begin, nextName - begin + 1);
			nextCommand = *nextName;
			nextName++;
		}

		switch (command) {
		case '^':	/* first string */
			if (Q_streq(name, "this")) {
				if (relativeNode == NULL)
					return;
				/** @todo find a way to fix the bad cast. only here to remove "discards qualifiers" warning */
				node = *(uiNode_t**) ((void*)&relativeNode);
			} else if (Q_streq(name, "parent")) {
				if (relativeNode == NULL)
					return;
				node = relativeNode->parent;
			} else if (Q_streq(name, "root")) {
				if (relativeNode == NULL)
					return;
				node = relativeNode->root;
			} else
				node = UI_GetWindow(name);
			break;
		case '.':	/* child node */
			if (Q_streq(name, "parent"))
				node = node->parent;
			else if (Q_streq(name, "root"))
				node = node->root;
			else
				node = UI_GetNode(node, name);
			break;
		case '#':	/* window index */
			/** @todo FIXME use a warning instead of an assert */
			assert(node->behaviour == ui_windowBehaviour);
			node = UI_WindowNodeGetIndexedChild(node, name);
			break;
		case '@':	/* property */
			assert(nextCommand == '\0');
			*resultProperty = UI_GetPropertyFromBehaviour(node->behaviour, name);
			*resultNode = node;
			return;
		}

		if (!node)
			return;
	}

	*resultNode = node;
	return;
}
Пример #7
0
/**
 * @brief Read a path and return every we can use (node and property)
 * @details The path token must be a window name, and then node child.
 * Reserved token 'root' and 'parent' can be used to navigate.
 * If relativeNode is set, the path can start with reserved token
 * 'this', 'root', 'parent' and 'child' (relative to this node).
 * The function can return a node property by using a '\@',
 * the path 'foo\@pos' will return the window foo and the property 'pos'
 * from the 'window' behaviour.
 * @param[in] path Path to read. Contain a node location with dot seprator and a facultative property
 * @param[in] relativeNode relative node where the path start. It allow to use facultative command to start the path (this, parent, root).
 * @param[in] iterationNode relative node referencing child in 'forchildin' iteration, mapped to '*node:child', can be nullptr
 * @param[out] resultNode Node found. Else nullptr.
 * @param[out] resultProperty Property found. Else nullptr.
 * @param[in,out] luaMethod A pointer to a value_t structure that is filled with a lua based method identification, can be nullptr
 * @note If luaMethod is set, the method will scan the known lua methods defined on the behaviour. If luaMethod is not set, only
 * registered local properties are scanned.
 * @todo Speed up, evilly used function, use strncmp instead of using buffer copy (name[MAX_VAR])
 * @note If luaMethod is set, make sure to release the allocated .name string.
 */
void UI_ReadNodePath (const char* path, const uiNode_t* relativeNode, const uiNode_t* iterationNode, uiNode_t** resultNode, const value_t** resultProperty, value_t *luaMethod) {
	char name[MAX_VAR];
	uiNode_t* node = nullptr;
	uiNode_t* childnode = nullptr;
	const char* nextName;
	char nextCommand = '^';

	*resultNode = nullptr;
	if (resultProperty)
		*resultProperty = nullptr;

	nextName = path;
	while (nextName && nextName[0] != '\0') {
		const char* begin = nextName;
		char command = nextCommand;
		nextName = strpbrk(begin, ".@#");
		if (!nextName) {
			Q_strncpyz(name, begin, sizeof(name));
			nextCommand = '\0';
		} else {
			assert(nextName - begin + 1 <= sizeof(name));
			Q_strncpyz(name, begin, nextName - begin + 1);
			nextCommand = *nextName;
			nextName++;
		}

		switch (command) {
		case '^':	/* first string */
			if (Q_streq(name, "this")) {
				if (relativeNode == nullptr)
					return;
				node = const_cast<uiNode_t *>(relativeNode);
			} else if (Q_streq(name, "parent")) {
				if (relativeNode == nullptr)
					return;
				node = relativeNode->parent;
			} else if (Q_streq(name, "root")) {
				if (relativeNode == nullptr)
					return;
				node = relativeNode->root;
			} else if (Q_streq(name, "child")) {
				if (iterationNode == nullptr)
					return;
				node = const_cast<uiNode_t *>(iterationNode);
			} else
				node = UI_GetWindow(name);
			break;
		case '.':	/* child node */
			if (Q_streq(name, "parent"))
				childnode = node->parent;
			else if (Q_streq(name, "root"))
				childnode = node->root;
			else {
				childnode = UI_GetNode(node, name);
				/* if no node found and if we need it, then it is possible we call a lua based function */
				if (luaMethod && !childnode) {
					*resultProperty = UI_GetPropertyOrLuaMethod(node, name, luaMethod);
					if (luaMethod->type) {
						childnode = node;
					}
				}
			}
			node = childnode;
			break;
		case '#':	/* window index */
			/** @todo FIXME use a warning instead of an assert */
			assert(UI_Node_IsWindow(node));
			node = UI_WindowNodeGetIndexedChild(node, name);
			break;
		case '@':	/* property */
			assert(nextCommand == '\0');
			*resultProperty = UI_GetPropertyOrLuaMethod(node, name, luaMethod);
			*resultNode = node;
			return;
		}

		if (!node)
			return;
	}

	*resultNode = node;
	return;
}
Пример #8
0
/**
 * @brief Try to autoplace an item from a container.
 * @param[in] node The context node
 * @param[in] ic An item from the node container
 * @todo We should use an item ID, and get the item inside the function, to avoid wrong uses.
 */
void UI_ContainerNodeAutoPlaceItem (uiNode_t* node, invList_t *ic)
{
	containerIndex_t target;
	uiNode_t *targetNode;
	bool ammoChanged = false;
	const invDef_t *container = EXTRADATA(node).container;

	/* Right click: automatic item assignment/removal. */
	if (container->id != csi.idEquip) {
		if (ic->item.ammo && ic->item.ammo != ic->item.item && ic->item.ammoLeft) {
			/* Remove ammo on removing weapon from a soldier */
			target = csi.idEquip;
			ammoChanged = INV_UnloadWeapon(ic, ui_inventory, INVDEF(target));
		} else {
			/* Move back to idEquip (ground, floor) container. */
			target = csi.idEquip;
			INV_MoveItem(ui_inventory, INVDEF(target), NONE, NONE, container, ic, NULL);
		}
	} else {
		bool packed = false;
		assert(ic->item.item);
		/* armour can only have one target */
		if (INV_IsArmour(ic->item.item)) {
			target = csi.idArmour;
			packed = INV_MoveItem(ui_inventory, INVDEF(target), 0, 0, container, ic, NULL);
		/* ammo or item */
		} else if (INV_IsAmmo(ic->item.item)) {
			/* Finally try left and right hand. There is no other place to put it now. */
			const containerIndex_t idxArray[] = { csi.idBelt, csi.idHolster, csi.idBackpack, csi.idLeft, csi.idRight };
			const size_t size = lengthof(idxArray);
			unsigned int i;
			for (i = 0; i < size; i++) {
				target = idxArray[i];
				packed = UI_ContainerNodeAddItem(container, ic, target, NULL);
				if (packed)
					break;
			}
		} else {
			if (ic->item.item->headgear) {
				target = csi.idHeadgear;
				packed = UI_ContainerNodeAddItem(container, ic, target, NULL);
			} else {
				/* left and right are single containers, but this might change - it's cleaner to check
				 * for available space here, too */
				const containerIndex_t idxArray[] = { csi.idRight, csi.idBelt, csi.idHolster, csi.idBackpack, csi.idLeft };
				const size_t size = lengthof(idxArray);
				invList_t *tItem = NULL;
				unsigned int i;
				for (i = 0; i < size; i++) {
					target = idxArray[i];
					packed = UI_ContainerNodeAddItem(container, ic, target, &tItem);
					if (packed) {
						if ((ic->item.item->weapon && !ic->item.ammoLeft) || ic->item.item->oneshot)
							ammoChanged = INV_LoadWeapon(tItem, ui_inventory, container, INVDEF(target));
						break;
					}
				}
			}
		}
		/* no need to continue here - placement wasn't successful at all */
		if (!packed)
			return;
	}

	/* Run onChange events */
	targetNode = UI_GetContainerNodeByContainerIDX(node->parent, target);
	if (node->onChange)
		UI_ExecuteEventActions(node, node->onChange);
	if (targetNode != NULL && node != targetNode && targetNode->onChange)
		UI_ExecuteEventActions(targetNode, targetNode->onChange);
	/* Also call onChange for equip_ammo if ammo moved
	 * Maybe there's a better way to do this? */
	if (INV_IsAmmo(ic->item.item) || ammoChanged) {
		/** @todo hard coded node name, remove it when it is possible */
		uiNode_t *ammoNode = UI_GetNode(node->root, "equip_ammo");
		if (ammoNode != NULL && node != ammoNode && ammoNode->onChange)
			UI_ExecuteEventActions(ammoNode, ammoNode->onChange);
	}
}
Пример #9
0
/**
 * @brief Search a child container node by the given container id
 * @note Only search with one depth
 */
uiNode_t *UI_GetContainerNodeByContainerIDX (const uiNode_t* const parent, const int index)
{
	const invDef_t* const container = INVDEF(index);
	uiNode_t *containerNode = UI_GetNode(parent, container->name);
	return containerNode;
}