Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

virtual-keyboard-parser.cpp

/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-0-0/backends/vkeybd/virtual-keyboard-parser.cpp $
 * $Id: virtual-keyboard-parser.cpp 41232 2009-06-06 17:21:50Z djwillis $
 *
 */


#ifdef ENABLE_VKEYBD

#include "backends/vkeybd/virtual-keyboard-parser.h"
#include "backends/vkeybd/polygon.h"

#include "common/keyboard.h"
#include "common/util.h"
#include "common/system.h"
#include "common/archive.h"

#include "graphics/imagedec.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["v_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
                  _mode->bitmapName.clear();
                  delete _mode->image;
                  _mode->image = 0;
                  _mode->imageMap.removeAllAreas();
                  _mode->displayArea = Rect();
            }
      }

      _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"];

      SeekableReadStream *file = _keyboard->_fileArchive->createReadStreamForMember(_mode->bitmapName);
      if (!file)
            return parserError("Bitmap '%s' not found", _mode->bitmapName.c_str());

      const Graphics::PixelFormat format = g_system->getOverlayFormat();

      _mode->image = Graphics::ImageDecoder::loadFile(*file, format);
      delete file;

      if (!_mode->image)
            return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str());

      int r, g, b;
      if (node->values.contains("transparent_color")) {
            if (!parseIntegerKey(node->values["transparent_color"].c_str(), 3, &r, &g, &b))
                  return parserError("Could not parse color value");
      } else {
            // default to purple
            r = 255;
            g = 0;
            b = 255;
      }
      _mode->transparentColor = format.RGBToColor(r, g, b);

      if (node->values.contains("display_font_color")) {
            if (!parseIntegerKey(node->values["display_font_color"].c_str(), 3, &r, &g, &b))
                  return parserError("Could not parse color value");
      } else {
            r = g = b = 0; // default to black
      }
      _mode->displayFontColor = format.RGBToColor(r, g, b);

      _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 = 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

Generated by  Doxygen 1.6.0   Back to index