actionmanager.cpp 18.8 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>
38
#include <utils/qtcassert.h>
con's avatar
con committed
39 40 41 42 43 44 45 46 47 48 49 50 51

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

namespace {
    enum { warnAboutFindFailures = 0 };
}

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

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

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

    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).

69 70 71
    \section1 Contexts

    All actions that are registered with the same string ID (but different context lists)
72
    are considered to be overloads of the same command, represented by an instance
con's avatar
con committed
73
    of the Command class.
74 75 76 77 78 79 80 81 82 83 84 85
    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
86
    The action that is visible to the user is the one returned by Command::action().
87
    If you provide yourself a user visible representation of your action you need
con's avatar
con committed
88
    to use Command::action() for this.
89 90 91
    When this action is invoked by the user,
    the signal is forwarded to the registered action that is valid for the current context.

92 93 94
    \section1 Registering Actions

    To register a globally active action "My Action"
95 96
    put the following in your plugin's IPlugin::initialize method:
    \code
97
        Core::ActionManager *am = Core::ICore::instance()->actionManager();
98
        QAction *myAction = new QAction(tr("My Action"), this);
con's avatar
con committed
99
        Core::Command *cmd = am->registerAction(myAction,
100
                                                 "myplugin.myaction",
101
                                                 Core::Context(C_GLOBAL));
102 103 104 105 106 107
        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
108
    from Command::action() to it:
109 110 111 112 113 114 115 116
    \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
117
    registerAction methods, get the action container for a specific ID (like specified in
118
    the Core::Constants namespace) with a call of
con's avatar
con committed
119 120
    actionContainer(const QString&) and add your command to this container.

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

126
    \section1 Important Guidelines:
con's avatar
con committed
127 128
    \list
    \o Always register your actions and shortcuts!
129 130
    \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
131
       in the keyboard settings dialog from the beginning.
132 133 134
    \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
135 136 137
    \o Use this class to add actions to the applications menus
    \endlist

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

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

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

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

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

/*!
con's avatar
con committed
165
    \fn Command *ActionManager::registerAction(QAction *action, const QString &id, const Context &context)
166 167 168 169 170 171 172
    \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
173 174 175
*/

/*!
con's avatar
con committed
176
    \fn Command *ActionManager::registerShortcut(QShortcut *shortcut, const QString &id, const Context &context)
177 178 179 180 181 182 183
    \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
184 185 186
*/

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

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

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

199 200 201
    \sa ActionManager::createMenu()
    \sa ActionManager::createMenuBar()
*/
202 203 204 205 206 207 208 209 210 211 212

/*!
    \fn Command *ActionManager::unregisterAction(QAction *action, const QString &id)
    \brief Removes the knowledge about an \a action under the specified string \a id.

    Usually you do not need to unregister actions. The only valid use case for unregistering
    actions, is for actions that represent user definable actions, like for the custom Locator
    filters. If the user removes such an action, it also has to be unregistered from the action manager,
    to make it disappear from shortcut settings etc.
*/

con's avatar
con committed
213
/*!
214 215
    \fn ActionManager::ActionManager(QObject *parent)
    \internal
con's avatar
con committed
216 217
*/
/*!
218 219
    \fn ActionManager::~ActionManager()
    \internal
con's avatar
con committed
220 221 222 223 224
*/

using namespace Core;
using namespace Core::Internal;

225
ActionManagerPrivate* ActionManagerPrivate::m_instance = 0;
con's avatar
con committed
226 227

/*!
228 229 230
    \class ActionManagerPrivate
    \inheaderfile actionmanager_p.h
    \internal
con's avatar
con committed
231 232
*/

233 234
ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
  : ActionManager(mainWnd),
con's avatar
con committed
235 236
    m_mainWnd(mainWnd)
{
237
    UniqueIDManager *uidmgr = UniqueIDManager::instance();
con's avatar
con committed
238 239 240 241 242 243 244
    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;

}

