actionmanager.cpp 17.6 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11 12 13 14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18 19 20 21 22 23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

30
#include "actionmanager_p.h"
con's avatar
con committed
31
#include "mainwindow.h"
32
#include "actioncontainer_p.h"
con's avatar
con committed
33
#include "command_p.h"
con's avatar
con committed
34 35 36
#include "uniqueidmanager.h"

#include <coreplugin/coreconstants.h>
37
#include <coreplugin/icore.h>
con's avatar
con committed
38 39 40 41 42 43 44 45 46 47 48 49 50

#include <QtCore/QDebug>
#include <QtCore/QSettings>
#include <QtGui/QMenu>
#include <QtGui/QAction>
#include <QtGui/QShortcut>
#include <QtGui/QMenuBar>

namespace {
    enum { warnAboutFindFailures = 0 };
}

/*!
51
    \class Core::ActionManager
con's avatar
con committed
52 53
    \mainclass

54 55
    \brief The action manager is responsible for registration of menus and
    menu items and keyboard shortcuts.
con's avatar
con committed
56

57
    The ActionManager is the central bookkeeper of actions and their shortcuts and layout.
58 59 60
    You get the only implementation of this class from the core interface
    ICore::actionManager() method, e.g.
    \code
61
        Core::ICore::instance()->actionManager()
62
    \endcode
con's avatar
con committed
63 64 65 66 67

    The main reasons for the need of this class is to provide a central place where the user
    can specify all his keyboard shortcuts, and to provide a solution for actions that should
    behave differently in different contexts (like the copy/replace/undo/redo actions).

68 69 70
    \section1 Contexts

    All actions that are registered with the same string ID (but different context lists)
71
    are considered to be overloads of the same command, represented by an instance
con's avatar
con committed
72
    of the Command class.
73 74 75 76 77 78 79 80 81 82 83 84
    Exactly only one of the registered actions with the same ID is active at any time.
    Which action this is, is defined by the context list that the actions were registered
    with:

    If the current focus widget was registered via \l{ICore::addContextObject()},
    all the contexts returned by its IContext object are active. In addition all
    contexts set via \l{ICore::addAdditionalContext()} are active as well. If one
    of the actions was registered for one of these active contexts, it is the one
    active action, and receives \c triggered and \c toggled signals. Also the
    appearance of the visible action for this ID might be adapted to this
    active action (depending on the settings of the corresponding \l{Command} object).

con's avatar
con committed
85
    The action that is visible to the user is the one returned by Command::action().
86
    If you provide yourself a user visible representation of your action you need
con's avatar
con committed
87
    to use Command::action() for this.
88 89 90
    When this action is invoked by the user,
    the signal is forwarded to the registered action that is valid for the current context.

91 92 93
    \section1 Registering Actions

    To register a globally active action "My Action"
94 95
    put the following in your plugin's IPlugin::initialize method:
    \code
96
        Core::ActionManager *am = Core::ICore::instance()->actionManager();
97
        QAction *myAction = new QAction(tr("My Action"), this);
con's avatar
con committed
98
        Core::Command *cmd = am->registerAction(myAction,
99
                                                 "myplugin.myaction",
100
                                                 Core::Context(C_GLOBAL));
101 102 103 104 105 106
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+u")));
        connect(myAction, SIGNAL(triggered()), this, SLOT(performMyAction()));
    \endcode

    So the \c connect is done to your own QAction instance. If you create e.g.
    a tool button that should represent the action you add the action
con's avatar
con committed
107
    from Command::action() to it:
108 109 110 111 112 113 114 115
    \code
        QToolButton *myButton = new QToolButton(someParentWidget);
        myButton->setDefaultAction(cmd->action());
    \endcode

    Also use the ActionManager to add items to registered
    action containers like the applications menu bar or menus in that menu bar.
    To do this, you register your action via the
116
    registerAction methods, get the action container for a specific ID (like specified in
117
    the Core::Constants namespace) with a call of
con's avatar
con committed
118 119
    actionContainer(const QString&) and add your command to this container.

120 121 122 123 124
    Following the example adding "My Action" to the "Tools" menu would be done by
    \code
        am->actionContainer(Core::M_TOOLS)->addAction(cmd);
    \endcode

125
    \section1 Important Guidelines:
con's avatar
con committed
126 127
    \list
    \o Always register your actions and shortcuts!
128 129
    \o Register your actions and shortcuts during your plugin's \l{ExtensionSystem::IPlugin::initialize()}
       or \l{ExtensionSystem::IPlugin::extensionsInitialized()} methods, otherwise the shortcuts won't appear
130
       in the keyboard settings dialog from the beginning.
131 132 133
    \o When registering an action with \c{cmd=registerAction(action, id, contexts)} be sure to connect
       your own action \c{connect(action, SIGNAL...)} but make \c{cmd->action()} visible to the user, i.e.
       \c{widget->addAction(cmd->action())}.
con's avatar
con committed
134 135 136
    \o Use this class to add actions to the applications menus
    \endlist

137
    \sa Core::ICore
con's avatar
con committed
138
    \sa Core::Command
139
    \sa Core::ActionContainer
140
    \sa Core::IContext
con's avatar
con committed
141 142 143
*/

