Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / ground / gcs / src / plugins / coreplugin / actionmanager / actionmanager.cpp
blob9b53d774c1c0953fefb06d6a972410496e8ceab6
1 /**
2 ******************************************************************************
4 * @file actionmanager.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup CorePlugin Core Plugin
10 * @{
11 * @brief The Core GCS plugin
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "actionmanager_p.h"
30 #include "mainwindow.h"
31 #include "actioncontainer_p.h"
32 #include "command_p.h"
33 #include "uniqueidmanager.h"
35 #include <coreplugin/coreconstants.h>
37 #include <QtCore/QDebug>
38 #include <QtCore/QSettings>
39 #include <QMenu>
40 #include <QAction>
41 #include <QShortcut>
42 #include <QMenuBar>
44 namespace {
45 enum { warnAboutFindFailures = 0 };
48 /*!
49 \class Core::ActionManager
50 \mainclass
52 \brief The action manager is responsible for registration of menus and
53 menu items and keyboard shortcuts.
55 The ActionManager is the central bookkeeper of actions and their shortcuts and layout.
56 You get the only implementation of this class from the core interface
57 ICore::actionManager() method, e.g.
58 \code
59 Core::ICore::instance()->actionManager()
60 \endcode
62 The main reasons for the need of this class is to provide a central place where the user
63 can specify all his keyboard shortcuts, and to provide a solution for actions that should
64 behave differently in different contexts (like the copy/replace/undo/redo actions).
66 \section1 Contexts
68 All actions that are registered with the same string ID (but different context lists)
69 are considered to be overloads of the same command, represented by an instance
70 of the Command class.
71 Exactly only one of the registered actions with the same ID is active at any time.
72 Which action this is, is defined by the context list that the actions were registered
73 with:
75 If the current focus widget was registered via \l{ICore::addContextObject()},
76 all the contexts returned by its IContext object are active. In addition all
77 contexts set via \l{ICore::addAdditionalContext()} are active as well. If one
78 of the actions was registered for one of these active contexts, it is the one
79 active action, and receives \c triggered and \c toggled signals. Also the
80 appearance of the visible action for this ID might be adapted to this
81 active action (depending on the settings of the corresponding \l{Command} object).
83 The action that is visible to the user is the one returned by Command::action().
84 If you provide yourself a user visible representation of your action you need
85 to use Command::action() for this.
86 When this action is invoked by the user,
87 the signal is forwarded to the registered action that is valid for the current context.
89 \section1 Registering Actions
91 To register a globally active action "My Action"
92 put the following in your plugin's IPlugin::initialize method:
93 \code
94 Core::ActionManager *am = Core::ICore::instance()->actionManager();
95 QAction *myAction = new QAction(tr("My Action"), this);
96 Core::Command *cmd = am->registerAction(myAction,
97 "myplugin.myaction",
98 QList<int>() << C_GLOBAL_ID);
99 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+u")));
100 connect(myAction, SIGNAL(triggered()), this, SLOT(performMyAction()));
101 \endcode
103 So the \c connect is done to your own QAction instance. If you create e.g.
104 a tool button that should represent the action you add the action
105 from Command::action() to it:
106 \code
107 QToolButton *myButton = new QToolButton(someParentWidget);
108 myButton->setDefaultAction(cmd->action());
109 \endcode
111 Also use the ActionManager to add items to registered
112 action containers like the applications menu bar or menus in that menu bar.
113 To do this, you register your action via the
114 registerAction methods, get the action container for a specific ID (like specified in
115 the Core::Constants namespace) with a call of
116 actionContainer(const QString&) and add your command to this container.
118 Following the example adding "My Action" to the "Tools" menu would be done by
119 \code
120 am->actionContainer(Core::M_TOOLS)->addAction(cmd);
121 \endcode
123 \section1 Important Guidelines:
124 \list
125 \o Always register your actions and shortcuts!
126 \o Register your actions and shortcuts during your plugin's \l{ExtensionSystem::IPlugin::initialize()}
127 or \l{ExtensionSystem::IPlugin::extensionsInitialized()} methods, otherwise the shortcuts won't appear
128 in the keyboard settings dialog from the beginning.
129 \o When registering an action with \c{cmd=registerAction(action, id, contexts)} be sure to connect
130 your own action \c{connect(action, SIGNAL...)} but make \c{cmd->action()} visible to the user, i.e.
131 \c{widget->addAction(cmd->action())}.
132 \o Use this class to add actions to the applications menus
133 \endlist
135 \sa Core::ICore
136 \sa Core::Command
137 \sa Core::ActionContainer
138 \sa Core::IContext
142 \fn ActionContainer *ActionManager::createMenu(const QString &id)
143 \brief Creates a new menu with the given string \a id.
145 Returns a new ActionContainer that you can use to get the QMenu instance
146 or to add menu items to the menu. The ActionManager owns
147 the returned ActionContainer.
148 Add your menu to some other menu or a menu bar via the
149 ActionManager::actionContainer and ActionContainer::addMenu methods.
153 \fn ActionContainer *ActionManager::createMenuBar(const QString &id)
154 \brief Creates a new menu bar with the given string \a id.
156 Returns a new ActionContainer that you can use to get the QMenuBar instance
157 or to add menus to the menu bar. The ActionManager owns
158 the returned ActionContainer.
162 \fn Command *ActionManager::registerAction(QAction *action, const QString &id, const QList<int> &context)
163 \brief Makes an \a action known to the system under the specified string \a id.
165 Returns a command object that represents the action in the application and is
166 owned by the ActionManager. You can registered several actions with the
167 same \a id as long as the \a context is different. In this case
168 a trigger of the actual action is forwarded to the registered QAction
169 for the currently active context.
173 \fn Command *ActionManager::registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context)
174 \brief Makes a \a shortcut known to the system under the specified string \a id.
176 Returns a command object that represents the shortcut in the application and is
177 owned by the ActionManager. You can registered several shortcuts with the
178 same \a id as long as the \a context is different. In this case
179 a trigger of the actual shortcut is forwarded to the registered QShortcut
180 for the currently active context.
184 \fn Command *ActionManager::command(const QString &id) const
185 \brief Returns the Command object that is known to the system
186 under the given string \a id.
188 \sa ActionManager::registerAction()
192 \fn ActionContainer *ActionManager::actionContainer(const QString &id) const
193 \brief Returns the IActionContainter object that is know to the system
194 under the given string \a id.
196 \sa ActionManager::createMenu()
197 \sa ActionManager::createMenuBar()
200 \fn ActionManager::ActionManager(QObject *parent)
201 \internal
204 \fn ActionManager::~ActionManager()
205 \internal
208 using namespace Core;
209 using namespace Core::Internal;
211 ActionManagerPrivate *ActionManagerPrivate::m_instance = 0;
214 \class ActionManagerPrivate
215 \inheaderfile actionmanager_p.h
216 \internal
219 ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
220 : ActionManager(mainWnd),
221 m_mainWnd(mainWnd)
223 UniqueIDManager *uidmgr = UniqueIDManager::instance();
225 m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_ONE);
226 m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_TWO);
227 m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_THREE);
228 m_instance = this;
231 ActionManagerPrivate::~ActionManagerPrivate()
233 qDeleteAll(m_idCmdMap.values());
234 qDeleteAll(m_idContainerMap.values());
237 ActionManagerPrivate *ActionManagerPrivate::instance()
239 return m_instance;
242 QList<int> ActionManagerPrivate::defaultGroups() const
244 return m_defaultGroups;
247 QList<CommandPrivate *> ActionManagerPrivate::commands() const
249 return m_idCmdMap.values();
252 QList<ActionContainerPrivate *> ActionManagerPrivate::containers() const
254 return m_idContainerMap.values();
257 bool ActionManagerPrivate::hasContext(int context) const
259 return m_context.contains(context);
262 void ActionManagerPrivate::setContext(const QList<int> &context)
264 // here are possibilities for speed optimization if necessary:
265 // let commands (de-)register themselves for contexts
266 // and only update commands that are either in old or new contexts
267 m_context = context;
268 const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd();
269 for (IdCmdMap::const_iterator it = m_idCmdMap.constBegin(); it != cmdcend; ++it) {
270 it.value()->setCurrentContext(m_context);
273 const IdContainerMap::const_iterator acend = m_idContainerMap.constEnd();
274 for (IdContainerMap::const_iterator it = m_idContainerMap.constBegin(); it != acend; ++it) {
275 it.value()->update();
279 bool ActionManagerPrivate::hasContext(QList<int> context) const
281 for (int i = 0; i < m_context.count(); ++i) {
282 if (context.contains(m_context.at(i))) {
283 return true;
286 return false;
289 ActionContainer *ActionManagerPrivate::createMenu(const QString &id)
291 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
292 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
294 if (it != m_idContainerMap.constEnd()) {
295 return it.value();
298 QMenu *m = new QMenu(m_mainWnd);
299 m->setObjectName(id);
301 MenuActionContainer *mc = new MenuActionContainer(uid);
302 mc->setMenu(m);
304 m_idContainerMap.insert(uid, mc);
306 return mc;
309 ActionContainer *ActionManagerPrivate::createMenuBar(const QString &id)
311 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
312 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
314 if (it != m_idContainerMap.constEnd()) {
315 return it.value();
318 QMenuBar *mb = new QMenuBar; // No parent (System menu bar on Mac OS X)
319 mb->setObjectName(id);
321 MenuBarActionContainer *mbc = new MenuBarActionContainer(uid);
322 mbc->setMenuBar(mb);
324 m_idContainerMap.insert(uid, mbc);
326 return mbc;
329 Command *ActionManagerPrivate::registerAction(QAction *action, const QString &id, const QList<int> &context)
331 OverrideableAction *a = 0;
332 Command *c = registerOverridableAction(action, id, false);
334 a = static_cast<OverrideableAction *>(c);
335 if (a) {
336 a->addOverrideAction(action, context);
338 return a;
341 Command *ActionManagerPrivate::registerOverridableAction(QAction *action, const QString &id, bool checkUnique)
343 OverrideableAction *a = 0;
344 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
346 if (CommandPrivate * c = m_idCmdMap.value(uid, 0)) {
347 a = qobject_cast<OverrideableAction *>(c);
348 if (!a) {
349 qWarning() << "registerAction: id" << id << "is registered with a different command type.";
350 return c;
352 } else {
353 a = new OverrideableAction(uid);
354 m_idCmdMap.insert(uid, a);
357 if (!a->action()) {
358 QAction *baseAction = new QAction(m_mainWnd);
359 baseAction->setObjectName(id);
360 baseAction->setCheckable(action->isCheckable());
361 baseAction->setIcon(action->icon());
362 baseAction->setIconText(action->iconText());
363 baseAction->setText(action->text());
364 baseAction->setToolTip(action->toolTip());
365 baseAction->setStatusTip(action->statusTip());
366 baseAction->setWhatsThis(action->whatsThis());
367 baseAction->setChecked(action->isChecked());
368 baseAction->setSeparator(action->isSeparator());
369 baseAction->setShortcutContext(Qt::ApplicationShortcut);
370 baseAction->setEnabled(false);
371 baseAction->setParent(m_mainWnd);
372 #ifdef Q_WS_MAC
373 baseAction->setIconVisibleInMenu(false);
374 #endif
375 a->setAction(baseAction);
376 m_mainWnd->addAction(baseAction);
377 a->setKeySequence(a->keySequence());
378 a->setDefaultKeySequence(QKeySequence());
379 } else if (checkUnique) {
380 qWarning() << "registerOverridableAction: id" << id << "is already registered.";
383 return a;
386 Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context)
388 Shortcut *sc = 0;
389 int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
391 if (CommandPrivate * c = m_idCmdMap.value(uid, 0)) {
392 sc = qobject_cast<Shortcut *>(c);
393 if (!sc) {
394 qWarning() << "registerShortcut: id" << id << "is registered with a different command type.";
395 return c;
397 } else {
398 sc = new Shortcut(uid);
399 m_idCmdMap.insert(uid, sc);
402 if (sc->shortcut()) {
403 qWarning() << "registerShortcut: action already registered (id" << id << ".";
404 return sc;
407 if (!hasContext(context)) {
408 shortcut->setEnabled(false);
410 shortcut->setObjectName(id);
411 shortcut->setParent(m_mainWnd);
412 sc->setShortcut(shortcut);
414 if (context.isEmpty()) {
415 sc->setContext(QList<int>() << 0);
416 } else {
417 sc->setContext(context);
420 sc->setKeySequence(shortcut->key());
421 sc->setDefaultKeySequence(QKeySequence());
423 return sc;
426 Command *ActionManagerPrivate::command(const QString &id) const
428 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
429 const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
431 if (it == m_idCmdMap.constEnd()) {
432 if (warnAboutFindFailures) {
433 qWarning() << "ActionManagerPrivate::command(): failed to find :" << id << '/' << uid;
435 return 0;
437 return it.value();
440 ActionContainer *ActionManagerPrivate::actionContainer(const QString &id) const
442 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
443 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
445 if (it == m_idContainerMap.constEnd()) {
446 if (warnAboutFindFailures) {
447 qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << id << '/' << uid;
449 return 0;
451 return it.value();
454 Command *ActionManagerPrivate::command(int uid) const
456 const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
458 if (it == m_idCmdMap.constEnd()) {
459 if (warnAboutFindFailures) {
460 qWarning() << "ActionManagerPrivate::command(): failed to find :" << UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << '/' << uid;
462 return 0;
464 return it.value();
467 ActionContainer *ActionManagerPrivate::actionContainer(int uid) const
469 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
471 if (it == m_idContainerMap.constEnd()) {
472 if (warnAboutFindFailures) {
473 qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << uid;
475 return 0;
477 return it.value();
480 static const char *settingsGroup = "KeyBindings";
481 static const char *idKey = "ID";
482 static const char *sequenceKey = "Keysequence";
484 void ActionManagerPrivate::readSettings(QSettings &settings)
486 const int shortcuts = settings.beginReadArray(QLatin1String(settingsGroup));
488 for (int i = 0; i < shortcuts; ++i) {
489 settings.setArrayIndex(i);
490 const QString sid = settings.value(QLatin1String(idKey)).toString();
491 const QKeySequence key(settings.value(QLatin1String(sequenceKey)).toString());
492 const int id = UniqueIDManager::instance()->uniqueIdentifier(sid);
494 Command *cmd = command(id);
495 if (cmd) {
496 cmd->setKeySequence(key);
499 settings.endArray();
502 void ActionManagerPrivate::saveSettings(QSettings &settings) const
504 settings.beginWriteArray(QLatin1String(settingsGroup));
505 int count = 0;
507 const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd();
508 for (IdCmdMap::const_iterator j = m_idCmdMap.constBegin(); j != cmdcend; ++j) {
509 const int id = j.key();
510 CommandPrivate *cmd = j.value();
511 QKeySequence key = cmd->keySequence();
512 if (key != cmd->defaultKeySequence()) {
513 const QString sid = UniqueIDManager::instance()->stringForUniqueIdentifier(id);
514 settings.setArrayIndex(count);
515 settings.setValue(QLatin1String(idKey), sid);
516 settings.setValue(QLatin1String(sequenceKey), key.toString());
517 count++;
521 settings.endArray();