Redesigned shortcut handler to be capable of dealing with child and

parent widgets. This should be the final design change. Also, some
various bug fixes.
pull/29/head
Aesylwinn 9 years ago
parent acdb636935
commit f251c3867d

@ -1,15 +1,15 @@
#include "shortcut.hpp" #include "shortcut.hpp"
#include <QKeyEvent> #include <cassert>
#include <QMouseEvent>
#include <QShortcut> #include <QWidget>
#include "state.hpp" #include "state.hpp"
#include "shortcutmanager.hpp" #include "shortcutmanager.hpp"
namespace CSMPrefs namespace CSMPrefs
{ {
Shortcut::Shortcut(const std::string& name, QObject* parent) Shortcut::Shortcut(const std::string& name, QWidget* parent)
: QObject(parent) : QObject(parent)
, mEnabled(true) , mEnabled(true)
, mName(name) , mName(name)
@ -20,13 +20,15 @@ namespace CSMPrefs
, mActivationStatus(AS_Inactive) , mActivationStatus(AS_Inactive)
, mModifierStatus(false) , mModifierStatus(false)
{ {
assert (parent);
State::get().getShortcutManager().addShortcut(this); State::get().getShortcutManager().addShortcut(this);
ShortcutManager::SequenceData data = State::get().getShortcutManager().getSequence(name); ShortcutManager::SequenceData data = State::get().getShortcutManager().getSequence(name);
setSequence(data.first); setSequence(data.first);
setModifier(data.second); setModifier(data.second);
} }
Shortcut::Shortcut(const std::string& name, SecondaryMode secMode, QObject* parent) Shortcut::Shortcut(const std::string& name, SecondaryMode secMode, QWidget* parent)
: QObject(parent) : QObject(parent)
, mEnabled(true) , mEnabled(true)
, mName(name) , mName(name)
@ -37,6 +39,8 @@ namespace CSMPrefs
, mActivationStatus(AS_Inactive) , mActivationStatus(AS_Inactive)
, mModifierStatus(false) , mModifierStatus(false)
{ {
assert (parent);
State::get().getShortcutManager().addShortcut(this); State::get().getShortcutManager().addShortcut(this);
ShortcutManager::SequenceData data = State::get().getShortcutManager().getSequence(name); ShortcutManager::SequenceData data = State::get().getShortcutManager().getSequence(name);
setSequence(data.first); setSequence(data.first);

@ -7,9 +7,7 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
class QKeyEvent; class QWidget;
class QMouseEvent;
class QShortcut;
namespace CSMPrefs namespace CSMPrefs
{ {
@ -34,8 +32,8 @@ namespace CSMPrefs
SM_Ignore ///< The secondary signal will not ever be emitted SM_Ignore ///< The secondary signal will not ever be emitted
}; };
Shortcut(const std::string& name, QObject* parent); Shortcut(const std::string& name, QWidget* parent);
Shortcut(const std::string& name, SecondaryMode secMode, QObject* parent); Shortcut(const std::string& name, SecondaryMode secMode, QWidget* parent);
~Shortcut(); ~Shortcut();

@ -1,7 +1,7 @@
#include "shortcuteventhandler.hpp" #include "shortcuteventhandler.hpp"
#include <algorithm> #include <algorithm>
#include <iostream> #include <cassert>
#include <QEvent> #include <QEvent>
#include <QKeyEvent> #include <QKeyEvent>
@ -19,54 +19,88 @@ namespace CSMPrefs
void ShortcutEventHandler::addShortcut(Shortcut* shortcut) void ShortcutEventHandler::addShortcut(Shortcut* shortcut)
{ {
mShortcuts.push_back(shortcut); // Enforced by shortcut class
QWidget* widget = static_cast<QWidget*>(shortcut->parent());
// Check if widget setup is needed
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
if (shortcutListIt == mWidgetShortcuts.end())
{
// Create list
shortcutListIt = mWidgetShortcuts.insert(std::make_pair(widget, ShortcutList())).first;
// Check if widget has a parent with shortcuts, unfortunately it is not typically set yet
updateParent(widget);
// Intercept widget events
widget->installEventFilter(this);
connect(widget, SIGNAL(destroyed()), this, SLOT(widgetDestroyed()));
}
// Add to list
shortcutListIt->second.push_back(shortcut);
} }
void ShortcutEventHandler::removeShortcut(Shortcut* shortcut) void ShortcutEventHandler::removeShortcut(Shortcut* shortcut)
{ {
std::remove(mShortcuts.begin(), mShortcuts.end(), shortcut); // Enforced by shortcut class
QWidget* widget = static_cast<QWidget*>(shortcut->parent());
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
if (shortcutListIt != mWidgetShortcuts.end())
{
std::remove(shortcutListIt->second.begin(), shortcutListIt->second.end(), shortcut);
}
} }
bool ShortcutEventHandler::eventFilter(QObject* watched, QEvent* event) bool ShortcutEventHandler::eventFilter(QObject* watched, QEvent* event)
{ {
// Process event
if (event->type() == QEvent::KeyPress) if (event->type() == QEvent::KeyPress)
{ {
QWidget* widget = static_cast<QWidget*>(watched);
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
unsigned int mod = (unsigned int) keyEvent->modifiers(); unsigned int mod = (unsigned int) keyEvent->modifiers();
unsigned int key = (unsigned int) keyEvent->key(); unsigned int key = (unsigned int) keyEvent->key();
if (!keyEvent->isAutoRepeat()) if (!keyEvent->isAutoRepeat())
return activate(mod, key); return activate(widget, mod, key);
} }
else if (event->type() == QEvent::KeyRelease) else if (event->type() == QEvent::KeyRelease)
{ {
QWidget* widget = static_cast<QWidget*>(watched);
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
unsigned int mod = (unsigned int) keyEvent->modifiers(); unsigned int mod = (unsigned int) keyEvent->modifiers();
unsigned int key = (unsigned int) keyEvent->key(); unsigned int key = (unsigned int) keyEvent->key();
if (!keyEvent->isAutoRepeat()) if (!keyEvent->isAutoRepeat())
return deactivate(mod, key); return deactivate(widget, mod, key);
} }
else if (event->type() == QEvent::MouseButtonPress) else if (event->type() == QEvent::MouseButtonPress)
{ {
QWidget* widget = static_cast<QWidget*>(watched);
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
unsigned int mod = (unsigned int) mouseEvent->modifiers(); unsigned int mod = (unsigned int) mouseEvent->modifiers();
unsigned int button = (unsigned int) mouseEvent->button(); unsigned int button = (unsigned int) mouseEvent->button();
return activate(mod, button); return activate(widget, mod, button);
} }
else if (event->type() == QEvent::MouseButtonRelease) else if (event->type() == QEvent::MouseButtonRelease)
{ {
QWidget* widget = static_cast<QWidget*>(watched);
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
unsigned int mod = (unsigned int) mouseEvent->modifiers(); unsigned int mod = (unsigned int) mouseEvent->modifiers();
unsigned int button = (unsigned int) mouseEvent->button(); unsigned int button = (unsigned int) mouseEvent->button();
return deactivate(mod, button); return deactivate(widget, mod, button);
} }
else if (event->type() == QEvent::FocusOut) else if (event->type() == QEvent::FocusOut)
{ {
QWidget* widget = static_cast<QWidget*>(watched);
ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
// Deactivate in case events are missed // Deactivate in case events are missed
for (std::vector<Shortcut*>::iterator it = mShortcuts.begin(); it != mShortcuts.end(); ++it) for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
{ {
Shortcut* shortcut = *it; Shortcut* shortcut = *it;
@ -85,41 +119,76 @@ namespace CSMPrefs
} }
} }
} }
else if (event->type() == QEvent::FocusIn)
{
QWidget* widget = static_cast<QWidget*>(watched);
updateParent(widget);
}
return false; return false;
} }
bool ShortcutEventHandler::activate(unsigned int mod, unsigned int button) void ShortcutEventHandler::updateParent(QWidget* widget)
{
QWidget* parent = widget->parentWidget();
while (parent)
{
ShortcutMap::iterator parentIt = mWidgetShortcuts.find(parent);
if (parentIt != mWidgetShortcuts.end())
{
mChildParentRelations.insert(std::make_pair(widget, parent));
break;
}
// Check next
parent = parent->parentWidget();
}
}
bool ShortcutEventHandler::activate(QWidget* widget, unsigned int mod, unsigned int button)
{ {
std::vector<std::pair<MatchResult, Shortcut*> > potentials; std::vector<std::pair<MatchResult, Shortcut*> > potentials;
bool used = false; bool used = false;
// Find potential activations while (widget)
for (std::vector<Shortcut*>::iterator it = mShortcuts.begin(); it != mShortcuts.end(); ++it)
{ {
Shortcut* shortcut = *it; ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
assert(shortcutListIt != mWidgetShortcuts.end());
if (!shortcut->isEnabled()) // Find potential activations
continue; for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
{
Shortcut* shortcut = *it;
int pos = shortcut->getPosition(); if (!shortcut->isEnabled())
int lastPos = shortcut->getLastPosition(); continue;
MatchResult result = match(mod, button, shortcut->getSequence()[pos]);
if (result == Matches_WithMod || result == Matches_NoMod) if (checkModifier(mod, button, shortcut, true))
{ used = true;
if (pos < lastPos && (result == Matches_WithMod || pos > 0))
{ if (shortcut->getActivationStatus() != Shortcut::AS_Inactive)
shortcut->setPosition(pos+1); continue;
}
else if (pos == lastPos) int pos = shortcut->getPosition();
int lastPos = shortcut->getLastPosition();
MatchResult result = match(mod, button, shortcut->getSequence()[pos]);
if (result == Matches_WithMod || result == Matches_NoMod)
{ {
potentials.push_back(std::make_pair(result, shortcut)); if (pos < lastPos && (result == Matches_WithMod || pos > 0))
{
shortcut->setPosition(pos+1);
}
else if (pos == lastPos)
{
potentials.push_back(std::make_pair(result, shortcut));
}
} }
} }
if (checkModifier(mod, button, shortcut, true)) // Move on to parent
used = true; WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);
widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;
} }
// Only activate the best match; in exact conflicts, this will favor the first shortcut added. // Only activate the best match; in exact conflicts, this will favor the first shortcut added.
@ -147,39 +216,49 @@ namespace CSMPrefs
return used; return used;
} }
bool ShortcutEventHandler::deactivate(unsigned int mod, unsigned int button) bool ShortcutEventHandler::deactivate(QWidget* widget, unsigned int mod, unsigned int button)
{ {
const int KeyMask = 0x01FFFFFF; const int KeyMask = 0x01FFFFFF;
bool used = false; bool used = false;
for (std::vector<Shortcut*>::iterator it = mShortcuts.begin(); it != mShortcuts.end(); ++it) while (widget)
{ {
Shortcut* shortcut = *it; ShortcutMap::iterator shortcutListIt = mWidgetShortcuts.find(widget);
assert(shortcutListIt != mWidgetShortcuts.end());
if (checkModifier(mod, button, shortcut, false)) for (ShortcutList::iterator it = shortcutListIt->second.begin(); it != shortcutListIt->second.end(); ++it)
used = true;
int pos = shortcut->getPosition();
MatchResult result = match(0, button, shortcut->getSequence()[pos] & KeyMask);
if (result != Matches_Not)
{ {
shortcut->setPosition(0); Shortcut* shortcut = *it;
if (shortcut->getActivationStatus() == Shortcut::AS_Regular) if (checkModifier(mod, button, shortcut, false))
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->signalActivated(false);
used = true; used = true;
}
else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary) int pos = shortcut->getPosition();
MatchResult result = match(0, button, shortcut->getSequence()[pos] & KeyMask);
if (result != Matches_Not)
{ {
shortcut->setActivationStatus(Shortcut::AS_Inactive); shortcut->setPosition(0);
shortcut->signalSecondary(false);
used = true; if (shortcut->getActivationStatus() == Shortcut::AS_Regular)
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->signalActivated(false);
used = true;
}
else if (shortcut->getActivationStatus() == Shortcut::AS_Secondary)
{
shortcut->setActivationStatus(Shortcut::AS_Inactive);
shortcut->signalSecondary(false);
used = true;
}
} }
} }
// Move on to parent
WidgetMap::iterator widgetIt = mChildParentRelations.find(widget);
widget = (widgetIt != mChildParentRelations.end()) ? widgetIt->second : 0;
} }
return used; return used;
@ -187,7 +266,8 @@ namespace CSMPrefs
bool ShortcutEventHandler::checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate) bool ShortcutEventHandler::checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate)
{ {
if (!shortcut->isEnabled() || !shortcut->getModifier() || shortcut->getSecondaryMode() == Shortcut::SM_Ignore) if (!shortcut->isEnabled() || !shortcut->getModifier() || shortcut->getSecondaryMode() == Shortcut::SM_Ignore ||
shortcut->getModifierStatus() == activate)
return false; return false;
MatchResult result = match(mod, button, shortcut->getModifier()); MatchResult result = match(mod, button, shortcut->getModifier());
@ -208,8 +288,6 @@ namespace CSMPrefs
{ {
shortcut->signalSecondary(false); shortcut->signalSecondary(false);
} }
used = true;
} }
else if (!activate && shortcut->getActivationStatus() == Shortcut::AS_Secondary) else if (!activate && shortcut->getActivationStatus() == Shortcut::AS_Secondary)
{ {
@ -243,9 +321,17 @@ namespace CSMPrefs
bool ShortcutEventHandler::sort(const std::pair<MatchResult, Shortcut*>& left, bool ShortcutEventHandler::sort(const std::pair<MatchResult, Shortcut*>& left,
const std::pair<MatchResult, Shortcut*>& right) const std::pair<MatchResult, Shortcut*>& right)
{ {
if (left.first == Matches_WithMod && left.first != right.first) if (left.first == Matches_WithMod && right.first == Matches_NoMod)
return true; return true;
else else
return left.second->getPosition() >= right.second->getPosition(); return left.second->getPosition() >= right.second->getPosition();
} }
void ShortcutEventHandler::widgetDestroyed()
{
QWidget* widget = static_cast<QWidget*>(sender());
mWidgetShortcuts.erase(widget);
mChildParentRelations.erase(widget);
}
} }

