actionmanager.cpp 16.9 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 8
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
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 26
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
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 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).

    All actions that are registered with the same string id (but different context lists)
68
    are considered to be overloads of the same command, represented by an instance
con's avatar
con committed
69 70
    of the Command class.
    The action that is visible to the user is the one returned by Command::action().
71
    If you provide yourself a user visible representation of your action you need
con's avatar
con committed
72
    to use Command::action() for this.
73 74 75 76 77 78
    When this action is invoked by the user,
    the signal is forwarded to the registered action that is valid for the current context.

    So to register a globally active action "My Action"
    put the following in your plugin's IPlugin::initialize method:
    \code
79
        Core::ActionManager *am = Core::ICore::instance()->actionManager();
80
        QAction *myAction = new QAction(tr("My Action"), this);
con's avatar
con committed
81
        Core::Command *cmd = am->registerAction(myAction,
82 83 84 85 86 87 88 89
                                                 "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
90
    from Command::action() to it:
91 92 93 94 95 96 97 98
    \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
con's avatar
con committed
99
    registerAction methods, get the action container for a specific id (like specified in
100
    the Core::Constants namespace) with a call of
con's avatar
con committed
101 102
    actionContainer(const QString&) and add your command to this container.

103 104 105 106 107 108
    Following the example adding "My Action" to the "Tools" menu would be done by
    \code
        am->actionContainer(Core::M_TOOLS)->addAction(cmd);
    \endcode

    Important guidelines:
con's avatar
con committed
109 110
    \list
    \o Always register your actions and shortcuts!
111 112 113
    \o Register your actions and shortcuts during your plugin's IPlugin::initialize
       or IPlugin::extensionsInitialized methods, otherwise the shortcuts won't appear
       in the keyboard settings dialog from the beginning.
con's avatar
con committed
114 115 116 117 118 119
    \o When registering an action with cmd=registerAction(action, id, contexts) be sure to connect
       your own action connect(action, SIGNAL...) but make cmd->action() visible to the user, i.e.
       widget->addAction(cmd->action()).
    \o Use this class to add actions to the applications menus
    \endlist

120
    \sa Core::ICore
con's avatar
con committed
121
    \sa Core::Command
122
    \sa Core::ActionContainer
123
    \sa Core::IContext
con's avatar
con committed
124 125 126
*/

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

130
    Returns a new ActionContainer that you can use to get the QMenu instance
131
    or to add menu items to the menu. The ActionManager owns
132
    the returned ActionContainer.
133
    Add your menu to some other menu or a menu bar via the
134
    ActionManager::actionContainer and ActionContainer::addMenu methods.
con's avatar
con committed
135 136 137
*/

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

141
    Returns a new ActionContainer that you can use to get the QMenuBar instance
142
    or to add menus to the menu bar. The ActionManager owns
143
    the returned ActionContainer.
con's avatar
con committed
144 145 146
*/

/*!
con's avatar
con committed
147
    \fn Command *ActionManager::registerAction(QAction *action, const QString &id, const QList<int> &context)
148 149 150 151 152 153 154
    \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
155 156 157
*/

/*!
con's avatar
con committed
158
    \fn Command *ActionManager::registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context)
159 160 161 162 163 164 165
    \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
166 167 168
*/

/*!
con's avatar
con committed
169 170
    \fn Command *ActionManager::command(const QString &id) const
    \brief Returns the Command object that is known to the system
171 172 173
    under the given string \a id.

    \sa ActionManager::registerAction()
con's avatar
con committed
174 175 176
*/

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

181 182 183
    \sa ActionManager::createMenu()
    \sa ActionManager::createMenuBar()
*/
con's avatar
con committed
184
/*!
185 186
    \fn ActionManager::ActionManager(QObject *parent)
    \internal
con's avatar
con committed
187 188
*/
/*!
189 190
    \fn ActionManager::~ActionManager()
    \internal
con's avatar
con committed
191 192 193 194 195
*/

using namespace Core;
using namespace Core::Internal;

196
ActionManagerPrivate* ActionManagerPrivate::m_instance = 0;
con's avatar
con committed
197 198

/*!
199 200 201
    \class ActionManagerPrivate
    \inheaderfile actionmanager_p.h
    \internal
con's avatar
con committed
202 203
*/

204 205
ActionManagerPrivate::ActionManagerPrivate(MainWindow *mainWnd)
  : ActionManager(mainWnd),
con's avatar
con committed
206 207
    m_mainWnd(mainWnd)
{
208
    UniqueIDManager *uidmgr = UniqueIDManager::instance();
con's avatar
con committed
209 210 211 212 213 214 215
    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;

}

216
ActionManagerPrivate::~ActionManagerPrivate()
con's avatar
con committed
217 218 219 220 221
{
    qDeleteAll(m_idCmdMap.values());
    qDeleteAll(m_idContainerMap.values());
}

222
ActionManagerPrivate *ActionManagerPrivate::instance()
con's avatar
con committed
223 224 225 226
{
    return m_instance;
}

227
QList<int> ActionManagerPrivate::defaultGroups() const
con's avatar
con committed
228 229 230 231
{
    return m_defaultGroups;
}

con's avatar
con committed
232
QList<CommandPrivate *> ActionManagerPrivate::commands() const
con's avatar
con committed
233 234 235 236
{
    return m_idCmdMap.values();
}

237
QList<ActionContainerPrivate *> ActionManagerPrivate::containers() const
con's avatar
con committed
238 239 240 241
{
    return m_idContainerMap.values();
}

242
bool ActionManagerPrivate::hasContext(int context) const
con's avatar
con committed
243 244 245 246
{
    return m_context.contains(context);
}

247
void ActionManagerPrivate::setContext(const QList<int> &context)
con's avatar
con committed
248 249 250 251 252 253 254 255 256 257
{
    // 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();
258
    for (IdContainerMap::const_iterator it = m_idContainerMap.constBegin(); it != acend; ++it)
con's avatar
con committed
259 260 261
        it.value()->update();
}

262
bool ActionManagerPrivate::hasContext(QList<int> context) const
con's avatar
con committed
263 264 265 266 267 268 269 270
{
    for (int i=0; i<m_context.count(); ++i) {
        if (context.contains(m_context.at(i)))
            return true;
    }
    return false;
}

271
ActionContainer *ActionManagerPrivate::createMenu(const QString &id)
con's avatar
con committed
272
{
273
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    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;
}

289
ActionContainer *ActionManagerPrivate::createMenuBar(const QString &id)
con's avatar
con committed
290
{
291
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    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
307
Command *ActionManagerPrivate::registerAction(QAction *action, const QString &id, const QList<int> &context)
con's avatar
con committed
308 309
{
    OverrideableAction *a = 0;
con's avatar
con committed
310
    Command *c = registerOverridableAction(action, id, false);
con's avatar
con committed
311 312 313 314 315 316
    a = static_cast<OverrideableAction *>(c);
    if (a)
        a->addOverrideAction(action, context);
    return a;
}

con's avatar
con committed
317
Command *ActionManagerPrivate::registerOverridableAction(QAction *action, const QString &id, bool checkUnique)
con's avatar
con committed
318 319
{
    OverrideableAction *a = 0;
320
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
321
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
con's avatar
con committed
322
        if (c->type() != Command::CT_OverridableAction) {
con's avatar
con committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
            qWarning() << "registerAction: id" << id << "is registered with a different command type.";
            return c;
        }
        a = static_cast<OverrideableAction *>(c);
    }
    if (!a) {
        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->setObjectName(id);
        baseAction->setParent(m_mainWnd);
#ifdef Q_OS_MAC
        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
363
Command *ActionManagerPrivate::registerShortcut(QShortcut *shortcut, const QString &id, const QList<int> &context)
con's avatar
con committed
364 365
{
    Shortcut *sc = 0;
366
    int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
367
    if (CommandPrivate *c = m_idCmdMap.value(uid, 0)) {
con's avatar
con committed
368
        if (c->type() != Command::CT_Shortcut) {
con's avatar
con committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
            qWarning() << "registerShortcut: id" << id << "is registered with a different command type.";
            return c;
        }
        sc = static_cast<Shortcut *>(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
400
Command *ActionManagerPrivate::command(const QString &id) const
con's avatar
con committed
401
{
402
    const int uid = UniqueIDManager::instance()->uniqueIdentifier(id);
con's avatar
con committed
403 404 405
    const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
    if (it == m_idCmdMap.constEnd()) {
        if (warnAboutFindFailures)
406
            qWarning() << "ActionManagerPrivate::command(): failed to find :" << id << '/' << uid;
con's avatar
con committed
407 408 409 410 411
        return 0;
    }
    return it.value();
}

412
ActionContainer *ActionManagerPrivate::actionContainer(const QString &id) const
con's avatar
con committed
413
{
414 415 416
    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
417
        if (warnAboutFindFailures)
418
            qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << id << '/' << uid;
con's avatar
con committed
419 420 421 422 423
        return 0;
    }
    return it.value();
}

con's avatar
con committed
424
Command *ActionManagerPrivate::command(int uid) const
con's avatar
con committed
425 426 427 428
{
    const IdCmdMap::const_iterator it = m_idCmdMap.constFind(uid);
    if (it == m_idCmdMap.constEnd()) {
        if (warnAboutFindFailures)
429
            qWarning() << "ActionManagerPrivate::command(): failed to find :" <<  UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << '/' << uid;
con's avatar
con committed
430 431 432 433 434
        return 0;
    }
    return it.value();
}

435
ActionContainer *ActionManagerPrivate::actionContainer(int uid) const
con's avatar
con committed
436 437 438 439
{
    const IdContainerMap::const_iterator it = m_idContainerMap.constFind(uid);
    if (it == m_idContainerMap.constEnd()) {
        if (warnAboutFindFailures)
440
            qWarning() << "ActionManagerPrivate::actionContainer(): failed to find :" << UniqueIDManager::instance()->stringForUniqueIdentifier(uid) << uid;
con's avatar
con committed
441 442 443 444
        return 0;
    }
    return it.value();
}
hjk's avatar
hjk committed
445

con's avatar
con committed
446 447 448 449
static const char *settingsGroup = "KeyBindings";
static const char *idKey = "ID";
static const char *sequenceKey = "Keysequence";

450
void ActionManagerPrivate::initialize()
con's avatar
con committed
451 452 453 454 455 456 457
{
    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());
458
        const int id = UniqueIDManager::instance()->uniqueIdentifier(sid);
con's avatar
con committed
459

con's avatar
con committed
460
        Command *cmd = command(id);
con's avatar
con committed
461 462 463 464 465 466
        if (cmd)
            cmd->setKeySequence(key);
    }
    settings->endArray();
}

467
void ActionManagerPrivate::saveSettings(QSettings *settings)
con's avatar
con committed
468 469 470 471 472 473 474
{
    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
475
        CommandPrivate *cmd = j.value();
con's avatar
con committed
476 477
        QKeySequence key = cmd->keySequence();
        if (key != cmd->defaultKeySequence()) {
478
            const QString sid = UniqueIDManager::instance()->stringForUniqueIdentifier(id);
con's avatar
con committed
479 480 481 482 483 484 485 486 487
            settings->setArrayIndex(count);
            settings->setValue(QLatin1String(idKey), sid);
            settings->setValue(QLatin1String(sequenceKey), key.toString());
            count++;
        }
    }

    settings->endArray();
}