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

virtual-keyboard-parser.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 "common/scummsys.h"
00024 
00025 #ifdef ENABLE_VKEYBD
00026 
00027 #include "backends/vkeybd/virtual-keyboard-parser.h"
00028 #include "backends/vkeybd/polygon.h"
00029 
00030 #include "common/keyboard.h"
00031 #include "common/util.h"
00032 #include "common/system.h"
00033 #include "common/archive.h"
00034 #include "common/tokenizer.h"
00035 #include "common/stream.h"
00036 
00037 #include "image/bmp.h"
00038 
00039 namespace Common {
00040 
00041 VirtualKeyboardParser::VirtualKeyboardParser(VirtualKeyboard *kbd)
00042     : XMLParser(), _keyboard(kbd) {
00043 }
00044 
00045 void VirtualKeyboardParser::cleanup() {
00046     _mode = 0;
00047     _kbdParsed = false;
00048     _initialModeName.clear();
00049     if (_parseMode == kParseFull) {
00050         // reset keyboard to remove existing config
00051         _keyboard->reset();
00052     }
00053 }
00054 
00055 bool VirtualKeyboardParser::closedKeyCallback(ParserNode *node) {
00056     if (node->name.equalsIgnoreCase("keyboard")) {
00057         _kbdParsed = true;
00058         if (!_keyboard->_initialMode)
00059             return parserError("Initial mode of keyboard pack not defined");
00060     } else if (node->name.equalsIgnoreCase("mode")) {
00061         if (!_layoutParsed) {
00062             return parserError("'" + _mode->resolution + "' layout missing from '" + _mode->name + "' mode");
00063         }
00064     }
00065     return true;
00066 }
00067 
00068 bool VirtualKeyboardParser::parserCallback_keyboard(ParserNode *node) {
00069     if (_kbdParsed)
00070         return parserError("Only a single keyboard element is allowed");
00071 
00072     // if not full parse then we're done
00073     if (_parseMode == kParseCheckResolutions)
00074         return true;
00075 
00076     _initialModeName = node->values["initial_mode"];
00077 
00078     if (node->values.contains("h_align")) {
00079         String h = node->values["h_align"];
00080         if (h.equalsIgnoreCase("left"))
00081             _keyboard->_hAlignment = VirtualKeyboard::kAlignLeft;
00082         else if (h.equalsIgnoreCase("centre") || h.equalsIgnoreCase("center"))
00083             _keyboard->_hAlignment = VirtualKeyboard::kAlignCenter;
00084         else if (h.equalsIgnoreCase("right"))
00085             _keyboard->_hAlignment = VirtualKeyboard::kAlignRight;
00086     }
00087 
00088     if (node->values.contains("v_align")) {
00089         String v = node->values["v_align"];
00090         if (v.equalsIgnoreCase("top"))
00091             _keyboard->_vAlignment = VirtualKeyboard::kAlignTop;
00092         else if (v.equalsIgnoreCase("middle") || v.equalsIgnoreCase("center"))
00093             _keyboard->_vAlignment = VirtualKeyboard::kAlignMiddle;
00094         else if (v.equalsIgnoreCase("bottom"))
00095             _keyboard->_vAlignment = VirtualKeyboard::kAlignBottom;
00096     }
00097 
00098     return true;
00099 }
00100 
00101 bool VirtualKeyboardParser::parserCallback_mode(ParserNode *node) {
00102     String name = node->values["name"];
00103 
00104     if (_parseMode == kParseFull) {
00105         // if full parse then add new mode to keyboard
00106         if (_keyboard->_modes.contains(name))
00107             return parserError("Mode '" + name + "' has already been defined");
00108 
00109         VirtualKeyboard::Mode mode;
00110         mode.name = name;
00111         _keyboard->_modes[name] = mode;
00112     }
00113 
00114     _mode = &(_keyboard->_modes[name]);
00115     if (name == _initialModeName)
00116         _keyboard->_initialMode = _mode;
00117 
00118     String resolutions = node->values["resolutions"];
00119     StringTokenizer tok(resolutions, " ,");
00120 
00121     // select best resolution simply by minimising the difference between the
00122     // overlay size and the resolution dimensions.
00123     // TODO: improve this by giving preference to a resolution that is smaller
00124     // than the overlay res (so the keyboard can't be too big for the screen)
00125     uint16 scrW = g_system->getOverlayWidth(), scrH = g_system->getOverlayHeight();
00126     uint32 diff = 0xFFFFFFFF;
00127     String newResolution;
00128     for (String res = tok.nextToken(); res.size() > 0; res = tok.nextToken()) {
00129         int resW, resH;
00130         if (sscanf(res.c_str(), "%dx%d", &resW, &resH) != 2) {
00131             return parserError("Invalid resolution specification");
00132         } else {
00133             if (resW == scrW && resH == scrH) {
00134                 newResolution = res;
00135                 break;
00136             } else {
00137                 uint32 newDiff = ABS(scrW - resW) + ABS(scrH - resH);
00138                 if (newDiff < diff) {
00139                     diff = newDiff;
00140                     newResolution = res;
00141                 }
00142             }
00143         }
00144     }
00145 
00146     if (newResolution.empty())
00147         return parserError("No acceptable resolution was found");
00148 
00149     if (_parseMode == kParseCheckResolutions) {
00150         if (_mode->resolution == newResolution) {
00151             node->ignore = true;
00152             return true;
00153         } else {
00154             // remove data relating to old resolution
00155             _mode->bitmapName.clear();
00156             if (_mode->image) {
00157                 _mode->image->free();
00158                 delete _mode->image;
00159                 _mode->image = 0;
00160             }
00161             _mode->imageMap.removeAllAreas();
00162             _mode->displayArea = Rect();
00163         }
00164     }
00165 
00166     _mode->resolution = newResolution;
00167     _layoutParsed = false;
00168 
00169     return true;
00170 }
00171 
00172 bool VirtualKeyboardParser::parserCallback_event(ParserNode *node) {
00173     // if just checking resolutions we're done
00174     if (_parseMode == kParseCheckResolutions)
00175         return true;
00176 
00177     String name = node->values["name"];
00178     if (_mode->events.contains(name))
00179         return parserError("Event '" + name + "' has already been defined");
00180 
00181     VirtualKeyboard::VKEvent *evt = new VirtualKeyboard::VKEvent();
00182     evt->name = name;
00183 
00184     String type = node->values["type"];
00185     if (type.equalsIgnoreCase("key")) {
00186         if (!node->values.contains("code") || !node->values.contains("ascii")) {
00187             delete evt;
00188             return parserError("Key event element must contain code and ascii attributes");
00189         }
00190         evt->type = VirtualKeyboard::kVKEventKey;
00191 
00192         KeyState *ks = (KeyState *)malloc(sizeof(KeyState));
00193         ks->keycode = (KeyCode)atoi(node->values["code"].c_str());
00194         ks->ascii = atoi(node->values["ascii"].c_str());
00195         ks->flags = 0;
00196         if (node->values.contains("modifiers"))
00197             ks->flags = parseFlags(node->values["modifiers"]);
00198         evt->data = ks;
00199 
00200     } else if (type.equalsIgnoreCase("modifier")) {
00201         if (!node->values.contains("modifiers")) {
00202             delete evt;
00203             return parserError("Key modifier element must contain modifier attributes");
00204         }
00205 
00206         evt->type = VirtualKeyboard::kVKEventModifier;
00207         byte *flags = (byte *)malloc(sizeof(byte));
00208         if (!flags)
00209             error("[VirtualKeyboardParser::parserCallback_event] Cannot allocate memory");
00210 
00211         *(flags) = parseFlags(node->values["modifiers"]);
00212         evt->data = flags;
00213 
00214     } else if (type.equalsIgnoreCase("switch_mode")) {
00215         if (!node->values.contains("mode")) {
00216             delete evt;
00217             return parserError("Switch mode event element must contain mode attribute");
00218         }
00219 
00220         evt->type = VirtualKeyboard::kVKEventSwitchMode;
00221         String &mode = node->values["mode"];
00222         char *str = (char *)malloc(sizeof(char) * mode.size() + 1);
00223         if (!str)
00224             error("[VirtualKeyboardParser::parserCallback_event] Cannot allocate memory");
00225 
00226         memcpy(str, mode.c_str(), sizeof(char) * mode.size());
00227         str[mode.size()] = 0;
00228         evt->data = str;
00229     } else if (type.equalsIgnoreCase("submit")) {
00230         evt->type = VirtualKeyboard::kVKEventSubmit;
00231     } else if (type.equalsIgnoreCase("cancel")) {
00232         evt->type = VirtualKeyboard::kVKEventCancel;
00233     } else if (type.equalsIgnoreCase("clear")) {
00234         evt->type = VirtualKeyboard::kVKEventClear;
00235     } else if (type.equalsIgnoreCase("delete")) {
00236         evt->type = VirtualKeyboard::kVKEventDelete;
00237     } else if (type.equalsIgnoreCase("move_left")) {
00238         evt->type = VirtualKeyboard::kVKEventMoveLeft;
00239     } else if (type.equalsIgnoreCase("move_right")) {
00240         evt->type = VirtualKeyboard::kVKEventMoveRight;
00241     } else {
00242         delete evt;
00243         return parserError("Event type '" + type + "' not known");
00244     }
00245 
00246     _mode->events[name] = evt;
00247 
00248     return true;
00249 }
00250 
00251 bool VirtualKeyboardParser::parserCallback_layout(ParserNode *node) {
00252     assert(!_mode->resolution.empty());
00253 
00254     String res = node->values["resolution"];
00255 
00256     if (res != _mode->resolution) {
00257         node->ignore = true;
00258         return true;
00259     }
00260 
00261     _mode->bitmapName = node->values["bitmap"];
00262 
00263     SeekableReadStream *file = _keyboard->_fileArchive->createReadStreamForMember(_mode->bitmapName);
00264     if (!file)
00265         return parserError("Bitmap '" + _mode->bitmapName + "' not found");
00266 
00267     const Graphics::PixelFormat format = g_system->getOverlayFormat();
00268 
00269     {
00270         Image::BitmapDecoder bmp;
00271         if (!bmp.loadStream(*file))
00272             return parserError("Error loading bitmap '" + _mode->bitmapName + "'");
00273 
00274         _mode->image = bmp.getSurface()->convertTo(format);
00275     }
00276 
00277     delete file;
00278 
00279     int r, g, b;
00280     if (node->values.contains("transparent_color")) {
00281         if (!parseIntegerKey(node->values["transparent_color"], 3, &r, &g, &b))
00282             return parserError("Could not parse color value");
00283     } else {
00284         // default to purple
00285         r = 255;
00286         g = 0;
00287         b = 255;
00288     }
00289     _mode->transparentColor = format.RGBToColor(r, g, b);
00290 
00291     if (node->values.contains("display_font_color")) {
00292         if (!parseIntegerKey(node->values["display_font_color"], 3, &r, &g, &b))
00293             return parserError("Could not parse color value");
00294     } else {
00295         r = g = b = 0; // default to black
00296     }
00297     _mode->displayFontColor = format.RGBToColor(r, g, b);
00298 
00299     _layoutParsed = true;
00300 
00301     return true;
00302 }
00303 
00304 bool VirtualKeyboardParser::parserCallback_map(ParserNode *node) {
00305     return true;
00306 }
00307 
00308 bool VirtualKeyboardParser::parserCallback_area(ParserNode *node) {
00309     String &shape = node->values["shape"];
00310     String &target = node->values["target"];
00311     String &coords = node->values["coords"];
00312 
00313     if (target.equalsIgnoreCase("display_area")) {
00314         if (!shape.equalsIgnoreCase("rect"))
00315             return parserError("display_area must be a rect area");
00316         _mode->displayArea = Rect();
00317         return parseRect(_mode->displayArea, coords);
00318     } else if (shape.equalsIgnoreCase("rect")) {
00319         Polygon *poly = _mode->imageMap.createArea(target);
00320         if (!poly)
00321             return parserError(Common::String::format("Cannot define area '%s' again", target.c_str()));
00322         else
00323             return parseRectAsPolygon(*poly, coords);
00324     } else if (shape.equalsIgnoreCase("poly")) {
00325         Polygon *poly = _mode->imageMap.createArea(target);
00326         if (!poly)
00327             return parserError(Common::String::format("Cannot define area '%s' again", target.c_str()));
00328         else
00329             return parsePolygon(*poly, coords);
00330     }
00331     return parserError("Area shape '" + shape + "' not known");
00332 }
00333 
00334 byte VirtualKeyboardParser::parseFlags(const String &flags) {
00335     if (flags.empty())
00336         return 0;
00337 
00338     StringTokenizer tok(flags, ", ");
00339     byte val = 0;
00340     for (String fl = tok.nextToken(); !fl.empty(); fl = tok.nextToken()) {
00341         if (fl == "ctrl" || fl == "control")
00342             val |= KBD_CTRL;
00343         else if (fl == "alt")
00344             val |= KBD_ALT;
00345         else if (fl == "shift")
00346             val |= KBD_SHIFT;
00347     }
00348     return val;
00349 }
00350 
00351 bool VirtualKeyboardParser::parseRect(Rect &rect, const String &coords) {
00352     int x1, y1, x2, y2;
00353     if (!parseIntegerKey(coords, 4, &x1, &y1, &x2, &y2))
00354         return parserError("Invalid coords for rect area");
00355     rect.left = x1;
00356     rect.top = y1;
00357     rect.right = x2;
00358     rect.bottom = y2;
00359     if (!rect.isValidRect())
00360         return parserError("Rect area is not a valid rectangle");
00361     return true;
00362 }
00363 
00364 bool VirtualKeyboardParser::parsePolygon(Polygon &poly, const String &coords) {
00365     StringTokenizer tok(coords, ", ");
00366     for (String st = tok.nextToken(); !st.empty(); st = tok.nextToken()) {
00367         int x, y;
00368         if (sscanf(st.c_str(), "%d", &x) != 1)
00369             return parserError("Invalid coords for polygon area");
00370         st = tok.nextToken();
00371         if (sscanf(st.c_str(), "%d", &y) != 1)
00372             return parserError("Invalid coords for polygon area");
00373         poly.addPoint(x, y);
00374     }
00375     if (poly.getPointCount() < 3)
00376         return parserError("Invalid coords for polygon area");
00377 
00378     return true;
00379 }
00380 
00381 bool VirtualKeyboardParser::parseRectAsPolygon(Polygon &poly, const String &coords) {
00382     Rect rect;
00383     if (!parseRect(rect, coords))
00384         return false;
00385     poly.addPoint(rect.left, rect.top);
00386     poly.addPoint(rect.right, rect.top);
00387     poly.addPoint(rect.right, rect.bottom);
00388     poly.addPoint(rect.left, rect.bottom);
00389     return true;
00390 }
00391 
00392 } // End of namespace GUI
00393 
00394 #endif // #ifdef ENABLE_VKEYBD


Generated on Sat May 25 2019 05:00:56 for ResidualVM by doxygen 1.7.1
curved edge   curved edge