1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "actionmanager_p.h"
34 #include "mainwindow.h"
35 #include "actioncontainer_p.h"
36 #include "command_p.h"
37 #include "uniqueidmanager.h"
39 #include <coreplugin/coreconstants.h>
40 #include <coreplugin/icore.h>
41 #include <utils/qtcassert.h>
43 #include <QtCore/QDebug>
44 #include <QtCore/QSettings>
45 #include <QtGui/QMenu>
46 #include <QtGui/QAction>
47 #include <QtGui/QShortcut>
48 #include <QtGui/QMenuBar>
51 enum { warnAboutFindFailures = 0 };
55 \class Core::ActionManager
58 \brief The action manager is responsible for registration of menus and
59 menu items and keyboard shortcuts.
61 The ActionManager is the central bookkeeper of actions and their shortcuts and layout.
62 You get the only implementation of this class from the core interface
63 ICore::actionManager() method, e.g.
65 Core::ICore::instance()->actionManager()
68 The main reasons for the need of this class is to provide a central place where the user
69 can specify all his keyboard shortcuts, and to provide a solution for actions that should
70 behave differently in different contexts (like the copy/replace/undo/redo actions).
74 All actions that are registered with the same string ID (but different context lists)
75 are considered to be overloads of the same command, represented by an instance
77 Exactly only one of the registered actions with the same ID is active at any time.
78 Which action this is, is defined by the context list that the actions were registered
81 If the current focus widget was registered via \l{ICore::addContextObject()},
82 all the contexts returned by its IContext object are active. In addition all
83 contexts set via \l{ICore::addAdditionalContext()} are active as well. If one
84 of the actions was registered for one of these active contexts, it is the one
85 active action, and receives \c triggered and \c toggled signals. Also the
86 appearance of the visible action for this ID might be adapted to this
87 active action (depending on the settings of the corresponding \l{Command} object).
89 The action that is visible to the user is the one returned by Command::action().
90 If you provide yourself a user visible representation of your action you need
91 to use Command::action() for this.
92 When this action is invoked by the user,
93 the signal is forwarded to the registered action that is valid for the current context.
95 \section1 Registering Actions
97 To register a globally active action "My Action"
98 put the following in your plugin's IPlugin::initialize method:
100 Core::ActionManager *am = Core::ICore::instance()->actionManager();
101 QAction *myAction = new QAction(tr("My Action"), this);
102 Core::Command *cmd = am->registerAction(myAction,
104 Core::Context(C_GLOBAL));
105 cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+u")));
106 connect(myAction, SIGNAL(triggered()), this, SLOT(performMyAction()));
109 So the \c connect is done to your own QAction instance. If you create e.g.
110 a tool button that should represent the action you add the action
111 from Command::action() to it:
113 QToolButton *myButton = new QToolButton(someParentWidget);
114 myButton->setDefaultAction(cmd->action());
117 Also use the ActionManager to add items to registered
118 action containers like the applications menu bar or menus in that menu bar.
119 To do this, you register your action via the
120 registerAction methods, get the action container for a specific ID (like specified in
121 the Core::Constants namespace) with a call of
122 actionContainer(const QString&) and add your command to this container.
124 Following the example adding "My Action" to the "Tools" menu would be done by
126 am->actionContainer(Core::M_TOOLS)->addAction(cmd);
129 \section1 Important Guidelines:
131 \o Always register your actions and shortcuts!
132 \o Register your actions and shortcuts during your plugin's \l{ExtensionSystem::IPlugin::initialize()}
133 or \l{ExtensionSystem::IPlugin::extensionsInitialized()} methods, otherwise the shortcuts won't appear
134 in the keyboard settings dialog from the beginning.
135 \o When registering an action with \c{cmd=registerAction(action, id, contexts)} be sure to connect
136 your own action \c{connect(action, SIGNAL...)} but make \c{cmd->action()} visible to the user, i.e.
137 \c{widget->addAction(cmd->action())}.
138 \o Use this class to add actions to the applications menus
143 \sa Core::ActionContainer
148 \fn ActionContainer *ActionManager::createMenu(const QString &id)
149 \brief Creates a new menu with the given string \a id.
151 Returns a new ActionContainer that you can use to get the QMenu instance
152 or to add menu items to the menu. The ActionManager owns
153 the returned ActionContainer.
154 Add your menu to some other menu or a menu bar via the
155 ActionManager::actionContainer and ActionContainer::addMenu methods.
159 \fn ActionContainer *ActionManager::createMenuBar(const QString &id)
160 \brief Creates a new menu bar with the given string \a id.
162 Returns a new ActionContainer that you can use to get the QMenuBar instance
163 or to add menus to the menu bar. The ActionManager owns
164 the returned ActionContainer.
168 \fn Command *ActionManager::registerAction(QAction *action, const QString &id, const Context &context, bool scriptable)
169 \brief Makes an \a action known to the system under the specified string \a id.
171 Returns a command object that represents the action in the application and is
172 owned by the ActionManager. You can registered several actions with the
173 same \a id as long as the \a context is different. In this case
174 a trigger of the actual action is forwarded to the registered QAction
175 for the currently active context.
176 A scriptable action can be called from a script without the need for the user
181 \fn Command *ActionManager::registerShortcut(QShortcut *shortcut, const QString &id, const Context &context, bool scriptable)
182 \brief Makes a \a shortcut known to the system under the specified string \a id.
184 Returns a command object that represents the shortcut in the application and is
185 owned by the ActionManager. You can registered several shortcuts with the
186 same \a id as long as the \a context is different. In this case
187 a trigger of the actual shortcut is forwarded to the registered QShortcut
188 for the currently active context.
189 A scriptable shortcut can be called from a script without the need for the user
194 \fn Command *ActionManager::command(const QString &id) const
195 \brief Returns the Command object that is known to the system
196 under the given string \a id.
198 \sa ActionManager::registerAction()
202 \fn ActionContainer *ActionManager::actionContainer(const QString &id) const
203 \brief Returns the IActionContainter object that is know to the system
204 under the given string \a id.
206 \sa ActionManager::createMenu()
207 \sa ActionManager::createMenuBar()
211 \fn Command *ActionManager::unregisterAction(QAction *action, const QString &id)
212 \brief Removes the knowledge about an \a action under the specified string \a id.
214 Usually you do not need to unregister actions. The only valid use case for unregistering
215 actions, is for actions that represent user definable actions, like for the custom Locator
216 filters. If the user removes such an action, it also has to be unregistered from the action manager,
217 to make it disappear from shortcut settings etc.
221 \fn ActionManager::ActionManager(QObject *parent)
225 \fn ActionManager::~ActionManager()
229 using namespace Core;
230 using namespace Core::Internal;
232 ActionManagerPrivate* ActionManagerPrivate::m_instance = 0;
235 \class ActionManagerPrivate
236 \inheaderfile actionmanager_p.h
240 ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
241 : ActionManager(mainWnd),
247 ActionManagerPrivate::~ActionManagerPrivate()
249 // first delete containers to avoid them reacting to command deletion
250 foreach (ActionContainerPrivate *container, m_idContainerMap)
251 disconnect(container, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
252 qDeleteAll(m_idContainerMap.values());
253 qDeleteAll(m_idCmdMap.values());
256 ActionManagerPrivate *ActionManagerPrivate::instance()
261 QList<Command *> ActionManagerPrivate::commands() const
263 // transform list of CommandPrivate into list of Command
264 QList<Command *> result;
265 foreach (Command *cmd, m_idCmdMap.values())
270 bool ActionManagerPrivate::hasContext(int context) const
272 return m_context.contains(context);
275 QDebug operator<<(QDebug in, const Context &context)
277 UniqueIDManager *uidm = UniqueIDManager::instance();
279 foreach (int c, context)
280 in << " " << c << uidm->stringForUniqueIdentifier(c);
284 void ActionManagerPrivate::setContext(const Context &context)
286 // here are possibilities for speed optimization if necessary:
287 // let commands (de-)register themselves for contexts
288 // and only update commands that are either in old or new contexts
290 const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd();
291 for (IdCmdMap::const_iterator it = m_idCmdMap.constBegin(); it != cmdcend; ++it)
292 it.value()->setCurrentContext(m_context);
295 bool ActionManagerPrivate::hasContext(const Context &context) const
297 for (int i = 0; i < m_context.size(); ++i) {
298 if (context.contains(m_context.at(i)))
304 ActionContainer *ActionManagerPrivate::createMenu(const Id &id)
306 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
307 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
308 if (it != m_idContainerMap.constEnd())
311 QMenu *m = new QMenu(m_mainWnd);
312 m->setObjectName(id);
314 MenuActionContainer *mc = new MenuActionContainer(uid);
317 m_idContainerMap.insert(uid, mc);
318 connect(mc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
323 ActionContainer *ActionManagerPrivate::createMenuBar(const Id &id)
325 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
326 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
327 if (it != m_idContainerMap.constEnd())
330 QMenuBar *mb = new QMenuBar; // No parent (System menu bar on Mac OS X)
331 mb->setObjectName(id);
333 MenuBarActionContainer *mbc = new MenuBarActionContainer(uid);
336 m_idContainerMap.insert(uid, mbc);
337 connect(mbc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
342 void ActionManagerPrivate::containerDestroyed()
344 ActionContainerPrivate *container = static_cast<ActionContainerPrivate *>(sender());
345 m_idContainerMap.remove(m_idContainerMap.key(container));
348 Command *ActionManagerPrivate::registerAction(QAction *action, const Id &id, const Context &context, bool scriptable)
350 Action *a = overridableAction(id);
352 a->addOverrideAction(action, context, scriptable);
353 emit commandListChanged();
354 emit commandAdded(id);
359 Action *ActionManagerPrivate::overridableAction(const Id &id)
362 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
363 if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
364 a = qobject_cast<Action *>(c);
366 qWarning() << "registerAction: id" << id << "is registered with a different command type.";
371 m_idCmdMap.insert(uid, a);
372 m_mainWnd->addAction(a->action());
373 a->action()->setObjectName(id);
374 a->action()->setShortcutContext(Qt::ApplicationShortcut);
375 a->setCurrentContext(m_context);
381 void ActionManagerPrivate::unregisterAction(QAction *action, const Id &id)
384 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
385 CommandPrivate *c = m_idCmdMap.value(uid, 0);
386 QTC_ASSERT(c, return);
387 a = qobject_cast<Action *>(c);
389 qWarning() << "unregisterAction: id" << id << "is registered with a different command type.";
392 a->removeOverrideAction(action);
395 // ActionContainers listen to the commands' destroyed signals
396 m_mainWnd->removeAction(a->action());
398 m_idCmdMap.remove(uid);
401 emit commandListChanged();
404 Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const Id &id, const Context &context, bool scriptable)
407 int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
408 if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
409 sc = qobject_cast<Shortcut *>(c);
411 qWarning() << "registerShortcut: id" << id << "is registered with a different command type.";
415 sc = new Shortcut(uid);
416 m_idCmdMap.insert(uid, sc);
419 if (sc->shortcut()) {
420 qWarning() << "registerShortcut: action already registered (id" << id << ".";
424 if (!hasContext(context))
425 shortcut->setEnabled(false);
426 shortcut->setObjectName(id);
427 shortcut->setParent(m_mainWnd);
428 sc->setShortcut(shortcut);
429 sc->setScriptable(scriptable);
431 if (context.isEmpty())
432 sc->setContext(Context(0));
434 sc->setContext(context);
436 emit commandListChanged();
437 emit commandAdded(id);
441 Command *ActionManagerPrivate::command(const Id &id) const
443 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
444 const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
445 if (it == m_idCmdMap.constEnd()) {
446 if (warnAboutFindFailures)
447 qWarning() << "ActionManagerPrivate::command(): failed to find :" << id << '/' << uid;
453 ActionContainer *ActionManagerPrivate::actionContainer(const Id &id) const
455 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
456 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
457 if (it == m_idContainerMap.constEnd()) {
458 if (warnAboutFindFailures)
459 qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << id << '/' << uid;
465 Command *ActionManagerPrivate::command(int uid) const
467 const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
468 if (it == m_idCmdMap.constEnd()) {
469 if (warnAboutFindFailures)
470 qWarning() << "ActionManagerPrivate::command(): failed to find :" << UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << '/' << uid;
476 ActionContainer *ActionManagerPrivate::actionContainer(int uid) const
478 const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
479 if (it == m_idContainerMap.constEnd()) {
480 if (warnAboutFindFailures)
481 qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << uid;
487 static const char *settingsGroup = "KeyBindings";
488 static const char *idKey = "ID";
489 static const char *sequenceKey = "Keysequence";
491 void ActionManagerPrivate::initialize()
493 QSettings *settings = Core::ICore::instance()->settings();
494 const int shortcuts = settings->beginReadArray(QLatin1String(settingsGroup));
495 for (int i=0; i<shortcuts; ++i) {
496 settings->setArrayIndex(i);
497 const QString sid = settings->value(QLatin1String(idKey)).toString();
498 const QKeySequence key(settings->value(QLatin1String(sequenceKey)).toString());
499 const int id = UniqueIDManager::instance()->uniqueIdentifier(sid);
501 Command *cmd = command(id);
503 cmd->setKeySequence(key);
505 settings->endArray();
508 void ActionManagerPrivate::saveSettings(QSettings *settings)
510 settings->beginWriteArray(QLatin1String(settingsGroup));
513 const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd();
514 for (IdCmdMap::const_iterator j = m_idCmdMap.constBegin(); j != cmdcend; ++j) {
515 const int id = j.key();
516 CommandPrivate *cmd = j.value();
517 QKeySequence key = cmd->keySequence();
518 if (key != cmd->defaultKeySequence()) {
519 const QString sid = UniqueIDManager::instance()->stringForUniqueIdentifier(id);
520 settings->setArrayIndex(count);
521 settings->setValue(QLatin1String(idKey), sid);
522 settings->setValue(QLatin1String(sequenceKey), key.toString());
527 settings->endArray();
530 void ActionManagerPrivate::unregisterShortcut(const Core::Id &id)
533 const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
534 CommandPrivate *c = m_idCmdMap.value(uid, 0);
535 QTC_ASSERT(c, return);
536 sc = qobject_cast<Shortcut *>(c);
538 qWarning() << "unregisterShortcut: id" << id << "is registered with a different command type.";
541 delete sc->shortcut();
542 m_idCmdMap.remove(uid);
544 emit commandListChanged();