OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / coreplugin / actionmanager / actionmanager.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "actionmanager_p.h"
34 #include "mainwindow.h"
35 #include "actioncontainer_p.h"
36 #include "command_p.h"
37 #include "uniqueidmanager.h"
38
39 #include <coreplugin/coreconstants.h>
40 #include <coreplugin/icore.h>
41 #include <utils/qtcassert.h>
42
43 #include <QtCore/QDebug>
44 #include <QtCore/QSettings>
45 #include <QtGui/QMenu>
46 #include <QtGui/QAction>
47 #include <QtGui/QShortcut>
48 #include <QtGui/QMenuBar>
49
50 namespace {
51     enum { warnAboutFindFailures = 0 };
52 }
53
54 /*!
55     \class Core::ActionManager
56     \mainclass
57
58     \brief The action manager is responsible for registration of menus and
59     menu items and keyboard shortcuts.
60
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.
64     \code
65         Core::ICore::instance()->actionManager()
66     \endcode
67
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).
71
72     \section1 Contexts
73
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
76     of the Command class.
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
79     with:
80
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).
88
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.
94
95     \section1 Registering Actions
96
97     To register a globally active action "My Action"
98     put the following in your plugin's IPlugin::initialize method:
99     \code
100         Core::ActionManager *am = Core::ICore::instance()->actionManager();
101         QAction *myAction = new QAction(tr("My Action"), this);
102         Core::Command *cmd = am->registerAction(myAction,
103                                                  "myplugin.myaction",
104                                                  Core::Context(C_GLOBAL));
105         cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+u")));
106         connect(myAction, SIGNAL(triggered()), this, SLOT(performMyAction()));
107     \endcode
108
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:
112     \code
113         QToolButton *myButton = new QToolButton(someParentWidget);
114         myButton->setDefaultAction(cmd->action());
115     \endcode
116
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.
123
124     Following the example adding "My Action" to the "Tools" menu would be done by
125     \code
126         am->actionContainer(Core::M_TOOLS)->addAction(cmd);
127     \endcode
128
129     \section1 Important Guidelines:
130     \list
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
139     \endlist
140
141     \sa Core::ICore
142     \sa Core::Command
143     \sa Core::ActionContainer
144     \sa Core::IContext
145 */
146
147 /*!
148     \fn ActionContainer *ActionManager::createMenu(const QString &id)
149     \brief Creates a new menu with the given string \a id.
150
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.
156 */
157
158 /*!
159     \fn ActionContainer *ActionManager::createMenuBar(const QString &id)
160     \brief Creates a new menu bar with the given string \a id.
161
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.
165 */
166
167 /*!
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.
170
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
177     to interact with it.
178 */
179
180 /*!
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.
183
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
190     to interact with it.
191 */
192
193 /*!
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.
197
198     \sa ActionManager::registerAction()
199 */
200
201 /*!
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.
205
206     \sa ActionManager::createMenu()
207     \sa ActionManager::createMenuBar()
208 */
209
210 /*!
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.
213
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.
218 */
219
220 /*!
221     \fn ActionManager::ActionManager(QObject *parent)
222     \internal
223 */
224 /*!
225     \fn ActionManager::~ActionManager()
226     \internal
227 */
228
229 using namespace Core;
230 using namespace Core::Internal;
231
232 ActionManagerPrivate* ActionManagerPrivate::m_instance = 0;
233
234 /*!
235     \class ActionManagerPrivate
236     \inheaderfile actionmanager_p.h
237     \internal
238 */
239
240 ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
241   : ActionManager(mainWnd),
242     m_mainWnd(mainWnd)
243 {
244     m_instance = this;
245 }
246
247 ActionManagerPrivate::~ActionManagerPrivate()
248 {
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());
254 }
255
256 ActionManagerPrivate *ActionManagerPrivate::instance()
257 {
258     return m_instance;
259 }
260
261 QList<Command *> ActionManagerPrivate::commands() const
262 {
263     // transform list of CommandPrivate into list of Command
264     QList<Command *> result;
265     foreach (Command *cmd, m_idCmdMap.values())
266         result << cmd;
267     return result;
268 }
269
270 bool ActionManagerPrivate::hasContext(int context) const
271 {
272     return m_context.contains(context);
273 }
274
275 QDebug operator<<(QDebug in, const Context &context)
276 {
277     UniqueIDManager *uidm = UniqueIDManager::instance();
278     in << "CONTEXT: ";
279     foreach (int c, context)
280         in << "   " << c << uidm->stringForUniqueIdentifier(c);
281     return in;
282 }
283
284 void ActionManagerPrivate::setContext(const Context &context)
285 {
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
289     m_context = context;
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);
293 }
294
295 bool ActionManagerPrivate::hasContext(const Context &context) const
296 {
297     for (int i = 0; i < m_context.size(); ++i) {
298         if (context.contains(m_context.at(i)))
299             return true;
300     }
301     return false;
302 }
303
304 ActionContainer *ActionManagerPrivate::createMenu(const Id &id)
305 {
306     const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
307     const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
308     if (it !=  m_idContainerMap.constEnd())
309         return it.value();
310
311     QMenu *m = new QMenu(m_mainWnd);
312     m->setObjectName(id);
313
314     MenuActionContainer *mc = new MenuActionContainer(uid);
315     mc->setMenu(m);
316
317     m_idContainerMap.insert(uid, mc);
318     connect(mc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
319
320     return mc;
321 }
322
323 ActionContainer *ActionManagerPrivate::createMenuBar(const Id &id)
324 {
325     const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
326     const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
327     if (it !=  m_idContainerMap.constEnd())
328         return it.value();
329
330     QMenuBar *mb = new QMenuBar; // No parent (System menu bar on Mac OS X)
331     mb->setObjectName(id);
332
333     MenuBarActionContainer *mbc = new MenuBarActionContainer(uid);
334     mbc->setMenuBar(mb);
335
336     m_idContainerMap.insert(uid, mbc);
337     connect(mbc, SIGNAL(destroyed()), this, SLOT(containerDestroyed()));
338
339     return mbc;
340 }
341
342 void ActionManagerPrivate::containerDestroyed()
343 {
344     ActionContainerPrivate *container = static_cast<ActionContainerPrivate *>(sender());
345     m_idContainerMap.remove(m_idContainerMap.key(container));
346 }
347
348 Command *ActionManagerPrivate::registerAction(QAction *action, const Id &id, const Context &context, bool scriptable)
349 {
350     Action *a = overridableAction(id);
351     if (a) {
352         a->addOverrideAction(action, context, scriptable);
353         emit commandListChanged();
354         emit commandAdded(id);
355     }
356     return a;
357 }
358
359 Action *ActionManagerPrivate::overridableAction(const Id &id)
360 {
361     Action *a = 0;
362     const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
363     if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
364         a = qobject_cast<Action *>(c);
365         if (!a) {
366             qWarning() << "registerAction: id" << id << "is registered with a different command type.";
367             return 0;
368         }
369     } else {
370         a = new Action(uid);
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);
376     }
377
378     return a;
379 }
380
381 void ActionManagerPrivate::unregisterAction(QAction *action, const Id &id)
382 {
383     Action *a = 0;
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);
388     if (!a) {
389         qWarning() << "unregisterAction: id" << id << "is registered with a different command type.";
390         return;
391     }
392     a->removeOverrideAction(action);
393     if (a->isEmpty()) {
394         // clean up
395         // ActionContainers listen to the commands' destroyed signals
396         m_mainWnd->removeAction(a->action());
397         delete a->action();
398         m_idCmdMap.remove(uid);
399         delete a;
400     }
401     emit commandListChanged();
402 }
403
404 Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const Id &id, const Context &context, bool scriptable)
405 {
406     Shortcut *sc = 0;
407     int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
408     if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
409         sc = qobject_cast<Shortcut *>(c);
410         if (!sc) {
411             qWarning() << "registerShortcut: id" << id << "is registered with a different command type.";
412             return c;
413         }
414     } else {
415         sc = new Shortcut(uid);
416         m_idCmdMap.insert(uid, sc);
417     }
418
419     if (sc->shortcut()) {
420         qWarning() << "registerShortcut: action already registered (id" << id << ".";
421         return sc;
422     }
423
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);
430
431     if (context.isEmpty())
432         sc->setContext(Context(0));
433     else
434         sc->setContext(context);
435
436     emit commandListChanged();
437     emit commandAdded(id);
438     return sc;
439 }
440
441 Command *ActionManagerPrivate::command(const Id &id) const
442 {
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;
448         return 0;
449     }
450     return it.value();
451 }
452
453 ActionContainer *ActionManagerPrivate::actionContainer(const Id &id) const
454 {
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;
460         return 0;
461     }
462     return it.value();
463 }
464
465 Command *ActionManagerPrivate::command(int uid) const
466 {
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;
471         return 0;
472     }
473     return it.value();
474 }
475
476 ActionContainer *ActionManagerPrivate::actionContainer(int uid) const
477 {
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;
482         return 0;
483     }
484     return it.value();
485 }
486
487 static const char *settingsGroup = "KeyBindings";
488 static const char *idKey = "ID";
489 static const char *sequenceKey = "Keysequence";
490
491 void ActionManagerPrivate::initialize()
492 {
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);
500
501         Command *cmd = command(id);
502         if (cmd)
503             cmd->setKeySequence(key);
504     }
505     settings->endArray();
506 }
507
508 void ActionManagerPrivate::saveSettings(QSettings *settings)
509 {
510     settings->beginWriteArray(QLatin1String(settingsGroup));
511     int count = 0;
512
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());
523             count++;
524         }
525     }
526
527     settings->endArray();
528 }
529
530 void ActionManagerPrivate::unregisterShortcut(const Core::Id &id)
531 {
532     Shortcut *sc = 0;
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);
537     if (!sc) {
538         qWarning() << "unregisterShortcut: id" << id << "is registered with a different command type.";
539         return;
540     }
541     delete sc->shortcut();
542     m_idCmdMap.remove(uid);
543     delete sc;
544     emit commandListChanged();
545 }