modemanager.cpp 10.4 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) 2012 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
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
#include "modemanager.h"
hjk's avatar
hjk committed
34

con's avatar
con committed
35 36
#include "fancytabwidget.h"
#include "fancyactionbar.h"
37
#include "icore.h"
con's avatar
con committed
38 39 40
#include "mainwindow.h"

#include <aggregation/aggregate.h>
hjk's avatar
hjk committed
41

42
#include <coreplugin/actionmanager/actionmanager.h>
con's avatar
con committed
43
#include <coreplugin/actionmanager/command.h>
con's avatar
con committed
44 45
#include <coreplugin/coreconstants.h>
#include <coreplugin/imode.h>
46
#include <coreplugin/id.h>
con's avatar
con committed
47

48 49
#include <extensionsystem/pluginmanager.h>

hjk's avatar
hjk committed
50 51
#include <utils/qtcassert.h>

52 53 54 55
#include <QDebug>
#include <QList>
#include <QMap>
#include <QVector>
56

57 58 59
#include <QSignalMapper>
#include <QShortcut>
#include <QAction>
con's avatar
con committed
60

61 62
namespace Core {

63 64 65 66 67 68 69 70 71
/*!
    \class Core::ModeManager

    The mode manager handles everything related to the instances of IMode
    that were added to the plugin manager's object pool as well as their
    buttons and the tool bar with the round buttons in the lower left
    corner of Qt Creator.
*/

72 73
struct ModeManagerPrivate
{
74 75 76
    Internal::MainWindow *m_mainWindow;
    Internal::FancyTabWidget *m_modeStack;
    Internal::FancyActionBar *m_actionBar;
77
    QMap<QAction*, int> m_actions;
78 79 80
    QVector<IMode*> m_modes;
    QVector<Command*> m_modeShortcuts;
    QSignalMapper *m_signalMapper;
81
    Context m_addedContexts;
82 83 84
    int m_oldCurrent;
};

85 86
static ModeManagerPrivate *d;
static ModeManager *m_instance = 0;
87

88
static int indexOf(Id id)
89
{
90 91 92 93
    for (int i = 0; i < d->m_modes.count(); ++i) {
        if (d->m_modes.at(i)->id() == id)
            return i;
    }
94
    qDebug() << "Warning, no such mode:" << id.toString();
95
    return -1;
96
}
97

98
ModeManager::ModeManager(Internal::MainWindow *mainWindow,
99
                         Internal::FancyTabWidget *modeStack)
con's avatar
con committed
100
{
101 102 103 104 105 106
    m_instance = this;
    d = new ModeManagerPrivate();
    d->m_mainWindow = mainWindow;
    d->m_modeStack = modeStack;
    d->m_signalMapper = new QSignalMapper(this);
    d->m_oldCurrent = -1;
107 108
    d->m_actionBar = new Internal::FancyActionBar(modeStack);
    d->m_modeStack->addCornerWidget(d->m_actionBar);
con's avatar
con committed
109

110 111
    connect(d->m_modeStack, SIGNAL(currentAboutToShow(int)), SLOT(currentTabAboutToChange(int)));
    connect(d->m_modeStack, SIGNAL(currentChanged(int)), SLOT(currentTabChanged(int)));
112
    connect(d->m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotActivateMode(int)));
con's avatar
con committed
113 114 115 116 117
}

void ModeManager::init()
{
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)),
118
                     m_instance, SLOT(objectAdded(QObject*)));
con's avatar
con committed
119
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)),
120
                     m_instance, SLOT(aboutToRemoveObject(QObject*)));
con's avatar
con committed
121 122
}

123 124 125
ModeManager::~ModeManager()
{
    delete d;
126 127
    d = 0;
    m_instance = 0;
128 129
}

con's avatar
con committed
130 131 132
void ModeManager::addWidget(QWidget *widget)
{
    // We want the actionbar to stay on the bottom
133
    // so d->m_modeStack->cornerWidgetCount() -1 inserts it at the position immediately above
con's avatar
con committed
134
    // the actionbar
135
    d->m_modeStack->insertCornerWidget(d->m_modeStack->cornerWidgetCount() -1, widget);
con's avatar
con committed
136 137
}

138
IMode *ModeManager::currentMode()
con's avatar
con committed
139
{
140
    int currentIndex = d->m_modeStack->currentIndex();
con's avatar
con committed
141 142
    if (currentIndex < 0)
        return 0;
143
    return d->m_modes.at(currentIndex);
con's avatar
con committed
144 145
}

