actionmanager.cpp 17.7 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 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
26
** contact the sales department at http://www.qtsoftware.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 37 38 39 40 41 42 43 44 45 46 47 48 49
#include "uniqueidmanager.h"

#include <coreplugin/coreconstants.h>

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

namespace {
    enum { warnAboutFindFailures = 0 };
}

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

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

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

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

67 68 69
    \section1 Contexts

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

90 91 92
    \section1 Registering Actions

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

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

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

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

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

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

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

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

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

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

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

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

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

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

using namespace Core;
using namespace Core::Internal;

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

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

220 221
ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
  : ActionManager(mainWnd),
con's avatar
con committed
222 223
    m_mainWnd(mainWnd)
{
224
    UniqueIDManager *uidmgr = UniqueIDManager::instance();
con's avatar
con committed
225 226 227 228 229 230 231
    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;

}

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

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

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

con's avatar
con committed
248
QList<CommandPrivate *> ActionManagerPrivate::commands() const
con's avatar
con committed
249 250 251 252
{
    return m_idCmdMap.values();
}

253
QList<ActionContainerPrivate *> ActionManagerPrivate::containers() const
con's avatar
con committed
254 255 256 257
{
    return m_idContainerMap.values();
}

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

263
void ActionManagerPrivate::setContext(const QList<int> &context)
con's avatar
con committed
264 265 266 267 268 269 270 271 272 273
{
    // 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);

    const IdContainerMap::const_iterator acend = m_idContainerMap.constEnd();
274
    for (IdContainerMap::const_iterator it = m_idContainerMap.constBegin(); it != acend; ++it)
con's avatar
con committed
275 276 277
        it.value()->update();
}

278
bool ActionManagerPrivate::hasContext(QList<int> context) const
con's avatar
con committed
279 280 281 282 283 284 285 286
{
    for (int i=0; i<m_context.count(); ++i) {
        if (context.contains(m_context.at(i)))
            return true;
    }
    return false;
}

287
ActionContainer *ActionManagerPrivate::createMenu(const QString &id)
con's avatar
con committed
288
{
289
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
    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;
}

305
ActionContainer *ActionManagerPrivate::createMenuBar(const QString &id)
con's avatar
con committed
306
{
307
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
    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;
}

con's avatar
con committed
323
Command *ActionManagerPrivate::registerAction(QAction *action, const QString &id, const QList<int> &context)
con's avatar
con committed
324 325
{
    OverrideableAction *a = 0;
con's avatar
con committed
326
    Command *c = registerOverridableAction(action, id, false);
con's avatar
con committed
327 328 329 330 331 332
    a = static_cast<OverrideableAction *>(c);
    if (a)
        a->addOverrideAction(action, context);
    return a;
}

con's avatar
con committed
333
Command *ActionManagerPrivate::registerOverridableAction(QAction *action, const QString &id, bool checkUnique)
con's avatar
con committed
334 335
{
    OverrideableAction *a = 0;
336
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
337
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
con's avatar
con committed
338 339
        a = qobject_cast<OverrideableAction *>(c);
        if (!a) {
con's avatar
con committed
340 341 342
            qWarning() << "registerAction: id" << id << "is registered with a different command type.";
            return c;
        }
con's avatar
con committed
343
    } else {
con's avatar
con committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
        a = new OverrideableAction(uid);
        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
363
#ifdef Q_WS_MAC
con's avatar
con committed
364 365 366 367 368 369 370 371 372 373 374 375 376
        baseAction->setIconVisibleInMenu(false);
#endif
        a->setAction(baseAction);
        m_mainWnd->addAction(baseAction);
        a->setKeySequence(a->keySequence());
        a->setDefaultKeySequence(QKeySequence());
    } else  if (checkUnique) {
        qWarning() << "registerOverridableAction: id" << id << "is already registered.";
    }

    return a;
}

con's avatar
con committed
377
Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context)
con's avatar
con committed
378 379
{
    Shortcut *sc = 0;
380
    int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
381
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
con's avatar
con committed
382 383
        sc = qobject_cast<Shortcut *>(c);
        if (!sc) {
con's avatar
con committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
            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);

    if (context.isEmpty())
        sc->setContext(QList<int>() << 0);
    else
        sc->setContext(context);

    sc->setKeySequence(shortcut->key());
    sc->setDefaultKeySequence(QKeySequence());

    return sc;
}

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

426
ActionContainer *ActionManagerPrivate::actionContainer(const QString &id) const
con's avatar
con committed
427
{
428 429 430
    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
431
        if (warnAboutFindFailures)
432
            qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << id << '/' << uid;
con's avatar
con committed
433 434 435 436 437
        return 0;
    }
    return it.value();
}

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

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

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

464
void ActionManagerPrivate::initialize()
con's avatar
con committed
465 466 467 468 469 470 471
{
    QSettings *settings = m_mainWnd->settings();
    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());
472
        const int id = UniqueIDManager::instance()->uniqueIdentifier(sid);
con's avatar
con committed
473

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

481
void ActionManagerPrivate::saveSettings(QSettings *settings)
con's avatar
con committed
482 483 484 485 486 487 488
{
    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
489
        CommandPrivate *cmd = j.value();
con's avatar
con committed
490 491
        QKeySequence key = cmd->keySequence();
        if (key != cmd->defaultKeySequence()) {
492
            const QString sid = UniqueIDManager::instance()->stringForUniqueIdentifier(id);
con's avatar
con committed
493 494 495 496 497 498 499 500 501
            settings->setArrayIndex(count);
            settings->setValue(QLatin1String(idKey), sid);
            settings->setValue(QLatin1String(sequenceKey), key.toString());
            count++;
        }
    }

    settings->endArray();
}