/*!
144
    \fn ActionContainer *ActionManager::createMenu(const QString &id)
145
    \brief Creates a new menu with the given string \a id.
con's avatar
con committed
146

147
    Returns a new ActionContainer that you can use to get the QMenu instance
148
    or to add menu items to the menu. The ActionManager owns
149
    the returned ActionContainer.
150
    Add your menu to some other menu or a menu bar via the
151
    ActionManager::actionContainer and ActionContainer::addMenu methods.
con's avatar
con committed
152 153 154
*/

/*!
155
    \fn ActionContainer *ActionManager::createMenuBar(const QString &id)
156
    \brief Creates a new menu bar with the given string \a id.
con's avatar
con committed
157

158
    Returns a new ActionContainer that you can use to get the QMenuBar instance
159
    or to add menus to the menu bar. The ActionManager owns
160
    the returned ActionContainer.
con's avatar
con committed
161 162 163
*/

/*!
con's avatar
con committed
164
    \fn Command *ActionManager::registerAction(QAction *action, const QString &id, const Context &context)
165 166 167 168 169 170 171
    \brief Makes an \a action known to the system under the specified string \a id.

    Returns a command object that represents the action in the application and is
    owned by the ActionManager. You can registered several actions with the
    same \a id as long as the \a context is different. In this case
    a trigger of the actual action is forwarded to the registered QAction
    for the currently active context.
con's avatar
con committed
172 173 174
*/

/*!
con's avatar
con committed
175
    \fn Command *ActionManager::registerShortcut(QShortcut *shortcut, const QString &id, const Context &context)
176 177 178 179 180 181 182
    \brief Makes a \a shortcut known to the system under the specified string \a id.

    Returns a command object that represents the shortcut in the application and is
    owned by the ActionManager. You can registered several shortcuts with the
    same \a id as long as the \a context is different. In this case
    a trigger of the actual shortcut is forwarded to the registered QShortcut
    for the currently active context.
con's avatar
con committed
183 184 185
*/

/*!
con's avatar
con committed
186 187
    \fn Command *ActionManager::command(const QString &id) const
    \brief Returns the Command object that is known to the system
188 189 190
    under the given string \a id.

    \sa ActionManager::registerAction()
con's avatar
con committed
191 192 193
*/

