aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.common7
-rw-r--r--backends/common/hardware-key.h95
-rw-r--r--backends/common/keymap-manager.cpp65
-rw-r--r--backends/common/keymap-manager.h51
-rw-r--r--backends/common/keymap.cpp83
-rw-r--r--backends/common/keymap.h89
-rw-r--r--backends/common/keymapper.cpp61
-rw-r--r--backends/common/keymapper.h46
-rw-r--r--backends/common/user-action.h70
-rw-r--r--backends/common/virtual-keyboard-parser.cpp365
-rw-r--r--backends/common/virtual-keyboard-parser.h212
-rw-r--r--backends/common/virtual-keyboard.cpp385
-rw-r--r--backends/common/virtual-keyboard.h185
-rw-r--r--backends/events/default/default-events.cpp35
-rw-r--r--backends/events/default/default-events.h11
-rw-r--r--backends/module.mk4
-rw-r--r--common/events.h5
-rw-r--r--common/image-map.cpp79
-rw-r--r--common/image-map.h55
-rw-r--r--common/keyboard.h4
-rw-r--r--common/list.h5
-rw-r--r--common/module.mk3
-rw-r--r--common/polygon.cpp55
-rw-r--r--common/polygon.h115
-rw-r--r--common/queue.h71
-rw-r--r--common/rect.h57
-rw-r--r--common/shape.h105
-rw-r--r--common/xmlparser.cpp261
-rw-r--r--common/xmlparser.h369
-rw-r--r--dists/msvc8/scummvm.vcproj94
-rw-r--r--graphics/module.mk3
-rw-r--r--graphics/surface-keycolored.cpp44
-rw-r--r--graphics/surface-keycolored.h17
33 files changed, 3057 insertions, 49 deletions
diff --git a/Makefile.common b/Makefile.common
index c2704f2219..9644426180 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -27,7 +27,12 @@ MODULES += \
sound \
common \
engines \
- backends
+ backends \
+ common \
+ graphics # HACK/FIXME: this extra 'common' was added because of a circular dependency between
+ # backends and common (the newly added Virtual Keyboard stuff depends on things
+ # from common). This should be resolved in one way or another, perhaps by moving
+ # the VK code out of backends?
ifdef USE_MT32EMU
MODULES += sound/softsynth/mt32
diff --git a/backends/common/hardware-key.h b/backends/common/hardware-key.h
new file mode 100644
index 0000000000..de1984dc31
--- /dev/null
+++ b/backends/common/hardware-key.h
@@ -0,0 +1,95 @@
+#ifndef COMMON_HARDWAREKEY
+#define COMMON_HARDWAREKEY
+
+#include "backends/common/user-action.h"
+
+namespace Common {
+
+/**
+* Describes an available hardware key
+*/
+struct HardwareKey {
+ /** unique id used for saving/loading to config */
+ int32 id;
+ /** Human readable description */
+ String description;
+ /**
+ * The KeyState that is generated by the back-end
+ * when this hardware key is pressed.
+ */
+ KeyState key;
+
+ UserActionCategory preferredCategory;
+ UserActionType preferredType;
+ int16 group;
+
+ HardwareKey(KeyState ks = KeyState(), String des = "",
+ UserActionCategory cat = kGenericUserActionCategory,
+ UserActionType ty = kGenericUserActionType, int gr = 0) {
+ key = ks;
+ description = des;
+ preferredCategory = cat;
+ preferredType = ty;
+ group = gr;
+ }
+};
+
+
+/**
+ * Simple class to encapsulate a device's set of HardwareKeys.
+ * Each device should extend this and call addHardwareKey a number of times
+ * in its constructor to define the device's available keys.
+ */
+class HardwareKeySet {
+public:
+
+ HardwareKeySet() {}
+ ~HardwareKeySet() {
+ List<HardwareKey*>::iterator it;
+ for (it = _keys.begin(); it != _keys.end(); it++)
+ delete *it;
+ }
+
+ void addHardwareKey(HardwareKey *key) {
+ checkForKey(key);
+ _keys.push_back(key);
+ }
+
+ const HardwareKey *findHardwareKey(int32 id) const {
+ List<HardwareKey*>::iterator it;
+ for (it = _keys.begin(); it != _keys.end(); it++) {
+ if ((*it)->id == id)
+ return (*it);
+ }
+ return 0;
+ }
+
+ const HardwareKey *findHardwareKey(const KeyState& keystate) const {
+ List<HardwareKey*>::iterator it;
+ for (it = _keys.begin(); it != _keys.end(); it++) {
+ if ((*it)->key == keystate)
+ return (*it);
+ }
+ return 0;
+ }
+
+
+private:
+
+ void checkForKey(HardwareKey *key) {
+ List<HardwareKey*>::iterator it;
+ for (it = _keys.begin(); it != _keys.end(); it++) {
+ if ((*it)->id == key->id)
+ error("HardwareKey with id %d already given!\n", key->id);
+ else if ((*it)->key == key->key)
+ error("HardwareKey with same KeyState already given!\n");
+ }
+ }
+
+ List<HardwareKey*> _keys;
+};
+
+
+} // end of namespace Common
+
+#endif \ No newline at end of file
diff --git a/backends/common/keymap-manager.cpp b/backends/common/keymap-manager.cpp
new file mode 100644
index 0000000000..73ce705414
--- /dev/null
+++ b/backends/common/keymap-manager.cpp
@@ -0,0 +1,65 @@
+#include "backends/common/keymap-manager.h"
+
+#define GLOBAL_ID_STR "___GLOBAL"
+
+namespace Common {
+
+
+void KeymapManager::Domain::addDefaultKeymap(Keymap *map) {
+ _defaultKeymap = map;
+}
+
+void KeymapManager::Domain::addKeymap(const String& name, Keymap *map) {
+ if (_keymaps.contains(name))
+ delete _keymaps[name];
+ _keymaps[name] = map;
+}
+
+void KeymapManager::Domain::deleteAllKeyMaps() {
+ KeymapMap::iterator it;
+ for (it = _keymaps.begin(); it != _keymaps.end(); it++)
+ delete it->_value;
+ _keymaps.clear();
+}
+
+Keymap *KeymapManager::Domain::getDefaultKeymap() {
+ return _defaultKeymap;
+}
+
+Keymap *KeymapManager::Domain::getKeymap(const String& name) {
+ KeymapMap::iterator it = _keymaps.find(name);
+ if (it != _keymaps.end())
+ return it->_value;
+ else
+ return 0;
+}
+
+
+void KeymapManager::registerDefaultGlobalKeymap(Keymap *map) {
+ _globalDomain.addDefaultKeymap(map);
+}
+
+void KeymapManager::registerGlobalKeymap(const String& name, Keymap *map) {
+ _globalDomain.addKeymap(name, map);
+}
+
+void KeymapManager::registerDefaultGameKeymap(Keymap *map) {
+ _gameDomain.addDefaultKeymap(map);
+}
+
+void KeymapManager::registerGameKeymap(const String& name, Keymap *map) {
+ _gameDomain.addKeymap(name, map);
+}
+
+void KeymapManager::unregisterAllGameKeymaps() {
+ _gameDomain.deleteAllKeyMaps();
+}
+
+Keymap *KeymapManager::getKeymap(const String& name) {
+ Keymap *keymap = _gameDomain.getKeymap(name);
+ if (!keymap)
+ _globalDomain.getKeymap(name);
+ return keymap;
+}
+
+} // end of namespace Common \ No newline at end of file
diff --git a/backends/common/keymap-manager.h b/backends/common/keymap-manager.h
new file mode 100644
index 0000000000..e5152d2426
--- /dev/null
+++ b/backends/common/keymap-manager.h
@@ -0,0 +1,51 @@
+#ifndef COMMON_KEYMAP_MANAGER
+#define COMMON_KEYMAP_MANAGER
+
+#include "backends/common/keymap.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+
+namespace Common {
+
+class KeymapManager {
+public:
+
+ class Domain {
+ public:
+ Domain() : _defaultKeymap(0) {}
+
+ void addDefaultKeymap(Keymap *map);
+ void addKeymap(const String& name, Keymap *map);
+
+ void deleteAllKeyMaps();
+
+ Keymap *getDefaultKeymap();
+ Keymap *getKeymap(const String& name);
+
+ private:
+ typedef HashMap<String, Keymap*,
+ IgnoreCase_Hash, IgnoreCase_EqualTo> KeymapMap;
+
+ Keymap *_defaultKeymap;
+ KeymapMap _keymaps;
+ };
+
+ void registerDefaultGlobalKeymap(Keymap *map);
+ void registerGlobalKeymap(const String& name, Keymap *map);
+
+ void registerDefaultGameKeymap(Keymap *map);
+ void registerGameKeymap(const String& name, Keymap *map);
+
+ void unregisterAllGameKeymaps();
+
+ Keymap *KeymapManager::getKeymap(const String& name);
+
+private:
+
+ Domain _globalDomain;
+ Domain _gameDomain;
+};
+
+} // end of namespace Common
+
+#endif \ No newline at end of file
diff --git a/backends/common/keymap.cpp b/backends/common/keymap.cpp
new file mode 100644
index 0000000000..9fb80bcc58
--- /dev/null
+++ b/backends/common/keymap.cpp
@@ -0,0 +1,83 @@
+#include "backends/common/keymap.h"
+
+namespace Common {
+
+Keymap::Keymap(const Keymap& km) : _actions(km._actions), _keymap() {
+ init();
+ for (uint i = 0; i < _actions.size(); i++) {
+ if (_actions[i].hwKey) {
+ _keymap[_actions[i].hwKey->key] = &_actions[i];
+ }
+ }
+}
+
+void Keymap::init() {
+ _actions.reserve(20);
+}
+
+void Keymap::addAction(const UserAction& action) {
+ if (findUserAction(action.id))
+ error("UserAction with id %d already in KeyMap!\n", action.id);
+ _actions.push_back(action);
+ _actions[_actions.size()-1].hwKey = 0;
+}
+
+void Keymap::mapKeyToAction(UserAction *action, HardwareKey *key) {
+ for (uint i = 0; i < _actions.size(); i++) {
+ if (&_actions[i] == action) {
+ internalMapKey(action, key);
+ return;
+ }
+ }
+ error("UserAction not contained in KeyMap\n");
+}
+
+void Keymap::mapKeyToAction(int32 id, HardwareKey *key) {
+ UserAction *act = findUserAction(id);
+ if (act)
+ internalMapKey(act, key);
+}
+
+void Keymap::internalMapKey(UserAction *action, HardwareKey *hwKey) {
+ HashMap<KeyState, UserAction*>::iterator it;
+ it = _keymap.find(hwKey->key);
+ // if key is already mapped to an action then un-map it
+ if (it != _keymap.end())
+ it->_value->hwKey = 0;
+
+ action->hwKey = hwKey;
+ _keymap[hwKey->key] = action;
+}
+
+const UserAction *Keymap::getUserAction(int32 id) const {
+ return findUserAction(id);
+}
+
+UserAction *Keymap::findUserAction(int32 id) {
+ Array<UserAction>::iterator it;
+ for (it = _actions.begin(); it != _actions.end(); it++) {
+ if (it->id == id)
+ return &*it;
+ }
+ return 0;
+}
+
+const UserAction *Keymap::findUserAction(int32 id) const {
+ Array<UserAction>::const_iterator it;
+ for (it = _actions.begin(); it != _actions.end(); it++) {
+ if (it->id == id)
+ return &*it;
+ }
+ return 0;
+}
+
+UserAction *Keymap::getMappedAction(KeyState ks) const {
+ HashMap<KeyState, UserAction*>::iterator it;
+ it = _keymap.find(ks);
+ if (it == _keymap.end())
+ return 0;
+ else
+ return it->_value;
+}
+
+} // end of namespace Common \ No newline at end of file
diff --git a/backends/common/keymap.h b/backends/common/keymap.h
new file mode 100644
index 0000000000..a2e204827e
--- /dev/null
+++ b/backends/common/keymap.h
@@ -0,0 +1,89 @@
+#ifndef COMMON_KEYMAP
+#define COMMON_KEYMAP
+
+#include "backends/common/hardware-key.h"
+#include "backends/common/user-action.h"
+#include "common/array.h"
+#include "common/keyboard.h"
+#include "common/func.h"
+#include "common/hashmap.h"
+
+namespace Common {
+
+/**
+ * Hash function for KeyState
+ */
+template<> struct Hash<KeyState>
+ : public UnaryFunction<KeyState, uint> {
+
+ uint operator()(const KeyState &val) const {
+ return (uint)(val.keycode * (val.flags << 1));
+ }
+};
+
+class Keymap {
+public:
+ Keymap() { init(); }
+ Keymap(const Keymap& km);
+private:
+ void init();
+
+public:
+ /**
+ * Adds a new UserAction to this Map,
+ * adding it at the back of the internal array
+ * @param action the UserAction to add
+ */
+ void addAction(const UserAction& action);
+
+ /**
+ * Maps a HardwareKey to the given UserAction
+ * @param action must point to a UserAction in this Keymap
+ * @param key pointer to HardwareKey to map
+ * @note if action does not point to a UserAction in this Keymap a
+ * fatal error will occur
+ */
+ void mapKeyToAction(UserAction *action, HardwareKey *key);
+
+ /**
+ * Maps a HardwareKey to the UserAction of the given id
+ * @param id id of the UserAction to map to
+ * @param key pointer to HardwareKey to map
+ */
+ void mapKeyToAction(int32 id, HardwareKey *key);
+
+ /**
+ * Retrieves the UserAction with the given id
+ * @param id id of UserAction to retrieve
+ * @return Pointer to the UserAction or 0 if not found
+ */
+ const UserAction *getUserAction(int32 id) const;
+
+ /**
+ * Get a read-only array of all the UserActions contained in this Keymap
+ */
+ const Array<UserAction>& getUserActions() const { return _actions; }
+
+ /**
+ * Find the UserAction that a key is mapped to
+ * @param key the key that is mapped to the required UserAction
+ * @return a pointer to the UserAction or 0 if no
+ */
+ UserAction *getMappedAction(KeyState key) const;
+
+private:
+
+ UserAction *findUserAction(int32 id);
+ const UserAction *findUserAction(int32 id) const;
+
+ void internalMapKey(UserAction *action, HardwareKey *hwKey);
+
+ Array<UserAction> _actions;
+ HashMap<KeyState, UserAction*> _keymap;
+
+};
+
+
+} // end of namespace Common
+
+#endif \ No newline at end of file
diff --git a/backends/common/keymapper.cpp b/backends/common/keymapper.cpp
new file mode 100644
index 0000000000..a70a691907
--- /dev/null
+++ b/backends/common/keymapper.cpp
@@ -0,0 +1,61 @@
+#include "backends/common/keymapper.h"
+#include "backends/common/keymap-manager.h"
+#include "common/config-manager.h"
+namespace Common {
+
+Keymapper::Keymapper(EventManager *evtMgr) {
+ _eventMan = evtMgr;
+ _keymapMan = new KeymapManager();
+ _currentMap = 0;
+ _hardwareKeys = 0;
+}
+
+void Keymapper::registerHardwareKeySet(HardwareKeySet *keys) {
+ if (_hardwareKeys)
+ error("Hardware key set already registered!\n");
+ _hardwareKeys = keys;
+}
+
+const HardwareKeySet *Keymapper::getHardwareKeySet() const {
+ return _hardwareKeys;
+}
+
+void Keymapper::addGlobalKeyMap(const String& name, Keymap *keymap) {
+ _keymapMan->registerGlobalKeymap(name, keymap);
+}
+
+void Keymapper::addGameKeyMap(const String& name, Keymap *keymap) {
+ if (_gameId.size() == 0) {
+ initGame();
+ if (_gameId.size() == 0)
+ return;
+ }
+ _keymapMan->registerGameKeymap(name, keymap);
+}
+
+void Keymapper::initGame() {
+ if (ConfMan.getActiveDomain() == 0)
+ error("Call to Keymapper::initGame when no game loaded\n");
+
+ if (_gameId.size() > 0)
+ deInitGame();
+ _gameId = ConfMan.getActiveDomainName();
+}
+
+void Keymapper::deInitGame() {
+ _keymapMan->unregisterAllGameKeymaps();
+ _gameId.clear();
+}
+
+
+bool Keymapper::switchKeymap(const String& name) {
+ Keymap *new_map = _keymapMan->getKeymap(name);
+ if (!new_map) {
+ warning("Keymap '%s' could not be found\n", name.c_str());
+ return false;
+ }
+ _currentMap = new_map;
+ return true;
+}
+
+} // end of namespace Common
diff --git a/backends/common/keymapper.h b/backends/common/keymapper.h
new file mode 100644
index 0000000000..d9b1b050fb
--- /dev/null
+++ b/backends/common/keymapper.h
@@ -0,0 +1,46 @@
+#ifndef COMMON_KEYMAPPER
+#define COMMON_KEYMAPPER
+
+#include "backends/common/keymap.h"
+#include "common/list.h"
+
+namespace Common {
+
+class KeymapManager;
+
+class Keymapper {
+public:
+
+ Keymapper(EventManager *eventMan);
+
+ void registerHardwareKeySet(HardwareKeySet *keys);
+
+ const HardwareKeySet *getHardwareKeySet() const;
+
+ void addGlobalKeyMap(const String& name, Keymap *keymap);
+
+ void addGameKeyMap(const String& name, Keymap *keymap);
+
+ void initGame();
+ void deInitGame();
+
+ bool switchKeymap(const String& name);
+
+private:
+
+ typedef List<HardwareKey*>::iterator Iterator;
+
+ EventManager *_eventMan;
+ KeymapManager *_keymapMan;
+
+ String _gameId;
+
+ Keymap *_currentMap;
+
+ const HardwareKeySet *_hardwareKeys;
+
+};
+
+} // end of namespace Common
+
+#endif \ No newline at end of file
diff --git a/backends/common/user-action.h b/backends/common/user-action.h
new file mode 100644
index 0000000000..31b5473708
--- /dev/null
+++ b/backends/common/user-action.h
@@ -0,0 +1,70 @@
+#ifndef COMMON_USERACTION
+#define COMMON_USERACTION
+
+#include "common/events.h"
+#include "common/list.h"
+#include "common/str.h"
+
+namespace Common {
+
+struct HardwareKey;
+
+enum UserActionType {
+ kGenericUserActionType,
+
+ // common actions
+ kDirectionUpUserAction,
+ kDirectionDownUserAction,
+ kDirectionLeftUserAction,
+ kDirectionRightUserAction,
+ kLeftClickUserAction,
+ kRightClickUserAction,
+ kSaveUserAction,
+ kMenuUserAction,
+
+ kUserActionTypeMax
+};
+
+enum UserActionCategory {
+ kGenericUserActionCategory,
+ // classes of action - probably need to be slightly more specific than this
+ kInGameUserAction, // effects the actual gameplay
+ kSystemUserAction, //show a menu / change volume / etc
+
+ kUserActionCategoryMax
+};
+
+struct UserAction {
+ /** unique id used for saving/loading to config */
+ int32 id;
+ /** Human readable description */
+ String description;
+ /** Events to be sent when mapped key is pressed */
+ List<Event> events;
+
+ UserActionCategory category;
+ UserActionType type;
+ int priority;
+ int group;
+ int flags;
+
+ /** Hardware key that is mapped to this UserAction */
+ HardwareKey *hwKey;
+
+ UserAction( String des = "",
+ UserActionCategory cat = kGenericUserActionCategory,
+ UserActionType ty = kGenericUserActionType,
+ int pr = 0, int gr = 0, int fl = 0 ) {
+ description = des;
+ category = cat;
+ type = ty;
+ priority = pr;
+ group = gr;
+ flags = fl;
+ hwKey = 0;
+ }
+};
+
+} // end of namespace Common
+
+#endif \ No newline at end of file
diff --git a/backends/common/virtual-keyboard-parser.cpp b/backends/common/virtual-keyboard-parser.cpp
new file mode 100644
index 0000000000..7b1e79e937
--- /dev/null
+++ b/backends/common/virtual-keyboard-parser.cpp
@@ -0,0 +1,365 @@
+/* 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/common/virtual-keyboard-parser.h"
+
+#include "common/keyboard.h"
+#include "graphics/imageman.h"
+#include "common/util.h"
+
+namespace Common {
+
+VirtualKeyboardParser::VirtualKeyboardParser(VirtualKeyboard *kbd) : XMLParser() {
+ _keyboard = kbd;
+
+ _callbacks["keyboard"] = &VirtualKeyboardParser::parserCallback_Keyboard;
+ _callbacks["mode"] = &VirtualKeyboardParser::parserCallback_Mode;
+ _callbacks["event"] = &VirtualKeyboardParser::parserCallback_Event;
+ _callbacks["layout"] = &VirtualKeyboardParser::parserCallback_Layout;
+ _callbacks["map"] = &VirtualKeyboardParser::parserCallback_Map;
+ _callbacks["area"] = &VirtualKeyboardParser::parserCallback_Area;
+
+ _closedCallbacks["keyboard"] = &VirtualKeyboardParser::parserCallback_KeyboardClosed;
+ _closedCallbacks["mode"] = &VirtualKeyboardParser::parserCallback_ModeClosed;
+}
+
+void VirtualKeyboardParser::cleanup() {
+ _mode = 0;
+ _kbdParsed = false;
+ _initialModeName.clear();
+ if (_parseMode == kParseFull) {
+ // reset keyboard to remove existing config
+ _keyboard->reset();
+ }
+}
+
+bool VirtualKeyboardParser::keyCallback(Common::String keyName) {
+ if (!_callbacks.contains(_activeKey.top()->name))
+ return parserError("%s is not a valid key name.", keyName.c_str());
+
+ return (this->*(_callbacks[_activeKey.top()->name]))();
+}
+
+bool VirtualKeyboardParser::closedKeyCallback(Common::String keyName) {
+ if (!_closedCallbacks.contains(_activeKey.top()->name))
+ return true;
+
+ return (this->*(_closedCallbacks[_activeKey.top()->name]))();
+}
+
+bool VirtualKeyboardParser::parserCallback_Keyboard() {
+ ParserNode *kbdNode = getActiveNode();
+
+ assert(kbdNode->name == "keyboard");
+
+ if (getParentNode(kbdNode) != 0)
+ return parserError("Keyboard element must be root");
+
+ if (_kbdParsed)
+ return parserError("Only a single keyboard element is allowed");
+
+ // if not full parse then we're done
+ if (_parseMode == kParseCheckResolutions)
+ return true;
+
+ if (!kbdNode->values.contains("initial_mode"))
+ return parserError("Keyboard element must contain initial_mode attribute");
+
+ _initialModeName = kbdNode->values["initial_mode"];
+
+ if (kbdNode->values.contains("h_align")) {
+ Common::String h = kbdNode->values["h_align"];
+ if (h == "left")
+ _keyboard->_hAlignment = VirtualKeyboard::kAlignLeft;
+ else if (h == "centre" || h == "center")
+ _keyboard->_hAlignment = VirtualKeyboard::kAlignCentre;
+ else if (h == "right")
+ _keyboard->_hAlignment = VirtualKeyboard::kAlignRight;
+ }
+
+ if (kbdNode->values.contains("v_align")) {
+ Common::String v = kbdNode->values["h_align"];
+ if (v == "top")
+ _keyboard->_vAlignment = VirtualKeyboard::kAlignTop;
+ else if (v == "middle" || v == "center")
+ _keyboard->_vAlignment = VirtualKeyboard::kAlignMiddle;
+ else if (v == "bottom")
+ _keyboard->_vAlignment = VirtualKeyboard::kAlignBottom;
+ }
+
+ return true;
+}
+
+bool VirtualKeyboardParser::parserCallback_KeyboardClosed() {
+ _kbdParsed = true;
+ if (!_keyboard->_initialMode)
+ return parserError("Initial mode of keyboard pack not defined");
+ return true;
+}
+
+bool VirtualKeyboardParser::parserCallback_Mode() {
+ ParserNode *modeNode = getActiveNode();
+
+ assert(modeNode->name == "mode");
+
+ if (getParentNode(modeNode) == 0 || getParentNode(modeNode)->name != "keyboard")
+ return parserError("Mode element must be child of keyboard element");
+
+ if (!modeNode->values.contains("name") || !modeNode->values.contains("resolutions"))
+ return parserError("Mode element must contain name and resolutions attributes");
+
+ Common::String name = modeNode->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;
+ } else
+ _mode = &(_keyboard->_modes[name]);
+
+ Common::String resolutions = modeNode->values["resolutions"];
+ Common::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;
+ Common::String newResolution;
+ for (Common::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) {
+ modeNode->ignore = true;
+ return true;
+ } else {
+ // remove data relating to old resolution
+ ImageMan.unregisterSurface(_mode->bitmapName);
+ _mode->bitmapName.clear();
+ _mode->image = 0;
+ _mode->imageMap.removeAllAreas();
+ }
+ }
+
+ _mode->resolution = newResolution;
+ _layoutParsed = false;
+
+ return true;
+}
+
+bool VirtualKeyboardParser::parserCallback_ModeClosed() {
+ if (!_layoutParsed) {
+ return parserError("'%s' layout missing from '%s' mode", _mode->resolution.c_str(), _mode->name.c_str());
+ }
+ return true;
+}
+
+bool VirtualKeyboardParser::parserCallback_Event() {
+ ParserNode *evtNode = getActiveNode();
+
+ assert(evtNode->name == "event");
+
+ if (getParentNode(evtNode) == 0 || getParentNode(evtNode)->name != "mode")
+ return parserError("Event element must be child of mode element");
+
+ if (!evtNode->values.contains("name") || !evtNode->values.contains("type"))
+ return parserError("Event element must contain name and type attributes");
+
+ assert(_mode);
+
+ // if just checking resolutions we're done
+ if (_parseMode == kParseCheckResolutions)
+ return true;
+
+ Common::String name = evtNode->values["name"];
+ if (_mode->events.contains(name))
+ return parserError("Event '%s' has already been defined", name.c_str());
+
+ VirtualKeyboard::Event evt;
+ evt.name = name;
+
+ Common::String type = evtNode->values["type"];
+ if (type == "key") {
+ if (!evtNode->values.contains("code") || !evtNode->values.contains("ascii"))
+ return parserError("Key event element must contain code and ascii attributes");
+
+ evt.type = VirtualKeyboard::kEventKey;
+
+ Common::KeyCode code = (Common::KeyCode)atoi(evtNode->values["code"].c_str());
+ uint16 ascii = atoi(evtNode->values["ascii"].c_str());
+
+ byte flags = 0;
+ if (evtNode->values.contains("flags")) {
+ Common::StringTokenizer tok(evtNode->values["flags"], ", ");
+ for (Common::String fl = tok.nextToken(); !fl.empty(); fl = tok.nextToken()) {
+ if (fl == "ctrl" || fl == "control")
+ flags &= Common::KBD_CTRL;
+ else if (fl == "alt")
+ flags &= Common::KBD_ALT;
+ else if (fl == "shift")
+ flags &= Common::KBD_SHIFT;
+ }
+ }
+
+ evt.data = new Common::KeyState(code, ascii, flags);
+
+ } else if (type == "switch_mode") {
+ if (!evtNode->values.contains("mode"))
+ return parserError("Switch mode event element must contain mode attribute");
+
+ evt.type = VirtualKeyboard::kEventSwitchMode;
+ evt.data = new Common::String(evtNode->values["mode"]);
+ } else if (type == "close") {
+ evt.type = VirtualKeyboard::kEventClose;
+ evt.data = 0;
+ } else
+ return parserError("Event type '%s' not known", type.c_str());
+
+ _mode->events[name] = evt;
+
+ return true;
+}
+
+bool VirtualKeyboardParser::parserCallback_Layout() {
+ ParserNode *layoutNode = getActiveNode();
+
+ assert(layoutNode->name == "layout");
+
+ if (getParentNode(layoutNode) == 0 || getParentNode(layoutNode)->name != "mode")
+ return parserError("Layout element must be child of mode element");
+
+ if (!layoutNode->values.contains("resolution") || !layoutNode->values.contains("bitmap"))
+ return parserError("Layout element must contain resolution and bitmap attributes");
+
+ assert(!_mode->resolution.empty());
+
+ Common::String res = layoutNode->values["resolution"];
+
+ if (res != _mode->resolution) {
+ layoutNode->ignore = true;
+ return true;
+ }
+
+ _mode->bitmapName = layoutNode->values["bitmap"];
+
+ 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 (layoutNode->values.contains("transparent_color")) {
+ int r, g, b;
+ if (!parseIntegerKey(layoutNode->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
+ _mode->transparentColor = g_system->RGBToColor(255, 0, 255); // default to purple
+
+ _layoutParsed = true;
+
+ return true;
+}
+
+bool VirtualKeyboardParser::parserCallback_Map() {
+ ParserNode *mapNode = getActiveNode();
+
+ assert(mapNode->name == "map");
+
+ if (getParentNode(mapNode) == 0 || getParentNode(mapNode)->name != "layout")
+ return parserError("Map element must be child of layout element");
+
+ return true;
+}
+
+bool VirtualKeyboardParser::parserCallback_Area() {
+ ParserNode *areaNode = getActiveNode();
+
+ assert(areaNode->name == "area");
+
+ if (getParentNode(areaNode) == 0 || getParentNode(areaNode)->name != "map")
+ return parserError("Area element must be child of map element");
+
+ if (!areaNode->values.contains("shape") || !areaNode->values.contains("coords") || !areaNode->values.contains("target"))
+ return parserError("Area element must contain shape, coords and target attributes");
+
+ Common::String shape = areaNode->values["shape"];
+ if (shape == "rect") {
+ Common::Rect *rect = _mode->imageMap.createRectArea(areaNode->values["target"]);
+ int x1, y1, x2, y2;
+ if (!parseIntegerKey(areaNode->values["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;
+ } else if (shape == "poly") {
+ Common::StringTokenizer tok (areaNode->values["coords"], ", ");
+ Common::Polygon *poly = _mode->imageMap.createPolygonArea(areaNode->values["target"]);
+ for (Common::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");
+ } else
+ return parserError("Area shape '%s' not known", shape.c_str());
+
+ return true;
+}
+
+} // end of namespace GUI
diff --git a/backends/common/virtual-keyboard-parser.h b/backends/common/virtual-keyboard-parser.h
new file mode 100644
index 0000000000..cd2ea28faf
--- /dev/null
+++ b/backends/common/virtual-keyboard-parser.h
@@ -0,0 +1,212 @@
+/* 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$
+ *
+ */
+
+#ifndef COMMON_VIRTUAL_KEYBOARD_PARSER
+#define COMMON_VIRTUAL_KEYBOARD_PARSER
+
+#include "common/xmlparser.h"
+#include "backends/common/virtual-keyboard.h"
+
+/**
+ TODO - information about optional attributes and their default values
+
+
+ ***************************************
+ ** Virtual Keyboard Pack File Format **
+ ***************************************
+
+The new virtual keyboard for ScummVM is implemented in the same way as a HTML
+ImageMap. It uses a single bitmap of the entire keyboard layout and then a
+image map description allows certain areas of the bitmap to be given special
+actions. Most of these actions will be a virtual key press event, but there
+will also be special keys that will change the keyboard layout or close the
+keyboard. The HTML image map description is contained in a larger XML file that
+can describe all the different modes of the keyboard, and also different
+keyboard layouts for different screen resolutions.
+
+ ********************************************
+ ** Example keyboard pack description file **
+ ********************************************
+
+<keyboard modes="normal,caps" initial_mode="normal" v_align="bottom" h_align="centre">
+ <mode name="normal" resolutions="640x400,320x200">
+ <layout resolution="640x400" bitmap="normal_640x400.bmp" transparent_color="255,0,255">
+ <map>
+ <area shape="poly" coords="65,50,67,48,94,48,96,50,96,77,94,79,67,79,65,77" target="q" />
+ <area shape="poly" coords="105,50,107,48,134,48,136,50,136,77,134,79,107,79,105,77" target="w" />
+ <area shape="poly" coords="146,50,148,48,174,48,176,50,176,77,174,79,148,79,146,77" target="e" />
+ ...
+ <area shape="poly" coords="11,89,12,88,69,88,70,89,70,116,69,117,12,117,11,116" target="caps" />
+ </map>
+ </layout>
+ <layout resolution="320x200" bitmap="normal_320x200.bmp" transparent_color="255,0,255">
+ ...
+ </layout>
+ <event name="a" type="key" code="97" ascii="97" modifiers="" />
+ <event name="b" type="key" code="98" ascii="98" modifiers="" />
+ <event name="c" type="key" code="99" ascii="99" modifiers="" />
+ ...
+ <event name="caps" type="switch_mode" mode="caps" />
+ </mode>
+
+ <mode name="caps" resolutions="640x400">
+ <layout resolution="640x400" bitmap="caps_640x480.bmp" transparent_color="255,0,255">
+ <map>
+ <area shape="poly" coords="65,50,67,48,94,48,96,50,96,77,94,79,67,79,65,77" target="Q" />
+ ...
+ </map>
+ </layout>
+ <event name="A" type="key" code="97" ascii="65" modifiers="shift" />
+ <event name="B" type="key" code="98" ascii="66" modifiers="shift" />
+ <event name="C" type="key" code="99" ascii="67" modifiers="shift" />
+ ...
+ </mode>
+</keyboard>
+
+*************************
+** Description of tags **
+*************************
+
+<keyboard>
+
+This is the required, root element of the file format.
+
+attributes:
+ - modes: lists all the modes that the keyboard pack contains
+ - initial_mode: which mode the keyboard should show initially
+ - v_align/h_align: where on the screen should the keyboard appear initially
+
+child tags:
+ - mode
+
+-------------------------------------------------------------------------------
+
+<mode>
+
+This tag encapsulates a single mode of the keyboard. Within are a number of
+layouts, which provide the specific implementation at different resolutions.
+
+attributes:
+ - name: the name of the mode
+ - resolutions: list of the different layout resolutions
+
+child tags:
+ - layout
+ - event
+
+-------------------------------------------------------------------------------
+
+<event>
+
+These tags describe a particular event that will be triggered by a mouse click
+on a particular area. The target attribute of each image map area should be the
+same as an event's name.
+
+attributes:
+ - name: name of the event
+ - type: what sort of event is it (key | switch_mode | close)
+ - for key events
+ - code / ascii / modifiers: describe a key press in ScummVM KeyState format
+ - for switch_mode events
+ - mode: the mode that should be switched to
+
+-------------------------------------------------------------------------------
+
+<layout>
+
+These tags encapsulate an implementation of a mode at a particular resolution.
+
+attributes:
+ - resolution: the screen resolution that this layout is designed for
+ - bitmap: filename of the 24-bit bitmap that will be used for this layout
+ - transparent_color: color in r,b,g format that will be used for keycolor
+ transparency.
+
+child nodes:
+ - map: this describes the image map using the same format as html image maps
+
+-------------------------------------------------------------------------------
+
+<map>
+
+These tags describe the image map for a particular layout. It uses the exact
+same format as HTML image maps. The only area shapes that are supported are
+rectangles and polygons. The target attribute of each area should be the name
+of an event for this mode (see <event> tag). For information on HTML image map
+format see
+ - http://www.w3schools.com/TAGS/tag_map.asp
+ - http://www.w3schools.com/TAGS/tag_area.asp
+
+*/
+
+namespace Common {
+
+enum ParseMode {
+ kParseFull, // when loading keyboard pack for first time
+ kParseCheckResolutions // when re-parsing following a change in screen size
+};
+
+class VirtualKeyboardParser : public Common::XMLParser {
+
+ typedef bool (VirtualKeyboardParser::*ParserCallback)();
+
+public:
+
+ VirtualKeyboardParser(VirtualKeyboard *kbd);
+ void setParseMode(ParseMode m) {
+ _parseMode = m;
+ }
+
+protected:
+ VirtualKeyboard *_keyboard;
+
+ /** internal state variables of parser */
+ ParseMode _parseMode;
+ VirtualKeyboard::Mode *_mode; // pointer to mode currently being parsed
+ Common::String _initialModeName;
+ bool _kbdParsed;
+ bool _layoutParsed;
+
+ bool keyCallback(Common::String keyName);
+ bool closedKeyCallback(Common::String keyName);
+ void cleanup();
+
+ bool parserCallback_Keyboard();
+ bool parserCallback_Mode();
+ bool parserCallback_Event();
+ bool parserCallback_Layout();
+ bool parserCallback_Map();
+ bool parserCallback_Area();
+
+ bool parserCallback_KeyboardClosed();
+ bool parserCallback_ModeClosed();
+
+ Common::HashMap<Common::String, ParserCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _callbacks;
+ Common::HashMap<Common::String, ParserCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _closedCallbacks;
+};
+
+} // end of namespace GUI
+
+#endif
diff --git a/backends/common/virtual-keyboard.cpp b/backends/common/virtual-keyboard.cpp
new file mode 100644
index 0000000000..21d188dced
--- /dev/null
+++ b/backends/common/virtual-keyboard.cpp
@@ -0,0 +1,385 @@
+/* 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/common/virtual-keyboard.h"
+#include "backends/common/virtual-keyboard-parser.h"
+#include "common/config-manager.h"
+#include "common/events.h"
+#include "common/unzip.h"
+#include "graphics/cursorman.h"
+#include "graphics/imageman.h"
+#include "graphics/surface-keycolored.h"
+#include "gui/newgui.h"
+
+namespace Common {
+
+VirtualKeyboard::VirtualKeyboard() : _currentMode(0), _keyDown(0) {
+ assert(g_system);
+ _system = g_system;
+
+ _parser = new VirtualKeyboardParser(this);
+ _loaded = _displaying = _drag = false;
+ _lastScreenChanged = _system->getScreenChangeID();
+
+ memset(_cursor, 0xFF, sizeof(_cursor));
+}
+
+VirtualKeyboard::~VirtualKeyboard() {
+ // TODO: clean up event data pointers
+ deleteEventData();
+ delete _parser;
+}
+
+void VirtualKeyboard::reset() {
+ // TODO: clean up event data pointers
+ deleteEventData();
+ _modes.clear();
+ _initialMode = _currentMode = 0;
+ _kbdBound.left = _kbdBound.top
+ = _kbdBound.right = _kbdBound.bottom = 0;
+ _hAlignment = kAlignCentre;
+ _vAlignment = kAlignBottom;
+ _keyQueue.clear();
+ _keyDown = 0;
+ _displaying = _drag = false;
+ _firstRun = true;
+ _lastScreenChanged = _system->getScreenChangeID();
+}
+
+void VirtualKeyboard::deleteEventData() {
+ ModeMap::iterator it_m;
+ EventMap::iterator it_e;
+ for (it_m = _modes.begin(); it_m != _modes.end(); it_m++) {
+ EventMap *evt = &(it_m->_value.events);
+ for (it_e = evt->begin(); it_e != evt->end(); it_e++)
+ delete it_e->_value.data;
+ }
+}
+
+bool VirtualKeyboard::loadKeyboardPack(Common::String packName) {
+
+ if (Common::File::exists(packName + ".xml")) {
+ // uncompressed keyboard pack
+ if (!_parser->loadFile(packName + ".xml"))
+ return false;
+
+ } else if (Common::File::exists(packName + ".zip")) {
+ // compressed keyboard pack
+#ifdef USE_ZLIB
+ unzFile zipFile = unzOpen((packName + ".zip").c_str());
+ if (zipFile && unzLocateFile(zipFile, (packName + ".xml").c_str(), 2) == UNZ_OK) {
+ unz_file_info fileInfo;
+ unzOpenCurrentFile(zipFile);
+ unzGetCurrentFileInfo(zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
+ byte *buffer = new byte[fileInfo.uncompressed_size+1];
+ assert(buffer);
+ memset(buffer, 0, (fileInfo.uncompressed_size+1)*sizeof(byte));
+ unzReadCurrentFile(zipFile, buffer, fileInfo.uncompressed_size);
+ unzCloseCurrentFile(zipFile);
+ if (!_parser->loadBuffer(buffer, fileInfo.uncompressed_size+1, true)) {
+ unzClose(zipFile);
+ return false;
+ }
+ } else {
+ warning("Could not find %s.xml file in %s.zip keyboard pack\n", packName.c_str(), packName.c_str());
+ unzClose(zipFile);
+ return false;
+ }
+ unzClose(zipFile);
+
+ ImageMan.addArchive(packName + ".zip");
+#else
+ return false;
+#endif
+ } else {
+ warning("Keyboard pack not found");
+ return false;
+ }
+
+ _parser->setParseMode(kParseFull);
+ _loaded = _parser->parse();
+ if (_loaded)
+ printf("Keyboard pack '%s' loaded successfully!\n", packName.c_str());
+
+ return _loaded;
+}
+
+void VirtualKeyboard::setDefaultPosition()
+{
+ int16 scrW = _system->getOverlayWidth(), scrH = _system->getOverlayHeight();
+ int16 kbdW = _kbdBound.width(), kbdH = _kbdBound.height();
+ int16 posX = 0, posY = 0;
+ if (scrW != kbdW) {
+ switch (_hAlignment) {
+ case kAlignLeft:
+ posX = 0;
+ break;
+ case kAlignCentre:
+ posX = (scrW - kbdW) / 2;
+ break;
+ case kAlignRight:
+ posX = scrW - kbdW;
+ break;
+ }
+ }
+ if (scrH != kbdH) {
+ switch (_vAlignment) {
+ case kAlignTop:
+ posY = 0;
+ break;
+ case kAlignMiddle:
+ posY = (scrH - kbdH) / 2;
+ break;
+ case kAlignBottom:
+ posY = scrH - kbdH;
+ break;
+ }
+ }
+ _kbdBound.moveTo(posX, posY);
+}
+
+bool VirtualKeyboard::checkModeResolutions()
+{
+ _parser->setParseMode(kParseCheckResolutions);
+ _loaded = _parser->parse();
+ return _loaded;
+}
+
+void VirtualKeyboard::move(int16 x, int16 y) {
+ // snap to edge of screen
+ if (ABS(x) < SNAP_WIDTH)
+ x = 0;
+ int16 x2 = _system->getOverlayWidth() - _kbdBound.width();
+ if (ABS(x - x2) < SNAP_WIDTH)
+ x = x2;
+ if (ABS(y) < SNAP_WIDTH)
+ y = 0;
+ int16 y2 = _system->getOverlayHeight() - _kbdBound.height();
+ if (ABS(y - y2) < SNAP_WIDTH)
+ y = y2;
+
+ _kbdBound.moveTo(x, y);
+}
+
+Common::String VirtualKeyboard::findArea(int16 x, int16 y) {
+ x -= _kbdBound.left;
+ y -= _kbdBound.top;
+ if (x < 0 || x > _kbdBound.width()) return "";
+ if (y < 0 || y > _kbdBound.height()) return "";
+ return _currentMode->imageMap.findMapArea(x, y);
+}
+
+void VirtualKeyboard::processClick(const Common::String& area) {
+ if (!_currentMode->events.contains(area)) return;
+ Event evt = _currentMode->events[area];
+
+ switch (evt.type) {
+ case kEventKey:
+ // add virtual keypress to queue
+ _keyQueue.push(*(Common::KeyState*)evt.data);
+ break;
+ case kEventSwitchMode:
+ // switch to new mode
+ switchMode(*(Common::String *)evt.data);
+ break;
+ case kEventClose:
+ // close virtual keyboard
+ _displaying = false;
+ break;
+ }
+}
+
+void VirtualKeyboard::switchMode(Mode *newMode) {
+ _kbdBound.setWidth(newMode->image->w);
+ _kbdBound.setHeight(newMode->image->h);
+ _currentMode = newMode;
+ _needRedraw = true;
+}
+
+void VirtualKeyboard::switchMode(const Common::String& newMode) {
+ if (!_modes.contains(newMode)) {
+ warning("Keyboard mode '%s' unknown", newMode.c_str());
+ return;
+ }
+ switchMode(&_modes[newMode]);
+}
+
+void VirtualKeyboard::show() {
+ if (!_loaded) {
+ // if not loaded then load default "vkeybd" pack
+ if (!loadKeyboardPack("vkeybd")) {
+ warning("Keyboard not loaded therefore can't be shown");
+ return;
+ }
+ }
+ if (_lastScreenChanged != _system->getScreenChangeID())
+ screenChanged();
+ switchMode(_initialMode);
+ _displaying = true;
+ if (_firstRun) {
+ _firstRun = false;
+ setDefaultPosition();
+ }
+
+ if (!g_gui.isActive()) {
+ _system->showOverlay();
+ _system->clearOverlay();
+ }
+
+ _overlayBackup.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor));
+ _system->grabOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w);
+ setupCursor();
+
+ runLoop();
+
+ removeCursor();
+ _system->copyRectToOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w, 0, 0, _overlayBackup.w, _overlayBackup.h);
+ if (!g_gui.isActive()) _system->hideOverlay();
+ _overlayBackup.free();
+}
+
+void VirtualKeyboard::hide() {
+ _displaying = false;
+}
+
+void VirtualKeyboard::screenChanged() {
+ _lastScreenChanged = _system->getScreenChangeID();
+ if (!checkModeResolutions())
+ _displaying = false;
+}
+
+void VirtualKeyboard::runLoop() {
+ Common::EventManager *eventMan = _system->getEventManager();
+
+ while (_displaying) {
+ if (_needRedraw) redraw();
+
+ animateCursor();
+ _system->updateScreen();
+ Common::Event event;
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_LBUTTONDOWN:
+ if (_kbdBound.contains(event.mouse)) {
+ _areaDown = findArea(event.mouse.x, event.mouse.y);
+ if (_areaDown.empty()) {
+ _drag = true;
+ _dragPoint.x = event.mouse.x - _kbdBound.left;
+ _dragPoint.y = event.mouse.y - _kbdBound.top;
+ }
+ } else
+ _areaDown.clear();
+ break;
+ case Common::EVENT_LBUTTONUP:
+ if (_drag) _drag = false;
+ if (!_areaDown.empty() && _areaDown == findArea(event.mouse.x, event.mouse.y)) {
+ processClick(_areaDown);
+ _areaDown.clear();
+ }
+ break;
+ case Common::EVENT_MOUSEMOVE:
+ if (_drag) {
+ move(event.mouse.x - _dragPoint.x,
+ event.mouse.y - _dragPoint.y);
+ _needRedraw = true;
+ }
+ break;
+ case Common::EVENT_SCREEN_CHANGED:
+ screenChanged();
+ break;
+ case Common::EVENT_QUIT:
+ _system->quit();
+ return;
+ default:
+ break;
+ }
+ }
+ // Delay for a moment
+ _system->delayMillis(10);
+ }
+
+ // push keydown & keyup events into the event manager
+ Common::Event evt;
+ evt.synthetic = false;
+ while (!_keyQueue.empty()) {
+ evt.kbd = _keyQueue.pop();
+ evt.type = Common::EVENT_KEYDOWN;
+ eventMan->pushEvent(evt);
+ evt.type = Common::EVENT_KEYUP;
+ eventMan->pushEvent(evt);
+ }
+}
+
+void VirtualKeyboard::redraw() {
+ Graphics::SurfaceKeyColored surf;
+
+ surf.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor));
+
+ memcpy(surf.pixels, _overlayBackup.pixels, surf.w * surf.h * sizeof(OverlayColor));
+ surf.blit(_currentMode->image, _kbdBound.left, _kbdBound.top, _currentMode->transparentColor);
+
+ _system->copyRectToOverlay((OverlayColor*)surf.pixels, surf.w, 0, 0, surf.w, surf.h);
+
+ surf.free();
+
+ _needRedraw = false;
+}
+
+void VirtualKeyboard::setupCursor() {
+ const byte palette[] = {
+ 255, 255, 255, 0,
+ 255, 255, 255, 0,
+ 171, 171, 171, 0,
+ 87, 87, 87, 0
+ };
+
+ CursorMan.pushCursorPalette(palette, 0, 4);
+ CursorMan.pushCursor(NULL, 0, 0, 0, 0);
+ CursorMan.showMouse(true);
+}
+
+void VirtualKeyboard::animateCursor() {
+ int time = _system->getMillis();
+ if (time > _cursorAnimateTimer + kCursorAnimateDelay) {
+ for (int i = 0; i < 15; i++) {
+ if ((i < 6) || (i > 8)) {
+ _cursor[16 * 7 + i] = _cursorAnimateCounter;
+ _cursor[16 * i + 7] = _cursorAnimateCounter;
+ }
+ }
+
+ CursorMan.replaceCursor(_cursor, 16, 16, 7, 7);
+
+ _cursorAnimateTimer = time;
+ _cursorAnimateCounter = (_cursorAnimateCounter + 1) % 4;
+ }
+}
+
+void VirtualKeyboard::removeCursor() {
+ CursorMan.popCursor();
+ CursorMan.popCursorPalette();
+}
+
+} // end of namespace Common
diff --git a/backends/common/virtual-keyboard.h b/backends/common/virtual-keyboard.h
new file mode 100644
index 0000000000..fc2300c24c
--- /dev/null
+++ b/backends/common/virtual-keyboard.h
@@ -0,0 +1,185 @@
+/* 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$
+ *
+ */
+
+#ifndef COMMON_VIRTUAL_KEYBOARD_H
+#define COMMON_VIRTUAL_KEYBOARD_H
+
+class OSystem;
+
+#include "common/events.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/image-map.h"
+#include "common/keyboard.h"
+#include "common/queue.h"
+#include "common/str.h"
+#include "graphics/surface.h"
+
+namespace Common {
+
+class VirtualKeyboardParser;
+
+class VirtualKeyboard {
+
+ enum EventType {
+ kEventKey,
+ kEventSwitchMode,
+ kEventClose
+ };
+
+ struct Event {
+ Common::String name;
+ EventType type;
+ Graphics::Surface *rollOverImage;
+ void *data;
+ };
+
+ typedef Common::HashMap<Common::String, Event, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> EventMap;
+
+ struct Mode {
+ Common::String name;
+ Common::String resolution;
+ Common::String bitmapName;
+ Graphics::Surface *image;
+ OverlayColor transparentColor;
+ Common::ImageMap imageMap;
+ EventMap events;
+ Mode() : image(0) {}
+ };
+
+ typedef Common::HashMap<Common::String, Mode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ModeMap;
+
+ enum HorizontalAlignment {
+ kAlignLeft,
+ kAlignCentre,
+ kAlignRight
+ };
+
+ enum VerticalAlignment {
+ kAlignTop,
+ kAlignMiddle,
+ kAlignBottom
+ };
+
+public:
+ VirtualKeyboard();
+ virtual ~VirtualKeyboard();
+
+ /**
+ * Loads the keyboard pack with the given name.
+ * The system first looks for an uncompressed keyboard pack by searching
+ * for packName.xml in the filesystem, if this does not exist then it
+ * searches for a compressed keyboard pack by looking for packName.zip.
+ * @param packName name of the keyboard pack
+ */
+ bool loadKeyboardPack(Common::String packName);
+
+ /**
+ * Shows the keyboard, starting an event loop that will intercept all
+ * user input (like a modal GUI dialog).
+ * It is assumed that the game has been paused, before this is called
+ */
+ void show();
+
+ /**
+ * Hides the keyboard, ending the event loop.
+ */
+ void hide();
+
+ /**
+ * Returns true if the keyboard is currently being shown
+ */
+ bool isDisplaying() {
+ return _displaying;
+ }
+
+ /**
+ * Returns true if the keyboard is loaded and ready to be shown
+ */
+ bool isLoaded() {
+ return _loaded;
+ }
+
+protected:
+ OSystem *_system;
+
+ static const int SNAP_WIDTH = 10;
+
+ friend class VirtualKeyboardParser;
+ VirtualKeyboardParser *_parser;
+
+ // TODO : sort order of all this stuff
+ void reset();
+ void deleteEventData();
+ void screenChanged();
+ bool checkModeResolutions();
+ void setDefaultPosition();
+ void move(int16 x, int16 y);
+ void switchMode(Mode *newMode);
+ void switchMode(const Common::String& newMode);
+ Common::String findArea(int16 x, int16 y);
+ void processClick(const Common::String &area);
+ void runLoop();
+ void redraw();
+
+ Graphics::Surface _overlayBackup;
+
+ bool _loaded;
+ bool _displaying;
+ bool _needRedraw;
+ bool _firstRun;
+
+ ModeMap _modes;
+ Mode *_initialMode;
+ Mode *_currentMode;
+
+ int _lastScreenChanged;
+ Common::Rect _kbdBound;
+
+ HorizontalAlignment _hAlignment;
+ VerticalAlignment _vAlignment;
+
+ Common::String _areaDown;
+ Common::Point _dragPoint;
+ bool _drag;
+
+ Common::Queue<Common::KeyState> _keyQueue;
+ Common::KeyState *_keyDown;
+
+ static const int kCursorAnimateDelay = 250;
+ int _cursorAnimateCounter;
+ int _cursorAnimateTimer;
+ byte _cursor[2048];
+ void setupCursor();
+ void removeCursor();
+ void animateCursor();
+
+};
+
+
+} // End of namespace GUI
+
+
+#endif
diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp
index 0caba25792..3a77b0114c 100644
--- a/backends/events/default/default-events.cpp
+++ b/backends/events/default/default-events.cpp
@@ -191,9 +191,13 @@ DefaultEventManager::DefaultEventManager(OSystem *boss) :
_hasPlaybackEvent = false;
}
+
+ _vk = new Common::VirtualKeyboard();
+ _artificialEventCounter = 0;
}
DefaultEventManager::~DefaultEventManager() {
+ delete _vk;
_boss->lockMutex(_timeMutex);
_boss->lockMutex(_recorderMutex);
_recordMode = kPassthrough;
@@ -348,8 +352,17 @@ void DefaultEventManager::processMillis(uint32 &millis) {
bool DefaultEventManager::pollEvent(Common::Event &event) {
uint32 time = _boss->getMillis();
bool result;
-
- result = _boss->pollEvent(event);
+
+ if (!_artificialEventQueue.empty()) {
+ // delay the feeding of artificial events
+ if (++_artificialEventCounter % kArtificialEventDelay == 0) {
+ event = _artificialEventQueue.pop();
+ result = true;
+ _artificialEventCounter = 0;
+ } else
+ result = _boss->pollEvent(event);
+ } else
+ result = _boss->pollEvent(event);
if (_recordMode != kPassthrough) {
@@ -384,6 +397,20 @@ bool DefaultEventManager::pollEvent(Common::Event &event) {
_currentKeyDown.flags = event.kbd.flags;
_keyRepeatTime = time + kKeyRepeatInitialDelay;
#endif
+
+ // HACK to show/hide keyboard (keyboard is not shown if gui is active)
+ if (event.kbd.keycode == Common::KEYCODE_F6 && event.kbd.flags == 0) {
+ if (_vk->isDisplaying()) {
+ _vk->hide();
+ } else {
+ bool isPaused = (g_engine) ? g_engine->isPaused() : true;
+ if (!isPaused) g_engine->pauseEngine(true);
+ _vk->show();
+ if (!isPaused) g_engine->pauseEngine(false);
+ result = false;
+ }
+ }
+
break;
case Common::EVENT_KEYUP:
_modifierState = event.kbd.flags;
@@ -447,4 +474,8 @@ bool DefaultEventManager::pollEvent(Common::Event &event) {
return result;
}
+void DefaultEventManager::pushEvent(const Common::Event &event) {
+ _artificialEventQueue.push(event);
+}
+
#endif // !defined(DISABLE_DEFAULT_EVENTMANAGER)
diff --git a/backends/events/default/default-events.h b/backends/events/default/default-events.h
index 98dcd4b3de..891ee3e744 100644
--- a/backends/events/default/default-events.h
+++ b/backends/events/default/default-events.h
@@ -27,7 +27,9 @@
#define BACKEND_EVENTS_DEFAULT_H
#include "common/events.h"
+#include "common/queue.h"
#include "common/savefile.h"
+#include "backends/common/virtual-keyboard.h"
/*
At some point we will remove pollEvent from OSystem and change
@@ -44,6 +46,14 @@ use a subclass of EventProvider.
class DefaultEventManager : public Common::EventManager {
OSystem *_boss;
+ Common::VirtualKeyboard *_vk;
+
+ Common::Queue<Common::Event> _artificialEventQueue;
+ int _artificialEventCounter;
+ enum {
+ kArtificialEventDelay = 10
+ };
+
Common::Point _mousePos;
int _buttonState;
int _modifierState;
@@ -107,6 +117,7 @@ public:
~DefaultEventManager();
virtual bool pollEvent(Common::Event &event);
+ virtual void pushEvent(const Common::Event &event);
virtual void registerRandomSource(Common::RandomSource &rnd, const char *name);
virtual void processMillis(uint32 &millis);
diff --git a/backends/module.mk b/backends/module.mk
index 6642a3a281..fba5bbe907 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -27,7 +27,9 @@ MODULE_OBJS := \
saves/savefile.o \
saves/default/default-saves.o \
saves/compressed/compressed-saves.o \
- timer/default/default-timer.o
+ timer/default/default-timer.o \
+ common/virtual-keyboard.o \
+ common/virtual-keyboard-parser.o
# Include common rules
include $(srcdir)/rules.mk
diff --git a/common/events.h b/common/events.h
index d0cb740692..ba31610e92 100644
--- a/common/events.h
+++ b/common/events.h
@@ -142,6 +142,11 @@ public:
*/
virtual bool pollEvent(Common::Event &event) = 0;
+ /**
+ * Pushes a "fake" event into the event queue
+ */
+ virtual void pushEvent(const Common::Event &event) = 0;
+
/** Register random source so it can be serialized in game test purposes **/
virtual void registerRandomSource(Common::RandomSource &rnd, const char *name) = 0;
diff --git a/common/image-map.cpp b/common/image-map.cpp
new file mode 100644
index 0000000000..f9201618a4
--- /dev/null
+++ b/common/image-map.cpp
@@ -0,0 +1,79 @@
+/* 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 "common/image-map.h"
+
+namespace Common {
+
+ImageMap::~ImageMap() {
+ removeAllAreas();
+}
+
+Rect *ImageMap::createRectArea(const String& id) {
+ if (_areas.contains(id)) {
+ warning("Image map already contains an area with target of '%s'");
+ return 0;
+ }
+ Rect *r = new Rect();
+ _areas[id] = r;
+ return r;
+}
+
+Polygon *ImageMap::createPolygonArea(const String& id) {
+ if (_areas.contains(id)) {
+ warning("Image map already contains an area with target of '%s'");
+ return 0;
+ }
+ Polygon *p = new Polygon();
+ _areas[id] = p;
+ return p;
+}
+
+void ImageMap::removeArea(const String& id) {
+ if (!_areas.contains(id))
+ return;
+ delete _areas[id];
+ _areas.erase(id);
+}
+
+void ImageMap::removeAllAreas() {
+ HashMap<String, Shape*>::iterator it;
+ for (it = _areas.begin(); it != _areas.end(); it++) {
+ delete it->_value;
+ }
+ _areas.clear();
+}
+
+String ImageMap::findMapArea(int16 x, int16 y) {
+ HashMap<String, Shape*>::iterator it;
+ for (it = _areas.begin(); it != _areas.end(); it++) {
+ if (it->_value->contains(x, y))
+ return it->_key;
+ }
+ return "";
+}
+
+
+} // End of namespace Common
diff --git a/common/image-map.h b/common/image-map.h
new file mode 100644
index 0000000000..eaf803178d
--- /dev/null
+++ b/common/image-map.h
@@ -0,0 +1,55 @@
+/* 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$
+ *
+ */
+
+#ifndef COMMON_IMAGEMAP_H
+#define COMMON_IMAGEMAP_H
+
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/rect.h"
+#include "common/polygon.h"
+
+namespace Common {
+
+class ImageMap {
+
+public:
+
+ ~ImageMap();
+
+ Rect *createRectArea(const String& id);
+ Polygon *createPolygonArea(const String& id);
+ void removeArea(const String& id);
+ void removeAllAreas();
+ String findMapArea(int16 x, int16 y);
+
+protected:
+ HashMap<String, Shape*> _areas;
+};
+
+
+} // End of namespace Common
+
+#endif
diff --git a/common/keyboard.h b/common/keyboard.h
index 93579cbed6..a0ae941a08 100644
--- a/common/keyboard.h
+++ b/common/keyboard.h
@@ -259,6 +259,10 @@ struct KeyState {
keycode = KEYCODE_INVALID;
ascii = flags = 0;
}
+
+ bool operator ==(const KeyState &x) const {
+ return keycode == x.keycode && ascii == x.ascii && flags == x.flags;
+ }
};
} // End of namespace Common
diff --git a/common/list.h b/common/list.h
index c4e7b47644..0372d217b7 100644
--- a/common/list.h
+++ b/common/list.h
@@ -209,6 +209,11 @@ public:
++i;
}
+ void pop_front() {
+ iterator i = begin();
+ i = erase(i);
+ }
+
List<t_T> &operator=(const List<t_T> &list) {
if (this != &list) {
diff --git a/common/module.mk b/common/module.mk
index c3f2a38c3f..e947b7ddd9 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -6,16 +6,19 @@ MODULE_OBJS := \
config-manager.o \
file.o \
fs.o \
+ image-map.o \
hashmap.o \
memorypool.o \
md5.o \
mutex.o \
+ polygon.o \
str.o \
stream.o \
util.o \
system.o \
unarj.o \
unzip.o \
+ xmlparser.o \
zlib.o
# Include common rules
diff --git a/common/polygon.cpp b/common/polygon.cpp
new file mode 100644
index 0000000000..c1332b0bc4
--- /dev/null
+++ b/common/polygon.cpp
@@ -0,0 +1,55 @@
+/* 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 "common/polygon.h"
+
+namespace Common {
+
+bool Polygon::contains(int16 x, int16 y) const {
+ int yflag0;
+ int yflag1;
+ bool inside_flag = false;
+ unsigned int pt;
+
+ const Point *vtx0 = &_points[_points.size() - 1];
+ const Point *vtx1 = &_points[0];
+
+ yflag0 = (vtx0->y >= y);
+ for (pt = 0; pt < _points.size(); pt++, vtx1++) {
+ yflag1 = (vtx1->y >= y);
+ if (yflag0 != yflag1) {
+ if (((vtx1->y - y) * (vtx0->x - vtx1->x) >=
+ (vtx1->x - x) * (vtx0->y - vtx1->y)) == yflag1) {
+ inside_flag = !inside_flag;
+ }
+ }
+ yflag0 = yflag1;
+ vtx0 = vtx1;
+ }
+
+ return inside_flag;
+}
+
+} // end of namespace Common
diff --git a/common/polygon.h b/common/polygon.h
new file mode 100644
index 0000000000..e4a518193e
--- /dev/null
+++ b/common/polygon.h
@@ -0,0 +1,115 @@
+/* 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$
+ *
+ */
+
+#ifndef COMMON_POLYGON_H
+#define COMMON_POLYGON_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/shape.h"
+
+namespace Common {
+
+struct Polygon : public Shape {
+
+
+ Polygon() {}
+ Polygon(const Polygon& p) : Shape(), _points(p._points), _bound(p._bound) {}
+ Polygon(Array<Point> p) : _points(p) {
+ if (p.empty()) return;
+ _bound = Rect(p[0].x, p[0].y, p[0].x, p[0].y);
+ for (uint i = 1; i < p.size(); i++) {
+ _bound.extend(Rect(p[i].x, p[i].y, p[i].x, p[i].y));
+ }
+ }
+ Polygon(Point *p, int n) {
+ for (int i = 0; i < n; i++) {
+ addPoint(p[i]);
+ }
+ }
+ virtual ~Polygon() {}
+
+ void addPoint(const Point& p) {
+ _points.push_back(p);
+ _bound.extend(Rect(p.x, p.y, p.x, p.y));
+ }
+
+ void addPoint(int16 x, int16 y) {
+ addPoint(Point(x,y));
+ }
+
+ uint getPointCount() {
+ return _points.size();
+ }
+
+ /*! @brief check if given position is inside this polygon
+
+ @param x the horizontal position to check
+ @param y the vertical position to check
+
+ @return true if the given position is inside this polygon, false otherwise
+ */
+ virtual bool contains(int16 x, int16 y) const;
+
+ /*! @brief check if given point is inside this polygon
+
+ @param p the point to check
+
+ @return true if the given point is inside this polygon, false otherwise
+ */
+ virtual bool contains(const Point &p) const {
+ return contains(p.x, p.y);
+ }
+
+ virtual void moveTo(int16 x, int16 y) {
+ int16 dx = x - ((_bound.right + _bound.left) / 2);
+ int16 dy = y - ((_bound.bottom + _bound.top) / 2);
+ translate(dx, dy);
+ }
+
+ virtual void moveTo(const Point &p) {
+ moveTo(p.x, p.y);
+ }
+
+ virtual void translate(int16 dx, int16 dy) {
+ Array<Point>::iterator it;
+ for (it = _points.begin(); it != _points.end(); it++) {
+ it->x += dx;
+ it->y += dy;
+ }
+ }
+
+ virtual Rect getBoundingRect() const {
+ return _bound;
+ }
+
+private:
+ Array<Point> _points;
+ Rect _bound;
+};
+
+} // end of namespace Common
+
+#endif
diff --git a/common/queue.h b/common/queue.h
new file mode 100644
index 0000000000..cb29c59058
--- /dev/null
+++ b/common/queue.h
@@ -0,0 +1,71 @@
+/* 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$
+ */
+
+#ifndef COMMON_QUEUE_H
+#define COMMON_QUEUE_H
+
+#include "common/scummsys.h"
+#include "common/list.h"
+
+namespace Common {
+
+/**
+ * Variable size Queue class, implemented using our Array class.
+ */
+template<class T>
+class Queue {
+protected:
+ List<T> _queue;
+public:
+ Queue<T>() {}
+ Queue<T>(const List<T> &queueContent) : _queue(queueContent) {}
+
+ bool empty() const {
+ return _queue.empty();
+ }
+ void clear() {
+ _queue.clear();
+ }
+ void push(const T &x) {
+ _queue.push_back(x);
+ }
+ T back() const {
+ return _queue.reverse_begin().operator*();
+ }
+ T front() const {
+ return _queue.begin().operator*();
+ }
+ T pop() {
+ T tmp = front();
+ _queue.pop_front();
+ return tmp;
+ }
+ int size() const {
+ return _queue.size();
+ }
+};
+
+} // End of namespace Common
+
+#endif
diff --git a/common/rect.h b/common/rect.h
index f71124434a..0c149f613f 100644
--- a/common/rect.h
+++ b/common/rect.h
@@ -26,45 +26,10 @@
#ifndef COMMON_RECT_H
#define COMMON_RECT_H
-#include "common/scummsys.h"
-#include "common/util.h"
+#include "common/shape.h"
namespace Common {
-/*! @brief simple class for handling both 2D position and size
-
- This small class is an helper for position and size values.
-*/
-struct Point {
- int16 x; //!< The horizontal part of the point
- int16 y; //!< The vertical part of the point
-
- Point() : x(0), y(0) {}
- Point(const Point &p) : x(p.x), y(p.y) {}
- explicit Point(int16 x1, int16 y1) : x(x1), y(y1) {}
- Point & operator=(const Point & p) { x = p.x; y = p.y; return *this; };
- bool operator==(const Point & p) const { return x == p.x && y == p.y; };
- bool operator!=(const Point & p) const { return x != p.x || y != p.y; };
-
- /**
- * Return the square of the distance between this point and the point p.
- *
- * @param p the other point
- * @return the distance between this and p
- */
- uint sqrDist(const Point & p) const {
- int diffx = ABS(p.x - x);
- if (diffx >= 0x1000)
- return 0xFFFFFF;
-
- int diffy = ABS(p.y - y);
- if (diffy >= 0x1000)
- return 0xFFFFFF;
-
- return uint(diffx*diffx + diffy*diffy);
- }
-};
-
/*! @brief simple class for handling a rectangular zone.
This small class is an helper for rectangles.
@@ -75,7 +40,7 @@ struct Point {
Another very wide spread approach to rectangle classes treats (bottom,right)
also as a part of the rectangle.
- Coneptually, both are sound, but the approach we use saves many intermediate
+ Conceptually, both are sound, but the approach we use saves many intermediate
computations (like computing the height in our case is done by doing this:
height = bottom - top;
while in the alternate system, it would be
@@ -83,7 +48,7 @@ struct Point {
When writing code using our Rect class, always keep this principle in mind!
*/
-struct Rect {
+struct Rect : public Shape {
int16 top, left; //!< The point at the top left of the rectangle (part of the rect).
int16 bottom, right; //!< The point at the bottom right of the rectangle (not part of the rect).
@@ -92,6 +57,8 @@ struct Rect {
Rect(int16 x1, int16 y1, int16 x2, int16 y2) : top(y1), left(x1), bottom(y2), right(x2) {
assert(isValidRect());
}
+ virtual ~Rect() {}
+
int16 width() const { return right - left; }
int16 height() const { return bottom - top; }
@@ -110,7 +77,7 @@ struct Rect {
@return true if the given position is inside this rectangle, false otherwise
*/
- bool contains(int16 x, int16 y) const {
+ virtual bool contains(int16 x, int16 y) const {
return (left <= x) && (x < right) && (top <= y) && (y < bottom);
}
@@ -120,7 +87,7 @@ struct Rect {
@return true if the given point is inside this rectangle, false otherwise
*/
- bool contains(const Point &p) const {
+ virtual bool contains(const Point &p) const {
return contains(p.x, p.y);
}
@@ -181,19 +148,19 @@ struct Rect {
return (left <= right && top <= bottom);
}
- void moveTo(int16 x, int16 y) {
+ virtual void moveTo(int16 x, int16 y) {
bottom += y - top;
right += x - left;
top = y;
left = x;
}
- void translate(int16 dx, int16 dy) {
+ virtual void translate(int16 dx, int16 dy) {
left += dx; right += dx;
top += dy; bottom += dy;
}
- void moveTo(const Point &p) {
+ virtual void moveTo(const Point &p) {
moveTo(p.x, p.y);
}
@@ -207,6 +174,10 @@ struct Rect {
h /= 2;
return Rect(cx - w, cy - h, cx + w, cy + h);
}
+
+ virtual Rect getBoundingRect() const {
+ return *this;
+ }
};
} // End of namespace Common
diff --git a/common/shape.h b/common/shape.h
new file mode 100644
index 0000000000..2cce365bfc
--- /dev/null
+++ b/common/shape.h
@@ -0,0 +1,105 @@
+/* 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$
+ *
+ */
+
+#ifndef COMMON_SHAPE_H
+#define COMMON_SHAPE_H
+
+#include "common/scummsys.h"
+#include "common/util.h"
+
+namespace Common {
+
+struct Rect;
+
+/*! @brief simple class for handling both 2D position and size
+
+ This small class is an helper for position and size values.
+*/
+struct Point {
+ int16 x; //!< The horizontal part of the point
+ int16 y; //!< The vertical part of the point
+
+ Point() : x(0), y(0) {}
+ Point(const Point &p) : x(p.x), y(p.y) {}
+ explicit Point(int16 x1, int16 y1) : x(x1), y(y1) {}
+ Point & operator=(const Point & p) { x = p.x; y = p.y; return *this; };
+ bool operator==(const Point & p) const { return x == p.x && y == p.y; };
+ bool operator!=(const Point & p) const { return x != p.x || y != p.y; };
+
+ /**
+ * Return the square of the distance between this point and the point p.
+ *
+ * @param p the other point
+ * @return the distance between this and p
+ */
+ uint sqrDist(const Point & p) const {
+ int diffx = ABS(p.x - x);
+ if (diffx >= 0x1000)
+ return 0xFFFFFF;
+
+ int diffy = ABS(p.y - y);
+ if (diffy >= 0x1000)
+ return 0xFFFFFF;
+
+ return uint(diffx*diffx + diffy*diffy);
+ }
+};
+
+/*! @brief simple interface that provides common methods for 2D shapes
+
+*/
+struct Shape {
+
+ virtual ~Shape() {}
+ /*! @brief check if given position is inside this shape
+
+ @param x the horizontal position to check
+ @param y the vertical position to check
+
+ @return true if the given position is inside this shape, false otherwise
+ */
+ virtual bool contains(int16 x, int16 y) const = 0;
+
+ /*! @brief check if given point is inside this shape
+
+ @param p the point to check
+
+ @return true if the given point is inside this shape, false otherwise
+ */
+ virtual bool contains(const Point &p) const = 0;
+
+ virtual void moveTo(int16 x, int16 y) = 0;
+
+ virtual void moveTo(const Point &p) = 0;
+
+ virtual void translate(int16 dx, int16 dy) = 0;
+
+ virtual Rect getBoundingRect() const = 0;
+
+};
+
+} // end of namespace Common
+
+#endif
diff --git a/common/xmlparser.cpp b/common/xmlparser.cpp
new file mode 100644
index 0000000000..d0c89a9d3e
--- /dev/null
+++ b/common/xmlparser.cpp
@@ -0,0 +1,261 @@
+/* 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 "common/util.h"
+#include "common/system.h"
+#include "common/events.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/xmlparser.h"
+
+namespace Common {
+
+using namespace Graphics;
+
+bool XMLParser::parserError(const char *errorString, ...) {
+ _state = kParserError;
+
+ int pos = _pos;
+ int lineCount = 1;
+ int lineStart = 0;
+
+ do {
+ if (_text[pos] == '\n' || _text[pos] == '\r') {
+ lineCount++;
+
+ if (lineStart == 0)
+ lineStart = MAX(pos + 1, _pos - 60);
+ }
+ } while (pos-- > 0);
+
+ char lineStr[70];
+ _text.stream()->seek(lineStart, SEEK_SET);
+ _text.stream()->readLine(lineStr, 70);
+
+ printf(" File <%s>, line %d:\n", _fileName.c_str(), lineCount);
+
+ bool startFull = lineStr[0] == '<';
+ bool endFull = lineStr[strlen(lineStr) - 1] == '>';
+
+ printf("%s%s%s\n", startFull ? "" : "...", endFull ? "" : "...", lineStr);
+
+ int cursor = MIN(_pos - lineStart, 70);
+
+ if (!startFull)
+ cursor += 3;
+
+ while (cursor--)
+ printf(" ");
+
+ printf("^\n");
+ printf("Parser error: ");
+
+ va_list args;
+ va_start(args, errorString);
+ vprintf(errorString, args);
+ va_end(args);
+
+ printf("\n");
+
+ return false;
+}
+
+bool XMLParser::parseActiveKey(bool closed) {
+ bool ignore = false;
+
+ // check if any of the parents must be ignored.
+ // if a parent is ignored, all children are too.
+ for (int i = _activeKey.size() - 1; i >= 0; --i) {
+ if (_activeKey[i]->ignore)
+ ignore = true;
+ }
+
+ if (ignore == false && keyCallback(_activeKey.top()->name) == false) {
+ return false;
+ }
+
+ if (closed) {
+ delete _activeKey.pop();
+ }
+
+ return true;
+}
+
+bool XMLParser::parseKeyValue(Common::String keyName) {
+ assert(_activeKey.empty() == false);
+
+ if (_activeKey.top()->values.contains(keyName))
+ return false;
+
+ _token.clear();
+ char stringStart;
+
+ if (_text[_pos] == '"' || _text[_pos] == '\'') {
+ stringStart = _text[_pos++];
+
+ while (_text[_pos] && _text[_pos] != stringStart)
+ _token += _text[_pos++];
+
+ if (_text[_pos++] == 0)
+ return false;
+
+ } else if (!parseToken()) {
+ return false;
+ }
+
+ _activeKey.top()->values[keyName] = _token;
+ return true;
+}
+
+bool XMLParser::parse() {
+
+ if (_text.ready() == false)
+ return parserError("XML stream not ready for reading.");
+
+ cleanup();
+
+ bool activeClosure = false;
+ bool selfClosure = false;
+
+ _state = kParserNeedKey;
+ _pos = 0;
+ _activeKey.clear();
+
+ while (_text[_pos] && _state != kParserError) {
+ if (skipSpaces())
+ continue;
+
+ if (skipComments())
+ continue;
+
+ switch (_state) {
+ case kParserNeedKey:
+ if (_text[_pos++] != '<') {
+ parserError("Parser expecting key start.");
+ break;
+ }
+
+ if (_text[_pos] == 0) {
+ parserError("Unexpected end of file.");
+ break;
+ }
+
+ if (_text[_pos] == '/' && _text[_pos + 1] != '*') {
+ _pos++;
+ activeClosure = true;
+ }
+
+ _state = kParserNeedKeyName;
+ break;
+
+ case kParserNeedKeyName:
+ if (!parseToken()) {
+ parserError("Invalid key name.");
+ break;
+ }
+
+ if (activeClosure) {
+ if (_activeKey.empty() || _token != _activeKey.top()->name) {
+ parserError("Unexpected closure.");
+ break;
+ }
+ } else {
+ ParserNode *node = new ParserNode;
+ node->name = _token;
+ node->ignore = false;
+ node->depth = _activeKey.size();
+ _activeKey.push(node);
+ }
+
+ _state = kParserNeedPropertyName;
+ break;
+
+ case kParserNeedPropertyName:
+ if (activeClosure) {
+ if (!closedKeyCallback(_activeKey.top()->name)) {
+ parserError("Missing data when closing key '%s'.", _activeKey.top()->name.c_str());
+ break;
+ }
+
+ activeClosure = false;
+ delete _activeKey.pop();
+
+ if (_text[_pos++] != '>')
+ parserError("Invalid syntax in key closure.");
+ else
+ _state = kParserNeedKey;
+
+ break;
+ }
+
+ selfClosure = (_text[_pos] == '/');
+
+ if ((selfClosure && _text[_pos + 1] == '>') || _text[_pos] == '>') {
+ if (parseActiveKey(selfClosure)) {
+ _pos += selfClosure ? 2 : 1;
+ _state = kParserNeedKey;
+ }
+ break;
+ }
+
+ if (!parseToken())
+ parserError("Error when parsing key value.");
+ else
+ _state = kParserNeedPropertyOperator;
+
+ break;
+
+ case kParserNeedPropertyOperator:
+ if (_text[_pos++] != '=')
+ parserError("Syntax error after key name.");
+ else
+ _state = kParserNeedPropertyValue;
+
+ break;
+
+ case kParserNeedPropertyValue:
+ if (!parseKeyValue(_token))
+ parserError("Invalid key value.");
+ else
+ _state = kParserNeedPropertyName;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (_state == kParserError)
+ return false;
+
+ if (_state != kParserNeedKey || !_activeKey.empty())
+ return parserError("Unexpected end of file.");
+
+ return true;
+}
+
+}
+
diff --git a/common/xmlparser.h b/common/xmlparser.h
new file mode 100644
index 0000000000..4c77696482
--- /dev/null
+++ b/common/xmlparser.h
@@ -0,0 +1,369 @@
+/* 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$
+ *
+ */
+
+#ifndef XML_PARSER_H
+#define XML_PARSER_H
+
+#include "common/scummsys.h"
+#include "graphics/surface.h"
+#include "common/system.h"
+#include "common/xmlparser.h"
+#include "common/stream.h"
+#include "common/file.h"
+
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "common/stack.h"
+
+namespace Common {
+
+class XMLStream {
+protected:
+ SeekableReadStream *_stream;
+ int _pos;
+
+public:
+ XMLStream() : _stream(0), _pos(0) {}
+
+ ~XMLStream() {
+ delete _stream;
+ }
+
+ SeekableReadStream *stream() {
+ return _stream;
+ }
+
+ char operator [](int idx) {
+ assert(_stream && idx >= 0);
+
+ if (_pos + 1 != idx)
+ _stream->seek(idx, SEEK_SET);
+
+ _pos = idx;
+
+ return _stream->readByte();
+ }
+
+ void loadStream(SeekableReadStream *s) {
+ delete _stream;
+ _stream = s;
+ }
+
+ bool ready() {
+ return _stream != 0;
+ }
+};
+
+/**
+ * The base XMLParser class implements generic functionality for parsing
+ * XML-like files.
+ *
+ * In order to use it, it must be inherited with a child class that implements
+ * the XMLParser::keyCallback() function.
+ *
+ * @see XMLParser::keyCallback()
+ */
+class XMLParser {
+
+public:
+ /**
+ * Parser constructor.
+ */
+ XMLParser() {}
+
+ virtual ~XMLParser() {
+ while (!_activeKey.empty())
+ delete _activeKey.pop();
+ }
+
+ /** Active state for the parser */
+ enum ParserState {
+ kParserNeedKey,
+ kParserNeedKeyName,
+
+ kParserNeedPropertyName,
+ kParserNeedPropertyOperator,
+ kParserNeedPropertyValue,
+
+ kParserError
+ };
+
+ /** Struct representing a parsed node */
+ struct ParserNode {
+ Common::String name;
+ Common::StringMap values;
+ bool ignore;
+ int depth;
+ };
+
+ /**
+ * Loads a file into the parser.
+ * Used for the loading of Theme Description files
+ * straight from the filesystem.
+ *
+ * @param filename Name of the file to load.
+ */
+ virtual bool loadFile(Common::String filename) {
+ Common::File *f = new Common::File;
+
+ if (!f->open(filename, Common::File::kFileReadMode))
+ return false;
+
+ _fileName = filename;
+ _text.loadStream(f);
+ return true;
+ }
+
+ /**
+ * Loads a memory buffer into the parser.
+ * Used for loading the default theme fallback directly
+ * from memory if no themes can be found.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param size Size of the buffer
+ * @param disposable Sets if the XMLParser owns the buffer,
+ * i.e. if it can be freed safely after it's
+ * no longer needed by the parser.
+ */
+ virtual bool loadBuffer(const byte *buffer, uint32 size, bool disposable = false) {
+ _text.loadStream(new MemoryReadStream(buffer, size, disposable));
+ _fileName = "Memory Stream";
+ return true;
+ }
+
+ /**
+ * The actual parsing function.
+ * Parses the loaded data stream, returns true if successful.
+ */
+ virtual bool parse();
+
+ /**
+ * Returns the active node being parsed (the one on top of
+ * the node stack).
+ */
+ ParserNode *getActiveNode() {
+ if (!_activeKey.empty())
+ return _activeKey.top();
+
+ return 0;
+ }
+
+ /**
+ * Returns the parent of a given node in the stack.
+ */
+ ParserNode *getParentNode(ParserNode *child) {
+ return child->depth > 0 ? _activeKey[child->depth - 1] : 0;
+ }
+
+protected:
+ /**
+ * The keycallback function must be overloaded by inheriting classes
+ * to implement parser-specific functions.
+ *
+ * This function is called everytime a key has successfully been parsed.
+ * The keyName parameter contains the name of the key that has just been
+ * parsed; this same key is still on top of the Node Stack.
+ *
+ * Access the node stack to view this key's properties and its parents.
+ * Remember to leave the node stack _UNCHANGED_ in your own function. Removal
+ * of closed keys is done automatically.
+ *
+ * When parsing a key, one may chose to skip it, e.g. because it's not needed
+ * on the current configuration. In order to ignore a key, you must set
+ * the "ignore" field of its KeyNode struct to "true": The key and all its children
+ * will then be automatically ignored by the parser.
+ *
+ * Return true if the key was properly handled (this includes the case when the
+ * key is being ignored). False otherwise.
+ * See the sample implementation in GUI::ThemeParser.
+ */
+ virtual bool keyCallback(Common::String keyName) {
+ return false;
+ }
+
+ /**
+ * The closed key callback function must be overloaded by inheriting classes to
+ * implement parser-specific functions.
+ *
+ * The closedKeyCallback is issued once a key has been finished parsing, to let
+ * the parser verify that all the required subkeys, etc, were included.
+ *
+ * Returns true if the key was properly closed, false otherwise.
+ * By default, all keys are properly closed.
+ */
+ virtual bool closedKeyCallback(Common::String keyName) {
+ return true;
+ }
+
+ /**
+ * Parses the value of a given key. There's no reason to overload this.
+ */
+ virtual bool parseKeyValue(Common::String keyName);
+
+ /**
+ * Called once a key has been parsed. It handles the closing/cleanup of the
+ * node stack and calls the keyCallback.
+ * There's no reason to overload this.
+ */
+ virtual bool parseActiveKey(bool closed);
+
+ /**
+ * Prints an error message when parsing fails and stops the parser.
+ * Parser error always returns "false" so we can pass the return value directly
+ * and break down the parsing.
+ */
+ virtual bool parserError(const char *errorString, ...) GCC_PRINTF(2, 3);
+
+ /**
+ * Skips spaces/whitelines etc. Returns true if any spaces were skipped.
+ * Overload this if you want to make your parser depend on newlines or
+ * whatever.
+ */
+ virtual bool skipSpaces() {
+ if (!isspace(_text[_pos]))
+ return false;
+
+ while (_text[_pos] && isspace(_text[_pos]))
+ _pos++;
+
+ return true;
+ }
+
+ /**
+ * Skips comment blocks and comment lines.
+ * Returns true if any comments were skipped.
+ * Overload this if you want to disable comments on your XML syntax
+ * or to change the commenting syntax.
+ */
+ virtual bool skipComments() {
+ if (_text[_pos] == '/' && _text[_pos + 1] == '*') {
+ _pos += 2;
+ while (_text[_pos++]) {
+ if (_text[_pos - 2] == '*' && _text[_pos - 1] == '/')
+ break;
+ if (_text[_pos] == 0)
+ parserError("Comment has no closure.");
+ }
+ return true;
+ }
+
+ if (_text[_pos] == '/' && _text[_pos + 1] == '/') {
+ _pos += 2;
+ while (_text[_pos] && _text[_pos] != '\n' && _text[_pos] != '\r')
+ _pos++;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check if a given character can be part of a KEY or VALUE name.
+ * Overload this if you want to support keys with strange characters
+ * in their name.
+ */
+ virtual bool isValidNameChar(char c) {
+ return isalnum(c) || c == '_';
+ }
+
+ /**
+ * Parses a the first textual token found.
+ * There's no reason to overload this.
+ */
+ virtual bool parseToken() {
+ _token.clear();
+ while (isValidNameChar(_text[_pos]))
+ _token += _text[_pos++];
+
+ return isspace(_text[_pos]) != 0 || _text[_pos] == '>' || _text[_pos] == '=';
+ }
+
+ /**
+ * Parses the values inside an integer key.
+ * The count parameter specifies the number of values inside
+ * the key, which are expected to be separated with commas.
+ *
+ * Sample usage:
+ * parseIntegerKey("255, 255, 255", 3, &red, &green, &blue);
+ * [will parse each field into its own integer]
+ *
+ * parseIntegerKey("1234", 1, &number);
+ * [will parse the single number into the variable]
+ *
+ * @param key String containing the integers to be parsed.
+ * @param count Number of comma-separated ints in the string.
+ * @param ... Integer variables to store the parsed ints, passed
+ * by reference.
+ * @returns True if the parsing succeeded.
+ */
+ virtual bool parseIntegerKey(const char *key, int count, ...) {
+ char *parseEnd;
+ int *num_ptr;
+
+ va_list args;
+ va_start(args, count);
+
+ while (count--) {
+ while (isspace(*key))
+ key++;
+
+ num_ptr = va_arg(args, int*);
+ *num_ptr = strtol(key, &parseEnd, 10);
+
+ key = parseEnd;
+
+ while (isspace(*key))
+ key++;
+
+ if (count && *key++ != ',')
+ return false;
+ }
+
+ va_end(args);
+ return (*key == 0);
+ }
+
+ /**
+ * Overload if your parser needs to support parsing the same file
+ * several times, so you can clean up the internal state of the
+ * parser before each parse.
+ */
+ virtual void cleanup() {}
+
+ int _pos; /** Current position on the XML buffer. */
+ XMLStream _text; /** Buffer with the text being parsed */
+ Common::String _fileName;
+
+ ParserState _state; /** Internal state of the parser */
+
+ Common::String _error; /** Current error message */
+ Common::String _token; /** Current text token */
+
+ Common::Stack<ParserNode*> _activeKey; /** Node stack of the parsed keys */
+};
+
+}
+
+#endif
diff --git a/dists/msvc8/scummvm.vcproj b/dists/msvc8/scummvm.vcproj
index b144dc0b79..bab456e3d7 100644
--- a/dists/msvc8/scummvm.vcproj
+++ b/dists/msvc8/scummvm.vcproj
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
- Version="8,00"
+ Version="8.00"
Name="scummvm"
ProjectGUID="{8434CB15-D08F-427D-9E6D-581AE5B28440}"
RootNamespace="scummvm"
@@ -335,6 +335,14 @@
>
</File>
<File
+ RelativePath="..\..\common\image-map.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\image-map.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\keyboard.h"
>
</File>
@@ -379,10 +387,22 @@
>
</File>
<File
+ RelativePath="..\..\common\polygon.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\polygon.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\ptr.h"
>
</File>
<File
+ RelativePath="..\..\common\queue.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\rect.h"
>
</File>
@@ -395,6 +415,10 @@
>
</File>
<File
+ RelativePath="..\..\common\shape.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\singleton.h"
>
</File>
@@ -455,6 +479,14 @@
>
</File>
<File
+ RelativePath="..\..\common\xmlparser.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\common\xmlparser.h"
+ >
+ </File>
+ <File
RelativePath="..\..\common\zlib.cpp"
>
</File>
@@ -1021,6 +1053,58 @@
</File>
</Filter>
</Filter>
+ <Filter
+ Name="common"
+ >
+ <File
+ RelativePath="..\..\backends\common\hardware-key.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\keymap-manager.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\keymap-manager.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\keymap.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\keymap.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\keymapper.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\keymapper.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\user-action.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\virtual-keyboard-parser.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\virtual-keyboard-parser.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\virtual-keyboard.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\backends\common\virtual-keyboard.h"
+ >
+ </File>
+ </Filter>
</Filter>
<Filter
Name="gui"
@@ -1314,6 +1398,14 @@
>
</File>
<File
+ RelativePath="..\..\graphics\surface-keycolored.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\graphics\surface-keycolored.h"
+ >
+ </File>
+ <File
RelativePath="..\..\graphics\surface.cpp"
>
</File>
diff --git a/graphics/module.mk b/graphics/module.mk
index 93e2db26c5..a44042a044 100644
--- a/graphics/module.mk
+++ b/graphics/module.mk
@@ -16,7 +16,8 @@ MODULE_OBJS := \
primitives.o \
scaler.o \
scaler/thumbnail.o \
- surface.o
+ surface.o \
+ surface-keycolored.o
ifndef DISABLE_SCALERS
MODULE_OBJS += \
diff --git a/graphics/surface-keycolored.cpp b/graphics/surface-keycolored.cpp
new file mode 100644
index 0000000000..f533213fb6
--- /dev/null
+++ b/graphics/surface-keycolored.cpp
@@ -0,0 +1,44 @@
+#include "graphics/surface-keycolored.h"
+
+namespace Graphics {
+
+void SurfaceKeyColored::blit(Surface *surf_src, int16 x, int16 y, OverlayColor transparent) {
+
+ if (bytesPerPixel != sizeof(OverlayColor) || surf_src->bytesPerPixel != sizeof(OverlayColor)) return ;
+
+ const OverlayColor *src = (const OverlayColor*)surf_src->pixels;
+ int blitW = surf_src->w;
+ int blitH = surf_src->h;
+
+ // clip co-ordinates
+ if (x < 0) {
+ blitW += x;
+ src -= x;
+ x = 0;
+ }
+ if (y < 0) {
+ blitH += y;
+ src -= y * surf_src->w;
+ y = 0;
+ }
+ if (blitW > w - x) blitW = w - x;
+ if (blitH > h - y) blitH = h - y;
+ if (blitW <= 0 || blitH <= 0)
+ return;
+
+ OverlayColor *dst = (OverlayColor*) getBasePtr(x, y);
+ int dstAdd = w - blitW;
+ int srcAdd = surf_src->w - blitW;
+
+ for (int i = 0; i < blitH; ++i) {
+ for (int j = 0; j < blitW; ++j, ++dst, ++src) {
+ OverlayColor col = *src;
+ if (col != transparent)
+ *dst = col;
+ }
+ dst += dstAdd;
+ src += srcAdd;
+ }
+}
+
+} // end of namespace Graphics \ No newline at end of file
diff --git a/graphics/surface-keycolored.h b/graphics/surface-keycolored.h
new file mode 100644
index 0000000000..608bf7a482
--- /dev/null
+++ b/graphics/surface-keycolored.h
@@ -0,0 +1,17 @@
+
+#ifndef GRAPHICS_SURFACE_KEYCOLORED_H
+#define GRAPHICS_SURFACE_KEYCOLORED_H
+
+#include "graphics/surface.h"
+
+namespace Graphics {
+
+struct SurfaceKeyColored : Surface {
+
+ void blit(Surface *surf_src, int16 x, int16 y, OverlayColor transparent);
+};
+
+
+} // end of namespace Graphics
+
+#endif \ No newline at end of file