@ -1,6 +1,7 @@
#ifndef CSM_PREFS_SHORTCUT_EVENT_HANDLER_H #ifndef CSM_PREFS_SHORTCUT_EVENT_HANDLER_H
#define CSM_PREFS_SHORTCUT_EVENT_HANDLER_H #define CSM_PREFS_SHORTCUT_EVENT_HANDLER_H
#include <map>
#include <vector> #include <vector>
#include <QObject> #include <QObject>
@ -19,7 +20,7 @@ namespace CSMPrefs
public: public:
ShortcutEventHandler(QObject* parent=0); ShortcutEventHandler(QObject* parent);
void addShortcut(Shortcut* shortcut); void addShortcut(Shortcut* shortcut);
void removeShortcut(Shortcut* shortcut); void removeShortcut(Shortcut* shortcut);
@ -30,6 +31,11 @@ namespace CSMPrefs
private: private:
typedef std::vector<Shortcut*> ShortcutList;
// Child, Parent
typedef std::map<QWidget*, QWidget*> WidgetMap;
typedef std::map<QWidget*, ShortcutList> ShortcutMap;
enum MatchResult enum MatchResult
{ {
Matches_WithMod, Matches_WithMod,
@ -37,9 +43,11 @@ namespace CSMPrefs
Matches_Not Matches_Not
}; };
bool activate(unsigned int mod, unsigned int button); void updateParent(QWidget* widget);
bool activate(QWidget* widget, unsigned int mod, unsigned int button);
bool deactivate(unsigned int mod, unsigned int button); bool deactivate(QWidget* widget, unsigned int mod, unsigned int button);
bool checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate); bool checkModifier(unsigned int mod, unsigned int button, Shortcut* shortcut, bool activate);
@ -49,7 +57,12 @@ namespace CSMPrefs
static bool sort(const std::pair<MatchResult, Shortcut*>& left, static bool sort(const std::pair<MatchResult, Shortcut*>& left,
const std::pair<MatchResult, Shortcut*>& right); const std::pair<MatchResult, Shortcut*>& right);
std::vector<Shortcut*> mShortcuts; WidgetMap mChildParentRelations;
ShortcutMap mWidgetShortcuts;
private slots:
void widgetDestroyed();
}; };
} }

