/** * @brief Search a file name inside pics/ according to the sprite name * If it exists, generate a "single" sprite using the size of the image * @param name Name of the sprite * @return A sprite, else nullptr */ static uiSprite_t* UI_AutoGenerateSprite (const char* name) { uiSprite_t* sprite = nullptr; const char* suffix[SPRITE_STATUS_MAX] = {"", "_hovered", "_disabled", "_clicked"}; char basePicNameBuf[MAX_QPATH]; const image_t* pic; int i; Q_strncpyz(basePicNameBuf, name, sizeof(basePicNameBuf)); pic = UI_LoadImage(basePicNameBuf); if (pic == nullptr) return nullptr; sprite = UI_AllocStaticSprite(basePicNameBuf); sprite->image[SPRITE_STATUS_NORMAL] = UI_AllocStaticString(basePicNameBuf, 0); sprite->size[0] = pic->width; sprite->size[1] = pic->height; for (i = 1; i < SPRITE_STATUS_MAX; i++) { char picNameBuf[MAX_QPATH]; Com_sprintf(picNameBuf, sizeof(picNameBuf), "%s%s", basePicNameBuf, suffix[i]); pic = UI_LoadImage(picNameBuf); if (pic != nullptr) sprite->image[i] = UI_AllocStaticString(picNameBuf, 0); } return sprite; }
/** * 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; } }
/** * @brief Search a file name inside pics/ according to the sprite name * If it exists, generate a "single" sprite using the size of the image * @param name Name of the sprite * @return A sprite, else NULL */ static uiSprite_t* UI_AutoGenerateSprite (const char* name) { uiSprite_t* sprite = NULL; const char* suffix[SPRITE_STATUS_MAX] = {"", "_hovered", "_disabled", "_clicked"}; int i; const char *picName = name; const image_t *pic = UI_LoadImage(picName); if (pic == NULL) return NULL; sprite = UI_AllocStaticSprite(name); sprite->image[SPRITE_STATUS_NORMAL] = UI_AllocStaticString(picName, 0); sprite->size[0] = pic->width; sprite->size[1] = pic->height; for (i = 1; i < SPRITE_STATUS_MAX; i++) { picName = va("%s%s", name, suffix[i]); pic = UI_LoadImage(picName); if (pic != NULL) sprite->image[i] = UI_AllocStaticString(picName, 0); } return sprite; }
static void UI_ModelNodeLoaded (uiNode_t *node) { /* a tag without but not a submodel */ if (EXTRADATA(node).tag != NULL && node->behaviour != node->parent->behaviour) { Com_Printf("UI_ModelNodeLoaded: '%s' use a tag but is not a submodel. Tag removed.\n", UI_GetPath(node)); EXTRADATA(node).tag = NULL; } if (EXTRADATA(node).oldRefValue == NULL) EXTRADATA(node).oldRefValue = UI_AllocStaticString("", MAX_OLDREFVALUE); /* no tag but no size */ if (EXTRADATA(node).tag == NULL && (node->size[0] == 0 || node->size[1] == 0)) { Com_Printf("UI_ModelNodeLoaded: Please set a pos and size to the node '%s'. Note: 'origin' is a relative value to the center of the node\n", UI_GetPath(node)); } }
/** * @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; }
/** * @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; }
/** * @brief Read a value from the stream and init an action with it * @return An initialized action else nullptr */ static uiAction_t *UI_ParseValueExpression (const char **text) { const char* token; uiAction_t *expression = UI_AllocStaticAction(); token = Com_Parse(text); if (*text == nullptr) { Com_Printf("UI_ParseTerminalExpression: Token expected\n"); return nullptr; } /* it is a const string (or an injection tag for compatibility) */ if (Com_GetType(text) == TT_QUOTED_WORD || token[0] == '<') { expression->d.terminal.d1.constString = UI_AllocStaticString(token, 0); if (UI_IsInjectedString(token)) expression->type = EA_VALUE_STRING_WITHINJECTION; else expression->type = EA_VALUE_STRING; return expression; } /* it is a param */ if (!Q_strncasecmp(token, "param", 5)) { if (!Q_strcasecmp(token, "paramcount")) { expression->type = EA_VALUE_PARAMCOUNT; return expression; } else if (token[5] >= '1' && token[5] <= '9') { int i; if (sscanf(token + 5, "%i", &i) == 1) { /* token range 1-9 already avoid 0 */ assert(i != 0); /** @todo when it is possible, we must check range of param id */ expression->type = EA_VALUE_PARAM; expression->d.terminal.d1.integer = i; return expression; } } } /* it is a cvarname */ if (char const* const cvarName = Q_strstart(token, "*cvar:")) { expression->d.terminal.d1.constString = UI_AllocStaticString(cvarName, 0); if (UI_IsInjectedString(cvarName)) expression->type = EA_VALUE_CVARNAME_WITHINJECTION; else expression->type = EA_VALUE_CVARNAME; return expression; } /* it is a node property or it is a OLD syntax node property */ /** @todo We MUST remove the OLD code as fast as possible */ if (char const* path = Q_strstart(token, "*")) { const char *propertyName; #if 0 /* it looks useless, an unused cache */ const value_t *property; #endif char const* const relativeToNode = Q_strstart(path, "node:"); if (relativeToNode) path = relativeToNode; if (UI_IsInjectedString(path)) expression->type = EA_VALUE_PATHPROPERTY_WITHINJECTION; else expression->type = EA_VALUE_PATHPROPERTY; if (!relativeToNode) { Com_Printf("UI_ParseExpression: Old syntax, please prefix '%s' with \"*node:root.\" \n", token); path = va("root.%s", path); } expression->d.terminal.d1.constString = UI_AllocStaticString(path, 0); /* get property name */ propertyName = strchr(path, '@'); if (propertyName == nullptr) { if (expression->type == EA_VALUE_PATHPROPERTY_WITHINJECTION) expression->type = EA_VALUE_PATHNODE_WITHINJECTION; else expression->type = EA_VALUE_PATHNODE; return expression; } propertyName++; return expression; } /* unsigned and signed number */ if ((token[0] >= '0' && token[0] <= '9') || (token[0] == '-' && token[1] >= '0' && token[1] <= '9')) { /** @todo use a better check - e.g. Com_ParseValue with V_INT or V_FLOAT */ float f = atof(token); expression->d.terminal.d1.number = f; expression->type = EA_VALUE_FLOAT; return expression; } /* boolean */ if (Q_streq(token, "true")) { expression->d.terminal.d1.number = 1.0; expression->type = EA_VALUE_FLOAT; return expression; } if (Q_streq(token, "false")) { expression->d.terminal.d1.number = 0.0; expression->type = EA_VALUE_FLOAT; return expression; } Com_Error(ERR_FATAL, "UI_ParseValueExpression: Token \"%s\" unknown. String must use quotes, cvar and nodes must use prefix.\n", token); }
/** * @brief Call to update a cloned node */ void uiModelNode::clone (const uiNode_t* source, uiNode_t* clone) { uiLocatedNode::clone(source, clone); if (!clone->dynamic) EXTRADATA(clone).oldRefValue = UI_AllocStaticString("", MAX_OLDREFVALUE); }
/** * @brief Call to update a cloned node */ static void UI_ModelNodeClone (const uiNode_t *source, uiNode_t *clone) { localBehaviour->super->clone(source, clone); if (!clone->dynamic) EXTRADATA(clone).oldRefValue = UI_AllocStaticString("", MAX_OLDREFVALUE); }