245
ActionManagerPrivate::~ActionManagerPrivate()
con's avatar
con committed
246 247 248 249 250
{
    qDeleteAll(m_idCmdMap.values());
    qDeleteAll(m_idContainerMap.values());
}

251
ActionManagerPrivate *ActionManagerPrivate::instance()
con's avatar
con committed
252 253 254 255
{
    return m_instance;
}

256
QList<int> ActionManagerPrivate::defaultGroups() const
con's avatar
con committed
257 258 259 260
{
    return m_defaultGroups;
}

261
QList<Command *> ActionManagerPrivate::commands() const
con's avatar
con committed
262
{
263 264
    // transform list of CommandPrivate into list of Command
    QList<Command *> result;
hjk's avatar
hjk committed
265
    foreach (Command *cmd, m_idCmdMap.values())
266 267
        result << cmd;
    return result;
con's avatar
con committed
268 269
}

270
bool ActionManagerPrivate::hasContext(int context) const
con's avatar
con committed
271
{
hjk's avatar
hjk committed
272
    return m_context.contains(context);
con's avatar
con committed
273 274
}

275 276 277 278 279 280 281 282 283
QDebug operator<<(QDebug in, const Context &context)
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
    in << "CONTEXT: ";
    foreach (int c, context)
        in << "   " << c << uidm->stringForUniqueIdentifier(c);
    return in;
}

284
void ActionManagerPrivate::setContext(const Context &context)
con's avatar
con committed
285 286 287 288 289 290 291 292 293 294
{
    // 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);
}

295
bool ActionManagerPrivate::hasContext(const Context &context) const
con's avatar
con committed
296
{
hjk's avatar
hjk committed
297 298
    for (int i = 0; i < m_context.size(); ++i) {
        if (context.contains(m_context.at(i)))
con's avatar
con committed
299 300 301 302 303
            return true;
    }
    return false;
}

hjk's avatar
hjk committed
304
ActionContainer *ActionManagerPrivate::createMenu(const Id &id)
con's avatar
con committed
305
{
306
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    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
322
ActionContainer *ActionManagerPrivate::createMenuBar(const Id &id)
con's avatar
con committed
323
{
324
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
    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
340
Command *ActionManagerPrivate::registerAction(QAction *action, const Id &id, const Context &context)
con's avatar
con committed
341
{
342
    Action *a = 0;
con's avatar
con committed
343
    Command *c = registerOverridableAction(action, id, false);
344
    a = static_cast<Action *>(c);
con's avatar
con committed
345 346
    if (a)
        a->addOverrideAction(action, context);
347
    emit commandListChanged();
con's avatar
con committed
348 349 350
    return a;
}

hjk's avatar
hjk committed
351
Command *ActionManagerPrivate::registerOverridableAction(QAction *action, const Id &id, bool checkUnique)
con's avatar
con committed
352
{
353
    Action *a = 0;
354
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
355
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
356
        a = qobject_cast<Action *>(c);
con's avatar
con committed
357
        if (!a) {
con's avatar
con committed
358 359 360
            qWarning() << "registerAction: id" << id << "is registered with a different command type.";
            return c;
        }
con's avatar
con committed
361
    } else {
362
        a = new Action(uid);
con's avatar
con committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
        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
381
#ifdef Q_WS_MAC
con's avatar
con committed
382
        baseAction->setIconVisibleInMenu(false);
383 384
#else
        baseAction->setIconVisibleInMenu(action->isIconVisibleInMenu());
con's avatar
con committed
385 386 387 388 389 390 391 392 393
#endif
        a->setAction(baseAction);
        m_mainWnd->addAction(baseAction);
    } else  if (checkUnique) {
        qWarning() << "registerOverridableAction: id" << id << "is already registered.";
    }
    return a;
}

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
void ActionManagerPrivate::unregisterAction(QAction *action, const Id &id)
{
    Action *a = 0;
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
    CommandPrivate *c = m_idCmdMap.value(uid, 0);
    QTC_ASSERT(c, return);
    a = qobject_cast<Action *>(c);
    if (!a) {
        qWarning() << "registerAction: id" << id << "is registered with a different command type.";
        return;
    }
    a->removeOverrideAction(action);
    if (a->isEmpty()) {
        // clean up
        m_mainWnd->removeAction(a->action());
        delete a->action();
        m_idCmdMap.remove(uid);
        delete a;
    }
    emit commandListChanged();
}

hjk's avatar
hjk committed
416
Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const Id &id, const Context &context)
con's avatar
con committed
417 418
{
    Shortcut *sc = 0;
419
    int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
420
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
con's avatar
con committed
421 422
        sc = qobject_cast<Shortcut *>(c);
        if (!sc) {
con's avatar
con committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
            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
442
    if (context.isEmpty())
443
        sc->setContext(Context(0));
con's avatar
con committed
444 445 446
    else
        sc->setContext(context);

447
    emit commandListChanged();
con's avatar
con committed
448 449 450
    return sc;
}

hjk's avatar
hjk committed
451
Command *ActionManagerPrivate::command(const Id &id) const
con's avatar
con committed
452
{
453
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
454 455 456
    const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
    if (it == m_idCmdMap.constEnd()) {
        if (warnAboutFindFailures)
457
            qWarning() << "ActionManagerPrivate::command(): failed to find :" << id << '/' << uid;
con's avatar
con committed
458 459 460 461 462
        return 0;
    }
    return it.value();
}

hjk's avatar
hjk committed
463
ActionContainer *ActionManagerPrivate::actionContainer(const Id &id) const
con's avatar
con committed
464
{
465 466 467
    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
468
        if (warnAboutFindFailures)
469
            qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << id << '/' << uid;
con's avatar
con committed
470 471 472 473 474
        return 0;
    }
    return it.value();
}