/*!
194
    \fn ActionContainer *ActionManager::actionContainer(const QString &id) const
195 196
    \brief Returns the IActionContainter object that is know to the system
    under the given string \a id.
con's avatar
con committed
197

198 199 200
    \sa ActionManager::createMenu()
    \sa ActionManager::createMenuBar()
*/
con's avatar
con committed
201
/*!
202 203
    \fn ActionManager::ActionManager(QObject *parent)
    \internal
con's avatar
con committed
204 205
*/
/*!
206 207
    \fn ActionManager::~ActionManager()
    \internal
con's avatar
con committed
208 209 210 211 212
*/

using namespace Core;
using namespace Core::Internal;

213
ActionManagerPrivate* ActionManagerPrivate::m_instance = 0;
con's avatar
con committed
214 215

/*!
216 217 218
    \class ActionManagerPrivate
    \inheaderfile actionmanager_p.h
    \internal
con's avatar
con committed
219 220
*/

221 222
ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
  : ActionManager(mainWnd),
con's avatar
con committed
223 224
    m_mainWnd(mainWnd)
{
225
    UniqueIDManager *uidmgr = UniqueIDManager::instance();
con's avatar
con committed
226 227 228 229 230 231 232
    m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_ONE);
    m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_TWO);
    m_defaultGroups << uidmgr->uniqueIdentifier(Constants::G_DEFAULT_THREE);
    m_instance = this;

}

233
ActionManagerPrivate::~ActionManagerPrivate()
con's avatar
con committed
234 235 236 237 238
{
    qDeleteAll(m_idCmdMap.values());
    qDeleteAll(m_idContainerMap.values());
}

239
ActionManagerPrivate *ActionManagerPrivate::instance()
con's avatar
con committed
240 241 242 243
{
    return m_instance;
}

244
QList<int> ActionManagerPrivate::defaultGroups() const
con's avatar
con committed
245 246 247 248
{
    return m_defaultGroups;
}

249
QList<Command *> ActionManagerPrivate::commands() const
con's avatar
con committed
250
{
251 252
    // transform list of CommandPrivate into list of Command
    QList<Command *> result;
hjk's avatar
hjk committed
253
    foreach (Command *cmd, m_idCmdMap.values())
254 255
        result << cmd;
    return result;
con's avatar
con committed
256 257
}

258
bool ActionManagerPrivate::hasContext(int context) const
con's avatar
con committed
259
{
hjk's avatar
hjk committed
260
    return m_context.contains(context);
con's avatar
con committed
261 262
}

263 264 265 266 267 268 269 270 271
QDebug operator<<(QDebug in, const Context &context)
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
    in << "CONTEXT: ";
    foreach (int c, context)
        in << "   " << c << uidm->stringForUniqueIdentifier(c);
    return in;
}

272
void ActionManagerPrivate::setContext(const Context &context)
con's avatar
con committed
273 274 275 276 277 278 279 280 281 282
{
    // here are possibilities for speed optimization if necessary:
    // let commands (de-)register themselves for contexts
    // and only update commands that are either in old or new contexts
    m_context = context;
    const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd();
    for (IdCmdMap::const_iterator it = m_idCmdMap.constBegin(); it != cmdcend; ++it)
        it.value()->setCurrentContext(m_context);
}

283
bool ActionManagerPrivate::hasContext(const Context &context) const
con's avatar
con committed
284
{
hjk's avatar
hjk committed
285 286
    for (int i = 0; i < m_context.size(); ++i) {
        if (context.contains(m_context.at(i)))
con's avatar
con committed
287 288 289 290 291
            return true;
    }
    return false;
}

hjk's avatar
hjk committed
292
ActionContainer *ActionManagerPrivate::createMenu(const Id &id)
con's avatar
con committed
293
{
294
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
    const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
    if (it !=  m_idContainerMap.constEnd())
        return it.value();

    QMenu *m = new QMenu(m_mainWnd);
    m->setObjectName(id);

    MenuActionContainer *mc = new MenuActionContainer(uid);
    mc->setMenu(m);

    m_idContainerMap.insert(uid, mc);

    return mc;
}

