* added vkeybd, not tested
This commit is contained in:
parent
657272f7ba
commit
ba58b9033d
22 changed files with 3564 additions and 0 deletions
376
backends/vkeybd/virtual-keyboard-parser.cpp
Normal file
376
backends/vkeybd/virtual-keyboard-parser.cpp
Normal file
|
@ -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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef ENABLE_VKEYBD
|
||||
|
||||
#include "backends/vkeybd/virtual-keyboard-parser.h"
|
||||
#include "backends/vkeybd/polygon.h"
|
||||
|
||||
#include "common/keyboard.h"
|
||||
#include "common/util.h"
|
||||
#include "common/system.h"
|
||||
#include "common/archive.h"
|
||||
|
||||
#include "graphics/imagedec.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
VirtualKeyboardParser::VirtualKeyboardParser(VirtualKeyboard *kbd)
|
||||
: XMLParser(), _keyboard(kbd) {
|
||||
}
|
||||
|
||||
void VirtualKeyboardParser::cleanup() {
|
||||
_mode = 0;
|
||||
_kbdParsed = false;
|
||||
_initialModeName.clear();
|
||||
if (_parseMode == kParseFull) {
|
||||
// reset keyboard to remove existing config
|
||||
_keyboard->reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::closedKeyCallback(ParserNode *node) {
|
||||
if (node->name.equalsIgnoreCase("keyboard")) {
|
||||
_kbdParsed = true;
|
||||
if (!_keyboard->_initialMode)
|
||||
return parserError("Initial mode of keyboard pack not defined");
|
||||
} else if (node->name.equalsIgnoreCase("mode")) {
|
||||
if (!_layoutParsed) {
|
||||
return parserError("'%s' layout missing from '%s' mode",
|
||||
_mode->resolution.c_str(), _mode->name.c_str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parserCallback_keyboard(ParserNode *node) {
|
||||
if (_kbdParsed)
|
||||
return parserError("Only a single keyboard element is allowed");
|
||||
|
||||
// if not full parse then we're done
|
||||
if (_parseMode == kParseCheckResolutions)
|
||||
return true;
|
||||
|
||||
_initialModeName = node->values["initial_mode"];
|
||||
|
||||
if (node->values.contains("h_align")) {
|
||||
String h = node->values["h_align"];
|
||||
if (h.equalsIgnoreCase("left"))
|
||||
_keyboard->_hAlignment = VirtualKeyboard::kAlignLeft;
|
||||
else if (h.equalsIgnoreCase("centre") || h.equalsIgnoreCase("center"))
|
||||
_keyboard->_hAlignment = VirtualKeyboard::kAlignCentre;
|
||||
else if (h.equalsIgnoreCase("right"))
|
||||
_keyboard->_hAlignment = VirtualKeyboard::kAlignRight;
|
||||
}
|
||||
|
||||
if (node->values.contains("v_align")) {
|
||||
String v = node->values["v_align"];
|
||||
if (v.equalsIgnoreCase("top"))
|
||||
_keyboard->_vAlignment = VirtualKeyboard::kAlignTop;
|
||||
else if (v.equalsIgnoreCase("middle") || v.equalsIgnoreCase("center"))
|
||||
_keyboard->_vAlignment = VirtualKeyboard::kAlignMiddle;
|
||||
else if (v.equalsIgnoreCase("bottom"))
|
||||
_keyboard->_vAlignment = VirtualKeyboard::kAlignBottom;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parserCallback_mode(ParserNode *node) {
|
||||
String name = node->values["name"];
|
||||
|
||||
if (_parseMode == kParseFull) {
|
||||
// if full parse then add new mode to keyboard
|
||||
if (_keyboard->_modes.contains(name))
|
||||
return parserError("Mode '%s' has already been defined", name.c_str());
|
||||
|
||||
VirtualKeyboard::Mode mode;
|
||||
mode.name = name;
|
||||
_keyboard->_modes[name] = mode;
|
||||
}
|
||||
|
||||
_mode = &(_keyboard->_modes[name]);
|
||||
if (name == _initialModeName)
|
||||
_keyboard->_initialMode = _mode;
|
||||
|
||||
String resolutions = node->values["resolutions"];
|
||||
StringTokenizer tok (resolutions, " ,");
|
||||
|
||||
// select best resolution simply by minimising the difference between the
|
||||
// overlay size and the resolution dimensions.
|
||||
// TODO: improve this by giving preference to a resolution that is smaller
|
||||
// than the overlay res (so the keyboard can't be too big for the screen)
|
||||
uint16 scrW = g_system->getOverlayWidth(), scrH = g_system->getOverlayHeight();
|
||||
uint32 diff = 0xFFFFFFFF;
|
||||
String newResolution;
|
||||
for (String res = tok.nextToken(); res.size() > 0; res = tok.nextToken()) {
|
||||
int resW, resH;
|
||||
if (sscanf(res.c_str(), "%dx%d", &resW, &resH) != 2) {
|
||||
return parserError("Invalid resolution specification");
|
||||
} else {
|
||||
if (resW == scrW && resH == scrH) {
|
||||
newResolution = res;
|
||||
break;
|
||||
} else {
|
||||
uint32 newDiff = ABS(scrW - resW) + ABS(scrH - resH);
|
||||
if (newDiff < diff) {
|
||||
diff = newDiff;
|
||||
newResolution = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newResolution.empty())
|
||||
return parserError("No acceptable resolution was found");
|
||||
|
||||
if (_parseMode == kParseCheckResolutions) {
|
||||
if (_mode->resolution == newResolution) {
|
||||
node->ignore = true;
|
||||
return true;
|
||||
} else {
|
||||
// remove data relating to old resolution
|
||||
_mode->bitmapName.clear();
|
||||
delete _mode->image;
|
||||
_mode->image = 0;
|
||||
_mode->imageMap.removeAllAreas();
|
||||
_mode->displayArea = Rect();
|
||||
}
|
||||
}
|
||||
|
||||
_mode->resolution = newResolution;
|
||||
_layoutParsed = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parserCallback_event(ParserNode *node) {
|
||||
// if just checking resolutions we're done
|
||||
if (_parseMode == kParseCheckResolutions)
|
||||
return true;
|
||||
|
||||
String name = node->values["name"];
|
||||
if (_mode->events.contains(name))
|
||||
return parserError("Event '%s' has already been defined", name.c_str());
|
||||
|
||||
VirtualKeyboard::VKEvent *evt = new VirtualKeyboard::VKEvent();
|
||||
evt->name = name;
|
||||
|
||||
String type = node->values["type"];
|
||||
if (type.equalsIgnoreCase("key")) {
|
||||
if (!node->values.contains("code") || !node->values.contains("ascii")) {
|
||||
delete evt;
|
||||
return parserError("Key event element must contain code and ascii attributes");
|
||||
}
|
||||
evt->type = VirtualKeyboard::kVKEventKey;
|
||||
|
||||
KeyState *ks = (KeyState*) malloc(sizeof(KeyState));
|
||||
ks->keycode = (KeyCode)atoi(node->values["code"].c_str());
|
||||
ks->ascii = atoi(node->values["ascii"].c_str());
|
||||
ks->flags = 0;
|
||||
if (node->values.contains("modifiers"))
|
||||
ks->flags = parseFlags(node->values["modifiers"]);
|
||||
evt->data = ks;
|
||||
|
||||
} else if (type.equalsIgnoreCase("modifier")) {
|
||||
if (!node->values.contains("modifiers")) {
|
||||
delete evt;
|
||||
return parserError("Key modifier element must contain modifier attributes");
|
||||
}
|
||||
|
||||
evt->type = VirtualKeyboard::kVKEventModifier;
|
||||
byte *flags = (byte*) malloc(sizeof(byte));
|
||||
*(flags) = parseFlags(node->values["modifiers"]);
|
||||
evt->data = flags;
|
||||
|
||||
} else if (type.equalsIgnoreCase("switch_mode")) {
|
||||
if (!node->values.contains("mode")) {
|
||||
delete evt;
|
||||
return parserError("Switch mode event element must contain mode attribute");
|
||||
}
|
||||
|
||||
evt->type = VirtualKeyboard::kVKEventSwitchMode;
|
||||
String& mode = node->values["mode"];
|
||||
char *str = (char*) malloc(sizeof(char) * mode.size() + 1);
|
||||
memcpy(str, mode.c_str(), sizeof(char) * mode.size());
|
||||
str[mode.size()] = 0;
|
||||
evt->data = str;
|
||||
} else if (type.equalsIgnoreCase("submit")) {
|
||||
evt->type = VirtualKeyboard::kVKEventSubmit;
|
||||
} else if (type.equalsIgnoreCase("cancel")) {
|
||||
evt->type = VirtualKeyboard::kVKEventCancel;
|
||||
} else if (type.equalsIgnoreCase("clear")) {
|
||||
evt->type = VirtualKeyboard::kVKEventClear;
|
||||
} else if (type.equalsIgnoreCase("delete")) {
|
||||
evt->type = VirtualKeyboard::kVKEventDelete;
|
||||
} else if (type.equalsIgnoreCase("move_left")) {
|
||||
evt->type = VirtualKeyboard::kVKEventMoveLeft;
|
||||
} else if (type.equalsIgnoreCase("move_right")) {
|
||||
evt->type = VirtualKeyboard::kVKEventMoveRight;
|
||||
} else {
|
||||
delete evt;
|
||||
return parserError("Event type '%s' not known", type.c_str());
|
||||
}
|
||||
|
||||
_mode->events[name] = evt;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parserCallback_layout(ParserNode *node) {
|
||||
assert(!_mode->resolution.empty());
|
||||
|
||||
String res = node->values["resolution"];
|
||||
|
||||
if (res != _mode->resolution) {
|
||||
node->ignore = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
_mode->bitmapName = node->values["bitmap"];
|
||||
|
||||
SeekableReadStream *file = _keyboard->_fileArchive->createReadStreamForMember(_mode->bitmapName);
|
||||
if (!file)
|
||||
return parserError("Bitmap '%s' not found", _mode->bitmapName.c_str());
|
||||
|
||||
const Graphics::PixelFormat format = g_system->getOverlayFormat();
|
||||
|
||||
_mode->image = Graphics::ImageDecoder::loadFile(*file, format);
|
||||
delete file;
|
||||
|
||||
if (!_mode->image)
|
||||
return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str());
|
||||
|
||||
int r, g, b;
|
||||
if (node->values.contains("transparent_color")) {
|
||||
if (!parseIntegerKey(node->values["transparent_color"].c_str(), 3, &r, &g, &b))
|
||||
return parserError("Could not parse color value");
|
||||
} else {
|
||||
// default to purple
|
||||
r = 255;
|
||||
g = 0;
|
||||
b = 255;
|
||||
}
|
||||
_mode->transparentColor = format.RGBToColor(r, g, b);
|
||||
|
||||
if (node->values.contains("display_font_color")) {
|
||||
if (!parseIntegerKey(node->values["display_font_color"].c_str(), 3, &r, &g, &b))
|
||||
return parserError("Could not parse color value");
|
||||
} else {
|
||||
r = g = b = 0; // default to black
|
||||
}
|
||||
_mode->displayFontColor = format.RGBToColor(r, g, b);
|
||||
|
||||
_layoutParsed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parserCallback_map(ParserNode *node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parserCallback_area(ParserNode *node) {
|
||||
String& shape = node->values["shape"];
|
||||
String& target = node->values["target"];
|
||||
String& coords = node->values["coords"];
|
||||
|
||||
if (target.equalsIgnoreCase("display_area")) {
|
||||
if (!shape.equalsIgnoreCase("rect"))
|
||||
return parserError("display_area must be a rect area");
|
||||
_mode->displayArea = Rect();
|
||||
return parseRect(_mode->displayArea, coords);
|
||||
} else if (shape.equalsIgnoreCase("rect")) {
|
||||
Polygon *poly = _mode->imageMap.createArea(target);
|
||||
return parseRectAsPolygon(*poly, coords);
|
||||
} else if (shape.equalsIgnoreCase("poly")) {
|
||||
Polygon *poly = _mode->imageMap.createArea(target);
|
||||
return parsePolygon(*poly, coords);
|
||||
}
|
||||
return parserError("Area shape '%s' not known", shape.c_str());
|
||||
}
|
||||
|
||||
byte VirtualKeyboardParser::parseFlags(const String& flags) {
|
||||
if (flags.empty())
|
||||
return 0;
|
||||
|
||||
StringTokenizer tok(flags, ", ");
|
||||
byte val = 0;
|
||||
for (String fl = tok.nextToken(); !fl.empty(); fl = tok.nextToken()) {
|
||||
if (fl == "ctrl" || fl == "control")
|
||||
val |= KBD_CTRL;
|
||||
else if (fl == "alt")
|
||||
val |= KBD_ALT;
|
||||
else if (fl == "shift")
|
||||
val |= KBD_SHIFT;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parseRect(Rect &rect, const String& coords) {
|
||||
int x1, y1, x2, y2;
|
||||
if (!parseIntegerKey(coords.c_str(), 4, &x1, &y1, &x2, &y2))
|
||||
return parserError("Invalid coords for rect area");
|
||||
rect.left = x1;
|
||||
rect.top = y1;
|
||||
rect.right = x2;
|
||||
rect.bottom = y2;
|
||||
if (!rect.isValidRect())
|
||||
return parserError("Rect area is not a valid rectangle");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parsePolygon(Polygon &poly, const String& coords) {
|
||||
StringTokenizer tok(coords, ", ");
|
||||
for (String st = tok.nextToken(); !st.empty(); st = tok.nextToken()) {
|
||||
int x, y;
|
||||
if (sscanf(st.c_str(), "%d", &x) != 1)
|
||||
return parserError("Invalid coords for polygon area");
|
||||
st = tok.nextToken();
|
||||
if (sscanf(st.c_str(), "%d", &y) != 1)
|
||||
return parserError("Invalid coords for polygon area");
|
||||
poly.addPoint(x, y);
|
||||
}
|
||||
if (poly.getPointCount() < 3)
|
||||
return parserError("Invalid coords for polygon area");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VirtualKeyboardParser::parseRectAsPolygon(Polygon &poly, const String& coords) {
|
||||
Rect rect;
|
||||
if (!parseRect(rect, coords))
|
||||
return false;
|
||||
poly.addPoint(rect.left, rect.top);
|
||||
poly.addPoint(rect.right, rect.top);
|
||||
poly.addPoint(rect.right, rect.bottom);
|
||||
poly.addPoint(rect.left, rect.bottom);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end of namespace GUI
|
||||
|
||||
#endif // #ifdef ENABLE_VKEYBD
|
Loading…
Add table
Add a link
Reference in a new issue