@ -3,17 +3,25 @@
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <QApplication>
#include <QMetaEnum> #include <QMetaEnum>
#include <QRegExp> #include <QRegExp>
#include <QStringList> #include <QStringList>
#include "shortcut.hpp" #include "shortcut.hpp"
#include "shortcuteventhandler.hpp"
namespace CSMPrefs namespace CSMPrefs
{ {
ShortcutManager::ShortcutManager()
{
mEventHandler = new ShortcutEventHandler(this);
}
void ShortcutManager::addShortcut(Shortcut* shortcut) void ShortcutManager::addShortcut(Shortcut* shortcut)
{ {
mShortcuts.insert(std::make_pair(shortcut->getName(), shortcut)); mShortcuts.insert(std::make_pair(shortcut->getName(), shortcut));
mEventHandler->addShortcut(shortcut);
} }
void ShortcutManager::removeShortcut(Shortcut* shortcut) void ShortcutManager::removeShortcut(Shortcut* shortcut)
@ -31,6 +39,8 @@ namespace CSMPrefs
++it; ++it;
} }
} }
mEventHandler->removeShortcut(shortcut);
} }
ShortcutManager::SequenceData ShortcutManager::getSequence(const std::string& name) const ShortcutManager::SequenceData ShortcutManager::getSequence(const std::string& name) const