hjk's avatar
hjk committed
310
ActionContainer *ActionManagerPrivate::createMenuBar(const Id &id)
con's avatar
con committed
311
{
312
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
    const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
    if (it !=  m_idContainerMap.constEnd())
        return it.value();

    QMenuBar *mb = new QMenuBar; // No parent (System menu bar on Mac OS X)
    mb->setObjectName(id);

    MenuBarActionContainer *mbc = new MenuBarActionContainer(uid);
    mbc->setMenuBar(mb);

    m_idContainerMap.insert(uid, mbc);

    return mbc;
}

hjk's avatar
hjk committed
328
Command *ActionManagerPrivate::registerAction(QAction *action, const Id &id, const Context &context)
con's avatar
con committed
329
{
330
    Action *a = 0;
con's avatar
con committed
331
    Command *c = registerOverridableAction(action, id, false);
332
    a = static_cast<Action *>(c);
con's avatar
con committed
333 334 335 336 337
    if (a)
        a->addOverrideAction(action, context);
    return a;
}

hjk's avatar
hjk committed
338
Command *ActionManagerPrivate::registerOverridableAction(QAction *action, const Id &id, bool checkUnique)
con's avatar
con committed
339
{
340
    Action *a = 0;
341
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
342
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
343
        a = qobject_cast<Action *>(c);
con's avatar
con committed
344
        if (!a) {
con's avatar
con committed
345 346 347
            qWarning() << "registerAction: id" << id << "is registered with a different command type.";
            return c;
        }
con's avatar
con committed
348
    } else {
349
        a = new Action(uid);
con's avatar
con committed
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
        m_idCmdMap.insert(uid, a);
    }

    if (!a->action()) {
        QAction *baseAction = new QAction(m_mainWnd);
        baseAction->setObjectName(id);
        baseAction->setCheckable(action->isCheckable());
        baseAction->setIcon(action->icon());
        baseAction->setIconText(action->iconText());
        baseAction->setText(action->text());
        baseAction->setToolTip(action->toolTip());
        baseAction->setStatusTip(action->statusTip());
        baseAction->setWhatsThis(action->whatsThis());
        baseAction->setChecked(action->isChecked());
        baseAction->setSeparator(action->isSeparator());
        baseAction->setShortcutContext(Qt::ApplicationShortcut);
        baseAction->setEnabled(false);
        baseAction->setParent(m_mainWnd);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
368
#ifdef Q_WS_MAC
con's avatar
con committed
369
        baseAction->setIconVisibleInMenu(false);
370 371
#else
        baseAction->setIconVisibleInMenu(action->isIconVisibleInMenu());
con's avatar
con committed
372 373 374 375 376 377 378 379 380 381
#endif
        a->setAction(baseAction);
        m_mainWnd->addAction(baseAction);
    } else  if (checkUnique) {
        qWarning() << "registerOverridableAction: id" << id << "is already registered.";
    }

    return a;
}

hjk's avatar
hjk committed
382
Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const Id &id, const Context &context)
con's avatar
con committed
383 384
{
    Shortcut *sc = 0;
385
    int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
386
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
con's avatar
con committed
387 388
        sc = qobject_cast<Shortcut *>(c);
        if (!sc) {
con's avatar
con committed
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
            qWarning() << "registerShortcut: id" << id << "is registered with a different command type.";
            return c;
        }
    } else {
        sc = new Shortcut(uid);
        m_idCmdMap.insert(uid, sc);
    }

    if (sc->shortcut()) {
        qWarning() << "registerShortcut: action already registered (id" << id << ".";
        return sc;
    }

    if (!hasContext(context))
        shortcut->setEnabled(false);
    shortcut->setObjectName(id);
    shortcut->setParent(m_mainWnd);
    sc->setShortcut(shortcut);

hjk's avatar
hjk committed
408
    if (context.isEmpty())
409
        sc->setContext(Context(0));
con's avatar
con committed
410 411 412 413 414 415
    else
        sc->setContext(context);

    return sc;
}