146
IMode *ModeManager::mode(Id id)
con's avatar
con committed
147 148 149
{
    const int index = indexOf(id);
    if (index >= 0)
150
        return d->m_modes.at(index);
con's avatar
con committed
151 152 153
    return 0;
}

154
void ModeManager::activateModeType(Id type)
155
{
156 157
    if (currentMode() && currentMode()->type() == type)
        return;
158 159 160 161 162 163 164 165 166 167 168
    int index = -1;
    for (int i = 0; i < d->m_modes.count(); ++i) {
        if (d->m_modes.at(i)->type() == type) {
            index = i;
            break;
        }
    }
    if (index != -1)
        d->m_modeStack->setCurrentIndex(index);
}

169
void ModeManager::slotActivateMode(int id)
170
{
171
    m_instance->activateMode(Id::fromUniqueIdentifier(id));
172 173
}

174
void ModeManager::activateMode(Id id)
con's avatar
con committed
175 176 177
{
    const int index = indexOf(id);
    if (index >= 0)
178
        d->m_modeStack->setCurrentIndex(index);
con's avatar
con committed
179 180 181 182 183 184 185 186
}

void ModeManager::objectAdded(QObject *obj)
{
    IMode *mode = Aggregation::query<IMode>(obj);
    if (!mode)
        return;

187
    d->m_mainWindow->addContextObject(mode);
con's avatar
con committed
188 189 190

    // Count the number of modes with a higher priority
    int index = 0;
191
    foreach (const IMode *m, d->m_modes)
con's avatar
con committed
192 193 194
        if (m->priority() > mode->priority())
            ++index;

195 196 197
    d->m_modes.insert(index, mode);
    d->m_modeStack->insertTab(index, mode->widget(), mode->icon(), mode->displayName());
    d->m_modeStack->setTabEnabled(index, mode->isEnabled());
con's avatar
con committed
198 199

    // Register mode shortcut
200
    ActionManager *am = d->m_mainWindow->actionManager();
201
    const Id shortcutId(QLatin1String("QtCreator.Mode.") + mode->id().toString());
202
    QShortcut *shortcut = new QShortcut(d->m_mainWindow);
203
    shortcut->setWhatsThis(tr("Switch to <b>%1</b> mode").arg(mode->displayName()));
204
    Command *cmd = am->registerShortcut(shortcut, shortcutId, Context(Constants::C_GLOBAL));
con's avatar
con committed
205

206
    d->m_modeShortcuts.insert(index, cmd);
con's avatar
con committed
207
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updateModeToolTip()));
208 209
    for (int i = 0; i < d->m_modeShortcuts.size(); ++i) {
        Command *currentCmd = d->m_modeShortcuts.at(i);
210 211 212
        // we need this hack with currentlyHasDefaultSequence
        // because we call setDefaultShortcut multiple times on the same cmd
        // and still expect the current shortcut to change with it
con's avatar
con committed
213 214
        bool currentlyHasDefaultSequence = (currentCmd->keySequence()
                                            == currentCmd->defaultKeySequence());
215
#ifdef Q_OS_MAC
216
        currentCmd->setDefaultKeySequence(QKeySequence(QString::fromLatin1("Meta+%1").arg(i+1)));
con's avatar
con committed
217
#else
218
        currentCmd->setDefaultKeySequence(QKeySequence(QString::fromLatin1("Ctrl+%1").arg(i+1)));
con's avatar
con committed
219
#endif
con's avatar
con committed
220 221
        if (currentlyHasDefaultSequence)
            currentCmd->setKeySequence(currentCmd->defaultKeySequence());
con's avatar
con committed
222 223
    }

224
    d->m_signalMapper->setMapping(shortcut, mode->id().uniqueIdentifier());
225
    connect(shortcut, SIGNAL(activated()), d->m_signalMapper, SLOT(map()));
con's avatar
con committed
226 227
    connect(mode, SIGNAL(enabledStateChanged(bool)),
            this, SLOT(enabledStateChanged()));
con's avatar
con committed
228 229 230 231
}

