ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

ThemeParser.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "gui/ThemeEngine.h"
00024 #include "gui/ThemeEval.h"
00025 #include "gui/ThemeParser.h"
00026 
00027 #include "graphics/VectorRenderer.h"
00028 
00029 #include "common/system.h"
00030 #include "common/tokenizer.h"
00031 
00032 namespace GUI {
00033 
00034 struct TextDataInfo {
00035     TextData id;
00036     const char *name;
00037 };
00038 
00039 static const TextDataInfo kTextDataDefaults[] = {
00040     { kTextDataDefault,         "text_default" },
00041     { kTextDataButton,          "text_button" },
00042     { kTextDataNormalFont,      "text_normal" },
00043     { kTextDataTooltip,         "tooltip_normal" },
00044     { kTextDataConsole,         "console" }
00045 };
00046 
00047 
00048 static TextData parseTextDataId(const Common::String &name) {
00049     for (int i = 0; i < kTextDataMAX; ++i)
00050         if (name.compareToIgnoreCase(kTextDataDefaults[i].name) == 0)
00051             return kTextDataDefaults[i].id;
00052 
00053     return kTextDataNone;
00054 }
00055 
00056 struct TextColorDataInfo {
00057     TextColor id;
00058     const char *name;
00059 };
00060 
00061 static const TextColorDataInfo kTextColorDefaults[] = {
00062     { kTextColorNormal,                 "color_normal" },
00063     { kTextColorNormalInverted,         "color_normal_inverted" },
00064     { kTextColorNormalHover,            "color_normal_hover" },
00065     { kTextColorNormalDisabled,         "color_normal_disabled" },
00066     { kTextColorAlternative,            "color_alternative" },
00067     { kTextColorAlternativeInverted,    "color_alternative_inverted" },
00068     { kTextColorAlternativeHover,       "color_alternative_hover" },
00069     { kTextColorAlternativeDisabled,    "color_alternative_disabled" },
00070     { kTextColorButton,                 "color_button" },
00071     { kTextColorButtonHover,            "color_button_hover" },
00072     { kTextColorButtonDisabled,         "color_button_disabled" }
00073 };
00074 
00075 static TextColor parseTextColorId(const Common::String &name) {
00076     for (int i = 0; i < kTextColorMAX; ++i)
00077         if (name.compareToIgnoreCase(kTextColorDefaults[i].name) == 0)
00078             return kTextColorDefaults[i].id;
00079 
00080     return kTextColorMAX;
00081 }
00082 
00083 static Graphics::TextAlign parseTextHAlign(const Common::String &val) {
00084     if (val == "left")
00085         return Graphics::kTextAlignLeft;
00086     else if (val == "right")
00087         return Graphics::kTextAlignRight;
00088     else if (val == "center")
00089         return Graphics::kTextAlignCenter;
00090     else
00091         return Graphics::kTextAlignInvalid;
00092 }
00093 
00094 static GUI::ThemeEngine::TextAlignVertical parseTextVAlign(const Common::String &val) {
00095     if (val == "top")
00096         return GUI::ThemeEngine::kTextAlignVTop;
00097     else if (val == "center")
00098         return GUI::ThemeEngine::kTextAlignVCenter;
00099     else if (val == "bottom")
00100         return GUI::ThemeEngine::kTextAlignVBottom;
00101     else
00102         return GUI::ThemeEngine::kTextAlignVInvalid;
00103 }
00104 
00105 
00106 ThemeParser::ThemeParser(ThemeEngine *parent) : XMLParser() {
00107     _defaultStepGlobal = defaultDrawStep();
00108     _defaultStepLocal = nullptr;
00109     _theme = parent;
00110 }
00111 
00112 ThemeParser::~ThemeParser() {
00113     delete _defaultStepGlobal;
00114     delete _defaultStepLocal;
00115 }
00116 
00117 void ThemeParser::cleanup() {
00118     delete _defaultStepGlobal;
00119     delete _defaultStepLocal;
00120 
00121     _defaultStepGlobal = defaultDrawStep();
00122     _defaultStepLocal = nullptr;
00123     _palette.clear();
00124 }
00125 
00126 Graphics::DrawStep *ThemeParser::defaultDrawStep() {
00127     Graphics::DrawStep *step = new Graphics::DrawStep;
00128 
00129     step->xAlign = Graphics::DrawStep::kVectorAlignManual;
00130     step->yAlign = Graphics::DrawStep::kVectorAlignManual;
00131     step->factor = 1;
00132     step->autoWidth = true;
00133     step->autoHeight = true;
00134     step->fillMode = Graphics::VectorRenderer::kFillDisabled;
00135     step->scale = (1 << 16);
00136     step->radius = 0xFF;
00137 
00138     return step;
00139 }
00140 
00141 Graphics::DrawStep *ThemeParser::newDrawStep() {
00142     assert(_defaultStepGlobal);
00143     Graphics::DrawStep *step = nullptr; //new DrawStep;
00144 
00145     if (_defaultStepLocal) {
00146         step = new Graphics::DrawStep(*_defaultStepLocal);
00147     } else {
00148         step = new Graphics::DrawStep(*_defaultStepGlobal);
00149     }
00150 
00151     return step;
00152 }
00153 
00154 bool ThemeParser::parserCallback_defaults(ParserNode *node) {
00155     ParserNode *parentNode = getParentNode(node);
00156     Graphics::DrawStep *step = nullptr;
00157 
00158     if (parentNode->name == "render_info") {
00159         step = _defaultStepGlobal;
00160     } else if (parentNode->name == "drawdata") {
00161         if (_defaultStepLocal == nullptr)
00162             _defaultStepLocal = new Graphics::DrawStep(*_defaultStepGlobal);
00163 
00164         step = _defaultStepLocal;
00165     } else {
00166         return parserError("<default> key out of scope. Must be inside <drawdata> or <render_info> keys.");
00167     }
00168 
00169     return parseDrawStep(node, step, false);
00170 }
00171 
00172 bool ThemeParser::parserCallback_font(ParserNode *node) {
00173     if (resolutionCheck(node->values["resolution"]) == false) {
00174         node->ignore = true;
00175         return true;
00176     }
00177 
00178     // Default to a point size of 12.
00179     int pointsize = 12;
00180     if (node->values.contains("point_size")) {
00181         if (sscanf(node->values["point_size"].c_str(), "%d", &pointsize) != 1 || pointsize <= 0)
00182             return parserError(Common::String::format("Font \"%s\" has invalid point size \"%s\"", node->values["id"].c_str(), node->values["point_size"].c_str()));
00183     }
00184 
00185     TextData textDataId = parseTextDataId(node->values["id"]);
00186     if (!_theme->addFont(textDataId, node->values["file"], node->values["scalable_file"], pointsize))
00187         return parserError("Error loading Font in theme engine.");
00188 
00189     return true;
00190 }
00191 
00192 bool ThemeParser::parserCallback_text_color(ParserNode *node) {
00193     int red, green, blue;
00194 
00195     TextColor colorId = parseTextColorId(node->values["id"]);
00196     if (colorId == kTextColorMAX)
00197         return parserError("Error text color is not defined.");
00198 
00199     if (_palette.contains(node->values["color"]))
00200         getPaletteColor(node->values["color"], red, green, blue);
00201     else if (!parseIntegerKey(node->values["color"], 3, &red, &green, &blue))
00202         return parserError("Error parsing color value for text color definition.");
00203 
00204     if (!_theme->addTextColor(colorId, red, green, blue))
00205         return parserError("Error while adding text color information.");
00206 
00207     return true;
00208 }
00209 
00210 bool ThemeParser::parserCallback_fonts(ParserNode *node) {
00211     return true;
00212 }
00213 
00214 bool ThemeParser::parserCallback_cursor(ParserNode *node) {
00215     if (resolutionCheck(node->values["resolution"]) == false) {
00216         node->ignore = true;
00217         return true;
00218     }
00219 
00220     int spotx, spoty;
00221 
00222     if (!parseIntegerKey(node->values["hotspot"], 2, &spotx, &spoty))
00223         return parserError("Error parsing cursor Hot Spot coordinates.");
00224 
00225     if (!_theme->createCursor(node->values["file"], spotx, spoty))
00226         return parserError("Error creating Bitmap Cursor.");
00227 
00228     return true;
00229 }
00230 
00231 bool ThemeParser::parserCallback_bitmap(ParserNode *node) {
00232     if (resolutionCheck(node->values["resolution"]) == false) {
00233         node->ignore = true;
00234         return true;
00235     }
00236 
00237     if (!_theme->addBitmap(node->values["filename"]))
00238         return parserError("Error loading Bitmap file '" + node->values["filename"] + "'");
00239 
00240     return true;
00241 }
00242 
00243 bool ThemeParser::parserCallback_alphabitmap(ParserNode *node) {
00244     if (resolutionCheck(node->values["resolution"]) == false) {
00245         node->ignore = true;
00246         return true;
00247     }
00248 
00249     if (!_theme->addAlphaBitmap(node->values["filename"]))
00250         return parserError("Error loading Bitmap file '" + node->values["filename"] + "'");
00251 
00252     return true;
00253 }
00254 
00255 bool ThemeParser::parserCallback_text(ParserNode *node) {
00256     Graphics::TextAlign alignH;
00257     GUI::ThemeEngine::TextAlignVertical alignV;
00258 
00259     if ((alignH = parseTextHAlign(node->values["horizontal_align"])) == Graphics::kTextAlignInvalid)
00260         return parserError("Invalid value for text alignment.");
00261 
00262     if ((alignV = parseTextVAlign(node->values["vertical_align"])) == GUI::ThemeEngine::kTextAlignVInvalid)
00263         return parserError("Invalid value for text alignment.");
00264 
00265     Common::String id = getParentNode(node)->values["id"];
00266     TextData textDataId = parseTextDataId(node->values["font"]);
00267     TextColor textColorId = parseTextColorId(node->values["text_color"]);
00268 
00269     if (!_theme->addTextData(id, textDataId, textColorId, alignH, alignV))
00270         return parserError("Error adding Text Data for '" + id + "'.");
00271 
00272     return true;
00273 }
00274 
00275 bool ThemeParser::parserCallback_render_info(ParserNode *node) {
00276     if (resolutionCheck(node->values["resolution"]) == false)
00277         node->ignore = true;
00278 
00279     return true;
00280 }
00281 
00282 bool ThemeParser::parserCallback_layout_info(ParserNode *node) {
00283     if (resolutionCheck(node->values["resolution"]) == false)
00284         node->ignore = true;
00285 
00286     return true;
00287 }
00288 
00289 bool ThemeParser::parserCallback_palette(ParserNode *node) {
00290     return true;
00291 }
00292 
00293 bool ThemeParser::parserCallback_color(ParserNode *node) {
00294     Common::String name = node->values["name"];
00295 
00296     if (_palette.contains(name))
00297         return parserError("Color '" + name + "' has already been defined.");
00298 
00299     int red, green, blue;
00300 
00301     if (parseIntegerKey(node->values["rgb"], 3, &red, &green, &blue) == false ||
00302         red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255)
00303         return parserError("Error parsing RGB values for palette color '" + name + "'");
00304 
00305     _palette[name].r = red;
00306     _palette[name].g = green;
00307     _palette[name].b = blue;
00308 
00309     return true;
00310 }
00311 
00312 
00313 static Graphics::DrawingFunctionCallback getDrawingFunctionCallback(const Common::String &name) {
00314 
00315     if (name == "circle")
00316         return &Graphics::VectorRenderer::drawCallback_CIRCLE;
00317     if (name == "square")
00318         return &Graphics::VectorRenderer::drawCallback_SQUARE;
00319     if (name == "roundedsq")
00320         return &Graphics::VectorRenderer::drawCallback_ROUNDSQ;
00321     if (name == "bevelsq")
00322         return &Graphics::VectorRenderer::drawCallback_BEVELSQ;
00323     if (name == "line")
00324         return &Graphics::VectorRenderer::drawCallback_LINE;
00325     if (name == "triangle")
00326         return &Graphics::VectorRenderer::drawCallback_TRIANGLE;
00327     if (name == "fill")
00328         return &Graphics::VectorRenderer::drawCallback_FILLSURFACE;
00329     if (name == "tab")
00330         return &Graphics::VectorRenderer::drawCallback_TAB;
00331     if (name == "void")
00332         return &Graphics::VectorRenderer::drawCallback_VOID;
00333     if (name == "bitmap")
00334         return &Graphics::VectorRenderer::drawCallback_BITMAP;
00335     if (name == "cross")
00336         return &Graphics::VectorRenderer::drawCallback_CROSS;
00337     if (name == "alphabitmap")
00338         return &Graphics::VectorRenderer::drawCallback_ALPHABITMAP;
00339 
00340     return nullptr;
00341 }
00342 
00343 
00344 bool ThemeParser::parserCallback_drawstep(ParserNode *node) {
00345     Graphics::DrawStep *drawstep = newDrawStep();
00346 
00347     Common::String functionName = node->values["func"];
00348 
00349     drawstep->drawingCall = getDrawingFunctionCallback(functionName);
00350 
00351     if (drawstep->drawingCall == nullptr) {
00352         delete drawstep;
00353         return parserError(functionName + " is not a valid drawing function name");
00354     }
00355 
00356     if (!parseDrawStep(node, drawstep, true)) {
00357         delete drawstep;
00358         return false;
00359     }
00360 
00361     _theme->addDrawStep(getParentNode(node)->values["id"], *drawstep);
00362     delete drawstep;
00363 
00364     return true;
00365 }
00366 
00367 bool ThemeParser::parserCallback_drawdata(ParserNode *node) {
00368     bool cached = false;
00369 
00370     if (resolutionCheck(node->values["resolution"]) == false) {
00371         node->ignore = true;
00372         return true;
00373     }
00374 
00375     if (node->values.contains("cache")) {
00376         if (!Common::parseBool(node->values["cache"], cached))
00377             return parserError("'Parsed' value must be either true or false.");
00378     }
00379 
00380     if (_theme->addDrawData(node->values["id"], cached) == false)
00381         return parserError("Error adding Draw Data set: Invalid DrawData name.");
00382 
00383     delete _defaultStepLocal;
00384     _defaultStepLocal = nullptr;
00385 
00386     return true;
00387 }
00388 
00389 bool ThemeParser::parseDrawStep(ParserNode *stepNode, Graphics::DrawStep *drawstep, bool functionSpecific) {
00390     int red, green, blue, x;
00391     Common::String val;
00392 
00403 #define PARSER_ASSIGN_INT(struct_name, key_name, force) \
00404     if (stepNode->values.contains(key_name)) { \
00405         if (!parseIntegerKey(stepNode->values[key_name], 1, &x)) \
00406             return parserError("Error parsing key value for '" + Common::String(key_name) + "'."); \
00407         \
00408         drawstep->struct_name = x; \
00409     } else if (force) { \
00410         return parserError("Missing necessary key '" + Common::String(key_name) + "'."); \
00411     }
00412 
00422 #define PARSER_ASSIGN_RGB(struct_name, key_name) \
00423     if (stepNode->values.contains(key_name)) { \
00424         val = stepNode->values[key_name]; \
00425         if (_palette.contains(val)) { \
00426             red = _palette[val].r; \
00427             green = _palette[val].g; \
00428             blue = _palette[val].b; \
00429         } else if (parseIntegerKey(val, 3, &red, &green, &blue) == false || \
00430             red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) \
00431             return parserError("Error parsing color struct '" + val + "'");\
00432         \
00433         drawstep->struct_name.r = red; \
00434         drawstep->struct_name.g = green; \
00435         drawstep->struct_name.b = blue; \
00436         drawstep->struct_name.set = true; \
00437     }
00438 
00439     PARSER_ASSIGN_INT(stroke, "stroke", false);
00440     PARSER_ASSIGN_INT(bevel, "bevel", false);
00441     PARSER_ASSIGN_INT(shadow, "shadow", false);
00442     PARSER_ASSIGN_INT(factor, "gradient_factor", false);
00443 
00444     PARSER_ASSIGN_RGB(fgColor, "fg_color");
00445     PARSER_ASSIGN_RGB(bgColor, "bg_color");
00446     PARSER_ASSIGN_RGB(gradColor1, "gradient_start");
00447     PARSER_ASSIGN_RGB(gradColor2, "gradient_end");
00448     PARSER_ASSIGN_RGB(bevelColor, "bevel_color");
00449 
00450     if (functionSpecific) {
00451         assert(stepNode->values.contains("func"));
00452         Common::String functionName = stepNode->values["func"];
00453 
00454         if (functionName == "bitmap") {
00455             if (!stepNode->values.contains("file"))
00456                 return parserError("Need to specify a filename for Bitmap blitting.");
00457 
00458             drawstep->blitSrc = _theme->getBitmap(stepNode->values["file"]);
00459 
00460             if (!drawstep->blitSrc)
00461                 return parserError("The given filename hasn't been loaded into the GUI.");
00462         }
00463 
00464         if (functionName == "alphabitmap") {
00465             if (!stepNode->values.contains("file"))
00466                 return parserError("Need to specify a filename for AlphaBitmap blitting.");
00467 
00468             drawstep->blitAlphaSrc = _theme->getAlphaBitmap(stepNode->values["file"]);
00469 
00470             if (!drawstep->blitAlphaSrc)
00471                 return parserError("The given filename hasn't been loaded into the GUI.");
00472 
00473             if (stepNode->values.contains("autoscale")) {
00474                 if (stepNode->values["autoscale"] == "true" || stepNode->values["autoscale"] == "stretch") {
00475                     drawstep->autoscale = ThemeEngine::kAutoScaleStretch;
00476                 } else if (stepNode->values["autoscale"] == "fit") {
00477                     drawstep->autoscale = ThemeEngine::kAutoScaleFit;
00478                 } else if (stepNode->values["autoscale"] == "9patch") {
00479                     drawstep->autoscale = ThemeEngine::kAutoScaleNinePatch;
00480                 } else {
00481                     drawstep->autoscale = ThemeEngine::kAutoScaleNone;
00482                 }
00483             }
00484 
00485             if (stepNode->values.contains("xpos")) {
00486                 val = stepNode->values["xpos"];
00487 
00488                 if (parseIntegerKey(val, 1, &x))
00489                     drawstep->x = x;
00490                 else if (val == "center")
00491                     drawstep->xAlign = Graphics::DrawStep::kVectorAlignCenter;
00492                 else if (val == "left")
00493                     drawstep->xAlign = Graphics::DrawStep::kVectorAlignLeft;
00494                 else if (val == "right")
00495                     drawstep->xAlign = Graphics::DrawStep::kVectorAlignRight;
00496                 else
00497                     return parserError("Invalid value for X Position");
00498             }
00499 
00500             if (stepNode->values.contains("ypos")) {
00501                 val = stepNode->values["ypos"];
00502 
00503                 if (parseIntegerKey(val, 1, &x))
00504                     drawstep->y = x;
00505                 else if (val == "center")
00506                     drawstep->yAlign = Graphics::DrawStep::kVectorAlignCenter;
00507                 else if (val == "top")
00508                     drawstep->yAlign = Graphics::DrawStep::kVectorAlignTop;
00509                 else if (val == "bottom")
00510                     drawstep->yAlign = Graphics::DrawStep::kVectorAlignBottom;
00511                 else
00512                     return parserError("Invalid value for Y Position");
00513             }
00514         }
00515 
00516         if (functionName == "roundedsq" || functionName == "circle" || functionName == "tab") {
00517             if (stepNode->values.contains("radius") && stepNode->values["radius"] == "auto") {
00518                 drawstep->radius = 0xFF;
00519             } else {
00520                 PARSER_ASSIGN_INT(radius, "radius", true);
00521             }
00522         }
00523 
00524         if (functionName == "triangle") {
00525             drawstep->extraData = Graphics::VectorRenderer::kTriangleUp;
00526 
00527             if (stepNode->values.contains("orientation")) {
00528                 val = stepNode->values["orientation"];
00529 
00530                 if (val == "top")
00531                     drawstep->extraData = Graphics::VectorRenderer::kTriangleUp;
00532                 else if (val == "bottom")
00533                     drawstep->extraData = Graphics::VectorRenderer::kTriangleDown;
00534                 else if (val == "left")
00535                     drawstep->extraData = Graphics::VectorRenderer::kTriangleLeft;
00536                 else if (val == "right")
00537                     drawstep->extraData = Graphics::VectorRenderer::kTriangleRight;
00538                 else
00539                     return parserError("'" + val + "' is not a valid value for triangle orientation.");
00540             }
00541         }
00542 
00543         if (stepNode->values.contains("size")) {
00544             warning("The <size> keyword has been deprecated. Use <width> and <height> instead");
00545         }
00546 
00547         if (stepNode->values.contains("width") && stepNode->values["width"] != "auto") {
00548             drawstep->autoWidth = false;
00549 
00550             val = stepNode->values["width"];
00551             if (parseIntegerKey(val, 1, &x))
00552                 drawstep->w = x;
00553             else if (val == "height")
00554                 drawstep->w = -1;
00555             else return parserError("Invalid value for vector width.");
00556 
00557             if (stepNode->values.contains("xpos")) {
00558                 val = stepNode->values["xpos"];
00559 
00560                 if (parseIntegerKey(val, 1, &x))
00561                     drawstep->x = x;
00562                 else if (val == "center")
00563                     drawstep->xAlign = Graphics::DrawStep::kVectorAlignCenter;
00564                 else if (val == "left")
00565                     drawstep->xAlign = Graphics::DrawStep::kVectorAlignLeft;
00566                 else if (val == "right")
00567                     drawstep->xAlign = Graphics::DrawStep::kVectorAlignRight;
00568                 else
00569                     return parserError("Invalid value for X Position");
00570             } else {
00571                 return parserError("When width is not set to 'auto', a <xpos> tag must be included.");
00572             }
00573         }
00574 
00575         if (stepNode->values.contains("height") && stepNode->values["height"] != "auto") {
00576             drawstep->autoHeight = false;
00577 
00578             val = stepNode->values["height"];
00579             if (parseIntegerKey(val, 1, &x))
00580                 drawstep->h = x;
00581             else if (val == "width")
00582                 drawstep->h = -1;
00583             else return parserError("Invalid value for vector height.");
00584 
00585             if (stepNode->values.contains("ypos")) {
00586                 val = stepNode->values["ypos"];
00587 
00588                 if (parseIntegerKey(val, 1, &x))
00589                     drawstep->y = x;
00590                 else if (val == "center")
00591                     drawstep->yAlign = Graphics::DrawStep::kVectorAlignCenter;
00592                 else if (val == "top")
00593                     drawstep->yAlign = Graphics::DrawStep::kVectorAlignTop;
00594                 else if (val == "bottom")
00595                     drawstep->yAlign = Graphics::DrawStep::kVectorAlignBottom;
00596                 else
00597                     return parserError("Invalid value for Y Position");
00598             } else {
00599                 return parserError("When height is not set to 'auto', a <ypos> tag must be included.");
00600             }
00601         }
00602 
00603         if (drawstep->h == -1 && drawstep->w == -1)
00604             return parserError("Cross-reference in Vector Size: Height is set to width and width is set to height.");
00605     }
00606 
00607     if (stepNode->values.contains("fill")) {
00608         val = stepNode->values["fill"];
00609         if (val == "none")
00610             drawstep->fillMode = Graphics::VectorRenderer::kFillDisabled;
00611         else if (val == "foreground")
00612             drawstep->fillMode = Graphics::VectorRenderer::kFillForeground;
00613         else if (val == "background")
00614             drawstep->fillMode = Graphics::VectorRenderer::kFillBackground;
00615         else if (val == "gradient")
00616             drawstep->fillMode = Graphics::VectorRenderer::kFillGradient;
00617         else
00618             return parserError("'" + stepNode->values["fill"] + "' is not a valid fill mode for a shape.");
00619     }
00620 
00621     if (stepNode->values.contains("padding")) {
00622         val = stepNode->values["padding"];
00623         int pr, pt, pl, pb;
00624         if (parseIntegerKey(val, 4, &pl, &pt, &pr, &pb)) {
00625             drawstep->padding.left = pl;
00626             drawstep->padding.top = pt;
00627             drawstep->padding.right = pr;
00628             drawstep->padding.bottom = pb;
00629         }
00630     }
00631 
00632     if (stepNode->values.contains("clip")) {
00633         val = stepNode->values["clip"];
00634         int cl, ct, cr, cb;
00635         if (parseIntegerKey(val, 4, &cl, &ct, &cr, &cb)) {
00636             drawstep->clip.left = cl;
00637             drawstep->clip.top = ct;
00638             drawstep->clip.right = cr;
00639             drawstep->clip.bottom = cb;
00640         }
00641     }
00642 
00643 #undef PARSER_ASSIGN_INT
00644 #undef PARSER_ASSIGN_RGB
00645 
00646     return true;
00647 }
00648 
00649 bool ThemeParser::parserCallback_def(ParserNode *node) {
00650     if (resolutionCheck(node->values["resolution"]) == false) {
00651         node->ignore = true;
00652         return true;
00653     }
00654 
00655     Common::String var = "Globals." + node->values["var"];
00656     int value;
00657 
00658     if (_theme->getEvaluator()->hasVar(node->values["value"]) == true)
00659         value = _theme->getEvaluator()->getVar(node->values["value"]);
00660 
00661     else if (!parseIntegerKey(node->values["value"], 1, &value))
00662         return parserError("Invalid definition for '" + var + "'.");
00663 
00664     _theme->getEvaluator()->setVar(var, value);
00665     return true;
00666 }
00667 
00668 bool ThemeParser::parserCallback_widget(ParserNode *node) {
00669     Common::String var;
00670 
00671     if (getParentNode(node)->name == "globals") {
00672 
00673         if (resolutionCheck(node->values["resolution"]) == false) {
00674             node->ignore = true;
00675             return true;
00676         }
00677 
00678         var = "Globals." + node->values["name"] + ".";
00679         if (!parseCommonLayoutProps(node, var))
00680             return parserError("Error parsing Layout properties of '" + var + "'.");
00681 
00682     } else {
00683         // FIXME: Shouldn't we distinguish the name/id and the label of a widget?
00684         var = node->values["name"];
00685         int width = -1;
00686         int height = -1;
00687 
00688         if (node->values.contains("width")) {
00689             if (_theme->getEvaluator()->hasVar(node->values["width"]) == true)
00690                 width = _theme->getEvaluator()->getVar(node->values["width"]);
00691 
00692             else if (!parseIntegerKey(node->values["width"], 1, &width))
00693                 return parserError("Corrupted width value in key for " + var);
00694         }
00695 
00696         if (node->values.contains("height")) {
00697             if (_theme->getEvaluator()->hasVar(node->values["height"]) == true)
00698                 height = _theme->getEvaluator()->getVar(node->values["height"]);
00699 
00700             else if (!parseIntegerKey(node->values["height"], 1, &height))
00701                 return parserError("Corrupted height value in key for " + var);
00702         }
00703 
00704         Graphics::TextAlign alignH = Graphics::kTextAlignLeft;
00705 
00706         if (node->values.contains("textalign")) {
00707             if ((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid)
00708                 return parserError("Invalid value for text alignment.");
00709         }
00710 
00711         _theme->getEvaluator()->addWidget(var, node->values["type"], width, height, alignH);
00712     }
00713 
00714     return true;
00715 }
00716 
00717 bool ThemeParser::parserCallback_dialog(ParserNode *node) {
00718     Common::String name = node->values["name"];
00719     int inset = 0;
00720 
00721     if (resolutionCheck(node->values["resolution"]) == false) {
00722         node->ignore = true;
00723         return true;
00724     }
00725 
00726     if (node->values.contains("inset")) {
00727         if (!parseIntegerKey(node->values["inset"], 1, &inset))
00728             return false;
00729     }
00730 
00731     Common::String overlays = node->values["overlays"];
00732     // The size of a dialog depends on the value of its 'overlays' property:
00733     //  - 'screen', means the dialog fills the whole screen.
00734     //  - 'screen_center', means the size of the dialog is determined
00735     //      by its contents, unless an explicit size is specified.
00736     //  - if it is set to a user interface element name, the dialog takes
00737     //      its size.
00738 
00739     int width = -1, height = -1;
00740     if (node->values.contains("size")) {
00741         if (overlays != "screen_center")
00742             return parserError("Dialogs can only have an explicit size if they overlay 'screen_center'.");
00743 
00744         if (!parseIntegerKey(node->values["size"], 2, &width, &height))
00745             return false;
00746     }
00747 
00748     _theme->getEvaluator()->addDialog(name, overlays, width, height, inset);
00749 
00750     if (node->values.contains("shading")) {
00751         int shading = 0;
00752         if (node->values["shading"] == "dim")
00753             shading = 1;
00754         else if (node->values["shading"] == "luminance")
00755             shading = 2;
00756         else return parserError("Invalid value for Dialog background shading.");
00757 
00758         _theme->getEvaluator()->setVar("Dialog." + name + ".Shading", shading);
00759     }
00760 
00761     return true;
00762 }
00763 
00764 bool ThemeParser::parserCallback_import(ParserNode *node) {
00765     Common::String importedName = node->values["layout"];
00766 
00767     if (!_theme->getEvaluator()->hasDialog(importedName))
00768         return parserError("Imported layout was not found: " + importedName);
00769 
00770     _theme->getEvaluator()->addImportedLayout(importedName);
00771 
00772     return true;
00773 }
00774 
00775 bool ThemeParser::parserCallback_layout(ParserNode *node) {
00776     int spacing = -1;
00777 
00778     if (node->values.contains("spacing")) {
00779         if (!parseIntegerKey(node->values["spacing"], 1, &spacing))
00780             return false;
00781     }
00782 
00783     ThemeLayout::ItemAlign itemAlign = ThemeLayout::kItemAlignStart;
00784 
00785     if (node->values.contains("align")) {
00786         Common::String val = node->values["align"];
00787         if (val == "start") {
00788             itemAlign = ThemeLayout::kItemAlignStart;
00789         } else if (val == "center") {
00790             itemAlign = ThemeLayout::kItemAlignCenter;
00791         } else if (val == "end") {
00792             itemAlign = ThemeLayout::kItemAlignEnd;
00793         } else if (val == "stretch") {
00794             itemAlign = ThemeLayout::kItemAlignStretch;
00795         } else {
00796             return parserError("'" + val + "' is not a valid item alignment for a layout.");
00797         }
00798     }
00799 
00800     if (node->values["type"] == "vertical")
00801         _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutVertical, spacing, itemAlign);
00802     else if (node->values["type"] == "horizontal")
00803         _theme->getEvaluator()->addLayout(GUI::ThemeLayout::kLayoutHorizontal, spacing, itemAlign);
00804     else
00805         return parserError("Invalid layout type. Only 'horizontal' and 'vertical' layouts allowed.");
00806 
00807     if (node->values.contains("padding")) {
00808         int paddingL, paddingR, paddingT, paddingB;
00809 
00810         if (!parseIntegerKey(node->values["padding"], 4, &paddingL, &paddingR, &paddingT, &paddingB))
00811             return false;
00812 
00813         _theme->getEvaluator()->addPadding(paddingL, paddingR, paddingT, paddingB);
00814     }
00815 
00816     return true;
00817 }
00818 
00819 bool ThemeParser::parserCallback_space(ParserNode *node) {
00820     int size = -1;
00821 
00822     if (node->values.contains("size")) {
00823         if (_theme->getEvaluator()->hasVar(node->values["size"]))
00824             size = _theme->getEvaluator()->getVar(node->values["size"]);
00825 
00826         else if (!parseIntegerKey(node->values["size"], 1, &size))
00827             return parserError("Invalid value for Spacing size.");
00828     }
00829 
00830     _theme->getEvaluator()->addSpace(size);
00831     return true;
00832 }
00833 
00834 bool ThemeParser::closedKeyCallback(ParserNode *node) {
00835     if (node->name == "layout")
00836         _theme->getEvaluator()->closeLayout();
00837     else if (node->name == "dialog")
00838         _theme->getEvaluator()->closeDialog();
00839 
00840     return true;
00841 }
00842 
00843 bool ThemeParser::parseCommonLayoutProps(ParserNode *node, const Common::String &var) {
00844     if (node->values.contains("size")) {
00845         int width, height;
00846 
00847         if (!parseIntegerKey(node->values["size"], 2, &width, &height)) {
00848             Common::StringTokenizer tokenizer(node->values["size"], " ,");
00849             Common::String wtoken, htoken;
00850             char *parseEnd;
00851 
00852             wtoken = tokenizer.nextToken();
00853 
00854             if (_theme->getEvaluator()->hasVar(wtoken)) {
00855                 width = _theme->getEvaluator()->getVar(wtoken);
00856             } else {
00857                 width = strtol(wtoken.c_str(), &parseEnd, 10);
00858 
00859                 if (*parseEnd != 0 && !(*parseEnd == '%' && *(parseEnd + 1) == 0))
00860                     return false;
00861 
00862                 if (wtoken.lastChar() == '%')
00863                     width = g_system->getOverlayWidth() * width / 100;
00864             }
00865 
00866             htoken = tokenizer.nextToken();
00867 
00868             if (_theme->getEvaluator()->hasVar(htoken)) {
00869                 height = _theme->getEvaluator()->getVar(htoken);
00870             } else {
00871                 height = strtol(htoken.c_str(), &parseEnd, 10);
00872 
00873                 if (*parseEnd != 0 && !(*parseEnd == '%' && *(parseEnd + 1) == 0))
00874                     return false;
00875 
00876                 if (htoken.lastChar() == '%')
00877                     height = g_system->getOverlayHeight() * height / 100;
00878             }
00879 
00880             if (!tokenizer.empty())
00881                 return false;
00882         }
00883 
00884 
00885         _theme->getEvaluator()->setVar(var + "Width", width);
00886         _theme->getEvaluator()->setVar(var + "Height", height);
00887     }
00888 
00889     if (node->values.contains("pos")) {
00890         int x, y;
00891 
00892         if (!parseIntegerKey(node->values["pos"], 2, &x, &y)) {
00893             Common::StringTokenizer tokenizer(node->values["pos"], " ,");
00894             Common::String xpos, ypos;
00895             char *parseEnd;
00896 
00897             xpos = tokenizer.nextToken();
00898 
00899             if (xpos == "center") {
00900                 if (!_theme->getEvaluator()->hasVar(var + "Width"))
00901                     return false;
00902 
00903                 x = (g_system->getOverlayWidth() / 2) - (_theme->getEvaluator()->getVar(var + "Width") / 2);
00904 
00905             } else if (_theme->getEvaluator()->hasVar(xpos)) {
00906                 x = _theme->getEvaluator()->getVar(xpos);
00907             } else {
00908                 x = strtol(xpos.c_str(), &parseEnd, 10);
00909 
00910                 if (*parseEnd != 0 && !(*parseEnd == 'r' && *(parseEnd + 1) == 0))
00911                     return false;
00912 
00913                 if (xpos.lastChar() == 'r')
00914                     x = g_system->getOverlayWidth() - x;
00915             }
00916 
00917             ypos = tokenizer.nextToken();
00918 
00919             if (ypos == "center") {
00920                 if (!_theme->getEvaluator()->hasVar(var + "Height"))
00921                     return false;
00922 
00923                 y = (g_system->getOverlayHeight() / 2) - (_theme->getEvaluator()->getVar(var + "Height") / 2);
00924 
00925             } else if (_theme->getEvaluator()->hasVar(ypos)) {
00926                 y = _theme->getEvaluator()->getVar(ypos);
00927             } else {
00928                 y = strtol(ypos.c_str(), &parseEnd, 10);
00929 
00930                 if (*parseEnd != 0 && !(*parseEnd == 'b' && *(parseEnd + 1) == 0))
00931                     return false;
00932 
00933                 if (ypos.lastChar() == 'b')
00934                     y = g_system->getOverlayHeight() - y;
00935             }
00936 
00937             if (!tokenizer.empty())
00938                 return false;
00939         }
00940 
00941         _theme->getEvaluator()->setVar(var + "X", x);
00942         _theme->getEvaluator()->setVar(var + "Y", y);
00943     }
00944 
00945     if (node->values.contains("padding")) {
00946         int paddingL, paddingR, paddingT, paddingB;
00947 
00948         if (!parseIntegerKey(node->values["padding"], 4, &paddingL, &paddingR, &paddingT, &paddingB))
00949             return false;
00950 
00951         _theme->getEvaluator()->setVar(var + "Padding.Left", paddingL);
00952         _theme->getEvaluator()->setVar(var + "Padding.Right", paddingR);
00953         _theme->getEvaluator()->setVar(var + "Padding.Top", paddingT);
00954         _theme->getEvaluator()->setVar(var + "Padding.Bottom", paddingB);
00955     }
00956 
00957 
00958     if (node->values.contains("textalign")) {
00959         Graphics::TextAlign alignH = Graphics::kTextAlignLeft;
00960 
00961         if ((alignH = parseTextHAlign(node->values["textalign"])) == Graphics::kTextAlignInvalid)
00962             return parserError("Invalid value for text alignment.");
00963 
00964         _theme->getEvaluator()->setVar(var + "Align", alignH);
00965     }
00966     return true;
00967 }
00968 
00969 bool ThemeParser::resolutionCheck(const Common::String &resolution) {
00970     if (resolution.empty())
00971         return true;
00972 
00973     Common::StringTokenizer globTokenizer(resolution, ", ");
00974     Common::String cur;
00975 
00976     while (!globTokenizer.empty()) {
00977         cur = globTokenizer.nextToken();
00978 
00979         bool lt;
00980         int val;
00981 
00982         if (cur.size() < 5) {
00983             warning("Invalid theme 'resolution' token '%s'", resolution.c_str());
00984             return false;
00985         }
00986 
00987         if (cur[0] == 'x') {
00988             val = g_system->getOverlayWidth();
00989         } else if (cur[0] == 'y') {
00990             val = g_system->getOverlayHeight();
00991         } else {
00992             warning("Error parsing theme 'resolution' token '%s'", resolution.c_str());
00993             return false;
00994         }
00995 
00996         if (cur[1] == '<') {
00997             lt = true;
00998         } else if (cur[1] == '>') {
00999             lt = false;
01000         } else {
01001             warning("Error parsing theme 'resolution' token '%s'", resolution.c_str());
01002             return false;
01003         }
01004 
01005         int token = atoi(cur.c_str() + 2);
01006 
01007         // check inverse for unfulfilled requirements
01008         if (lt) {
01009             if (val >= token)
01010                 return false;
01011         } else {
01012             if (val <= token)
01013                 return false;
01014         }
01015     }
01016 
01017     return true;
01018 }
01019 
01020 } // End of namespace GUI


Generated on Sat May 23 2020 05:00:44 for ResidualVM by doxygen 1.7.1
curved edge   curved edge