@ -9,6 +9,7 @@
namespace CSMPrefs namespace CSMPrefs
{ {
class Shortcut; class Shortcut;
class ShortcutEventHandler;
/// Class used to track and update shortcuts/sequences /// Class used to track and update shortcuts/sequences
class ShortcutManager : public QObject class ShortcutManager : public QObject
@ -20,6 +21,9 @@ namespace CSMPrefs
/// Key Sequence, Modifier (for secondary signal) /// Key Sequence, Modifier (for secondary signal)
typedef std::pair<QKeySequence, int> SequenceData; typedef std::pair<QKeySequence, int> SequenceData;
ShortcutManager();
/// The shortcut class will do this automatically /// The shortcut class will do this automatically
void addShortcut(Shortcut* shortcut); void addShortcut(Shortcut* shortcut);
@ -40,6 +44,8 @@ namespace CSMPrefs
ShortcutMap mShortcuts; ShortcutMap mShortcuts;
SequenceMap mSequences; SequenceMap mSequences;
ShortcutEventHandler* mEventHandler;
}; };
} }

@ -229,6 +229,10 @@ void CSMPrefs::State::declare()
addValues (insertOutsideVisibleCell); addValues (insertOutsideVisibleCell);
declareCategory ("Key Bindings"); declareCategory ("Key Bindings");
declareShortcut ("document-save", "Save", QKeySequence(Qt::ControlModifier | Qt::Key_S));
declareSeparator ();
declareShortcut ("free-forward", "Free camera forward", QKeySequence(Qt::Key_W), Qt::Key_Shift); declareShortcut ("free-forward", "Free camera forward", QKeySequence(Qt::Key_W), Qt::Key_Shift);
declareShortcut ("free-backward", "Free camera backward", QKeySequence(Qt::Key_S)); declareShortcut ("free-backward", "Free camera backward", QKeySequence(Qt::Key_S));
declareShortcut ("free-left", "Free camera left", QKeySequence(Qt::Key_A)); declareShortcut ("free-left", "Free camera left", QKeySequence(Qt::Key_A));

@ -16,6 +16,7 @@
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../../model/prefs/state.hpp" #include "../../model/prefs/state.hpp"
#include "../../model/prefs/shortcut.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
@ -62,6 +63,9 @@ void CSVDoc::View::setupFileMenu()
connect (mSave, SIGNAL (triggered()), this, SLOT (save())); connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
file->addAction (mSave); file->addAction (mSave);
CSMPrefs::Shortcut* saveShortcut = new CSMPrefs::Shortcut("document-save", this);
connect (saveShortcut, SIGNAL(activated()), this, SLOT(save()));
mVerify = new QAction (tr ("&Verify"), this); mVerify = new QAction (tr ("&Verify"), this);
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
file->addAction (mVerify); file->addAction (mVerify);

