From 5840c2480f2ca97a565d22c79a937615b5da3d25 Mon Sep 17 00:00:00 2001 From: Stephen Kennedy Date: Mon, 7 Jul 2008 15:06:43 +0000 Subject: - renaming of files svn-id: r32942 --- backends/common/virtual-keyboard-parser.cpp | 292 ++++++++++++++++++++++++++++ backends/common/virtual-keyboard-parser.h | 70 +++++++ backends/common/virtual-keyboard.cpp | 261 +++++++++++++++++++++++++ backends/common/virtual-keyboard.h | 147 ++++++++++++++ backends/common/virtualKeyboard.cpp | 261 ------------------------- backends/common/virtualKeyboard.h | 147 -------------- backends/common/virtualKeyboardParser.cpp | 292 ---------------------------- backends/common/virtualKeyboardParser.h | 70 ------- backends/events/default/default-events.h | 2 +- 9 files changed, 771 insertions(+), 771 deletions(-) create mode 100644 backends/common/virtual-keyboard-parser.cpp create mode 100644 backends/common/virtual-keyboard-parser.h create mode 100644 backends/common/virtual-keyboard.cpp create mode 100644 backends/common/virtual-keyboard.h delete mode 100644 backends/common/virtualKeyboard.cpp delete mode 100644 backends/common/virtualKeyboard.h delete mode 100644 backends/common/virtualKeyboardParser.cpp delete mode 100644 backends/common/virtualKeyboardParser.h (limited to 'backends') diff --git a/backends/common/virtual-keyboard-parser.cpp b/backends/common/virtual-keyboard-parser.cpp new file mode 100644 index 0000000000..6da5f690ba --- /dev/null +++ b/backends/common/virtual-keyboard-parser.cpp @@ -0,0 +1,292 @@ +/* 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 GUI { + +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; +} + +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::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"); + _kbdParsed = 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_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 (_keyboard->_modes.contains(name)) + return parserError("Mode '%s' has already been defined", name); + + // create new mode + VirtualKeyboard::Mode mode; + mode.name = name; + _keyboard->_modes[name] = mode; + _mode = &(_keyboard->_modes[name]); + + // if this is the keyboard's initial mode + // then set it to be the current mode + if (name == _initialModeName) + _keyboard->_initialMode = _mode; + + Common::String resolutions = modeNode->values["resolutions"]; + Common::StringTokenizer tok(resolutions, " ,"); + + uint16 scrX = g_system->getOverlayWidth(), scrY = g_system->getOverlayHeight(); + uint32 diff = 0xFFFFFFFF; + + for (Common::String res = tok.nextToken(); res.size() > 0; res = tok.nextToken()) { + int resX, resY; + if (sscanf(res.c_str(), "%dx%d", &resX, &resY) != 2) { + parserError("Invalid resolution specification"); + } else { + if (resX == scrX && resY == scrY) { + _mode->resolution = res; + break; + } else { + uint16 newDiff = ABS(scrX - resX) + ABS(scrY - resY); + if (newDiff < diff) { + diff = newDiff; + _mode->resolution = res; + } + } + } + } + + if (_mode->resolution.empty()) + return parserError("No acceptable resolution was found"); + + 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); + + Common::String name = evtNode->values["name"]; + if (_mode->events.contains(name)) + return parserError("Event '%s' has already been defined", name); + + 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 + return parserError("Event type '%s' not known", type); + + _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()); + + 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") { + int x1, y1, x2, y2; + if (!parseIntegerKey(areaNode->values["coords"].c_str(), 4, &x1, &y1, &x2, &y2)) + return parserError("Invalid coords for rect area"); + + Common::Rect rect(x1, y1, x2, y2); + _mode->imageMap.addRectMapArea(rect, areaNode->values["target"]); + } else if (shape == "poly") { + Common::StringTokenizer tok (areaNode->values["coords"], ", "); + Common::Polygon poly; + 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"); + _mode->imageMap.addPolygonMapArea(poly, areaNode->values["target"]); + } else + return parserError("Area shape '%s' not known", shape); + + 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..64651dfa13 --- /dev/null +++ b/backends/common/virtual-keyboard-parser.h @@ -0,0 +1,70 @@ +/* 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 GUI_VIRTUAL_KEYBOARD_PARSER +#define GUI_VIRTUAL_KEYBOARD_PARSER + +#include "common/xmlparser.h" +#include "backends/common/virtual-keyboard.h" + +namespace GUI { + +class VirtualKeyboardParser : public Common::XMLParser { + + typedef bool (VirtualKeyboardParser::*ParserCallback)(); + +public: + + VirtualKeyboardParser(VirtualKeyboard *kbd); + +protected: + VirtualKeyboard *_keyboard; + + /** internal state variables of parser */ + VirtualKeyboard::Mode *_mode; // pointer to mode currently being parsed + bool _modeParsed; + Common::String _initialModeName; // name of initial keyboard mode + bool _kbdParsed; + + bool keyCallback(Common::String keyName); + void cleanup() { + _mode = 0; + _kbdParsed = _modeParsed = false; + _initialModeName.clear(); + } + + bool parserCallback_Keyboard(); + bool parserCallback_Mode(); + bool parserCallback_Event(); + bool parserCallback_Layout(); + bool parserCallback_Map(); + bool parserCallback_Area(); + + Common::HashMap _callbacks; +}; + +} // 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..d19471b6bd --- /dev/null +++ b/backends/common/virtual-keyboard.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 "backends/common/virtual-keyboard.h" +#include "backends/common/virtual-keyboard-parser.h" +#include "common/config-manager.h" +#include "common/events.h" +#include "graphics/imageman.h" +#include "common/unzip.h" + +namespace GUI { + +VirtualKeyboard::VirtualKeyboard() : _currentMode(0), _keyDown(0) { + assert(g_system); + _system = g_system; + + _parser = new VirtualKeyboardParser(this); + _loaded = _displaying = false; +} + +VirtualKeyboard::~VirtualKeyboard() { + // TODO: clean up event data pointers + delete _parser; +} + +void VirtualKeyboard::reset() { + // TODO: clean up event data pointers + _modes.clear(); + _initialMode = _currentMode = 0; + _pos.x = _pos.y = 0; + _hAlignment = kAlignCentre; + _vAlignment = kAlignBottom; + _keyQueue.clear(); + _keyDown = 0; + _displaying = false; +} + +bool VirtualKeyboard::loadKeyboardPack(Common::String packName) { + // reset to default settings + reset(); + + if (ConfMan.hasKey("extrapath")) + Common::File::addDefaultDirectoryRecursive(ConfMan.get("extrapath")); + + 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 { + unzClose(zipFile); + return false; + } + unzClose(zipFile); + + ImageMan.addArchive(packName + ".zip"); +#else + return false; +#endif + } else { + warning("Keyboard pack not found"); + return false; + } + + if (!_parser->parse()) + return false; + + if (!_initialMode) + warning("Initial mode of keyboard pack not defined"); + + ModeMap::iterator it; + for (it = _modes.begin(); it != _modes.end(); it++) { + // if no image then it means layout tag for the + // required resolution was missing from the mode tag. + if (!it->_value.image) { + warning("'%s' layout missing from '%s' mode", it->_value.resolution, it->_value.name); + return false; + } + } + + _loaded = true; + return true; +} + +void VirtualKeyboard::reposition() +{ + // calculate keyboard co-ordinates + int16 scrW = _system->getOverlayWidth(), scrH = _system->getOverlayHeight(); + int16 keyW = _currentMode->image->w, keyH = _currentMode->image->h; + if (scrW != keyW) { + switch (_hAlignment) { + case kAlignCentre: + _pos.x = (scrW - keyW) / 2; + break; + case kAlignRight: + _pos.x = scrW - keyW; + break; + } + } + if (scrH != keyH) { + switch (_vAlignment) { + case kAlignMiddle: + _pos.y = (scrH - keyH) / 2; + break; + case kAlignBottom: + _pos.y = scrH - keyH; + break; + } + } +} + +void VirtualKeyboard::processClick(int16 x, int16 y) +{ + x -= _pos.x; + y -= _pos.y; + if (x < 0 || x > _currentMode->image->w) return; + if (y < 0 || y > _currentMode->image->h) return; + + Common::MapArea *area = _currentMode->imageMap.findMapArea(x, y); + if (!area) return; + if (!_currentMode->events.contains(area->getTarget())) return; + Event evt = _currentMode->events[area->getTarget()]; + + switch (evt.type) { + case kEventKey: + // add virtual keypress to queue + _keyQueue.push_back(*(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) { + _currentMode = newMode; + reposition(); + _needRedraw = true; +} + +void VirtualKeyboard::switchMode(const Common::String& newMode) { + if (!_modes.contains(newMode)) { + warning("Keyboard mode '%s' unknown", newMode.c_str()); + return; + } + _currentMode = &_modes[newMode]; + reposition(); + _needRedraw = true; +} + +void VirtualKeyboard::show() { + switchMode(_initialMode); + _displaying = true; + runLoop(); +} + +void VirtualKeyboard::hide() { + _displaying = false; +} + +void VirtualKeyboard::runLoop() { + Common::EventManager *eventMan = _system->getEventManager(); + + _system->showOverlay(); + // capture mouse clicks + while (_displaying) { + if (_needRedraw) redraw(); + + Common::Event event; + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONDOWN: + _mouseDown = event.mouse; + break; + case Common::EVENT_LBUTTONUP: + if (ABS(_mouseDown.x - event.mouse.x) < 5 + && ABS(_mouseDown.y - event.mouse.y) < 5) + processClick(event.mouse.x, event.mouse.y); + break; + case Common::EVENT_QUIT: + _system->quit(); + return; + } + } + } + // clear keyboard from overlay + _system->hideOverlay(); +} + +void VirtualKeyboard::redraw() { + _needRedraw = false; + _system->clearOverlay(); + _system->copyRectToOverlay((OverlayColor*)_currentMode->image->pixels, + _currentMode->image->w, _pos.x, _pos.y, + _currentMode->image->w, _currentMode->image->h); + _system->updateScreen(); +} + +bool VirtualKeyboard::pollEvent(Common::Event &event) { + if (_displaying || (_keyQueue.empty() && !_keyDown)) + return false; + + event.synthetic = false; // ??? + if (_keyDown) { + event.type = Common::EVENT_KEYUP; + event.kbd = *_keyDown; + _keyQueue.remove_at(0); + _keyDown = 0; + } else { + _keyDown = _keyQueue.begin(); + event.type = Common::EVENT_KEYDOWN; + event.kbd = *_keyDown; + } + return true; +} + +} // end of namespace GUI diff --git a/backends/common/virtual-keyboard.h b/backends/common/virtual-keyboard.h new file mode 100644 index 0000000000..b41f346989 --- /dev/null +++ b/backends/common/virtual-keyboard.h @@ -0,0 +1,147 @@ +/* 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 GUI_VIRTUAL_KEYBOARD_H +#define GUI_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/str.h" +#include "graphics/surface.h" + +namespace GUI { + +class VirtualKeyboardParser; + + + +class VirtualKeyboard { + + /** Type of key event */ + enum EventType { + kEventKey, + kEventSwitchMode, + kEventClose, + + kEventMax + }; + + struct Event { + Common::String name; + EventType type; + void *data; + }; + + typedef Common::HashMap EventMap; + + struct Mode { + Common::String name; + Common::String resolution; + Common::String bitmapName; + Graphics::Surface *image; + Common::ImageMap imageMap; + EventMap events; + Mode() : image(0) {} + }; + + typedef Common::HashMap ModeMap; + + enum HorizontalAlignment { + kAlignLeft, + kAlignCentre, + kAlignRight + }; + + enum VerticalAlignment { + kAlignTop, + kAlignMiddle, + kAlignBottom + }; + +public: + VirtualKeyboard(); + virtual ~VirtualKeyboard(); + + bool loadKeyboardPack(Common::String packName); + void show(); + void hide(); + bool isDisplaying() { + return _displaying; + } + bool isLoaded() { + return _loaded; + } + + /** + * Get the next virtual key event in the event queue. + * @param event point to an Event struct, which will be filled with the event data. + * @return true if an event was retrieved. + */ + bool pollEvent(Common::Event &event); + +private: + OSystem *_system; + + friend class VirtualKeyboardParser; + VirtualKeyboardParser *_parser; + + // TODO : sort order of all this stuff + void reset(); + void reposition(); + void switchMode(Mode *newMode); + void switchMode(const Common::String& newMode); + void processClick(int16 x, int16 y); + void runLoop(); + void redraw(); + + bool _loaded; + bool _displaying; + bool _needRedraw; + + ModeMap _modes; + Mode *_initialMode; + Mode *_currentMode; + + Common::Point _pos; + HorizontalAlignment _hAlignment; + VerticalAlignment _vAlignment; + + Common::Point _mouseDown; + + Common::Array _keyQueue; + Common::KeyState *_keyDown; + +}; + + +} // End of namespace GUI + + +#endif diff --git a/backends/common/virtualKeyboard.cpp b/backends/common/virtualKeyboard.cpp deleted file mode 100644 index 9937127df4..0000000000 --- a/backends/common/virtualKeyboard.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* 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/virtualKeyboard.h" -#include "backends/common/virtualKeyboardParser.h" -#include "common/config-manager.h" -#include "common/events.h" -#include "graphics/imageman.h" -#include "common/unzip.h" - -namespace GUI { - -VirtualKeyboard::VirtualKeyboard() : _currentMode(0), _keyDown(0) { - assert(g_system); - _system = g_system; - - _parser = new VirtualKeyboardParser(this); - _loaded = _displaying = false; -} - -VirtualKeyboard::~VirtualKeyboard() { - // TODO: clean up event data pointers - delete _parser; -} - -void VirtualKeyboard::reset() { - // TODO: clean up event data pointers - _modes.clear(); - _initialMode = _currentMode = 0; - _pos.x = _pos.y = 0; - _hAlignment = kAlignCentre; - _vAlignment = kAlignBottom; - _keyQueue.clear(); - _keyDown = 0; - _displaying = false; -} - -bool VirtualKeyboard::loadKeyboardPack(Common::String packName) { - // reset to default settings - reset(); - - if (ConfMan.hasKey("extrapath")) - Common::File::addDefaultDirectoryRecursive(ConfMan.get("extrapath")); - - 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 { - unzClose(zipFile); - return false; - } - unzClose(zipFile); - - ImageMan.addArchive(packName + ".zip"); -#else - return false; -#endif - } else { - warning("Keyboard pack not found"); - return false; - } - - if (!_parser->parse()) - return false; - - if (!_initialMode) - warning("Initial mode of keyboard pack not defined"); - - ModeMap::iterator it; - for (it = _modes.begin(); it != _modes.end(); it++) { - // if no image then it means layout tag for the - // required resolution was missing from the mode tag. - if (!it->_value.image) { - warning("'%s' layout missing from '%s' mode", it->_value.resolution, it->_value.name); - return false; - } - } - - _loaded = true; - return true; -} - -void VirtualKeyboard::reposition() -{ - // calculate keyboard co-ordinates - int16 scrW = _system->getOverlayWidth(), scrH = _system->getOverlayHeight(); - int16 keyW = _currentMode->image->w, keyH = _currentMode->image->h; - if (scrW != keyW) { - switch (_hAlignment) { - case kAlignCentre: - _pos.x = (scrW - keyW) / 2; - break; - case kAlignRight: - _pos.x = scrW - keyW; - break; - } - } - if (scrH != keyH) { - switch (_vAlignment) { - case kAlignMiddle: - _pos.y = (scrH - keyH) / 2; - break; - case kAlignBottom: - _pos.y = scrH - keyH; - break; - } - } -} - -void VirtualKeyboard::processClick(int16 x, int16 y) -{ - x -= _pos.x; - y -= _pos.y; - if (x < 0 || x > _currentMode->image->w) return; - if (y < 0 || y > _currentMode->image->h) return; - - Common::MapArea *area = _currentMode->imageMap.findMapArea(x, y); - if (!area) return; - if (!_currentMode->events.contains(area->getTarget())) return; - Event evt = _currentMode->events[area->getTarget()]; - - switch (evt.type) { - case kEventKey: - // add virtual keypress to queue - _keyQueue.push_back(*(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) { - _currentMode = newMode; - reposition(); - _needRedraw = true; -} - -void VirtualKeyboard::switchMode(const Common::String& newMode) { - if (!_modes.contains(newMode)) { - warning("Keyboard mode '%s' unknown", newMode.c_str()); - return; - } - _currentMode = &_modes[newMode]; - reposition(); - _needRedraw = true; -} - -void VirtualKeyboard::show() { - switchMode(_initialMode); - _displaying = true; - runLoop(); -} - -void VirtualKeyboard::hide() { - _displaying = false; -} - -void VirtualKeyboard::runLoop() { - Common::EventManager *eventMan = _system->getEventManager(); - - _system->showOverlay(); - // capture mouse clicks - while (_displaying) { - if (_needRedraw) redraw(); - - Common::Event event; - while (eventMan->pollEvent(event)) { - switch (event.type) { - case Common::EVENT_LBUTTONDOWN: - _mouseDown = event.mouse; - break; - case Common::EVENT_LBUTTONUP: - if (ABS(_mouseDown.x - event.mouse.x) < 5 - && ABS(_mouseDown.y - event.mouse.y) < 5) - processClick(event.mouse.x, event.mouse.y); - break; - case Common::EVENT_QUIT: - _system->quit(); - return; - } - } - } - // clear keyboard from overlay - _system->hideOverlay(); -} - -void VirtualKeyboard::redraw() { - _needRedraw = false; - _system->clearOverlay(); - _system->copyRectToOverlay((OverlayColor*)_currentMode->image->pixels, - _currentMode->image->w, _pos.x, _pos.y, - _currentMode->image->w, _currentMode->image->h); - _system->updateScreen(); -} - -bool VirtualKeyboard::pollEvent(Common::Event &event) { - if (_displaying || (_keyQueue.empty() && !_keyDown)) - return false; - - event.synthetic = false; // ??? - if (_keyDown) { - event.type = Common::EVENT_KEYUP; - event.kbd = *_keyDown; - _keyQueue.remove_at(0); - _keyDown = 0; - } else { - _keyDown = _keyQueue.begin(); - event.type = Common::EVENT_KEYDOWN; - event.kbd = *_keyDown; - } - return true; -} - -} // end of namespace GUI diff --git a/backends/common/virtualKeyboard.h b/backends/common/virtualKeyboard.h deleted file mode 100644 index b44d2cd459..0000000000 --- a/backends/common/virtualKeyboard.h +++ /dev/null @@ -1,147 +0,0 @@ -/* 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 GUI_VIRTUAL_KEYBOARD_H -#define GUI_VIRTUAL_KEYBOARD_H - -class OSystem; - -#include "common/events.h" -#include "common/hashmap.h" -#include "common/hash-str.h" -#include "common/imagemap.h" -#include "common/keyboard.h" -#include "common/str.h" -#include "graphics/surface.h" - -namespace GUI { - -class VirtualKeyboardParser; - - - -class VirtualKeyboard { - - /** Type of key event */ - enum EventType { - kEventKey, - kEventSwitchMode, - kEventClose, - - kEventMax - }; - - struct Event { - Common::String name; - EventType type; - void *data; - }; - - typedef Common::HashMap EventMap; - - struct Mode { - Common::String name; - Common::String resolution; - Common::String bitmapName; - Graphics::Surface *image; - Common::ImageMap imageMap; - EventMap events; - Mode() : image(0) {} - }; - - typedef Common::HashMap ModeMap; - - enum HorizontalAlignment { - kAlignLeft, - kAlignCentre, - kAlignRight - }; - - enum VerticalAlignment { - kAlignTop, - kAlignMiddle, - kAlignBottom - }; - -public: - VirtualKeyboard(); - virtual ~VirtualKeyboard(); - - bool loadKeyboardPack(Common::String packName); - void show(); - void hide(); - bool isDisplaying() { - return _displaying; - } - bool isLoaded() { - return _loaded; - } - - /** - * Get the next virtual key event in the event queue. - * @param event point to an Event struct, which will be filled with the event data. - * @return true if an event was retrieved. - */ - bool pollEvent(Common::Event &event); - -private: - OSystem *_system; - - friend class VirtualKeyboardParser; - VirtualKeyboardParser *_parser; - - // TODO : sort order of all this stuff - void reset(); - void reposition(); - void switchMode(Mode *newMode); - void switchMode(const Common::String& newMode); - void processClick(int16 x, int16 y); - void runLoop(); - void redraw(); - - bool _loaded; - bool _displaying; - bool _needRedraw; - - ModeMap _modes; - Mode *_initialMode; - Mode *_currentMode; - - Common::Point _pos; - HorizontalAlignment _hAlignment; - VerticalAlignment _vAlignment; - - Common::Point _mouseDown; - - Common::Array _keyQueue; - Common::KeyState *_keyDown; - -}; - - -} // End of namespace GUI - - -#endif diff --git a/backends/common/virtualKeyboardParser.cpp b/backends/common/virtualKeyboardParser.cpp deleted file mode 100644 index 8475f3e4f6..0000000000 --- a/backends/common/virtualKeyboardParser.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* 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/virtualKeyboardParser.h" - -#include "common/keyboard.h" -#include "graphics/imageman.h" -#include "common/util.h" - -namespace GUI { - -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; -} - -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::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"); - _kbdParsed = 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_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 (_keyboard->_modes.contains(name)) - return parserError("Mode '%s' has already been defined", name); - - // create new mode - VirtualKeyboard::Mode mode; - mode.name = name; - _keyboard->_modes[name] = mode; - _mode = &(_keyboard->_modes[name]); - - // if this is the keyboard's initial mode - // then set it to be the current mode - if (name == _initialModeName) - _keyboard->_initialMode = _mode; - - Common::String resolutions = modeNode->values["resolutions"]; - Common::StringTokenizer tok(resolutions, " ,"); - - uint16 scrX = g_system->getOverlayWidth(), scrY = g_system->getOverlayHeight(); - uint32 diff = 0xFFFFFFFF; - - for (Common::String res = tok.nextToken(); res.size() > 0; res = tok.nextToken()) { - int resX, resY; - if (sscanf(res.c_str(), "%dx%d", &resX, &resY) != 2) { - parserError("Invalid resolution specification"); - } else { - if (resX == scrX && resY == scrY) { - _mode->resolution = res; - break; - } else { - uint16 newDiff = ABS(scrX - resX) + ABS(scrY - resY); - if (newDiff < diff) { - diff = newDiff; - _mode->resolution = res; - } - } - } - } - - if (_mode->resolution.empty()) - return parserError("No acceptable resolution was found"); - - 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); - - Common::String name = evtNode->values["name"]; - if (_mode->events.contains(name)) - return parserError("Event '%s' has already been defined", name); - - 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 - return parserError("Event type '%s' not known", type); - - _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()); - - 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") { - int x1, y1, x2, y2; - if (!parseIntegerKey(areaNode->values["coords"].c_str(), 4, &x1, &y1, &x2, &y2)) - return parserError("Invalid coords for rect area"); - - Common::Rect rect(x1, y1, x2, y2); - _mode->imageMap.addRectMapArea(rect, areaNode->values["target"]); - } else if (shape == "poly") { - Common::StringTokenizer tok (areaNode->values["coords"], ", "); - Common::Polygon poly; - 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"); - _mode->imageMap.addPolygonMapArea(poly, areaNode->values["target"]); - } else - return parserError("Area shape '%s' not known", shape); - - return true; -} - -} // end of namespace GUI diff --git a/backends/common/virtualKeyboardParser.h b/backends/common/virtualKeyboardParser.h deleted file mode 100644 index 4325d8af31..0000000000 --- a/backends/common/virtualKeyboardParser.h +++ /dev/null @@ -1,70 +0,0 @@ -/* 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 GUI_VIRTUAL_KEYBOARD_PARSER -#define GUI_VIRTUAL_KEYBOARD_PARSER - -#include "common/xmlparser.h" -#include "backends/common/virtualKeyboard.h" - -namespace GUI { - -class VirtualKeyboardParser : public Common::XMLParser { - - typedef bool (VirtualKeyboardParser::*ParserCallback)(); - -public: - - VirtualKeyboardParser(VirtualKeyboard *kbd); - -protected: - VirtualKeyboard *_keyboard; - - /** internal state variables of parser */ - VirtualKeyboard::Mode *_mode; // pointer to mode currently being parsed - bool _modeParsed; - Common::String _initialModeName; // name of initial keyboard mode - bool _kbdParsed; - - bool keyCallback(Common::String keyName); - void cleanup() { - _mode = 0; - _kbdParsed = _modeParsed = false; - _initialModeName.clear(); - } - - bool parserCallback_Keyboard(); - bool parserCallback_Mode(); - bool parserCallback_Event(); - bool parserCallback_Layout(); - bool parserCallback_Map(); - bool parserCallback_Area(); - - Common::HashMap _callbacks; -}; - -} // end of namespace GUI - -#endif diff --git a/backends/events/default/default-events.h b/backends/events/default/default-events.h index c52535adfc..c21fb41b16 100644 --- a/backends/events/default/default-events.h +++ b/backends/events/default/default-events.h @@ -28,7 +28,7 @@ #include "common/events.h" #include "common/savefile.h" -#include "backends/common/virtualKeyboard.h" +#include "backends/common/virtual-keyboard.h" /* At some point we will remove pollEvent from OSystem and change -- cgit v1.2.3