aboutsummaryrefslogtreecommitdiff
path: root/backends/vkeybd/virtual-keyboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backends/vkeybd/virtual-keyboard.cpp')
-rw-r--r--backends/vkeybd/virtual-keyboard.cpp390
1 files changed, 390 insertions, 0 deletions
diff --git a/backends/vkeybd/virtual-keyboard.cpp b/backends/vkeybd/virtual-keyboard.cpp
new file mode 100644
index 0000000000..fab2d80d30
--- /dev/null
+++ b/backends/vkeybd/virtual-keyboard.cpp
@@ -0,0 +1,390 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "backends/vkeybd/virtual-keyboard.h"
+#include "backends/vkeybd/virtual-keyboard-gui.h"
+#include "backends/vkeybd/virtual-keyboard-parser.h"
+#include "backends/vkeybd/keycode-descriptions.h"
+#include "common/config-manager.h"
+#include "common/fs.h"
+#include "graphics/imageman.h"
+#include "common/unzip.h"
+
+#define KEY_START_CHAR ('[')
+#define KEY_END_CHAR (']')
+
+namespace Common {
+
+VirtualKeyboard::VirtualKeyboard() : _currentMode(0) {
+ assert(g_system);
+ _system = g_system;
+
+ _parser = new VirtualKeyboardParser(this);
+ _kbdGUI = new VirtualKeyboardGUI(this);
+ _submitKeys = _loaded = false;
+
+}
+
+VirtualKeyboard::~VirtualKeyboard() {
+ deleteEvents();
+ delete _kbdGUI;
+ delete _parser;
+}
+
+void VirtualKeyboard::deleteEvents() {
+ ModeMap::iterator it_m;
+ VKEventMap::iterator it_e;
+ for (it_m = _modes.begin(); it_m != _modes.end(); it_m++) {
+ VKEventMap *evt = &(it_m->_value.events);
+ for (it_e = evt->begin(); it_e != evt->end(); it_e++)
+ delete it_e->_value;
+ }
+}
+
+void VirtualKeyboard::reset() {
+ deleteEvents();
+ _modes.clear();
+ _initialMode = _currentMode = 0;
+ _hAlignment = kAlignCentre;
+ _vAlignment = kAlignBottom;
+ _keyQueue.clear();
+ _loaded = false;
+ _kbdGUI->reset();
+}
+
+bool VirtualKeyboard::loadKeyboardPack(String packName) {
+
+ _kbdGUI->initSize(_system->getOverlayWidth(), _system->getOverlayHeight());
+
+ FilesystemNode *vkDir = 0;
+ if (ConfMan.hasKey("vkeybdpath")) {
+ vkDir = new FilesystemNode(ConfMan.get("vkeybdpath"));
+ } else if (ConfMan.hasKey("extrapath")) {
+ vkDir = new FilesystemNode(ConfMan.get("extrapath"));
+ } else { // use current directory
+ vkDir = new FilesystemNode(".");
+ }
+
+ if (vkDir->getChild(packName + ".xml").exists()) {
+ // uncompressed keyboard pack
+
+ if (!_parser->loadFile(vkDir->getChild(packName + ".xml")))
+ return false;
+
+ } else if (vkDir->getChild(packName + ".zip").exists()) {
+ // compressed keyboard pack
+#ifdef USE_ZLIB
+ ZipArchive arch(vkDir->getChild(packName + ".zip").getPath().c_str());
+ if (arch.hasFile(packName + ".xml")) {
+ if (!_parser->loadStream(arch.openFile(packName + ".xml")))
+ return false;
+ } else {
+ warning("Could not find %s.xml file in %s.zip keyboard pack", packName.c_str(), packName.c_str());
+ return false;
+ }
+ ImageMan.addArchive(vkDir->getChild(packName + ".zip").getPath().c_str());
+#else
+ return false;
+#endif
+ } else {
+ warning("Keyboard pack not found");
+ return false;
+ }
+
+ _parser->setParseMode(VirtualKeyboardParser::kParseFull);
+ _loaded = _parser->parse();
+ if (_loaded)
+ printf("Keyboard pack '%s' loaded successfully!\n", packName.c_str());
+
+ return _loaded;
+}
+
+bool VirtualKeyboard::checkModeResolutions()
+{
+ _parser->setParseMode(VirtualKeyboardParser::kParseCheckResolutions);
+ _loaded = _parser->parse();
+ if (_currentMode) _kbdGUI->initMode(_currentMode);
+ return _loaded;
+}
+
+String VirtualKeyboard::findArea(int16 x, int16 y) {
+ return _currentMode->imageMap.findMapArea(x, y);
+}
+
+void VirtualKeyboard::processAreaClick(const String& area) {
+ if (!_currentMode->events.contains(area)) return;
+ VKEvent *evt = _currentMode->events[area];
+
+ switch (evt->type) {
+ case kVKEventKey: {
+ // add virtual keypress to queue
+ _keyQueue.insertKey(*(KeyState*)evt->data);
+ break;
+ }
+ case kVKEventModifier:
+ _keyQueue.toggleFlags(*(byte*)(evt->data));
+ break;
+ case kVKEventSwitchMode:
+ // switch to new mode
+ switchMode((char *)evt->data);
+ _keyQueue.clearFlags();
+ break;
+ case kVKEventSubmit:
+ close(true);
+ break;
+ case kVKEventCancel:
+ close(false);
+ break;
+ case kVKEventClear:
+ _keyQueue.clear();
+ break;
+ case kVKEventDelete:
+ _keyQueue.deleteKey();
+ break;
+ case kVKEventMoveLeft:
+ _keyQueue.moveLeft();
+ break;
+ case kVKEventMoveRight:
+ _keyQueue.moveRight();
+ break;
+ }
+}
+
+void VirtualKeyboard::switchMode(Mode *newMode) {
+ _kbdGUI->initMode(newMode);
+ _currentMode = newMode;
+}
+
+void VirtualKeyboard::switchMode(const String& newMode) {
+ if (!_modes.contains(newMode)) {
+ warning("Keyboard mode '%s' unknown", newMode.c_str());
+ return;
+ }
+ switchMode(&_modes[newMode]);
+}
+
+void VirtualKeyboard::handleMouseDown(int16 x, int16 y) {
+ _areaDown = findArea(x, y);
+ if (_areaDown.empty())
+ _kbdGUI->startDrag(x, y);
+}
+
+void VirtualKeyboard::handleMouseUp(int16 x, int16 y) {
+ if (!_areaDown.empty() && _areaDown == findArea(x, y)) {
+ processAreaClick(_areaDown);
+ _areaDown.clear();
+ }
+ _kbdGUI->endDrag();
+}
+
+void VirtualKeyboard::show() {
+ if (_loaded) _kbdGUI->checkScreenChanged();
+ if (!_loaded) {
+ warning("Virtual keyboard not loaded");
+ return;
+ }
+
+ switchMode(_initialMode);
+ _kbdGUI->run();
+
+ if (_submitKeys) {
+ EventManager *eventMan = _system->getEventManager();
+ assert(eventMan);
+
+ // push keydown & keyup events into the event manager
+ Event evt;
+ evt.synthetic = false;
+ while (!_keyQueue.empty()) {
+ evt.kbd = _keyQueue.pop();
+ evt.type = EVENT_KEYDOWN;
+ eventMan->pushEvent(evt);
+ evt.type = EVENT_KEYUP;
+ eventMan->pushEvent(evt);
+ }
+ } else {
+ _keyQueue.clear();
+ }
+}
+
+void VirtualKeyboard::close(bool submit) {
+ _submitKeys = submit;
+ _kbdGUI->close();
+}
+
+bool VirtualKeyboard::isDisplaying() {
+ return _kbdGUI->isDisplaying();
+}
+
+VirtualKeyboard::KeyPressQueue::KeyPressQueue() {
+ _keyPos = _keys.end();
+ _strPos = 0;
+ _strChanged = false;
+ _flags = 0;
+}
+
+void VirtualKeyboard::KeyPressQueue::toggleFlags(byte fl) {
+ _flags ^= fl;
+ _flagsStr.clear();
+ if (_flags) {
+ _flagsStr = KEY_START_CHAR;
+ if (_flags & KBD_CTRL)
+ _flagsStr += "Ctrl+";
+ if (_flags & KBD_ALT)
+ _flagsStr += "Alt+";
+ if (_flags & KBD_SHIFT)
+ _flagsStr += "Shift+";
+ }
+ _strChanged = true;
+}
+
+void VirtualKeyboard::KeyPressQueue::clearFlags() {
+ _flags = 0;
+ _flagsStr.clear();
+ _strChanged = true;
+}
+
+void VirtualKeyboard::KeyPressQueue::insertKey(KeyState key) {
+ _strChanged = true;
+ key.flags ^= _flags;
+ if ((key.keycode >= KEYCODE_a) && (key.keycode <= KEYCODE_z))
+ key.ascii = (key.flags & KBD_SHIFT) ? key.keycode - 32 : key.keycode;
+ clearFlags();
+
+ String keyStr;
+ if (key.flags & KBD_CTRL) keyStr += "Ctrl+";
+ if (key.flags & KBD_ALT) keyStr += "Alt+";
+
+ if (key.ascii >= 32 && key.ascii <= 255) {
+ if (key.flags & KBD_SHIFT && (key.ascii < 65 || key.ascii > 90))
+ keyStr += "Shift+";
+ keyStr += (char)key.ascii;
+ } else {
+ if (key.flags & KBD_SHIFT) keyStr += "Shift+";
+ if (key.keycode >= 0 && key.keycode < keycodeDescTableSize)
+ keyStr += keycodeDescTable[key.keycode];
+ }
+
+ if (keyStr.empty()) keyStr += "???";
+
+ _keysStr.insertChar(KEY_START_CHAR, _strPos++);
+ const char *k = keyStr.c_str();
+ while (char ch = *k++)
+ _keysStr.insertChar(ch, _strPos++);
+ _keysStr.insertChar(KEY_END_CHAR, _strPos++);
+
+ VirtualKeyPress kp;
+ kp.key = key;
+ kp.strLen = keyStr.size() + 2;
+ _keys.insert(_keyPos, kp);
+}
+
+void VirtualKeyboard::KeyPressQueue::deleteKey() {
+ if (_keyPos == _keys.begin())
+ return;
+ List<VirtualKeyPress>::iterator it = _keyPos;
+ it--;
+ _strPos -= it->strLen;
+ while((it->strLen)-- > 0)
+ _keysStr.deleteChar(_strPos);
+ _keys.erase(it);
+ _strChanged = true;
+}
+
+void VirtualKeyboard::KeyPressQueue::moveLeft() {
+ if (_keyPos == _keys.begin())
+ return;
+ _keyPos--;
+ _strPos -= _keyPos->strLen;
+ _strChanged = true;
+}
+
+void VirtualKeyboard::KeyPressQueue::moveRight() {
+ if (_keyPos == _keys.end())
+ return;
+ _strPos += _keyPos->strLen;
+ _keyPos++;
+ _strChanged = true;
+}
+
+KeyState VirtualKeyboard::KeyPressQueue::pop() {
+ bool front = (_keyPos == _keys.begin());
+ VirtualKeyPress kp = *(_keys.begin());
+ _keys.pop_front();
+
+ if (front)
+ _keyPos = _keys.begin();
+ else
+ _strPos -= kp.strLen;
+
+ while (kp.strLen-- > 0)
+ _keysStr.deleteChar(0);
+
+ return kp.key;
+}
+
+void VirtualKeyboard::KeyPressQueue::clear() {
+ _keys.clear();
+ _keyPos = _keys.end();
+ _keysStr.clear();
+ _strPos = 0;
+ clearFlags();
+ _strChanged = true;
+}
+
+bool VirtualKeyboard::KeyPressQueue::empty()
+{
+ return _keys.empty();
+}
+
+String VirtualKeyboard::KeyPressQueue::getString()
+{
+ if (_keysStr.empty())
+ return _flagsStr;
+ if (_flagsStr.empty())
+ return _keysStr;
+ if (_strPos == _keysStr.size())
+ return _keysStr + _flagsStr;
+
+ uint len = _keysStr.size() + _flagsStr.size();
+ char *str = new char[len];
+ memcpy(str, _keysStr.c_str(), _strPos);
+ memcpy(str + _strPos, _flagsStr.c_str(), _flagsStr.size());
+ memcpy(str + _strPos + _flagsStr.size(), _keysStr.c_str() + _strPos, _keysStr.size() - _strPos);
+ String ret(str, len);
+ delete[] str;
+ return ret;
+}
+
+uint VirtualKeyboard::KeyPressQueue::getInsertIndex() {
+ return _strPos + _flagsStr.size();
+}
+
+bool VirtualKeyboard::KeyPressQueue::hasStringChanged() {
+ bool ret = _strChanged;
+ _strChanged = false;
+ return ret;
+}
+
+} // end of namespace Common