/** * @brief Clone a node * @param[in] node Node to clone * @param[in] recursive True if we also must clone subnodes * @param[in] newWindow Window where the nodes must be add (this function only link node into window, not window into the new node) * @param[in] newName New node name, else nullptr to use the source name * @param[in] isDynamic Allocate a node in static or dynamic memory * @todo exclude rect is not safe cloned. * @todo actions are not cloned. It is be a problem if we use add/remove listener into a cloned node. */ uiNode_t* UI_CloneNode (const uiNode_t* node, uiNode_t* newWindow, bool recursive, const char* newName, bool isDynamic) { uiNode_t* newNode = UI_AllocNodeWithoutNew(nullptr, UI_Node_GetWidgetName(node), isDynamic); /* clone all data */ memcpy(newNode, node, UI_Node_GetMemorySize(node)); newNode->dynamic = isDynamic; /* custom name */ if (newName != nullptr) { Q_strncpyz(newNode->name, newName, sizeof(newNode->name)); if (strlen(newNode->name) != strlen(newName)) Com_Printf("UI_CloneNode: Node name \"%s\" truncated. New name is \"%s\"\n", newName, newNode->name); } /* clean up node navigation */ if (node->root == node && newWindow == nullptr) newWindow = newNode; newNode->root = newWindow; newNode->parent = nullptr; newNode->firstChild = nullptr; newNode->lastChild = nullptr; newNode->next = nullptr; newNode->super = node; /* clone node methods */ if (node->nodeMethods) { newNode->nodeMethods = HASH_CloneTable(node->nodeMethods); } /* allocate memories */ if (newNode->dynamic) { UI_Node_InitNodeDynamic(newNode); } /* typically, cloning makes a copy of all the internals of a source node; override the ::clone(node) method to alter this behaviour */ UI_Node_Clone(node, newNode); /* clone child */ if (recursive) { for (uiNode_t* childNode = node->firstChild; childNode; childNode = childNode->next) { uiNode_t* newChildNode = UI_CloneNode(childNode, newWindow, recursive, nullptr, isDynamic); UI_AppendNode(newNode, newChildNode); } } return newNode; }
/** * @brief Display in the conde the tree of nodes */ static void UI_DebugTree (const uiNode_t *node, int depth) { const uiNode_t *child = node->firstChild; int i; for (i = 0; i < depth; i++) { Com_Printf(" "); } Com_Printf("+ %s %s\n", UI_Node_GetWidgetName(node), node->name); while (child) { UI_DebugTree(child, depth + 1); child = child->next; } }
/** * @brief Clone a node * @param[in] node Node to clone * @param[in] recursive True if we also must clone subnodes * @param[in] newWindow Window where the nodes must be add (this function only link node into window, not window into the new node) * @param[in] newName New node name, else nullptr to use the source name * @param[in] isDynamic Allocate a node in static or dynamic memory * @todo exclude rect is not safe cloned. * @todo actions are not cloned. It is be a problem if we use add/remove listener into a cloned node. */ uiNode_t* UI_CloneNode (const uiNode_t* node, uiNode_t *newWindow, bool recursive, const char *newName, bool isDynamic) { uiNode_t* newNode = UI_AllocNodeWithoutNew(nullptr, UI_Node_GetWidgetName(node), isDynamic); /* clone all data */ memcpy(newNode, node, UI_Node_GetMemorySize(node)); newNode->dynamic = isDynamic; /* custom name */ if (newName != nullptr) { Q_strncpyz(newNode->name, newName, sizeof(newNode->name)); if (strlen(newNode->name) != strlen(newName)) Com_Printf("UI_CloneNode: Node name \"%s\" truncated. New name is \"%s\"\n", newName, newNode->name); } /* clean up node navigation */ if (node->root == node && newWindow == nullptr) newWindow = newNode; newNode->root = newWindow; newNode->parent = nullptr; newNode->firstChild = nullptr; newNode->lastChild = nullptr; newNode->next = nullptr; newNode->super = node; /* clone child */ if (recursive) { uiNode_t* childNode; for (childNode = node->firstChild; childNode; childNode = childNode->next) { uiNode_t* newChildNode = UI_CloneNode(childNode, newWindow, recursive, nullptr, isDynamic); UI_AppendNode(newNode, newChildNode); } } /* allocate memories */ if (newNode->dynamic) UI_Node_NewNode(newNode); UI_Node_Clone(node, newNode); return newNode; }
/** * @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 ""; }