con's avatar
con committed
475
Command *ActionManagerPrivate::command(int uid) const
con's avatar
con committed
476 477 478 479
{
    const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
    if (it == m_idCmdMap.constEnd()) {
        if (warnAboutFindFailures)
480
            qWarning() << "ActionManagerPrivate::command(): failed to find :" <<  UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << '/' << uid;
con's avatar
con committed
481 482 483 484 485
        return 0;
    }
    return it.value();
}

486
ActionContainer *ActionManagerPrivate::actionContainer(int uid) const
con's avatar
con committed
487 488 489 490
{
    const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
    if (it == m_idContainerMap.constEnd()) {
        if (warnAboutFindFailures)
491
            qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << uid;
con's avatar
con committed
492 493 494 495
        return 0;
    }
    return it.value();
}
hjk's avatar
hjk committed
496

con's avatar
con committed
497 498 499 500
static const char *settingsGroup = "KeyBindings";
static const char *idKey = "ID";
static const char *sequenceKey = "Keysequence";

501
void ActionManagerPrivate::initialize()
con's avatar
con committed
502
{
503
    QSettings *settings = Core::ICore::instance()->settings();
con's avatar
con committed
504 505 506 507 508
    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());
509
        const int id = UniqueIDManager::instance()->uniqueIdentifier(sid);
con's avatar
con committed
510

con's avatar
con committed
511
        Command *cmd = command(id);
con's avatar
con committed
512 513 514 515 516 517
        if (cmd)
            cmd->setKeySequence(key);
    }
    settings->endArray();
}

518
void ActionManagerPrivate::saveSettings(QSettings *settings)
con's avatar
con committed
519 520 521 522 523 524 525
{
    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
526
        CommandPrivate *cmd = j.value();
con's avatar
con committed
527 528
        QKeySequence key = cmd->keySequence();
        if (key != cmd->defaultKeySequence()) {
529
            const QString sid = UniqueIDManager::instance()->stringForUniqueIdentifier(id);
con's avatar
con committed
530 531 532 533 534 535 536 537 538
            settings->setArrayIndex(count);
            settings->setValue(QLatin1String(idKey), sid);
            settings->setValue(QLatin1String(sequenceKey), key.toString());
            count++;
        }
    }

    settings->endArray();
}