Move actors to the first frame on entering a room.
[scummvm-innocent.git] / gui / ThemeParser.cpp
blobf154ada67111c0111af82a6e5c5458cb5826260c
1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $URL$
22 * $Id$
26 #include "gui/ThemeEngine.h"
27 #include "gui/ThemeEval.h"
28 #include "gui/ThemeParser.h"
29 #include "gui/GuiManager.h"
31 #include "graphics/VectorRenderer.h"
33 namespace GUI {
35 struct TextDataInfo {
36 TextData id;
37 const char *name;
40 static const TextDataInfo kTextDataDefaults[] = {
41 {kTextDataDefault, "text_default"},
42 {kTextDataHover, "text_hover"},
43 {kTextDataDisabled, "text_disabled"},
44 {kTextDataInverted, "text_inverted"},
45 {kTextDataButton, "text_button"},
46 {kTextDataButtonHover, "text_button_hover"},
47 {kTextDataNormalFont, "text_normal"}
51 static TextData parseTextDataId(const Common::String &name) {
52 for (int i = 0; i < kTextDataMAX; ++i)
53 if (name.compareToIgnoreCase(kTextDataDefaults[i].name) == 0)
54 return kTextDataDefaults[i].id;
56 return kTextDataNone;
59 static Graphics::TextAlign parseTextHAlign(const Common::String &val) {
60 if (val == "left")
61 return Graphics::kTextAlignLeft;
62 else if (val == "right")
63 return Graphics::kTextAlignRight;
64 else if (val == "center")
65 return Graphics::kTextAlignCenter;
66 else
67 return Graphics::kTextAlignInvalid;
70 static GUI::ThemeEngine::TextAlignVertical parseTextVAlign(const Common::String &val) {
71 if (val == "top")
72 return GUI::ThemeEngine::kTextAlignVTop;
73 else if (val == "center")
74 return GUI::ThemeEngine::kTextAlignVCenter;
75 else if (val == "bottom")
76 return GUI::ThemeEngine::kTextAlignVBottom;
77 else
78 return GUI::ThemeEngine::kTextAlignVInvalid;
82 ThemeParser::ThemeParser(ThemeEngine *parent) : XMLParser() {
83 _defaultStepGlobal = defaultDrawStep();
84 _defaultStepLocal = 0;
85 _theme = parent;
88 ThemeParser::~ThemeParser() {
89 delete _defaultStepGlobal;
90 delete _defaultStepLocal;
93 void ThemeParser::cleanup() {
94 delete _defaultStepGlobal;
95 delete _defaultStepLocal;
97 _defaultStepGlobal = defaultDrawStep();
98 _defaultStepLocal = 0;
99 _palette.clear();
102 Graphics::DrawStep *ThemeParser::defaultDrawStep() {
103 Graphics::DrawStep *step = new Graphics::DrawStep;
105 memset(step, 0, sizeof(Graphics::DrawStep));
107 step->xAlign = Graphics::DrawStep::kVectorAlignManual;
108 step->yAlign = Graphics::DrawStep::kVectorAlignManual;
109 step->factor = 1;
110 step->autoWidth = true;
111 step->autoHeight = true;
112 step->fillMode = Graphics::VectorRenderer::kFillDisabled;
113 step->scale = (1 << 16);
114 step->radius = 0xFF;
116 return step;
119 Graphics::DrawStep *ThemeParser::newDrawStep() {
120 assert(_defaultStepGlobal);
121 Graphics::DrawStep *step = 0 ; //new DrawStep;
123 if (_defaultStepLocal) {
124 step = new Graphics::DrawStep(*_defaultStepLocal);
125 } else {
126 step = new Graphics::DrawStep(*_defaultStepGlobal);
129 return step;
132 bool ThemeParser::parserCallback_defaults(ParserNode *node) {
133 ParserNode *parentNode = getParentNode(node);
134 Graphics::DrawStep *step = 0;
136 if (parentNode->name == "render_info") {
137 step = _defaultStepGlobal;
138 } else if (parentNode->name == "drawdata") {
139 if (_defaultStepLocal == 0)
140 _defaultStepLocal = new Graphics::DrawStep(*_defaultStepGlobal);
142 step = _defaultStepLocal;
143 } else {
144 return parserError("<default> key out of scope. Must be inside <drawdata> or <render_info> keys.");
147 return parseDrawStep(node, step, false);
150 bool ThemeParser::parserCallback_font(ParserNode *node) {
151 int red, green, blue;
153 if (resolutionCheck(node->values["resolution"]) == false) {
154 node->ignore = true;
155 return true;
158 if (_palette.contains(node->values["color"]))
159 getPaletteColor(node->values["color"], red, green, blue);
160 else if (!parseIntegerKey(node->values["color"].c_str(), 3, &red, &green, &blue))
161 return parserError("Error parsing color value for font definition.");
163 TextData textDataId = parseTextDataId(node->values["id"]);
164 if (!_theme->addFont(textDataId, node->values["file"], red, green, blue))
165 return parserError("Error loading Font in theme engine.");
167 return true;
170 bool ThemeParser::parserCallback_fonts(ParserNode *node) {
171 return true;
174 bool ThemeParser::parserCallback_cursor(ParserNode *node) {
175 if (resolutionCheck(node->values["resolution"]) == false) {
176 node->ignore = true;
177 return true;
180 int spotx, spoty, scale;
182 if (!parseIntegerKey(node->values["hotspot"].c_str(), 2, &spotx, &spoty))
183 return parserError("Error parsing cursor Hot Spot coordinates.");
185 if (!parseIntegerKey(node->values["scale"].c_str(), 1, &scale))
186 return parserError("Error parsing cursor scale.");
188 if (!_theme->createCursor(node->values["file"], spotx, spoty, scale))
189 return parserError("Error creating Bitmap Cursor.");
191 return true;
194 bool ThemeParser::parserCallback_bitmap(ParserNode *node) {
195 if (resolutionCheck(node->values["resolution"]) == false) {
196 node->ignore = true;
197 return true;
200 if (!_theme->addBitmap(node->values["filename"]))
201 return parserError("Error loading Bitmap file '%s'", node->values["filename"].c_str());
203 return true;
206 bool ThemeParser::parserCallback_text(ParserNode *node) {
207 Graphics::TextAlign alignH;
208 GUI::ThemeEngine::TextAlignVertical alignV;
210 if ((alignH = parseTextHAlign(node->values["horizontal_align"])) == Graphics::kTextAlignInvalid)
211 return parserError("Invalid value for text alignment.");
213 if ((alignV = parseTextVAlign(node->values["vertical_align"])) == GUI::ThemeEngine::kTextAlignVInvalid)
214 return parserError("Invalid value for text alignment.");
216 Common::String id = getParentNode(node)->values["id"];
217 TextData textDataId = parseTextDataId(node->values["font"]);
219 if (!_theme->addTextData(id, textDataId, alignH, alignV))
220 return parserError("Error adding Text Data for '%s'.", id.c_str());
222 return true;
225 bool ThemeParser::parserCallback_render_info(ParserNode *node) {
226 if (resolutionCheck(node->values["resolution"]) == false)
227 node->ignore = true;
229 return true;
232 bool ThemeParser::parserCallback_layout_info(ParserNode *node) {
233 if (resolutionCheck(node->values["resolution"]) == false)
234 node->ignore = true;
236 return true;
239 bool ThemeParser::parserCallback_palette(ParserNode *node) {
240 return true;
243 bool ThemeParser::parserCallback_color(ParserNode *node) {
244 Common::String name = node->values["name"];
246 if (_palette.contains(name))
247 return parserError("Color '%s' has already been defined.", name.c_str());
249 int red, green, blue;
251 if (parseIntegerKey(node->values["rgb"].c_str(), 3, &red, &green, &blue) == false ||
252 red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255)
253 return parserError("Error parsing RGB values for palette color '%s'", name.c_str());\
255 _palette[name].r = red;
256 _palette[name].g = green;
257 _palette[name].b = blue;
259 return true;
263 static Graphics::DrawingFunctionCallback getDrawingFunctionCallback(const Common::String &name) {
265 if (name == "circle")
266 return &Graphics::VectorRenderer::drawCallback_CIRCLE;
267 if (name == "square")
268 return &Graphics::VectorRenderer::drawCallback_SQUARE;
269 if (name == "roundedsq")
270 return &Graphics::VectorRenderer::drawCallback_ROUNDSQ;
271 if (name == "bevelsq")
272 return &Graphics::VectorRenderer::drawCallback_BEVELSQ;
273 if (name == "line")
274 return &Graphics::VectorRenderer::drawCallback_LINE;
275 if (name == "triangle")
276 return &Graphics::VectorRenderer::drawCallback_TRIANGLE;
277 if (name == "fill")
278 return &Graphics::VectorRenderer::drawCallback_FILLSURFACE;
279 if (name == "tab")
280 return &Graphics::VectorRenderer::drawCallback_TAB;
281 if (name == "void")
282 return &Graphics::VectorRenderer::drawCallback_VOID;
283 if (name == "bitmap")
284 return &Graphics::VectorRenderer::drawCallback_BITMAP;
285 if (name == "cross")
286 return &Graphics::VectorRenderer::drawCallback_CROSS;
288 return 0;
292 bool ThemeParser::parserCallback_drawstep(ParserNode *node) {
293 Graphics::DrawStep *drawstep = newDrawStep();
295 Common::String functionName = node->values["func"];
297 drawstep->drawingCall = getDrawingFunctionCallback(functionName);
299 if (drawstep->drawingCall == 0)
300 return parserError("%s is not a valid drawing function name", functionName.c_str());
302 if (!parseDrawStep(node, drawstep, true))
303 return false;
305 _theme->addDrawStep(getParentNode(node)->values["id"], *drawstep);
306 delete drawstep;
308 return true;
311 bool ThemeParser::parserCallback_drawdata(ParserNode *node) {
312 bool cached = false;
314 if (resolutionCheck(node->values["resolution"]) == false) {
315 node->ignore = true;
316 return true;
319 if (node->values.contains("cache")) {
320 if (node->values["cache"] == "true")
321 cached = true;
322 else if (node->values["cache"] == "false")
323 cached = false;
324 else return parserError("'Parsed' value must be either true or false.");
327 if (_theme->addDrawData(node->values["id"], cached) == false)
328 return parserError("Error adding Draw Data set: Invalid DrawData name.");
330 if (_defaultStepLocal) {
331 delete _defaultStepLocal;
332 _defaultStepLocal = 0;
335 return true;
338 bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawstep, bool functionSpecific) {
339 int red, green, blue, x;
340 Common::String val;
343 * @def __PARSER_ASSIGN_INT(struct_name, key_name, force)
344 * Helper macro to sanitize and assign an integer value from a key
345 * to the draw step.
347 * @param struct_name Name of the field of a DrawStep struct that must be
348 * assigned.
349 * @param key_name Name as STRING of the key identifier as it appears in the
350 * theme description format.
351 * @param force Sets if the key is optional or necessary.
353 #define __PARSER_ASSIGN_INT(struct_name, key_name, force) \
354 if (stepNode->values.contains(key_name)) { \
355 if (!parseIntegerKey(stepNode->values[key_name].c_str(), 1, &x)) \
356 return parserError("Error parsing key value for '%s'.", key_name); \
358 drawstep->struct_name = x; \
359 } else if (force) { \
360 return parserError("Missing necessary key '%s'.", key_name); \
364 * Helper macro to sanitize and assign a RGB value from a key to the draw
365 * step. RGB values have the following syntax: "R, G, B".
367 * @param struct_name Name of the field of a DrawStep struct that must be
368 * assigned.
369 * @param key_name Name as STRING of the key identifier as it appears in the
370 * theme description format.
372 #define __PARSER_ASSIGN_RGB(struct_name, key_name) \
373 if (stepNode->values.contains(key_name)) { \
374 val = stepNode->values[key_name]; \
375 if (_palette.contains(val)) { \
376 red = _palette[val].r; \
377 green = _palette[val].g; \
378 blue = _palette[val].b; \
379 } else if (parseIntegerKey(val.c_str(), 3, &red, &green, &blue) == false || \
380 red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) \
381 return parserError("Error parsing color struct '%s'", val.c_str());\
383 drawstep->struct_name.r = red; \
384 drawstep->struct_name.g = green; \
385 drawstep->struct_name.b = blue; \
386 drawstep->struct_name.set = true; \
389 __PARSER_ASSIGN_INT(stroke, "stroke", false);
390 __PARSER_ASSIGN_INT(bevel, "bevel", false);
391 __PARSER_ASSIGN_INT(shadow, "shadow", false);
392 __PARSER_ASSIGN_INT(factor, "gradient_factor", false);
394 __PARSER_ASSIGN_RGB(fgColor, "fg_color");
395 __PARSER_ASSIGN_RGB(bgColor, "bg_color");
396 __PARSER_ASSIGN_RGB(gradColor1, "gradient_start");
397 __PARSER_ASSIGN_RGB(gradColor2, "gradient_end");
398 __PARSER_ASSIGN_RGB(bevelColor, "bevel_color");
400 if (functionSpecific) {
401 assert(stepNode->values.contains("func"));
402 Common::String functionName = stepNode->values["func"];
404 if (functionName == "bitmap") {
405 if (!stepNode->values.contains("file"))
406 return parserError("Need to specify a filename for Bitmap blitting.");
408 drawstep->blitSrc = _theme->getBitmap(stepNode->values["file"]);
410 if (!drawstep->blitSrc)
411 return parserError("The given filename hasn't been loaded into the GUI.");
414 if (functionName == "roundedsq" || functionName == "circle" || functionName == "tab") {
415 if (stepNode->values.contains("radius") && stepNode->values["radius"] == "auto") {
416 drawstep->radius = 0xFF;
417 } else {
418 __PARSER_ASSIGN_INT(radius, "radius", true);
422 if (functionName == "triangle") {
423 drawstep->extraData = Graphics::VectorRenderer::kTriangleUp;
425 if (stepNode->values.contains("orientation")) {
426 val = stepNode->values["orientation"];
428 if ( val == "top")
429 drawstep->extraData = Graphics::VectorRenderer::kTriangleUp;
430 else if (val == "bottom")
431 drawstep->extraData = Graphics::VectorRenderer::kTriangleDown;
432 else if (val == "left")
433 drawstep->extraData = Graphics::VectorRenderer::kTriangleLeft;
434 else if (val == "right")
435 drawstep->extraData = Graphics::VectorRenderer::kTriangleRight;
436 else
437 return parserError("'%s' is not a valid value for triangle orientation.", val.c_str());
441 if (stepNode->values.contains("size")) {
442 warning("The <size> keyword has been deprecated. Use <width> and <height> instead");
445 if (stepNode->values.contains("width") && stepNode->values["width"] != "auto") {
446 drawstep->autoWidth = false;
448 val = stepNode->values["width"];
449 if (parseIntegerKey(val.c_str(), 1, &x))
450 drawstep->w = x;
451 else if (val == "height")
452 drawstep->w = -1;
453 else return parserError("Invalid value for vector width.");
455 if (stepNode->values.contains("xpos")) {
456 val = stepNode->values["xpos"];
458 if (parseIntegerKey(val.c_str(), 1, &x))
459 drawstep->x = x;
460 else if (val == "center")
461 drawstep->xAlign = Graphics::DrawStep::kVectorAlignCenter;
462 else if (val == "left")
463 drawstep->xAlign = Graphics::DrawStep::kVectorAlignLeft;
464 else if (val == "right")
465 drawstep->xAlign = Graphics::DrawStep::kVectorAlignRight;
466 else
467 return parserError("Invalid value for X Position");
468 } else {
469 return parserError("When width is not set to 'auto', a <xpos> tag must be included.");
473 if (stepNode->values.contains("height") && stepNode->values["height"] != "auto") {
474 drawstep->autoHeight = false;
476 val = stepNode->values["height"];
477 if (parseIntegerKey(val.c_str(), 1, &x))
478 drawstep->h = x;
479 else if (val == "width")
480 drawstep->h = -1;
481 else return parserError("Invalid value for vector height.");
483 if (stepNode->values.contains("ypos")) {
484 val = stepNode->values["ypos"];
486 if (parseIntegerKey(val.c_str(), 1, &x))
487 drawstep->y = x;
488 else if (val == "center")
489 drawstep->yAlign = Graphics::DrawStep::kVectorAlignCenter;
490 else if (val == "top")
491 drawstep->yAlign = Graphics::DrawStep::kVectorAlignTop;
492 else if (val == "bottom")
493 drawstep->yAlign = Graphics::DrawStep::kVectorAlignBottom;
494 else
495 return parserError("Invalid value for Y Position");
496 } else {
497 return parserError("When height is not set to 'auto', a <ypos> tag must be included.");
501 if (drawstep->h == -1 && drawstep->w == -1)
502 return parserError("Cross-reference in Vector Size: Height is set to width and width is set to height.");
505 if (stepNode->values.contains("fill")) {
506 val = stepNode->values["fill"];
507 if (val == "none")
508 drawstep->fillMode = Graphics::VectorRenderer::kFillDisabled;
509 else if (val == "foreground")
510 drawstep->fillMode = Graphics::VectorRenderer::kFillForeground;
511 else if (val == "background")
512 drawstep->fillMode = Graphics::VectorRenderer::kFillBackground;
513 else if (val == "gradient")
514 drawstep->fillMode = Graphics::VectorRenderer::kFillGradient;
515 else
516 return parserError("'%s' is not a valid fill mode for a shape.", stepNode->values["fill"].c_str());
519 #undef __PARSER_ASSIGN_INT
520 #undef __PARSER_ASSIGN_RGB
522 return true;
525 bool ThemeParser::parserCallback_def(ParserNode *node) {
526 if (resolutionCheck(node->values["resolution"]) == false) {
527 node->ignore = true;
528 return true;
531 Common::String var = "Globals." + node->values["var"];
532 int value;
534 if (_theme->getEvaluator()->hasVar(node->values["value"]) == true)
535 value = _theme->getEvaluator()->getVar(node->values["value"]);
537 else if (!parseIntegerKey(node->values["value"].c_str(), 1, &value))
538 return parserError("Invalid definition for '%s'.", var.c_str());
540 _theme->getEvaluator()->setVar(var, value);
541 return true;
544 bool ThemeParser::parserCallback_widget(ParserNode *node) {
545 Common::String var;
547 if (getParentNode(node)->name == "globals") {
549 if (resolutionCheck(node->values["resolution"]) == false) {
550 node->ignore = true;
551 return true;
554 var = "Globals." + node->values["name"] + ".";
555 if (!parseCommonLayoutProps(node, var))
556 return parserError("Error parsing Layout properties of '%s'.", var.c_str());
558 } else {
559 // FIXME: Shouldn't we distinguish the name/id and the label of a widget?
560 var = node->values["name"];
561 int width = -1;
562 int height = -1;
563 bool enabled = true;
565 if (node->values.contains("enabled")) {
566 if (node->values["enabled"] == "false")
567 enabled = false;
568 else if (node->values["enabled"] != "true")
569 return parserError("Invalid value for Widget enabling (expecting true/false)");
572 if (node->values.contains("width")) {
573 if (_theme->getEvaluator()->hasVar(node->values["width"]) == true)
574 width = _theme->getEvaluator()->getVar(node->values["width"]);
576 else if (!parseIntegerKey(node->values["width"].c_str(), 1, &width))
577 return parserError("Corrupted width value in key for %s", var.c_str());
580 if (node->values.contains("height")) {
581 if (_theme->getEvaluator()->hasVar(node->values["height"]) == true)
582 height = _theme->getEvaluator()->getVar(node->values["height"]);
584 else if (!parseIntegerKey(node->values["height"].c_str(), 1, &height))
585 return parserError("Corrupted height value in key for %s", var.c_str());
588 Graphics::TextAlign alignH = Graphics::kTextAlignLeft;
590 if (node->values.contains("textalign")) {
591 if((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid)
592 return parserError("Invalid value for text alignment.");
595 _theme->getEvaluator()->addWidget(var, width, height, node->values["type"], enabled, alignH);
598 return true;
601 bool ThemeParser::parserCallback_dialog(ParserNode *node) {
602 Common::String var = "Dialog." + node->values["name"];
603 bool enabled = true;
604 int inset = 0;
606 if (resolutionCheck(node->values["resolution"]) == false) {
607 node->ignore = true;
608 return true;
611 if (node->values.contains("enabled")) {
612 if (node->values["enabled"] == "false")
613 enabled = false;
614 else if (node->values["enabled"] != "true")
615 return parserError("Invalid value for Dialog enabling (expecting true/false)");
618 if (node->values.contains("inset")) {
619 if (!parseIntegerKey(node->values["inset"].c_str(), 1, &inset))
620 return false;
623 _theme->getEvaluator()->addDialog(var, node->values["overlays"], enabled, inset);
625 if (node->values.contains("shading")) {
626 int shading = 0;
627 if (node->values["shading"] == "dim")
628 shading = 1;
629 else if (node->values["shading"] == "luminance")
630 shading = 2;
631 else return parserError("Invalid value for Dialog background shading.");
633 _theme->getEvaluator()->setVar(var + ".Shading", shading);
636 return true;
639 bool ThemeParser::parserCallback_import(ParserNode *node) {
641 if (!_theme->getEvaluator()->addImportedLayout(node->values["layout"]))
642 return parserError("Error importing external layout");
643 return true;
646 bool ThemeParser::parserCallback_layout(ParserNode *node) {
647 int spacing = -1;
649 if (node->values.contains("spacing")) {
650 if (!parseIntegerKey(node->values["spacing"].c_str(), 1, &spacing))
651 return false;
654 if (node->values["type"] == "vertical")
655 _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutVertical, spacing, node->values["center"] == "true");
656 else if (node->values["type"] == "horizontal")
657 _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutHorizontal, spacing, node->values["center"] == "true");
658 else
659 return parserError("Invalid layout type. Only 'horizontal' and 'vertical' layouts allowed.");
662 if (node->values.contains("padding")) {
663 int paddingL, paddingR, paddingT, paddingB;
665 if (!parseIntegerKey(node->values["padding"].c_str(), 4, &paddingL, &paddingR, &paddingT, &paddingB))
666 return false;
668 _theme->getEvaluator()->addPadding(paddingL, paddingR, paddingT, paddingB);
671 return true;
674 bool ThemeParser::parserCallback_space(ParserNode *node) {
675 int size = -1;
677 if (node->values.contains("size")) {
678 if (_theme->getEvaluator()->hasVar(node->values["size"]))
679 size = _theme->getEvaluator()->getVar(node->values["size"]);
681 else if (!parseIntegerKey(node->values["size"].c_str(), 1, &size))
682 return parserError("Invalid value for Spacing size.");
685 _theme->getEvaluator()->addSpace(size);
686 return true;
689 bool ThemeParser::closedKeyCallback(ParserNode *node) {
690 if (node->name == "layout")
691 _theme->getEvaluator()->closeLayout();
692 else if (node->name == "dialog")
693 _theme->getEvaluator()->closeDialog();
695 return true;
698 bool ThemeParser::parseCommonLayoutProps(ParserNode *node, const Common::String &var) {
699 if (node->values.contains("size")) {
700 int width, height;
702 if (!parseIntegerKey(node->values["size"].c_str(), 2, &width, &height)) {
703 Common::StringTokenizer tokenizer(node->values["size"], " ,");
704 Common::String wtoken, htoken;
705 char *parseEnd;
707 wtoken = tokenizer.nextToken();
709 if (_theme->getEvaluator()->hasVar(wtoken)) {
710 width = _theme->getEvaluator()->getVar(wtoken);
711 } else {
712 width = strtol(wtoken.c_str(), &parseEnd, 10);
714 if (*parseEnd != 0 && !(*parseEnd == '%' && *(parseEnd + 1) == 0))
715 return false;
717 if (wtoken.lastChar() == '%')
718 width = g_system->getOverlayWidth() * width / 100;
721 htoken = tokenizer.nextToken();
723 if (_theme->getEvaluator()->hasVar(htoken)) {
724 height = _theme->getEvaluator()->getVar(htoken);
725 } else {
726 height = strtol(htoken.c_str(), &parseEnd, 10);
728 if (*parseEnd != 0 && !(*parseEnd == '%' && *(parseEnd + 1) == 0))
729 return false;
731 if (htoken.lastChar() == '%')
732 height = g_system->getOverlayHeight() * height / 100;
735 if (!tokenizer.empty())
736 return false;
740 _theme->getEvaluator()->setVar(var + "Width", width);
741 _theme->getEvaluator()->setVar(var + "Height", height);
744 if (node->values.contains("pos")) {
745 int x, y;
747 if (!parseIntegerKey(node->values["pos"].c_str(), 2, &x, &y)) {
748 Common::StringTokenizer tokenizer(node->values["pos"], " ,");
749 Common::String xpos, ypos;
750 char *parseEnd;
752 xpos = tokenizer.nextToken();
754 if (xpos == "center") {
755 if (!_theme->getEvaluator()->hasVar(var + "Width"))
756 return false;
758 x = (g_system->getOverlayWidth() / 2) - (_theme->getEvaluator()->getVar(var + "Width") / 2);
760 } else if (_theme->getEvaluator()->hasVar(xpos)) {
761 x = _theme->getEvaluator()->getVar(xpos);
762 } else {
763 x = strtol(xpos.c_str(), &parseEnd, 10);
765 if (*parseEnd != 0 && !(*parseEnd == 'r' && *(parseEnd + 1) == 0))
766 return false;
768 if (xpos.lastChar() == 'r')
769 x = g_system->getOverlayWidth() - x;
772 ypos = tokenizer.nextToken();
774 if (ypos == "center") {
775 if (!_theme->getEvaluator()->hasVar(var + "Height"))
776 return false;
778 y = (g_system->getOverlayHeight() / 2) - (_theme->getEvaluator()->getVar(var + "Height") / 2);
780 } else if (_theme->getEvaluator()->hasVar(ypos)) {
781 y = _theme->getEvaluator()->getVar(ypos);
782 } else {
783 y = strtol(ypos.c_str(), &parseEnd, 10);
785 if (*parseEnd != 0 && !(*parseEnd == 'b' && *(parseEnd + 1) == 0))
786 return false;
788 if (ypos.lastChar() == 'b')
789 y = g_system->getOverlayHeight() - y;
792 if (!tokenizer.empty())
793 return false;
796 _theme->getEvaluator()->setVar(var + "X", x);
797 _theme->getEvaluator()->setVar(var + "Y", y);
800 if (node->values.contains("padding")) {
801 int paddingL, paddingR, paddingT, paddingB;
803 if (!parseIntegerKey(node->values["padding"].c_str(), 4, &paddingL, &paddingR, &paddingT, &paddingB))
804 return false;
806 _theme->getEvaluator()->setVar(var + "Padding.Left", paddingL);
807 _theme->getEvaluator()->setVar(var + "Padding.Right", paddingR);
808 _theme->getEvaluator()->setVar(var + "Padding.Top", paddingT);
809 _theme->getEvaluator()->setVar(var + "Padding.Bottom", paddingB);
813 if (node->values.contains("textalign")) {
814 Graphics::TextAlign alignH = Graphics::kTextAlignLeft;
816 if((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid)
817 return parserError("Invalid value for text alignment.");
819 _theme->getEvaluator()->setVar(var + "Align", alignH);
821 return true;
824 bool ThemeParser::resolutionCheck(const Common::String &resolution) {
825 if (resolution.empty())
826 return true;
828 Common::StringTokenizer globTokenizer(resolution, ", ");
829 Common::String cur, w, h;
830 bool definedRes = false;
832 while (!globTokenizer.empty()) {
833 bool ignore = false;
834 cur = globTokenizer.nextToken();
836 if (cur[0] == '-') {
837 ignore = true;
838 cur.deleteChar(0);
839 } else {
840 definedRes = true;
843 Common::StringTokenizer resTokenizer(cur, "x");
844 w = resTokenizer.nextToken();
845 h = resTokenizer.nextToken();
847 if ((w == "X" || atoi(w.c_str()) == g_system->getOverlayWidth()) &&
848 (h == "Y" || atoi(h.c_str()) == g_system->getOverlayHeight()))
849 return !ignore;
852 return !definedRes;
855 } // End of namespace GUI