diff options
author | Stephen Kennedy | 2008-08-07 16:38:39 +0000 |
---|---|---|
committer | Stephen Kennedy | 2008-08-07 16:38:39 +0000 |
commit | 2645ca48ad9bc219b43ab88cc5b071952297a992 (patch) | |
tree | d1383869820354fb99560d630c0d694535d5ad61 /backends/vkeybd | |
parent | 66e4e3ec1a038bc653bb1c04893c39033ac3fb62 (diff) | |
download | scummvm-rg350-2645ca48ad9bc219b43ab88cc5b071952297a992.tar.gz scummvm-rg350-2645ca48ad9bc219b43ab88cc5b071952297a992.tar.bz2 scummvm-rg350-2645ca48ad9bc219b43ab88cc5b071952297a992.zip |
* Moved VK code into backends/vkeybd
* Moved Keymapper code into backends/keymapper
svn-id: r33681
Diffstat (limited to 'backends/vkeybd')
-rw-r--r-- | backends/vkeybd/image-map.cpp | 69 | ||||
-rw-r--r-- | backends/vkeybd/image-map.h | 53 | ||||
-rw-r--r-- | backends/vkeybd/polygon.cpp | 55 | ||||
-rw-r--r-- | backends/vkeybd/polygon.h | 114 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-gui.cpp | 376 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-gui.h | 108 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-parser.cpp | 430 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-parser.h | 217 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard.cpp | 347 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard.h | 218 |
10 files changed, 1987 insertions, 0 deletions
diff --git a/backends/vkeybd/image-map.cpp b/backends/vkeybd/image-map.cpp new file mode 100644 index 0000000000..d97b662c7d --- /dev/null +++ b/backends/vkeybd/image-map.cpp @@ -0,0 +1,69 @@ +/* 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/image-map.h" + +namespace Common { + +ImageMap::~ImageMap() { + removeAllAreas(); +} + +Polygon *ImageMap::createArea(const String& id) { + if (_areas.contains(id)) { + warning("Image map already contains an area with target of '%s'", id.c_str()); + 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, Polygon*>::iterator it; + for (it = _areas.begin(); it != _areas.end(); it++) { + delete it->_value; + } + _areas.clear(); +} + +String ImageMap::findMapArea(int16 x, int16 y) { + HashMap<String, Polygon*>::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/backends/vkeybd/image-map.h b/backends/vkeybd/image-map.h new file mode 100644 index 0000000000..ed6feaa26e --- /dev/null +++ b/backends/vkeybd/image-map.h @@ -0,0 +1,53 @@ +/* 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 "backends/vkeybd/polygon.h" + +namespace Common { + +class ImageMap { + +public: + + ~ImageMap(); + + Polygon *createArea(const String& id); + void removeArea(const String& id); + void removeAllAreas(); + String findMapArea(int16 x, int16 y); + +protected: + HashMap<String, Polygon*> _areas; +}; + + +} // End of namespace Common + +#endif diff --git a/backends/vkeybd/polygon.cpp b/backends/vkeybd/polygon.cpp new file mode 100644 index 0000000000..77ef3f0f44 --- /dev/null +++ b/backends/vkeybd/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 "backends/vkeybd/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/backends/vkeybd/polygon.h b/backends/vkeybd/polygon.h new file mode 100644 index 0000000000..69df2c0ca3 --- /dev/null +++ b/backends/vkeybd/polygon.h @@ -0,0 +1,114 @@ +/* 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" + +namespace Common { + +struct Polygon { + + + Polygon() {} + Polygon(const Polygon& p) : _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/backends/vkeybd/virtual-keyboard-gui.cpp b/backends/vkeybd/virtual-keyboard-gui.cpp new file mode 100644 index 0000000000..0dcea70292 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-gui.cpp @@ -0,0 +1,376 @@ +/* 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-gui.h" +#include "graphics/cursorman.h" +#include "gui/newgui.h" + +namespace Common { + +VirtualKeyboardGUI::VirtualKeyboardGUI(VirtualKeyboard *kbd) { + _kbd = kbd; + + assert(g_system); + _system = g_system; + + _lastScreenChanged = _system->getScreenChangeID(); + + memset(_cursor, 0xFF, sizeof(_cursor)); + + _displaying = _needRedraw = _drag = _drawCaret = _displayEnabled = false; + _firstRun = true; + + _cursorAnimateTimer = 0; +} + +void VirtualKeyboardGUI::initMode(VirtualKeyboard::Mode *mode) { + _kbdSurface = mode->image; + _kbdTransparentColor = mode->transparentColor; + _kbdBound.setWidth(_kbdSurface->w + 1); + _kbdBound.setHeight(_kbdSurface->h + 1); + _needRedraw = true; + + _displayEnabled = false; + if (!mode->displayArea) + return; + Rect r = *(mode->displayArea); + + // choose font + _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + if (!fontIsSuitable(_dispFont, r)) { + _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + if (!fontIsSuitable(_dispFont, r)) + return; + } + _dispX = r.left; + _dispY = r.top + (_dispFont->getFontHeight() - r.height() + 1) / 2; + _dispSurface.free(); + _dispSurface.create(r.width() + 1, _dispFont->getFontHeight(), sizeof(OverlayColor)); + _dispI = 0; + _dispForeColor = mode->displayFontColor; + _dispBackColor = _dispForeColor + 0xFF; + _displayEnabled = true; +} + +bool VirtualKeyboardGUI::fontIsSuitable(const Graphics::Font *font, const Rect& rect) { + return (font->getMaxCharWidth() < rect.width() && + font->getFontHeight() < rect.height()); +} + +void VirtualKeyboardGUI::run() { + if (_lastScreenChanged != _system->getScreenChangeID()) + screenChanged(); + + // TODO: set default position if position is somehow invalid + 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); + resetDirtyRect(); + + setupCursor(); + + _displaying = true; + mainLoop(); + + removeCursor(); + + _system->copyRectToOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w, 0, 0, _overlayBackup.w, _overlayBackup.h); + if (!g_gui.isActive()) _system->hideOverlay(); + + _overlayBackup.free(); +} + +void VirtualKeyboardGUI::hide() { + _displaying = false; +} + +void VirtualKeyboardGUI::reset() { + _kbdBound.left = _kbdBound.top + = _kbdBound.right = _kbdBound.bottom = 0; + _displaying = _drag = false; + _firstRun = true; + _lastScreenChanged = _system->getScreenChangeID(); + _kbdSurface = 0; +} + +void VirtualKeyboardGUI::setDefaultPosition() +{ + int16 scrW = _system->getOverlayWidth(), scrH = _system->getOverlayHeight(); + int16 kbdW = _kbdBound.width(), kbdH = _kbdBound.height(); + int16 posX = 0, posY = 0; + if (scrW != kbdW) { + switch (_kbd->_hAlignment) { + case VirtualKeyboard::kAlignLeft: + posX = 0; + break; + case VirtualKeyboard::kAlignCentre: + posX = (scrW - kbdW) / 2; + break; + case VirtualKeyboard::kAlignRight: + posX = scrW - kbdW; + break; + } + } + if (scrH != kbdH) { + switch (_kbd->_vAlignment) { + case VirtualKeyboard::kAlignTop: + posY = 0; + break; + case VirtualKeyboard::kAlignMiddle: + posY = (scrH - kbdH) / 2; + break; + case VirtualKeyboard::kAlignBottom: + posY = scrH - kbdH; + break; + } + } + _kbdBound.moveTo(posX, posY); +} + +void VirtualKeyboardGUI::move(int16 x, int16 y) { + // add old position to dirty area + extendDirtyRect(_kbdBound); + _needRedraw = true; + + // 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); +} + +void VirtualKeyboardGUI::screenChanged() { + _lastScreenChanged = _system->getScreenChangeID(); + if (!_kbd->checkModeResolutions()) + _displaying = false; +} + + +void VirtualKeyboardGUI::mainLoop() { + Common::EventManager *eventMan = _system->getEventManager(); + + while (_displaying) { + if (_displayEnabled) { + if (_kbd->_keyQueue.hasStringChanged()) + _refreshDisplay = true; + animateCaret(); + if (_refreshDisplay) updateDisplay();; + } + 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)) { + _kbd->handleMouseDown(event.mouse.x - _kbdBound.left, + event.mouse.y - _kbdBound.top); + } + break; + case Common::EVENT_LBUTTONUP: + if (_kbdBound.contains(event.mouse)) { + _kbd->handleMouseUp(event.mouse.x - _kbdBound.left, + event.mouse.y - _kbdBound.top); + } + break; + case Common::EVENT_MOUSEMOVE: + if (_drag) + move(event.mouse.x - _dragPoint.x, + event.mouse.y - _dragPoint.y); + break; + case Common::EVENT_SCREEN_CHANGED: + screenChanged(); + break; + case Common::EVENT_QUIT: + _system->quit(); + return; + default: + break; + } + } + // Delay for a moment + _system->delayMillis(10); + } +} + +void VirtualKeyboardGUI::startDrag(int16 x, int16 y) { + _drag = true; + _dragPoint.x = x; + _dragPoint.y = y; +} + +void VirtualKeyboardGUI::endDrag() { + _drag = false; +} + +void VirtualKeyboardGUI::extendDirtyRect(const Rect &r) { + if (_dirtyRect.isValidRect()) { + _dirtyRect.extend(r); + } else { + _dirtyRect = r; + } + _dirtyRect.clip(Rect(0, 0, _overlayBackup.w, _overlayBackup.h)); +} + +void VirtualKeyboardGUI::resetDirtyRect() { + _dirtyRect.setWidth(-1); +} + +void VirtualKeyboardGUI::redraw() { + assert(_kbdSurface); + + extendDirtyRect(_kbdBound); + + Graphics::SurfaceKeyColored surf; + surf.create(_dirtyRect.width()+1, _dirtyRect.height()+1, sizeof(OverlayColor)); + + OverlayColor *scr = (OverlayColor *)surf.pixels; + const OverlayColor *ove = (OverlayColor *) _overlayBackup.getBasePtr(_dirtyRect.left, _dirtyRect.top); + int16 h = surf.h; + + while (h-- > 0) { + memcpy(scr, ove, surf.w * sizeof(OverlayColor)); + scr += surf.w; + ove += _overlayBackup.w; + } + + int16 keyX = _kbdBound.left - _dirtyRect.left; + int16 keyY = _kbdBound.top - _dirtyRect.top; + surf.blit(_kbdSurface, keyX, keyY, _kbdTransparentColor); + if (_displayEnabled) surf.blit(&_dispSurface, keyX + _dispX, keyY + _dispY, _dispBackColor); + _system->copyRectToOverlay((OverlayColor*)surf.pixels, surf.w, + _dirtyRect.left, _dirtyRect.top, surf.w, surf.h); + + surf.free(); + + _needRedraw = false; + + resetDirtyRect(); +} + +uint VirtualKeyboardGUI::calculateEndIndex(const String& str, uint startIndex) { + int16 w = 0; + while (w <= _dispSurface.w && startIndex < str.size()) { + w += _dispFont->getCharWidth(str[startIndex++]); + } + if (w > _dispSurface.w) startIndex--; + return startIndex; +} + +void VirtualKeyboardGUI::animateCaret() { + if (_system->getMillis() % kCaretBlinkTime < kCaretBlinkTime / 2) { + if (!_drawCaret) { + _drawCaret = true; + _refreshDisplay = true; + } + } else { + if (_drawCaret) { + _drawCaret = false; + _refreshDisplay = true; + } + } +} + +void VirtualKeyboardGUI::updateDisplay() { + if (!_displayEnabled) return; + + // calculate the text to display + uint cursorPos = _kbd->_keyQueue.getInsertIndex(); + String wholeText = _kbd->_keyQueue.getString(); + uint dispTextEnd; + if (_dispI > cursorPos) + _dispI = cursorPos; + + dispTextEnd = calculateEndIndex(wholeText, _dispI); + while (cursorPos > dispTextEnd) + dispTextEnd = calculateEndIndex(wholeText, ++_dispI); + + String dispText = String(wholeText.c_str() + _dispI, wholeText.c_str() + dispTextEnd); + + // draw to display surface + _dispSurface.fillRect(Rect(0, 0, _dispSurface.w, _dispSurface.h), _dispBackColor); + _dispFont->drawString(&_dispSurface, dispText, 0, 0, _dispSurface.w, _dispForeColor); + if (_drawCaret) { + String beforeCaret(wholeText.c_str() + _dispI, wholeText.c_str() + cursorPos); + int16 caretX = _dispFont->getStringWidth(beforeCaret); + _dispSurface.drawLine(caretX, 0, caretX, _dispSurface.h, _dispForeColor); + } + + _needRedraw = true; +} + +void VirtualKeyboardGUI::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 VirtualKeyboardGUI::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 VirtualKeyboardGUI::removeCursor() { + CursorMan.popCursor(); + CursorMan.popCursorPalette(); +} + +} // end of namespace Common diff --git a/backends/vkeybd/virtual-keyboard-gui.h b/backends/vkeybd/virtual-keyboard-gui.h new file mode 100644 index 0000000000..5836859262 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-gui.h @@ -0,0 +1,108 @@ +/* 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_GUI +#define COMMON_VIRTUAL_KEYBOARD_GUI + +#include "backends/vkeybd/virtual-keyboard.h" +#include "common/rect.h" +#include "common/system.h" +#include "graphics/fontman.h" +#include "graphics/surface-keycolored.h" + +namespace Common { + +class VirtualKeyboardGUI { + +public: + + VirtualKeyboardGUI(VirtualKeyboard *kbd); + void initMode(VirtualKeyboard::Mode *mode); + void run(); + void hide(); + bool isDisplaying() { return _displaying; } + void reset(); + void startDrag(int16 x, int16 y); + void endDrag(); + +private: + + OSystem *_system; + VirtualKeyboard *_kbd; + Graphics::Surface *_kbdSurface; + OverlayColor _kbdTransparentColor; + + static const int SNAP_WIDTH = 10; + + Graphics::Surface _overlayBackup; + + Rect _dirtyRect; + + bool _displayEnabled; + bool _refreshDisplay; + Graphics::Surface _dispSurface; + const Graphics::Font *_dispFont; + int16 _dispX, _dispY; + uint _dispI; + OverlayColor _dispForeColor, _dispBackColor; + + Rect _kbdBound; + + Point _dragPoint; + bool _drag; + + bool _displaying; + bool _firstRun; + bool _needRedraw; + int _lastScreenChanged; + + void setDefaultPosition(); + void move(int16 x, int16 y); + void screenChanged(); + void mainLoop(); + void extendDirtyRect(const Rect &r); + void resetDirtyRect(); + void redraw(); + void updateDisplay(); + bool fontIsSuitable(const Graphics::Font *font, const Rect& rect); + uint calculateEndIndex(const String& str, uint startIndex); + + bool _drawCaret; + static const int kCaretBlinkTime = 500; + void animateCaret(); + + static const int kCursorAnimateDelay = 250; + int _cursorAnimateCounter; + int _cursorAnimateTimer; + byte _cursor[2048]; + void setupCursor(); + void removeCursor(); + void animateCursor(); + +}; + +} // end of namespace Common + +#endif diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp new file mode 100644 index 0000000000..f3d71057b7 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-parser.cpp @@ -0,0 +1,430 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/vkeybd/virtual-keyboard-parser.h" + +#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(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(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")) { + 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")) { + 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"); + + 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]); + + String resolutions = modeNode->values["resolutions"]; + StringTokenizer tok (resolutions, " ,"); + + // select best resolution simply by minimising the difference between the + // overlay size and the resolution dimensions. + // TODO: improve this by giving preference to a resolution that is smaller + // than the overlay res (so the keyboard can't be too big for the screen) + uint16 scrW = g_system->getOverlayWidth(), scrH = g_system->getOverlayHeight(); + uint32 diff = 0xFFFFFFFF; + String newResolution; + for (String res = tok.nextToken(); res.size() > 0; res = tok.nextToken()) { + int resW, resH; + if (sscanf(res.c_str(), "%dx%d", &resW, &resH) != 2) { + return parserError("Invalid resolution specification"); + } else { + if (resW == scrW && resH == scrH) { + newResolution = res; + break; + } else { + uint32 newDiff = ABS(scrW - resW) + ABS(scrH - resH); + if (newDiff < diff) { + diff = newDiff; + newResolution = res; + } + } + } + } + + if (newResolution.empty()) + return parserError("No acceptable resolution was found"); + + if (_parseMode == kParseCheckResolutions) { + if (_mode->resolution == newResolution) { + 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; + + String name = evtNode->values["name"]; + if (_mode->events.contains(name)) + return parserError("Event '%s' has already been defined", name.c_str()); + + VirtualKeyboard::Event *evt = new VirtualKeyboard::Event(); + evt->name = name; + + String type = evtNode->values["type"]; + if (type == "key") { + if (!evtNode->values.contains("code") || !evtNode->values.contains("ascii")) { + delete evt; + return parserError("Key event element must contain code and ascii attributes"); + } + + evt->type = VirtualKeyboard::kEventKey; + + KeyCode code = (KeyCode)atoi(evtNode->values["code"].c_str()); + uint16 ascii = atoi(evtNode->values["ascii"].c_str()); + + byte flags = 0; + if (evtNode->values.contains("modifiers")) + flags = parseFlags(evtNode->values["modifiers"]); + + evt->data = new KeyState(code, ascii, flags); + + } else if (type == "modifier") { + if (!evtNode->values.contains("modifiers")) { + delete evt; + return parserError("Key modifier element must contain modifier attributes"); + } + + evt->type = VirtualKeyboard::kEventModifier; + byte *flags = new byte; + *(flags) = parseFlags(evtNode->values["modifiers"]); + evt->data = flags; + + } else if (type == "switch_mode") { + if (!evtNode->values.contains("mode")) { + delete evt; + return parserError("Switch mode event element must contain mode attribute"); + } + + evt->type = VirtualKeyboard::kEventSwitchMode; + evt->data = new String(evtNode->values["mode"]); + } else if (type == "close") { + evt->type = VirtualKeyboard::kEventClose; + evt->data = 0; + } else { + delete evt; + 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()); + + String res = layoutNode->values["resolution"]; + + if (res != _mode->resolution) { + layoutNode->ignore = true; + return true; + } + + _mode->bitmapName = layoutNode->values["bitmap"]; + _mode->image = ImageMan.getSurface(_mode->bitmapName); + if (!_mode->image) { + if (!ImageMan.registerSurface(_mode->bitmapName, 0)) + return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str()); + + _mode->image = ImageMan.getSurface(_mode->bitmapName); + if (!_mode->image) + return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str()); + } + + if (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 + + if (layoutNode->values.contains("display_font_color")) { + int r, g, b; + if (!parseIntegerKey(layoutNode->values["display_font_color"].c_str(), 3, &r, &g, &b)) + return parserError("Could not parse color value"); + _mode->displayFontColor = g_system->RGBToColor(r, g, b); + } else + _mode->displayFontColor = g_system->RGBToColor(0, 0, 0); // default to black + + _layoutParsed = true; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_Map() { + ParserNode *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"); + + String& shape = areaNode->values["shape"]; + String& target = areaNode->values["target"]; + String& coords = areaNode->values["coords"]; + + if (target == "display_area") { + if (shape != "rect") + return parserError("display_area must be a rect area"); + _mode->displayArea = new Rect(); + return parseRect(_mode->displayArea, coords); + } else if (shape == "rect") { + Polygon *poly = _mode->imageMap.createArea(target); + return parseRectAsPolygon(poly, coords); + } else if (shape == "poly") { + Polygon *poly = _mode->imageMap.createArea(target); + return parsePolygon(poly, coords); + } + return parserError("Area shape '%s' not known", shape.c_str()); +} + +byte VirtualKeyboardParser::parseFlags(const String& flags) { + if (flags.empty()) + return 0; + + StringTokenizer tok(flags, ", "); + byte val = 0; + for (String fl = tok.nextToken(); !fl.empty(); fl = tok.nextToken()) { + if (fl == "ctrl" || fl == "control") + val |= KBD_CTRL; + else if (fl == "alt") + val |= KBD_ALT; + else if (fl == "shift") + val |= KBD_SHIFT; + } + return val; +} + +bool VirtualKeyboardParser::parseRect(Rect *rect, const String& coords) { + int x1, y1, x2, y2; + if (!parseIntegerKey(coords.c_str(), 4, &x1, &y1, &x2, &y2)) + return parserError("Invalid coords for rect area"); + rect->left = x1; rect->top = y1; rect->right = x2; rect->bottom = y2; + if (!rect->isValidRect()) + return parserError("Rect area is not a valid rectangle"); + return true; +} + +bool VirtualKeyboardParser::parsePolygon(Polygon *poly, const String& coords) { + StringTokenizer tok (coords, ", "); + for (String st = tok.nextToken(); !st.empty(); st = tok.nextToken()) { + int x, y; + if (sscanf(st.c_str(), "%d", &x) != 1) + return parserError("Invalid coords for polygon area"); + st = tok.nextToken(); + if (sscanf(st.c_str(), "%d", &y) != 1) + return parserError("Invalid coords for polygon area"); + poly->addPoint(x, y); + } + if (poly->getPointCount() < 3) + return parserError("Invalid coords for polygon area"); + + return true; +} + +bool VirtualKeyboardParser::parseRectAsPolygon(Polygon *poly, const String& coords) { + Rect rect; + if (!parseRect(&rect, coords)) + return false; + poly->addPoint(rect.left, rect.top); + poly->addPoint(rect.right, rect.top); + poly->addPoint(rect.right, rect.bottom); + poly->addPoint(rect.left, rect.bottom); + return true; +} + +} // end of namespace GUI diff --git a/backends/vkeybd/virtual-keyboard-parser.h b/backends/vkeybd/virtual-keyboard-parser.h new file mode 100644 index 0000000000..59a770bdce --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-parser.h @@ -0,0 +1,217 @@ +/* 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/vkeybd/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 + String _initialModeName; + bool _kbdParsed; + bool _layoutParsed; + + bool keyCallback(String keyName); + bool closedKeyCallback(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(); + + byte parseFlags(const String& flags); + bool parseRect(Rect *rect, const String& coords); + bool parsePolygon(Polygon *poly, const String& coords); + bool parseRectAsPolygon(Polygon *poly, const String& coords); + + HashMap<String, ParserCallback, IgnoreCase_Hash, IgnoreCase_EqualTo> _callbacks; + HashMap<String, ParserCallback, IgnoreCase_Hash, IgnoreCase_EqualTo> _closedCallbacks; +}; + +} // end of namespace GUI + +#endif diff --git a/backends/vkeybd/virtual-keyboard.cpp b/backends/vkeybd/virtual-keyboard.cpp new file mode 100644 index 0000000000..ae0fd86ee5 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard.cpp @@ -0,0 +1,347 @@ +/* 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 "graphics/imageman.h" + +namespace Common { + +VirtualKeyboard::VirtualKeyboard() : _currentMode(0) { + assert(g_system); + _system = g_system; + + _parser = new VirtualKeyboardParser(this); + _kbdGUI = new VirtualKeyboardGUI(this); + _loaded = false; +} + +VirtualKeyboard::~VirtualKeyboard() { + deleteEvents(); + delete _kbdGUI; + delete _parser; +} + +void VirtualKeyboard::deleteEvents() { + 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; + } +} + +void VirtualKeyboard::reset() { + deleteEvents(); + _modes.clear(); + _initialMode = _currentMode = 0; + _hAlignment = kAlignCentre; + _vAlignment = kAlignBottom; + _keyQueue.clear(); + _loaded = false; + _kbdGUI->reset(); +} + +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 = (byte *)malloc(fileInfo.uncompressed_size+1 * sizeof(byte)); + 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", 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; +} + +bool VirtualKeyboard::checkModeResolutions() +{ + _parser->setParseMode(kParseCheckResolutions); + _loaded = _parser->parse(); + return _loaded; +} + +Common::String VirtualKeyboard::findArea(int16 x, int16 y) { + return _currentMode->imageMap.findMapArea(x, y); +} + +void VirtualKeyboard::processAreaClick(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.insertKey(*(Common::KeyState*)evt->data); + break; + } + case kEventModifier: + _keyQueue.toggleFlags(*(byte*)(evt->data)); + break; + case kEventSwitchMode: + // switch to new mode + switchMode(*(Common::String *)evt->data); + _keyQueue.clearFlags(); + break; + case kEventClose: + // close virtual keyboard + _kbdGUI->hide(); + break; + } +} + +void VirtualKeyboard::switchMode(Mode *newMode) { + _kbdGUI->initMode(newMode); + _currentMode = newMode; +} + +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::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) { + // if not loaded then load default "vkeybd" pack + if (!loadKeyboardPack("vkeybd")) { + warning("Keyboard not loaded therefore can't be shown"); + return; + } + } + switchMode(_initialMode); + + _kbdGUI->run(); + + EventManager *eventMan = _system->getEventManager(); + assert(eventMan); + + // 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::hide() { + _kbdGUI->hide(); +} + +bool VirtualKeyboard::isDisplaying() { + return _kbdGUI->isDisplaying(); +} + +VirtualKeyboard::KeyPressQueue::KeyPressQueue() { + _keyPos = _keys.end(); + _strPos = 0; +} + +void VirtualKeyboard::KeyPressQueue::toggleFlags(byte fl) { + _keyFlags ^= fl; + _strChanged = true; +} + +void VirtualKeyboard::KeyPressQueue::clearFlags() { + _keyFlags = 0; + _strChanged = true; +} + +void VirtualKeyboard::KeyPressQueue::insertKey(KeyState key) { + _strChanged = true; + switch (key.keycode) { + case KEYCODE_LEFT: + moveLeft(); + return; + case KEYCODE_RIGHT: + moveRight(); + return; + case KEYCODE_BACKSPACE: + deleteKey(); + return; + default: + ; + } + + key.flags ^= _keyFlags; + if ((key.keycode >= Common::KEYCODE_a) && (key.keycode <= Common::KEYCODE_z)) + key.ascii = (key.flags & Common::KBD_SHIFT) ? key.keycode - 32 : key.keycode; + clearFlags(); + + String keyStr; + if (key.keycode >= 32 && key.keycode <= 126) { + if (key.flags & KBD_CTRL) + keyStr += "Ctrl+"; + if (key.flags & KBD_ALT) + keyStr += "Alt+"; + if (key.flags & KBD_SHIFT && (key.ascii < 65 || key.ascii > 90)) + keyStr += "Shift+"; + keyStr += (char)key.ascii; + } + + const char *k = keyStr.c_str(); + while (char ch = *k++) + _str.insertChar(ch, _strPos++); + + VirtualKeyPress kp; + kp.key = key; + kp.strLen = keyStr.size(); + _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) + _str.deleteChar(_strPos); + _keys.erase(it); +} + +void VirtualKeyboard::KeyPressQueue::moveLeft() { + if (_keyPos == _keys.begin()) + return; + _keyPos--; + _strPos -= _keyPos->strLen; +} + +void VirtualKeyboard::KeyPressQueue::moveRight() { + if (_keyPos == _keys.end()) + return; + _strPos += _keyPos->strLen; + _keyPos++; +} + +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) + _str.deleteChar(0); + + return kp.key; +} + +void VirtualKeyboard::KeyPressQueue::clear() { + _keys.clear(); + _keyPos = _keys.end(); + _str.clear(); + _strPos = 0; + _keyFlags = 0; +} + +bool VirtualKeyboard::KeyPressQueue::empty() +{ + return _keys.empty(); +} + +String VirtualKeyboard::KeyPressQueue::getString() +{ + String flags; + if (_keyFlags & KBD_CTRL) + flags += "Ctrl+"; + if (_keyFlags & KBD_ALT) + flags += "Alt+"; + if (_keyFlags & KBD_SHIFT) + flags += "Shift+"; + return _str + flags; +} + +uint VirtualKeyboard::KeyPressQueue::getInsertIndex() { + return _strPos; +} + +bool VirtualKeyboard::KeyPressQueue::hasStringChanged() { + bool ret = _strChanged; + _strChanged = false; + return ret; +} + +} // end of namespace Common diff --git a/backends/vkeybd/virtual-keyboard.h b/backends/vkeybd/virtual-keyboard.h new file mode 100644 index 0000000000..f15eb4aece --- /dev/null +++ b/backends/vkeybd/virtual-keyboard.h @@ -0,0 +1,218 @@ +/* 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 "backends/vkeybd/image-map.h" +#include "common/keyboard.h" +#include "common/list.h" +#include "common/str.h" + +namespace Common { + +class VirtualKeyboardGUI; +class VirtualKeyboardParser; + +class VirtualKeyboard { +protected: + enum EventType { + kEventKey, + kEventModifier, + kEventSwitchMode, + kEventClose + }; + + struct Event { + Common::String name; + EventType type; + void *data; + + Event() : data(0) {} + ~Event() { + if (data) { + switch (type) { + case kEventKey: + delete (KeyState*)data; + break; + case kEventModifier: + delete (byte*)data; + break; + case kEventSwitchMode: + delete (String*)data; + break; + case kEventClose: + break; + } + } + } + }; + + typedef Common::HashMap<Common::String, Event*> EventMap; + + struct Mode { + Common::String name; + Common::String resolution; + Common::String bitmapName; + Graphics::Surface *image; + OverlayColor transparentColor; + Common::ImageMap imageMap; + EventMap events; + Common::Rect *displayArea; + OverlayColor displayFontColor; + + Mode() : image(0), displayArea(0) {} + ~Mode() { if (displayArea) delete displayArea; } + }; + + typedef Common::HashMap<Common::String, Mode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ModeMap; + + enum HorizontalAlignment { + kAlignLeft, + kAlignCentre, + kAlignRight + }; + + enum VerticalAlignment { + kAlignTop, + kAlignMiddle, + kAlignBottom + }; + + struct VirtualKeyPress { + Common::KeyState key; + uint strLen; + }; + + class KeyPressQueue { + public: + KeyPressQueue(); + void toggleFlags(byte fl); + void clearFlags(); + void insertKey(KeyState key); + void deleteKey(); + void moveLeft(); + void moveRight(); + KeyState pop(); + void clear(); + bool empty(); + String getString(); + uint getInsertIndex(); + bool hasStringChanged(); + + private: + byte _keyFlags; + + List<VirtualKeyPress> _keys; + String _str; + + bool _strChanged; + + List<VirtualKeyPress>::iterator _keyPos; + uint _strPos; + }; + +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(); + + /** + * Returns true if the keyboard is loaded and ready to be shown + */ + bool isLoaded() { + return _loaded; + } + +protected: // TODO : clean up all this stuff + + OSystem *_system; + + friend class VirtualKeyboardGUI; + VirtualKeyboardGUI *_kbdGUI; + + KeyPressQueue _keyQueue; + + friend class VirtualKeyboardParser; + VirtualKeyboardParser *_parser; + + void reset(); + void deleteEvents(); + bool checkModeResolutions(); + void switchMode(Mode *newMode); + void switchMode(const Common::String& newMode); + void handleMouseDown(int16 x, int16 y); + void handleMouseUp(int16 x, int16 y); + String findArea(int16 x, int16 y); + void processAreaClick(const Common::String &area); + + bool _loaded; + + ModeMap _modes; + Mode *_initialMode; + Mode *_currentMode; + + HorizontalAlignment _hAlignment; + VerticalAlignment _vAlignment; + + String _areaDown; + +}; + + +} // End of namespace GUI + + +#endif |