Keymapper WIP:

* Got rid of default keymaps as they over-complicated the API
* Remapping dialog WIP

svn-id: r33699
This commit is contained in:
Stephen Kennedy 2008-08-08 14:23:59 +00:00
parent 2645ca48ad
commit 4eacc09024
13 changed files with 360 additions and 112 deletions

View File

@ -29,6 +29,7 @@
#include "common/config-manager.h"
#include "backends/events/default/default-events.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/remap-dialog.h"
#include "backends/vkeybd/virtual-keyboard.h"
#include "engines/engine.h"
@ -427,6 +428,9 @@ bool DefaultEventManager::pollEvent(Common::Event &event) {
if (!isPaused) g_engine->pauseEngine(false);
result = false;
}
} else if (event.kbd.keycode == Common::KEYCODE_F7 && event.kbd.flags == 0) {
Common::RemapDialog dialog;
dialog.runModal();
}
break;

View File

@ -28,16 +28,11 @@
namespace Common {
void KeymapManager::Domain::setDefaultKeymap(Keymap *map) {
delete _defaultKeymap;
_defaultKeymap = map;
}
void KeymapManager::Domain::addKeymap(const String& name, Keymap *map) {
KeymapMap::iterator it = _keymaps.find(name);
void KeymapManager::Domain::addKeymap(Keymap *map) {
KeymapMap::iterator it = _keymaps.find(map->getName());
if (it != _keymaps.end())
delete _keymaps[name];
_keymaps[name] = map;
delete _keymaps[map->getName()];
_keymaps[map->getName()] = map;
}
void KeymapManager::Domain::deleteAllKeyMaps() {
@ -47,14 +42,6 @@ void KeymapManager::Domain::deleteAllKeyMaps() {
delete it->_value;
}
_keymaps.clear();
if (_defaultKeymap) {
//_defaultKeymap->saveMappings(_configDomain, "default");
delete _defaultKeymap;
}
}
Keymap *KeymapManager::Domain::getDefaultKeymap() {
return _defaultKeymap;
}
Keymap *KeymapManager::Domain::getKeymap(const String& name) {
@ -80,20 +67,9 @@ void KeymapManager::registerHardwareKeySet(HardwareKeySet *keys) {
_hardwareKeys = keys;
}
void KeymapManager::registerDefaultGlobalKeymap(Keymap *map) {
ConfigManager::Domain *dom = ConfMan.getDomain(ConfigManager::kApplicationDomain);
assert(dom);
initKeymap(dom, "default", map);
_globalDomain.setDefaultKeymap(map);
}
void KeymapManager::registerGlobalKeymap(const String& name, Keymap *map) {
ConfigManager::Domain *dom = ConfMan.getDomain(ConfigManager::kApplicationDomain);
assert(dom);
initKeymap(dom, name, map);
_globalDomain.addKeymap(name, map);
void KeymapManager::registerGlobalKeymap(Keymap *map) {
initKeymap(_globalDomain.getConfigDomain(), map);
_globalDomain.addKeymap(map);
}
void KeymapManager::refreshGameDomain() {
@ -103,22 +79,15 @@ void KeymapManager::refreshGameDomain() {
}
}
void KeymapManager::registerDefaultGameKeymap(Keymap *map) {
void KeymapManager::registerGameKeymap(Keymap *map) {
refreshGameDomain();
initKeymap(_gameDomain.getConfigDomain(), "default", map);
_gameDomain.setDefaultKeymap(map);
}
void KeymapManager::registerGameKeymap(const String& name, Keymap *map) {
refreshGameDomain();
initKeymap(_gameDomain.getConfigDomain(), name, map);
_gameDomain.addKeymap(name, map);
initKeymap(_gameDomain.getConfigDomain(), map);
_gameDomain.addKeymap(map);
}
void KeymapManager::initKeymap(ConfigManager::Domain *domain,
const String& name,
Keymap *map) {
map->loadMappings(domain, name, _hardwareKeys);
map->loadMappings(domain, _hardwareKeys);
if (map->isComplete(_hardwareKeys) == false)
automaticMap(map);
}
@ -161,7 +130,7 @@ void KeymapManager::automaticMap(Keymap *map) {
Keymap *KeymapManager::getKeymap(const String& name) {
Keymap *keymap = _gameDomain.getKeymap(name);
if (!keymap)
_globalDomain.getKeymap(name);
keymap = _globalDomain.getKeymap(name);
return keymap;
}

View File

@ -37,8 +37,11 @@ class KeymapManager {
public:
class Domain {
typedef HashMap<String, Keymap*,
IgnoreCase_Hash, IgnoreCase_EqualTo> KeymapMap;
public:
Domain() : _defaultKeymap(0), _configDomain(0) {}
Domain() : _configDomain(0) {}
~Domain() {
deleteAllKeyMaps();
}
@ -50,20 +53,22 @@ public:
return _configDomain;
}
void setDefaultKeymap(Keymap *map);
void addKeymap(const String& name, Keymap *map);
void addKeymap(Keymap *map);
void deleteAllKeyMaps();
Keymap *getDefaultKeymap();
Keymap *getKeymap(const String& name);
typedef KeymapMap::iterator iterator;
typedef KeymapMap::const_iterator const_iterator;
iterator begin() { return _keymaps.begin(); }
const_iterator begin() const { return _keymaps.begin(); }
iterator end() { return _keymaps.end(); }
const_iterator end() const { return _keymaps.end(); }
uint32 count() { return _keymaps.size(); }
private:
typedef HashMap<String, Keymap*,
IgnoreCase_Hash, IgnoreCase_EqualTo> KeymapMap;
ConfigManager::Domain *_configDomain;
Keymap *_defaultKeymap;
KeymapMap _keymaps;
};
@ -71,19 +76,21 @@ public:
~KeymapManager();
void registerHardwareKeySet(HardwareKeySet *keys);
HardwareKeySet *getHardwareKeySet() { return _hardwareKeys; }
void registerDefaultGlobalKeymap(Keymap *map);
void registerGlobalKeymap(const String& name, Keymap *map);
void registerGlobalKeymap(Keymap *map);
void refreshGameDomain();
void registerDefaultGameKeymap(Keymap *map);
void registerGameKeymap(const String& name, Keymap *map);
void registerGameKeymap(Keymap *map);
Keymap *getKeymap(const String& name);
Domain& getGlobalDomain() { return _globalDomain; }
Domain& getGameDomain() { return _gameDomain; }
private:
void initKeymap(ConfigManager::Domain *domain, const String& name, Keymap *keymap);
void initKeymap(ConfigManager::Domain *domain, Keymap *keymap);
void automaticMap(Keymap *map);
Domain _globalDomain;

View File

@ -48,17 +48,23 @@ void Keymap::addAction(Action *action) {
void Keymap::registerMapping(Action *action, const HardwareKey *hwKey) {
HashMap<KeyState, Action*>::iterator it;
it = _keymap.find(hwKey->key);
// if key is already mapped to an action then un-map it
if (it != _keymap.end())
// if key is already mapped to a different action then un-map it
if (it != _keymap.end() && action != it->_value) {
HashMap<KeyState, Action*>::iterator it2;
for (it2 = _keymap.begin(); it2 != _keymap.end(); it2++) {
printf("%d\n", it2->_value);
}
it->_value->mapKey(0);
}
_keymap[hwKey->key] = action;
}
void Keymap::unregisterMapping(Action *action) {
const HardwareKey *hwKey = action->getMappedKey();
if (hwKey)
_keymap[hwKey->key] = 0;
if (hwKey) {
_keymap.erase(hwKey->key);
}
}
Action *Keymap::getAction(int32 id) {
@ -92,9 +98,9 @@ Action *Keymap::getMappedAction(const KeyState& ks) const {
return it->_value;
}
void Keymap::loadMappings(ConfigManager::Domain *domain, const String& name, const HardwareKeySet *hwKeys) {
void Keymap::loadMappings(ConfigManager::Domain *domain, const HardwareKeySet *hwKeys) {
ConfigManager::Domain::iterator it;
String prefix = "km_" + name + "_";
String prefix = "km_" + _name + "_";
for (it = domain->begin(); it != domain->end(); it++) {
const String& key = it->_key;
if (!key.hasPrefix(prefix.c_str()))
@ -111,7 +117,7 @@ void Keymap::loadMappings(ConfigManager::Domain *domain, const String& name, con
Action *ua = getAction(actionId);
if (!ua) {
warning("'%s' keymap does not contain Action with ID %d",
name.c_str(), (int)actionId);
_name.c_str(), (int)actionId);
continue;
}
@ -131,14 +137,14 @@ void Keymap::loadMappings(ConfigManager::Domain *domain, const String& name, con
}
}
void Keymap::saveMappings(ConfigManager::Domain *domain, const String& name) {
void Keymap::saveMappings(ConfigManager::Domain *domain) {
if (!domain) return;
List<Action*>::const_iterator it;
char buf[11];
char buf[12];
String prefix = "km_" + _name + "_";
for (it = _actions.begin(); it != _actions.end(); it++) {
String key("km_");
sprintf(buf, "%d", (*it)->id);
key += name + "_" + buf;
String key = prefix + buf;
if ((*it)->getMappedKey())
sprintf(buf, "%d", (*it)->getMappedKey()->id);
else

View File

@ -51,7 +51,7 @@ template<> struct Hash<KeyState>
class Keymap {
public:
Keymap() {}
Keymap(const String& na) : _name(na) {}
Keymap(const Keymap& km);
public:
@ -70,9 +70,9 @@ public:
Action *getAction(int32 id);
/**
* Get a read-only array of all the Actions contained in this Keymap
* Get the list of all the Actions contained in this Keymap
*/
const List<Action*>& getActions() const { return _actions; }
List<Action*>& getActions() { return _actions; }
/**
* Find the Action that a key is mapped to
@ -84,17 +84,15 @@ public:
/**
* Load this keymap's mappings from the given config domain and hardware key set
* @param domain config domain to load keymap from
* @param name name of the keymap to load
* @param hwKeys the set to retrieve hardware key pointers from
*/
void loadMappings(ConfigManager::Domain *domain, const String& name, const HardwareKeySet *hwKeys);
void loadMappings(ConfigManager::Domain *domain, const HardwareKeySet *hwKeys);
/**
* Save this keymap's mappings to the given config domain
* @param domain config domain to save keymap to
* @param name name to save the keymap under
*/
void saveMappings(ConfigManager::Domain *domain, const String& name);
void saveMappings(ConfigManager::Domain *domain);
/**
* Returns true if all UserAction's in Keymap are mapped, or,
@ -102,6 +100,8 @@ public:
*/
bool isComplete(const HardwareKeySet *hwKeys);
const String& getName() { return _name; }
private:
friend struct Action;
/**
@ -124,6 +124,7 @@ private:
void internalMapKey(Action *action, HardwareKey *hwKey);
String _name;
List<Action*> _actions;
HashMap<KeyState, Action*> _keymap;

View File

@ -30,6 +30,7 @@ namespace Common {
Keymapper::Keymapper(EventManager *evtMgr) {
_eventMan = evtMgr;
_keymapMan = new KeymapManager();
_enabled = true;
}
Keymapper::~Keymapper() {
@ -40,28 +41,15 @@ void Keymapper::registerHardwareKeySet(HardwareKeySet *keys) {
_keymapMan->registerHardwareKeySet(keys);
}
void Keymapper::addGlobalKeymap(const String& name, Keymap *keymap) {
_keymapMan->registerGlobalKeymap(name, keymap);
void Keymapper::addGlobalKeymap(Keymap *keymap) {
_keymapMan->registerGlobalKeymap(keymap);
}
void Keymapper::setDefaultGlobalKeymap(Keymap *keymap) {
_keymapMan->registerDefaultGlobalKeymap(keymap);
pushKeymap(keymap, false);
}
void Keymapper::addGameKeymap(const String& name, Keymap *keymap) {
void Keymapper::addGameKeymap(Keymap *keymap) {
if (ConfMan.getActiveDomain() == 0)
error("Call to Keymapper::initGame when no game loaded");
error("Call to Keymapper::addGameKeymap when no game loaded");
_keymapMan->registerGameKeymap(name, keymap);
}
void Keymapper::setDefaultGameKeymap(Keymap *keymap) {
if (ConfMan.getActiveDomain() == 0)
error("Call to Keymapper::initGame when no game loaded");
_keymapMan->registerDefaultGameKeymap(keymap);
pushKeymap(keymap, true);
_keymapMan->registerGameKeymap(keymap);
}
bool Keymapper::pushKeymap(const String& name, bool inherit) {
@ -95,6 +83,7 @@ bool Keymapper::mapKeyUp(const KeyState& key) {
}
bool Keymapper::mapKey(const KeyState& key, bool isKeyDown) {
if (!_enabled) return false;
if (_activeMaps.empty()) return false;
Action *action = 0;
@ -143,4 +132,10 @@ bool Keymapper::mapKey(const KeyState& key, bool isKeyDown) {
return true;
}
const HardwareKey *Keymapper::getHardwareKey(const KeyState& key) {
HardwareKeySet *keyset = _keymapMan->getHardwareKeySet();
if (!keyset) return 0;
return keyset->findHardwareKey(key);
}
} // end of namespace Common

View File

@ -36,6 +36,8 @@
namespace Common {
class Keymapper {
friend class RemapDialog;
public:
Keymapper(EventManager *eventMan);
@ -49,39 +51,31 @@ public:
/**
* Add a general keymap to the global domain.
* Add a keymap to the global domain.
* If a saved key setup exists for it in the ini file it will be used.
* Else, the key setup will be automatically mapped.
*/
void addGlobalKeymap(const String& name, Keymap *keymap);
void addGlobalKeymap(Keymap *keymap);
/**
* Sets the default keymap for the global domain.
*/
void setDefaultGlobalKeymap(Keymap *keymap);
/**
* Add a general keymap to the game domain.
* Add a keymap to the game domain.
* @see addGlobalKeyMap
* @note initGame() should be called before any game keymaps are added.
*/
void addGameKeymap(const String& name, Keymap *keymap);
void addGameKeymap(Keymap *keymap);
/**
* Sets the default keymap for the game domain.
*/
void setDefaultGameKeymap(Keymap *keymap);
/**
* Push a new keymap to the top of the active stack, activating it for use.
* Push a new keymap to the top of the active stack, activating
* it for use.
* @param name name of the keymap to push
* @param inherit if true
* @return true if successful
* @param inherit if true keymapper will iterate down the
* stack it cannot find a key in the new map
* @return true if succesful
*/
bool pushKeymap(const String& name, bool inherit = false);
/**
* Pop the active keymap off the stack.
* Pop the top keymap off the active stack.
*/
void popKeymap();
@ -107,6 +101,10 @@ public:
*/
bool mapKeyUp(const KeyState& key);
const HardwareKey *getHardwareKey(const KeyState& key);
void setEnabled(bool enabled) { _enabled = enabled; }
private:
void pushKeymap(Keymap *newMap, bool inherit);
@ -116,6 +114,7 @@ private:
EventManager *_eventMan;
KeymapManager *_keymapMan;
bool _enabled;
struct MapRecord {
Keymap* keymap;

View File

@ -0,0 +1,174 @@
#include "backends/keymapper/remap-dialog.h"
#include "gui/eval.h"
#include "gui/newgui.h"
#include "gui/PopUpWidget.h"
namespace Common {
enum {
kRemapCmd = 'REMP'
};
RemapDialog::RemapDialog()
: Dialog("remap"), _activeRemap(0) {
const int screenW = g_system->getOverlayWidth();
const int screenH = g_system->getOverlayHeight();
_keymapper = g_system->getEventManager()->getKeymapper();
assert(_keymapper);
_activeKeymaps = &_keymapper->_activeMaps;
int keymapCount = 0;
_globalKeymaps = &_keymapper->_keymapMan->getGlobalDomain();
if (_globalKeymaps->count() == 0)
_globalKeymaps = 0;
else
keymapCount += _globalKeymaps->count();
if (ConfMan.getActiveDomain() != 0) {
_gameKeymaps = &_keymapper->_keymapMan->getGameDomain();
if (_gameKeymaps->count() == 0)
_gameKeymaps = 0;
else
keymapCount += _gameKeymaps->count();
} else
_gameKeymaps = 0;
_keymapTable = (Keymap**)malloc(sizeof(Keymap*) * keymapCount);
int labelWidth = g_gui.evaluator()->getVar("remap_popup_labelW");
_kmPopUp = new GUI::PopUpWidget(this, "remap_popup", "Keymap:", labelWidth);
if (_activeKeymaps->size() > 0) {
_kmPopUp->appendEntry(_activeKeymaps->top().keymap->getName() + " (Active)");
}
KeymapManager::Domain::iterator it;
uint32 idx = 0;
if (_globalKeymaps) {
_kmPopUp->appendEntry("");
for (it = _globalKeymaps->begin(); it != _globalKeymaps->end(); it++) {
_kmPopUp->appendEntry(it->_value->getName() + " (Global)", idx);
_keymapTable[idx++] = it->_value;
}
}
if (_gameKeymaps) {
_kmPopUp->appendEntry("");
for (it = _gameKeymaps->begin(); it != _gameKeymaps->end(); it++) {
_kmPopUp->appendEntry(it->_value->getName() + " (Game)", idx);
_keymapTable[idx++] = it->_value;
}
}
_w = screenW - 2 * 20;
_h = screenH - 2 * 20;
// Center the dialog
_x = (screenW - _w) / 2;
_y = (screenH - _h) / 2;
_colCount = 2;
_spacing = 10;
_colWidth = (_w - (_colCount - 1) * _spacing) / _colCount;
_widgetsY = g_gui.evaluator()->getVar("remap_widgetsY");
if (g_gui.getWidgetSize() == GUI::kBigWidgetSize)
_buttonHeight = GUI::kBigButtonHeight;
else
_buttonHeight = GUI::kButtonHeight;
}
RemapDialog::~RemapDialog() {
free(_keymapTable);
}
void RemapDialog::open() {
Dialog::open();
_kmPopUp->setSelected(0);
refreshKeymap();
}
void RemapDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
if (cmd >= kRemapCmd && cmd < kRemapCmd + _keymapMappings.size()) {
startRemapping(&_keymapMappings[cmd - kRemapCmd]);
} else if (cmd == GUI::kPopUpItemSelectedCmd) {
refreshKeymap();
} else {
GUI::Dialog::handleCommand(sender, cmd, data);
}
}
void RemapDialog::startRemapping(Mapping *remap) {
_activeRemap = remap;
_activeRemap->keyButton->setLabel("...");
_keymapper->setEnabled(false);
}
void RemapDialog::stopRemapping() {
refreshKeymap();
_activeRemap = 0;
_keymapper->setEnabled(true);
}
void RemapDialog::handleKeyDown(Common::KeyState state) {
if (_activeRemap) {
const HardwareKey *hwkey = _keymapper->getHardwareKey(state);
if (hwkey) {
_activeRemap->action->mapKey(hwkey);
stopRemapping();
}
} else {
GUI::Dialog::handleKeyDown(state);
}
}
void RemapDialog::refreshKeymap() {
if (_activeKeymaps->size() > 0 && _kmPopUp->getSelected() == 0) {
// TODO: show active keymaps (with inherited mappings)
} else {
Keymap *km = _keymapTable[_kmPopUp->getSelectedTag()];
List<Action*>& actions = km->getActions();
setNumOfWidgets(actions.size());
uint idx = 0;
List<Action*>::iterator it;
for (it = actions.begin(); it != actions.end(); it++, idx++) {
Mapping& ma = _keymapMappings[idx];
ma.action = *it;
ma.actionText->setLabel(ma.action->description + ":");
ma.keyButton->setLabel(ma.action->getMappedKey()->description);
}
}
}
void RemapDialog::setNumOfWidgets(uint newNum) {
uint num = _keymapMappings.size();
if (num < newNum) {
uint textYOff = (_buttonHeight - kLineHeight) / 2;
while (num < newNum) {
uint x = (num % _colCount) * (_colWidth + _spacing);
uint y = _widgetsY + (num / _colCount) * (_buttonHeight + _spacing);
Mapping ma;
ma.action = 0;
ma.actionText = new GUI::StaticTextWidget(this, x, y + textYOff,
_colWidth / 2, kLineHeight, "", Graphics::kTextAlignRight);
ma.keyButton = new GUI::ButtonWidget(this, x + _colWidth / 2,
y, _colWidth / 2, _buttonHeight, "", kRemapCmd + num);
_keymapMappings.push_back(ma);
num++;
}
} else {
while (num > newNum) {
Mapping ma = _keymapMappings.remove_at(num - 1);
removeWidget(ma.actionText);
delete ma.actionText;
removeWidget(ma.keyButton);
delete ma.keyButton;
num--;
}
}
}
} // end of namespace Common

View File

@ -0,0 +1,78 @@
/* 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 REMAP_DIALOG_H
#define REMAP_DIALOG_H
#include "backends/keymapper/keymapper.h"
#include "gui/dialog.h"
namespace GUI {
class PopupWidget;
}
namespace Common {
class RemapDialog : public GUI::Dialog {
public:
RemapDialog();
virtual ~RemapDialog();
virtual void open();
virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data);
virtual void handleKeyDown(Common::KeyState state);
protected:
struct Mapping {
Action *action;
GUI::StaticTextWidget *actionText;
GUI::ButtonWidget *keyButton;
};
void refreshKeymap();
void setNumOfWidgets(uint num);
void startRemapping(Mapping *remap);
void stopRemapping();
Keymapper *_keymapper;
Stack<Keymapper::MapRecord> *_activeKeymaps;
KeymapManager::Domain *_globalKeymaps;
KeymapManager::Domain *_gameKeymaps;
GUI::PopUpWidget *_kmPopUp;
Keymap** _keymapTable;
uint _colWidth;
uint _colCount;
uint _spacing;
uint _widgetsY;
uint _buttonHeight;
Mapping *_activeRemap;
Array<Mapping> _keymapMappings;
};
} // end of namespace Common
#endif

View File

@ -29,6 +29,7 @@ MODULE_OBJS := \
saves/compressed/compressed-saves.o \
timer/default/default-timer.o \
keymapper/action.o \
keymapper/remap-dialog.o \
keymapper/keymap.o \
keymapper/keymap-manager.o \
keymapper/keymapper.o \

View File

@ -532,7 +532,7 @@ void OSystem_SDL::setupKeymapper() {
keySet->addHardwareKey(new HardwareKey( 'f', KeyState(KEYCODE_f), "f" ));
mapper->registerHardwareKeySet(keySet);
Keymap *global = new Keymap();
Keymap *global = new Keymap("global");
Action *act;
Event evt;
@ -551,6 +551,7 @@ void OSystem_SDL::setupKeymapper() {
#undef ADD_KEYDOWN_EVENT
mapper->setDefaultGlobalKeymap(global);
mapper->addGlobalKeymap(global);
mapper->pushKeymap("global");
}

View File

@ -1116,6 +1116,14 @@
RelativePath="..\..\backends\keymapper\keymapper.h"
>
</File>
<File
RelativePath="..\..\backends\keymapper\remap-dialog.cpp"
>
</File>
<File
RelativePath="..\..\backends\keymapper\remap-dialog.h"
>
</File>
</Filter>
</Filter>
<Filter

View File

@ -373,6 +373,11 @@ const char *Theme::_defaultConfigINI =
"scummsaveload_choose=(prev.x2 + 10) prev.y prev.w prev.h\n"
"scummsaveload_extinfo.visible=true\n"
"\n"
"# Keymapper remap dialog\n"
"remap=8 8 (w - 16) (h - 16)\n"
"remap_popup_labelW=buttonWidth\n"
"remap_popup=0 0 parent.w (kLineHeight + 2)\n"
"remap_widgetsY=(remap_popup.h + 10)\n"
"############################################\n"
"[chooser]\n"
"chooserW=(w - 2 * 8)\n"