diff options
Diffstat (limited to 'backends/vkeybd/virtual-keyboard-parser.cpp')
-rw-r--r-- | backends/vkeybd/virtual-keyboard-parser.cpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp new file mode 100644 index 0000000000..ea484b8604 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-parser.cpp @@ -0,0 +1,367 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/vkeybd/virtual-keyboard-parser.h" + +#ifdef ENABLE_VKEYBD + +#include "common/keyboard.h" +#include "graphics/imageman.h" +#include "common/util.h" + +namespace Common { + +VirtualKeyboardParser::VirtualKeyboardParser(VirtualKeyboard *kbd) + : XMLParser(), _keyboard(kbd) { +} + +void VirtualKeyboardParser::cleanup() { + _mode = 0; + _kbdParsed = false; + _initialModeName.clear(); + if (_parseMode == kParseFull) { + // reset keyboard to remove existing config + _keyboard->reset(); + } +} + +bool VirtualKeyboardParser::closedKeyCallback(ParserNode *node) { + if (node->name.equalsIgnoreCase("keyboard")) { + _kbdParsed = true; + if (!_keyboard->_initialMode) + return parserError("Initial mode of keyboard pack not defined"); + } else if (node->name.equalsIgnoreCase("mode")) { + if (!_layoutParsed) { + return parserError("'%s' layout missing from '%s' mode", + _mode->resolution.c_str(), _mode->name.c_str()); + } + } + return true; +} + +bool VirtualKeyboardParser::parserCallback_keyboard(ParserNode *node) { + + if (_kbdParsed) + return parserError("Only a single keyboard element is allowed"); + + // if not full parse then we're done + if (_parseMode == kParseCheckResolutions) + return true; + + _initialModeName = node->values["initial_mode"]; + + if (node->values.contains("h_align")) { + String h = node->values["h_align"]; + if (h.equalsIgnoreCase("left")) + _keyboard->_hAlignment = VirtualKeyboard::kAlignLeft; + else if (h.equalsIgnoreCase("centre") || h.equalsIgnoreCase("center")) + _keyboard->_hAlignment = VirtualKeyboard::kAlignCentre; + else if (h.equalsIgnoreCase("right")) + _keyboard->_hAlignment = VirtualKeyboard::kAlignRight; + } + + if (node->values.contains("v_align")) { + String v = node->values["h_align"]; + if (v.equalsIgnoreCase("top")) + _keyboard->_vAlignment = VirtualKeyboard::kAlignTop; + else if (v.equalsIgnoreCase("middle") || v.equalsIgnoreCase("center")) + _keyboard->_vAlignment = VirtualKeyboard::kAlignMiddle; + else if (v.equalsIgnoreCase("bottom")) + _keyboard->_vAlignment = VirtualKeyboard::kAlignBottom; + } + + return true; +} + +bool VirtualKeyboardParser::parserCallback_mode(ParserNode *node) { + + String name = node->values["name"]; + + if (_parseMode == kParseFull) { + // if full parse then add new mode to keyboard + if (_keyboard->_modes.contains(name)) + return parserError("Mode '%s' has already been defined", name.c_str()); + + VirtualKeyboard::Mode mode; + mode.name = name; + _keyboard->_modes[name] = mode; + } + + _mode = &(_keyboard->_modes[name]); + if (name == _initialModeName) + _keyboard->_initialMode = _mode; + + String resolutions = node->values["resolutions"]; + StringTokenizer tok (resolutions, " ,"); + + // select best resolution simply by minimising the difference between the + // overlay size and the resolution dimensions. + // TODO: improve this by giving preference to a resolution that is smaller + // than the overlay res (so the keyboard can't be too big for the screen) + uint16 scrW = g_system->getOverlayWidth(), scrH = g_system->getOverlayHeight(); + uint32 diff = 0xFFFFFFFF; + String newResolution; + for (String res = tok.nextToken(); res.size() > 0; res = tok.nextToken()) { + int resW, resH; + if (sscanf(res.c_str(), "%dx%d", &resW, &resH) != 2) { + return parserError("Invalid resolution specification"); + } else { + if (resW == scrW && resH == scrH) { + newResolution = res; + break; + } else { + uint32 newDiff = ABS(scrW - resW) + ABS(scrH - resH); + if (newDiff < diff) { + diff = newDiff; + newResolution = res; + } + } + } + } + + if (newResolution.empty()) + return parserError("No acceptable resolution was found"); + + if (_parseMode == kParseCheckResolutions) { + if (_mode->resolution == newResolution) { + node->ignore = true; + return true; + } else { + // remove data relating to old resolution + ImageMan.unregisterSurface(_mode->bitmapName); + _mode->bitmapName.clear(); + _mode->image = 0; + _mode->imageMap.removeAllAreas(); + delete _mode->displayArea; + _mode->displayArea = 0; + } + } + + _mode->resolution = newResolution; + _layoutParsed = false; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_event(ParserNode *node) { + + // if just checking resolutions we're done + if (_parseMode == kParseCheckResolutions) + return true; + + String name = node->values["name"]; + if (_mode->events.contains(name)) + return parserError("Event '%s' has already been defined", name.c_str()); + + VirtualKeyboard::VKEvent *evt = new VirtualKeyboard::VKEvent(); + evt->name = name; + + String type = node->values["type"]; + if (type.equalsIgnoreCase("key")) { + if (!node->values.contains("code") || !node->values.contains("ascii")) { + delete evt; + return parserError("Key event element must contain code and ascii attributes"); + } + evt->type = VirtualKeyboard::kVKEventKey; + + KeyState *ks = (KeyState*) malloc(sizeof(KeyState)); + ks->keycode = (KeyCode)atoi(node->values["code"].c_str()); + ks->ascii = atoi(node->values["ascii"].c_str()); + ks->flags = 0; + if (node->values.contains("modifiers")) + ks->flags = parseFlags(node->values["modifiers"]); + evt->data = ks; + + } else if (type.equalsIgnoreCase("modifier")) { + if (!node->values.contains("modifiers")) { + delete evt; + return parserError("Key modifier element must contain modifier attributes"); + } + + evt->type = VirtualKeyboard::kVKEventModifier; + byte *flags = (byte*) malloc(sizeof(byte)); + *(flags) = parseFlags(node->values["modifiers"]); + evt->data = flags; + + } else if (type.equalsIgnoreCase("switch_mode")) { + if (!node->values.contains("mode")) { + delete evt; + return parserError("Switch mode event element must contain mode attribute"); + } + + evt->type = VirtualKeyboard::kVKEventSwitchMode; + String& mode = node->values["mode"]; + char *str = (char*) malloc(sizeof(char) * mode.size() + 1); + memcpy(str, mode.c_str(), sizeof(char) * mode.size()); + str[mode.size()] = 0; + evt->data = str; + } else if (type.equalsIgnoreCase("submit")) { + evt->type = VirtualKeyboard::kVKEventSubmit; + } else if (type.equalsIgnoreCase("cancel")) { + evt->type = VirtualKeyboard::kVKEventCancel; + } else if (type.equalsIgnoreCase("clear")) { + evt->type = VirtualKeyboard::kVKEventClear; + } else if (type.equalsIgnoreCase("delete")) { + evt->type = VirtualKeyboard::kVKEventDelete; + } else if (type.equalsIgnoreCase("move_left")) { + evt->type = VirtualKeyboard::kVKEventMoveLeft; + } else if (type.equalsIgnoreCase("move_right")) { + evt->type = VirtualKeyboard::kVKEventMoveRight; + } else { + delete evt; + return parserError("Event type '%s' not known", type.c_str()); + } + + _mode->events[name] = evt; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_layout(ParserNode *node) { + + assert(!_mode->resolution.empty()); + + String res = node->values["resolution"]; + + if (res != _mode->resolution) { + node->ignore = true; + return true; + } + + _mode->bitmapName = node->values["bitmap"]; + _mode->image = ImageMan.getSurface(_mode->bitmapName); + if (!_mode->image) { + if (!ImageMan.registerSurface(_mode->bitmapName, 0)) + return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str()); + + _mode->image = ImageMan.getSurface(_mode->bitmapName); + if (!_mode->image) + return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str()); + } + + if (node->values.contains("transparent_color")) { + int r, g, b; + if (!parseIntegerKey(node->values["transparent_color"].c_str(), 3, &r, &g, &b)) + return parserError("Could not parse color value"); + _mode->transparentColor = g_system->RGBToColor(r, g, b); + } else // default to purple + _mode->transparentColor = g_system->RGBToColor(255, 0, 255); + + if (node->values.contains("display_font_color")) { + int r, g, b; + if (!parseIntegerKey(node->values["display_font_color"].c_str(), 3, &r, &g, &b)) + return parserError("Could not parse color value"); + _mode->displayFontColor = g_system->RGBToColor(r, g, b); + } else + _mode->displayFontColor = g_system->RGBToColor(0, 0, 0); // default to black + + _layoutParsed = true; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_map(ParserNode *node) { + return true; +} + +bool VirtualKeyboardParser::parserCallback_area(ParserNode *node) { + + String& shape = node->values["shape"]; + String& target = node->values["target"]; + String& coords = node->values["coords"]; + + if (target.equalsIgnoreCase("display_area")) { + if (! shape.equalsIgnoreCase("rect")) + return parserError("display_area must be a rect area"); + _mode->displayArea = new Rect(); + return parseRect(_mode->displayArea, coords); + } else if (shape.equalsIgnoreCase("rect")) { + Polygon *poly = _mode->imageMap.createArea(target); + return parseRectAsPolygon(poly, coords); + } else if (shape.equalsIgnoreCase("poly")) { + Polygon *poly = _mode->imageMap.createArea(target); + return parsePolygon(poly, coords); + } + return parserError("Area shape '%s' not known", shape.c_str()); +} + +byte VirtualKeyboardParser::parseFlags(const String& flags) { + if (flags.empty()) + return 0; + + StringTokenizer tok(flags, ", "); + byte val = 0; + for (String fl = tok.nextToken(); !fl.empty(); fl = tok.nextToken()) { + if (fl == "ctrl" || fl == "control") + val |= KBD_CTRL; + else if (fl == "alt") + val |= KBD_ALT; + else if (fl == "shift") + val |= KBD_SHIFT; + } + return val; +} + +bool VirtualKeyboardParser::parseRect(Rect *rect, const String& coords) { + int x1, y1, x2, y2; + if (!parseIntegerKey(coords.c_str(), 4, &x1, &y1, &x2, &y2)) + return parserError("Invalid coords for rect area"); + rect->left = x1; rect->top = y1; rect->right = x2; rect->bottom = y2; + if (!rect->isValidRect()) + return parserError("Rect area is not a valid rectangle"); + return true; +} + +bool VirtualKeyboardParser::parsePolygon(Polygon *poly, const String& coords) { + StringTokenizer tok (coords, ", "); + for (String st = tok.nextToken(); !st.empty(); st = tok.nextToken()) { + int x, y; + if (sscanf(st.c_str(), "%d", &x) != 1) + return parserError("Invalid coords for polygon area"); + st = tok.nextToken(); + if (sscanf(st.c_str(), "%d", &y) != 1) + return parserError("Invalid coords for polygon area"); + poly->addPoint(x, y); + } + if (poly->getPointCount() < 3) + return parserError("Invalid coords for polygon area"); + + return true; +} + +bool VirtualKeyboardParser::parseRectAsPolygon(Polygon *poly, const String& coords) { + Rect rect; + if (!parseRect(&rect, coords)) + return false; + poly->addPoint(rect.left, rect.top); + poly->addPoint(rect.right, rect.top); + poly->addPoint(rect.right, rect.bottom); + poly->addPoint(rect.left, rect.bottom); + return true; +} + +} // end of namespace GUI + +#endif // #ifdef ENABLE_VKEYBD |