void ModeManager::updateModeToolTip()
{
con's avatar
con committed
232
    Command *cmd = qobject_cast<Command *>(sender());
con's avatar
con committed
233
    if (cmd) {
234
        int index = d->m_modeShortcuts.indexOf(cmd);
con's avatar
con committed
235
        if (index != -1)
236
            d->m_modeStack->setTabToolTip(index, cmd->stringWithAppendedShortcut(cmd->shortcut()->whatsThis()));
con's avatar
con committed
237 238 239
    }
}

con's avatar
con committed
240 241 242 243
void ModeManager::enabledStateChanged()
{
    IMode *mode = qobject_cast<IMode *>(sender());
    QTC_ASSERT(mode, return);
244
    int index = d->m_modes.indexOf(mode);
con's avatar
con committed
245
    QTC_ASSERT(index >= 0, return);
246
    d->m_modeStack->setTabEnabled(index, mode->isEnabled());
247 248 249 250 251 252 253 254 255 256 257 258

    // Make sure we leave any disabled mode to prevent possible crashes:
    if (mode == currentMode() && !mode->isEnabled()) {
        // This assumes that there is always at least one enabled mode.
        for (int i = 0; i < d->m_modes.count(); ++i) {
            if (d->m_modes.at(i) != mode &&
                d->m_modes.at(i)->isEnabled()) {
                activateMode(d->m_modes.at(i)->id());
                break;
            }
        }
    }
con's avatar
con committed
259 260
}

con's avatar
con committed
261 262 263 264 265 266
void ModeManager::aboutToRemoveObject(QObject *obj)
{
    IMode *mode = Aggregation::query<IMode>(obj);
    if (!mode)
        return;

267 268 269 270
    const int index = d->m_modes.indexOf(mode);
    d->m_modes.remove(index);
    d->m_modeShortcuts.remove(index);
    d->m_modeStack->removeTab(index);
con's avatar
con committed
271

272
    d->m_mainWindow->removeContextObject(mode);
con's avatar
con committed
273 274
}

275
void ModeManager::addAction(QAction *action, int priority)
con's avatar
con committed
276
{
277
    d->m_actions.insert(action, priority);
con's avatar
con committed
278 279 280

    // Count the number of commands with a higher priority
    int index = 0;
281
    foreach (int p, d->m_actions) {
con's avatar
con committed
282 283
        if (p > priority)
            ++index;
con's avatar
con committed
284
    }
con's avatar
con committed
285

286
    d->m_actionBar->insertAction(index, action);
con's avatar
con committed
287 288
}

289 290
void ModeManager::addProjectSelector(QAction *action)
{
291
    d->m_actionBar->addProjectSelector(action);
292
    d->m_actions.insert(0, INT_MAX);
293 294
}

con's avatar
con committed
295 296 297
void ModeManager::currentTabAboutToChange(int index)
{
    if (index >= 0) {
298
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
299 300 301 302 303 304 305 306 307
        if (mode)
            emit currentModeAboutToChange(mode);
    }
}

void ModeManager::currentTabChanged(int index)
{
    // Tab index changes to -1 when there is no tab left.
    if (index >= 0) {
308
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
309 310 311 312

        // FIXME: This hardcoded context update is required for the Debug and Edit modes, since
        // they use the editor widget, which is already a context widget so the main window won't
        // go further up the parent tree to find the mode context.
hjk's avatar
hjk committed
313
        ICore::updateAdditionalContexts(d->m_addedContexts, mode->context());
314
        d->m_addedContexts = mode->context();
315

316
        IMode *oldMode = 0;
317 318 319
        if (d->m_oldCurrent >= 0)
            oldMode = d->m_modes.at(d->m_oldCurrent);
        d->m_oldCurrent = index;
320
        emit currentModeChanged(mode, oldMode);
con's avatar
con committed
321 322 323 324 325 326
    }
}

void ModeManager::setFocusToCurrentMode()
{
    IMode *mode = currentMode();
hjk's avatar
hjk committed
327
    QTC_ASSERT(mode, return);
con's avatar
con committed
328 329 330 331 332 333 334 335 336
    QWidget *widget = mode->widget();
    if (widget) {
        QWidget *focusWidget = widget->focusWidget();
        if (focusWidget)
            focusWidget->setFocus();
        else
            widget->setFocus();
    }
}
337

338 339 340 341 342
void ModeManager::setModeBarHidden(bool hidden)
{
    d->m_modeStack->setSelectionWidgetHidden(hidden);
}

343 344
ModeManager *ModeManager::instance()
{
345
    return m_instance;
346 347 348
}

} // namespace Core