aboutsummaryrefslogtreecommitdiff
path: root/backends/vkeybd/virtual-keyboard-parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backends/vkeybd/virtual-keyboard-parser.cpp')
-rw-r--r--backends/vkeybd/virtual-keyboard-parser.cpp367
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