@ -2,7 +2,7 @@
#include <cmath> #include <cmath>
#include <QKeyEvent> #include <QWidget>
#include <osg/BoundingBox> #include <osg/BoundingBox>
#include <osg/Camera> #include <osg/Camera>
@ -15,7 +15,6 @@
#include <osgUtil/LineSegmentIntersector> #include <osgUtil/LineSegmentIntersector>
#include "../../model/prefs/shortcut.hpp" #include "../../model/prefs/shortcut.hpp"
#include "../../model/prefs/shortcuteventhandler.hpp"
#include "scenewidget.hpp" #include "scenewidget.hpp"
@ -79,27 +78,17 @@ namespace CSVRender
void CameraController::setCamera(osg::Camera* camera) void CameraController::setCamera(osg::Camera* camera)
{ {
bool wasActive = mActive;
mCamera = camera; mCamera = camera;
mActive = (mCamera != NULL); mActive = (mCamera != NULL);
if (mActive) if (mActive != wasActive)
{ {
onActivate(); for (std::vector<CSMPrefs::Shortcut*>::iterator it = mShortcuts.begin(); it != mShortcuts.end(); ++it)
QList<CSMPrefs::Shortcut*> shortcuts = findChildren<CSMPrefs::Shortcut*>();
for (QList<CSMPrefs::Shortcut*>::iterator it = shortcuts.begin(); it != shortcuts.end(); ++it)
{ {
(*it)->enable(true); CSMPrefs::Shortcut* shortcut = *it;
} shortcut->enable(mActive);
}
else
{
QList<CSMPrefs::Shortcut*> shortcuts = findChildren<CSMPrefs::Shortcut*>();
for (QList<CSMPrefs::Shortcut*>::iterator it = shortcuts.begin(); it != shortcuts.end(); ++it)
{
(*it)->enable(false);
} }
} }
} }
@ -160,14 +149,21 @@ namespace CSVRender
getCamera()->setViewMatrixAsLookAt(eye, center, up); getCamera()->setViewMatrixAsLookAt(eye, center, up);
} }
void CameraController::addShortcut(CSMPrefs::Shortcut* shortcut)
{
mShortcuts.push_back(shortcut);
}
/* /*
Free Camera Controller Free Camera Controller
*/ */
FreeCameraController::FreeCameraController(CSMPrefs::ShortcutEventHandler* handler, QObject* parent) FreeCameraController::FreeCameraController(QWidget* widget)
: CameraController(parent) : CameraController(widget)
, mLockUpright(false) , mLockUpright(false)
, mModified(false) , mModified(false)
, mNaviPrimary(false)
, mNaviSecondary(false)
, mFast(false) , mFast(false)
, mFastAlternate(false) , mFastAlternate(false)
, mLeft(false) , mLeft(false)
@ -181,52 +177,61 @@ namespace CSVRender
, mRotSpeed(osg::PI / 2) , mRotSpeed(osg::PI / 2)
, mSpeedMult(8) , mSpeedMult(8)
{ {
CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut("scene-navi-primary", this); CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut("scene-navi-primary", widget);
naviPrimaryShortcut->enable(false); naviPrimaryShortcut->enable(false);
handler->addShortcut(naviPrimaryShortcut);
connect(naviPrimaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviPrimary(bool))); connect(naviPrimaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviPrimary(bool)));
CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut("scene-navi-secondary", this); addShortcut(naviPrimaryShortcut);
CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut("scene-navi-secondary", widget);
naviSecondaryShortcut->enable(false); naviSecondaryShortcut->enable(false);
handler->addShortcut(naviSecondaryShortcut);
connect(naviSecondaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviSecondary(bool))); connect(naviSecondaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviSecondary(bool)));
addShortcut(naviSecondaryShortcut);
CSMPrefs::Shortcut* forwardShortcut = new CSMPrefs::Shortcut("free-forward", CSMPrefs::Shortcut::SM_Detach, CSMPrefs::Shortcut* forwardShortcut = new CSMPrefs::Shortcut("free-forward", CSMPrefs::Shortcut::SM_Detach,
this); widget);
forwardShortcut->enable(false); forwardShortcut->enable(false);
handler->addShortcut(forwardShortcut);
connect(forwardShortcut, SIGNAL(activated(bool)), this, SLOT(forward(bool))); connect(forwardShortcut, SIGNAL(activated(bool)), this, SLOT(forward(bool)));
connect(forwardShortcut, SIGNAL(secondary(bool)), this, SLOT(alternateFast(bool))); connect(forwardShortcut, SIGNAL(secondary(bool)), this, SLOT(alternateFast(bool)));
CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut("free-left", this); addShortcut(forwardShortcut);
CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut("free-left", widget);
leftShortcut->enable(false); leftShortcut->enable(false);
handler->addShortcut(leftShortcut);
connect(leftShortcut, SIGNAL(activated(bool)), this, SLOT(left(bool))); connect(leftShortcut, SIGNAL(activated(bool)), this, SLOT(left(bool)));
CSMPrefs::Shortcut* backShortcut = new CSMPrefs::Shortcut("free-backward", this); addShortcut(leftShortcut);
CSMPrefs::Shortcut* backShortcut = new CSMPrefs::Shortcut("free-backward", widget);
backShortcut->enable(false); backShortcut->enable(false);
handler->addShortcut(backShortcut);
connect(backShortcut, SIGNAL(activated(bool)), this, SLOT(backward(bool))); connect(backShortcut, SIGNAL(activated(bool)), this, SLOT(backward(bool)));
CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut("free-right", this); addShortcut(backShortcut);
CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut("free-right", widget);
rightShortcut->enable(false); rightShortcut->enable(false);
handler->addShortcut(rightShortcut);
connect(rightShortcut, SIGNAL(activated(bool)), this, SLOT(right(bool))); connect(rightShortcut, SIGNAL(activated(bool)), this, SLOT(right(bool)));
CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut("free-roll-left", this); addShortcut(rightShortcut);
CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut("free-roll-left", widget);
rollLeftShortcut->enable(false); rollLeftShortcut->enable(false);
handler->addShortcut(rollLeftShortcut);
connect(rollLeftShortcut, SIGNAL(activated(bool)), this, SLOT(rollLeft(bool))); connect(rollLeftShortcut, SIGNAL(activated(bool)), this, SLOT(rollLeft(bool)));
CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut("free-roll-right", this); addShortcut(rollLeftShortcut);
CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut("free-roll-right", widget);
rollRightShortcut->enable(false); rollRightShortcut->enable(false);
handler->addShortcut(rollRightShortcut);
connect(rollRightShortcut, SIGNAL(activated(bool)), this, SLOT(rollRight(bool))); connect(rollRightShortcut, SIGNAL(activated(bool)), this, SLOT(rollRight(bool)));
CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut("free-speed-mode", this); addShortcut(rollRightShortcut);
CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut("free-speed-mode", widget);
speedModeShortcut->enable(false); speedModeShortcut->enable(false);
handler->addShortcut(speedModeShortcut);
connect(speedModeShortcut, SIGNAL(activated()), this, SLOT(swapSpeedMode())); connect(speedModeShortcut, SIGNAL(activated()), this, SLOT(swapSpeedMode()));
addShortcut(speedModeShortcut);
} }
double FreeCameraController::getLinearSpeed() const double FreeCameraController::getLinearSpeed() const
@ -440,9 +445,11 @@ namespace CSVRender
Orbit Camera Controller Orbit Camera Controller
*/ */
OrbitCameraController::OrbitCameraController(CSMPrefs::ShortcutEventHandler* handler, QObject* parent) OrbitCameraController::OrbitCameraController(QWidget* widget)
: CameraController(parent) : CameraController(widget)
, mInitialized(false) , mInitialized(false)
, mNaviPrimary(false)
, mNaviSecondary(false)
, mFast(false) , mFast(false)
, mFastAlternate(false) , mFastAlternate(false)
, mLeft(false) , mLeft(false)
@ -457,51 +464,60 @@ namespace CSVRender
, mOrbitSpeed(osg::PI / 4) , mOrbitSpeed(osg::PI / 4)
, mOrbitSpeedMult(4) , mOrbitSpeedMult(4)
{ {
CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut("scene-navi-primary", this); CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut("scene-navi-primary", widget);
naviPrimaryShortcut->enable(false); naviPrimaryShortcut->enable(false);
handler->addShortcut(naviPrimaryShortcut);
connect(naviPrimaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviPrimary(bool))); connect(naviPrimaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviPrimary(bool)));
CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut("scene-navi-secondary", this); addShortcut(naviPrimaryShortcut);
CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut("scene-navi-secondary", widget);
naviSecondaryShortcut->enable(false); naviSecondaryShortcut->enable(false);
handler->addShortcut(naviSecondaryShortcut);
connect(naviSecondaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviSecondary(bool))); connect(naviSecondaryShortcut, SIGNAL(activated(bool)), this, SLOT(naviSecondary(bool)));
CSMPrefs::Shortcut* upShortcut = new CSMPrefs::Shortcut("orbit-up", CSMPrefs::Shortcut::SM_Detach, this); addShortcut(naviSecondaryShortcut);
CSMPrefs::Shortcut* upShortcut = new CSMPrefs::Shortcut("orbit-up", CSMPrefs::Shortcut::SM_Detach, widget);
upShortcut->enable(false); upShortcut->enable(false);
handler->addShortcut(upShortcut);
connect(upShortcut, SIGNAL(activated(bool)), this, SLOT(up(bool))); connect(upShortcut, SIGNAL(activated(bool)), this, SLOT(up(bool)));
connect(upShortcut, SIGNAL(secondary(bool)), this, SLOT(alternateFast(bool))); connect(upShortcut, SIGNAL(secondary(bool)), this, SLOT(alternateFast(bool)));
CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut("orbit-left", this); addShortcut(upShortcut);
CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut("orbit-left", widget);
leftShortcut->enable(false); leftShortcut->enable(false);
handler->addShortcut(leftShortcut);
connect(leftShortcut, SIGNAL(activated(bool)), this, SLOT(left(bool))); connect(leftShortcut, SIGNAL(activated(bool)), this, SLOT(left(bool)));
CSMPrefs::Shortcut* downShortcut = new CSMPrefs::Shortcut("orbit-down", this); addShortcut(leftShortcut);
CSMPrefs::Shortcut* downShortcut = new CSMPrefs::Shortcut("orbit-down", widget);
downShortcut->enable(false); downShortcut->enable(false);
handler->addShortcut(downShortcut);
connect(downShortcut, SIGNAL(activated(bool)), this, SLOT(down(bool))); connect(downShortcut, SIGNAL(activated(bool)), this, SLOT(down(bool)));
CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut("orbit-right", this); addShortcut(downShortcut);
CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut("orbit-right", widget);
rightShortcut->enable(false); rightShortcut->enable(false);
handler->addShortcut(rightShortcut);
connect(rightShortcut, SIGNAL(activated(bool)), this, SLOT(right(bool))); connect(rightShortcut, SIGNAL(activated(bool)), this, SLOT(right(bool)));
CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut("orbit-roll-left", this); addShortcut(rightShortcut);
CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut("orbit-roll-left", widget);
rollLeftShortcut->enable(false); rollLeftShortcut->enable(false);
handler->addShortcut(rollLeftShortcut);
connect(rollLeftShortcut, SIGNAL(activated(bool)), this, SLOT(rollLeft(bool))); connect(rollLeftShortcut, SIGNAL(activated(bool)), this, SLOT(rollLeft(bool)));
CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut("orbit-roll-right", this); addShortcut(rollLeftShortcut);
CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut("orbit-roll-right", widget);
rollRightShortcut->enable(false); rollRightShortcut->enable(false);
handler->addShortcut(rollRightShortcut);
connect(rollRightShortcut, SIGNAL(activated(bool)), this, SLOT(rollRight(bool))); connect(rollRightShortcut, SIGNAL(activated(bool)), this, SLOT(rollRight(bool)));
CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut("orbit-speed-mode", this); addShortcut(rollRightShortcut);
CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut("orbit-speed-mode", widget);
speedModeShortcut->enable(false); speedModeShortcut->enable(false);
handler->addShortcut(speedModeShortcut);
connect(speedModeShortcut, SIGNAL(activated()), this, SLOT(swapSpeedMode())); connect(speedModeShortcut, SIGNAL(activated()), this, SLOT(swapSpeedMode()));
addShortcut(speedModeShortcut);
} }
osg::Vec3d OrbitCameraController::getCenter() const osg::Vec3d OrbitCameraController::getCenter() const