hjk's avatar
hjk committed
416
Command *ActionManagerPrivate::command(const Id &id) const
con's avatar
con committed
417
{
418
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
419 420 421
    const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
    if (it == m_idCmdMap.constEnd()) {
        if (warnAboutFindFailures)
422
            qWarning() << "ActionManagerPrivate::command(): failed to find :" << id << '/' << uid;
con's avatar
con committed
423 424 425 426 427
        return 0;
    }
    return it.value();
}

hjk's avatar
hjk committed
428
ActionContainer *ActionManagerPrivate::actionContainer(const Id &id) const
con's avatar
con committed
429
{
430 431 432
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
    const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
    if (it == m_idContainerMap.constEnd()) {
con's avatar
con committed
433
        if (warnAboutFindFailures)
434
            qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << id << '/' << uid;
con's avatar
con committed
435 436 437 438 439
        return 0;
    }
    return it.value();
}

con's avatar
con committed
440
Command *ActionManagerPrivate::command(int uid) const
con's avatar
con committed
441 442 443 444
{
    const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
    if (it == m_idCmdMap.constEnd()) {
        if (warnAboutFindFailures)
445
            qWarning() << "ActionManagerPrivate::command(): failed to find :" <<  UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << '/' << uid;
con's avatar
con committed
446 447 448 449 450
        return 0;
    }
    return it.value();
}

451
ActionContainer *ActionManagerPrivate::actionContainer(int uid) const
con's avatar
con committed
452 453 454 455
{
    const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
    if (it == m_idContainerMap.constEnd()) {
        if (warnAboutFindFailures)
456
            qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << uid;
con's avatar
con committed
457 458 459 460
        return 0;
    }
    return it.value();
}
hjk's avatar
hjk committed
461

con's avatar
con committed
462 463 464 465
static const char *settingsGroup = "KeyBindings";
static const char *idKey = "ID";
static const char *sequenceKey = "Keysequence";

466
void ActionManagerPrivate::initialize()
con's avatar
con committed
467
{
468
    QSettings *settings = Core::ICore::instance()->settings();
con's avatar
con committed
469 470 471 472 473
    const int shortcuts = settings->beginReadArray(QLatin1String(settingsGroup));
    for (int i=0; i<shortcuts; ++i) {
        settings->setArrayIndex(i);
        const QString sid = settings->value(QLatin1String(idKey)).toString();
        const QKeySequence key(settings->value(QLatin1String(sequenceKey)).toString());
474
        const int id = UniqueIDManager::instance()->uniqueIdentifier(sid);
con's avatar
con committed
475

con's avatar
con committed
476
        Command *cmd = command(id);
con's avatar
con committed
477 478 479 480 481 482
        if (cmd)
            cmd->setKeySequence(key);
    }
    settings->endArray();
}

483
void ActionManagerPrivate::saveSettings(QSettings *settings)
con's avatar
con committed
484 485 486 487 488 489 490
{
    settings->beginWriteArray(QLatin1String(settingsGroup));
    int count = 0;

    const IdCmdMap::const_iterator cmdcend = m_idCmdMap.constEnd();
    for (IdCmdMap::const_iterator j = m_idCmdMap.constBegin(); j != cmdcend; ++j) {
        const int id = j.key();
con's avatar
con committed
491
        CommandPrivate *cmd = j.value();
con's avatar
con committed
492 493
        QKeySequence key = cmd->keySequence();
        if (key != cmd->defaultKeySequence()) {
494
            const QString sid = UniqueIDManager::instance()->stringForUniqueIdentifier(id);
con's avatar
con committed
495 496 497 498 499 500 501 502 503
            settings->setArrayIndex(count);
            settings->setValue(QLatin1String(idKey), sid);
            settings->setValue(QLatin1String(sequenceKey), key.toString());
            count++;
        }
    }

    settings->endArray();
}