@ -2,14 +2,13 @@
#define OPENCS_VIEW_CAMERACONTROLLER_H #define OPENCS_VIEW_CAMERACONTROLLER_H
#include <string> #include <string>
#include <vector>
#include <QObject> #include <QObject>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <osg/Vec3d> #include <osg/Vec3d>
class QKeyEvent;
namespace osg namespace osg
{ {
class Camera; class Camera;
@ -18,7 +17,7 @@ namespace osg
namespace CSMPrefs namespace CSMPrefs
{ {
class ShortcutEventHandler; class Shortcut;
} }
namespace CSVRender namespace CSVRender
@ -66,6 +65,8 @@ namespace CSVRender
virtual void onActivate(){} virtual void onActivate(){}
void addShortcut(CSMPrefs::Shortcut* shortcut);
private: private:
bool mActive, mInverted; bool mActive, mInverted;
@ -74,6 +75,8 @@ namespace CSVRender
double mWheelMoveMult; double mWheelMoveMult;
osg::Camera* mCamera; osg::Camera* mCamera;
std::vector<CSMPrefs::Shortcut*> mShortcuts;
}; };
class FreeCameraController : public CameraController class FreeCameraController : public CameraController
@ -82,7 +85,7 @@ namespace CSVRender
public: public:
FreeCameraController(CSMPrefs::ShortcutEventHandler* handler, QObject* parent=0); FreeCameraController(QWidget* parent);
double getLinearSpeed() const; double getLinearSpeed() const;
double getRotationalSpeed() const; double getRotationalSpeed() const;
@ -139,7 +142,7 @@ namespace CSVRender
public: public:
OrbitCameraController(CSMPrefs::ShortcutEventHandler* handler, QObject* parent=0); OrbitCameraController(QWidget* parent);
osg::Vec3d getCenter() const; osg::Vec3d getCenter() const;
double getOrbitSpeed() const; double getOrbitSpeed() const;

@ -9,22 +9,19 @@
namespace CSVRender namespace CSVRender
{ {
OrbitCameraMode::OrbitCameraMode(WorldspaceWidget* worldspaceWidget, CSMPrefs::ShortcutEventHandler* handler, OrbitCameraMode::OrbitCameraMode(WorldspaceWidget* worldspaceWidget, const QIcon& icon, const QString& tooltip,
const QIcon& icon, const QString& tooltip, QWidget* parent) QWidget* parent)
: ModeButton(icon, tooltip, parent) : ModeButton(icon, tooltip, parent)
, mWorldspaceWidget(worldspaceWidget) , mWorldspaceWidget(worldspaceWidget)
, mShortcutHandler(handler)
, mCenterOnSelection(0) , mCenterOnSelection(0)
{ {
mCenterShortcut = new CSMPrefs::Shortcut("orbit-center-selection", this); mCenterShortcut.reset(new CSMPrefs::Shortcut("orbit-center-selection", worldspaceWidget));
mCenterShortcut->enable(false); mCenterShortcut->enable(false);
mShortcutHandler->addShortcut(mCenterShortcut); connect(mCenterShortcut.get(), SIGNAL(activated()), this, SLOT(centerSelection()));
connect(mCenterShortcut, SIGNAL(activated()), this, SLOT(centerSelection()));
} }
OrbitCameraMode::~OrbitCameraMode() OrbitCameraMode::~OrbitCameraMode()
{ {
mShortcutHandler->removeShortcut(mCenterShortcut);
} }
void OrbitCameraMode::activate(CSVWidget::SceneToolbar* toolbar) void OrbitCameraMode::activate(CSVWidget::SceneToolbar* toolbar)

@ -1,12 +1,13 @@
#ifndef CSV_RENDER_ORBITCAMERAPICKMODE_H #ifndef CSV_RENDER_ORBITCAMERAPICKMODE_H
#define CSV_RENDER_ORBITCAMERAPICKMODE_H #define CSV_RENDER_ORBITCAMERAPICKMODE_H
#include <memory>
#include "../widget/modebutton.hpp" #include "../widget/modebutton.hpp"
namespace CSMPrefs namespace CSMPrefs
{ {
class Shortcut; class Shortcut;
class ShortcutEventHandler;
} }
namespace CSVRender namespace CSVRender
@ -19,8 +20,8 @@ namespace CSVRender
public: public:
OrbitCameraMode(WorldspaceWidget* worldspaceWidget, CSMPrefs::ShortcutEventHandler* shortcutHandler, OrbitCameraMode(WorldspaceWidget* worldspaceWidget, const QIcon& icon, const QString& tooltip = "",
const QIcon& icon, const QString& tooltip = "", QWidget* parent = 0); QWidget* parent = 0);
~OrbitCameraMode(); ~OrbitCameraMode();
virtual void activate(CSVWidget::SceneToolbar* toolbar); virtual void activate(CSVWidget::SceneToolbar* toolbar);
@ -30,9 +31,8 @@ namespace CSVRender
private: private:
WorldspaceWidget* mWorldspaceWidget; WorldspaceWidget* mWorldspaceWidget;
CSMPrefs::ShortcutEventHandler* mShortcutHandler;
QAction* mCenterOnSelection; QAction* mCenterOnSelection;
CSMPrefs::Shortcut* mCenterShortcut; std::auto_ptr<CSMPrefs::Shortcut> mCenterShortcut;
private slots: private slots:

@ -171,11 +171,8 @@ SceneWidget::SceneWidget(boost::shared_ptr<Resource::ResourceSystem> resourceSys
, mPrevMouseY(0) , mPrevMouseY(0)
, mCamPositionSet(false) , mCamPositionSet(false)
{ {
mShortcutHandler = new CSMPrefs::ShortcutEventHandler(this); mFreeCamControl = new FreeCameraController(this);
installEventFilter(mShortcutHandler); mOrbitCamControl = new OrbitCameraController(this);
mFreeCamControl = new FreeCameraController(mShortcutHandler, this);
mOrbitCamControl = new OrbitCameraController(mShortcutHandler, this);
mCurrentCamControl = mFreeCamControl; mCurrentCamControl = mFreeCamControl;
mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain); mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain);
@ -205,18 +202,14 @@ SceneWidget::SceneWidget(boost::shared_ptr<Resource::ResourceSystem> resourceSys
// Shortcuts // Shortcuts
CSMPrefs::Shortcut* focusToolbarShortcut = new CSMPrefs::Shortcut("scene-focus-toolbar", this); CSMPrefs::Shortcut* focusToolbarShortcut = new CSMPrefs::Shortcut("scene-focus-toolbar", this);
mShortcutHandler->addShortcut(focusToolbarShortcut);
connect(focusToolbarShortcut, SIGNAL(activated()), this, SIGNAL(focusToolbarRequest())); connect(focusToolbarShortcut, SIGNAL(activated()), this, SIGNAL(focusToolbarRequest()));
CSMPrefs::Shortcut* renderStatsShortcut = new CSMPrefs::Shortcut("scene-render-stats", this); CSMPrefs::Shortcut* renderStatsShortcut = new CSMPrefs::Shortcut("scene-render-stats", this);
mShortcutHandler->addShortcut(renderStatsShortcut);
connect(renderStatsShortcut, SIGNAL(activated()), this, SLOT(toggleRenderStats())); connect(renderStatsShortcut, SIGNAL(activated()), this, SLOT(toggleRenderStats()));
} }
SceneWidget::~SceneWidget() SceneWidget::~SceneWidget()
{ {
removeEventFilter(mShortcutHandler);
// Since we're holding on to the scene templates past the existance of this graphics context, we'll need to manually release the created objects // Since we're holding on to the scene templates past the existance of this graphics context, we'll need to manually release the created objects
mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState()); mResourceSystem->getSceneManager()->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
} }

@ -37,8 +37,6 @@ namespace CSVWidget
namespace CSMPrefs namespace CSMPrefs
{ {
class Setting; class Setting;
class Shortcut;
class ShortcutEventHandler;
} }
namespace CSVRender namespace CSVRender
@ -116,8 +114,6 @@ namespace CSVRender
OrbitCameraController* mOrbitCamControl; OrbitCameraController* mOrbitCamControl;
CameraController* mCurrentCamControl; CameraController* mCurrentCamControl;
CSMPrefs::ShortcutEventHandler *mShortcutHandler;
private: private:
bool mCamPositionSet; bool mCamPositionSet;

@ -98,23 +98,18 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg
// Shortcuts // Shortcuts
CSMPrefs::Shortcut* primaryEditShortcut = new CSMPrefs::Shortcut("scene-edit-primary", this); CSMPrefs::Shortcut* primaryEditShortcut = new CSMPrefs::Shortcut("scene-edit-primary", this);
mShortcutHandler->addShortcut(primaryEditShortcut);
connect(primaryEditShortcut, SIGNAL(activated(bool)), this, SLOT(primaryEdit(bool))); connect(primaryEditShortcut, SIGNAL(activated(bool)), this, SLOT(primaryEdit(bool)));
CSMPrefs::Shortcut* secondaryEditShortcut = new CSMPrefs::Shortcut("scene-edit-secondary", this); CSMPrefs::Shortcut* secondaryEditShortcut = new CSMPrefs::Shortcut("scene-edit-secondary", this);
mShortcutHandler->addShortcut(secondaryEditShortcut);
connect(secondaryEditShortcut, SIGNAL(activated(bool)), this, SLOT(secondaryEdit(bool))); connect(secondaryEditShortcut, SIGNAL(activated(bool)), this, SLOT(secondaryEdit(bool)));
CSMPrefs::Shortcut* primarySelectShortcut = new CSMPrefs::Shortcut("scene-select-primary", this); CSMPrefs::Shortcut* primarySelectShortcut = new CSMPrefs::Shortcut("scene-select-primary", this);
mShortcutHandler->addShortcut(primarySelectShortcut);
connect(primarySelectShortcut, SIGNAL(activated(bool)), this, SLOT(primarySelect(bool))); connect(primarySelectShortcut, SIGNAL(activated(bool)), this, SLOT(primarySelect(bool)));
CSMPrefs::Shortcut* secondarySelectShortcut = new CSMPrefs::Shortcut("scene-select-secondary", this); CSMPrefs::Shortcut* secondarySelectShortcut = new CSMPrefs::Shortcut("scene-select-secondary", this);
mShortcutHandler->addShortcut(secondarySelectShortcut);
connect(secondarySelectShortcut, SIGNAL(activated(bool)), this, SLOT(secondarySelect(bool))); connect(secondarySelectShortcut, SIGNAL(activated(bool)), this, SLOT(secondarySelect(bool)));
CSMPrefs::Shortcut* abortShortcut = new CSMPrefs::Shortcut("scene-edit-abort", this); CSMPrefs::Shortcut* abortShortcut = new CSMPrefs::Shortcut("scene-edit-abort", this);
mShortcutHandler->addShortcut(abortShortcut);
connect(abortShortcut, SIGNAL(activated()), this, SLOT(abortDrag())); connect(abortShortcut, SIGNAL(activated()), this, SLOT(abortDrag()));
} }
@ -184,7 +179,7 @@ CSVWidget::SceneToolMode *CSVRender::WorldspaceWidget::makeNavigationSelector (
"<li>Hold shift to speed up movement</li>" "<li>Hold shift to speed up movement</li>"
"</ul>"); "</ul>");
tool->addButton( tool->addButton(
new CSVRender::OrbitCameraMode(this, mShortcutHandler, QIcon(":scenetoolbar/orbiting-camera"), new CSVRender::OrbitCameraMode(this, QIcon(":scenetoolbar/orbiting-camera"),
"Orbiting Camera" "Orbiting Camera"
"<ul><li>Always facing the centre point</li>" "<ul><li>Always facing the centre point</li>"
"<li>Rotate around the centre point via WASD or by moving the mouse while holding the left button</li>" "<li>Rotate around the centre point via WASD or by moving the mouse while holding the left button</li>"

@ -15,7 +15,6 @@
namespace CSMPrefs namespace CSMPrefs
{ {
class Setting; class Setting;
class Shortcut;
} }
namespace CSMWorld namespace CSMWorld

Loading…
Cancel
Save