modemanager.cpp 9.69 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

con's avatar
con committed
31
#include "modemanager.h"
hjk's avatar
hjk committed
32

con's avatar
con committed
33 34
#include "fancytabwidget.h"
#include "fancyactionbar.h"
35
#include "icore.h"
con's avatar
con committed
36 37
#include "mainwindow.h"

38
#include <coreplugin/actionmanager/actionmanager.h>
39
#include <coreplugin/actionmanager/command.h>
con's avatar
con committed
40 41 42
#include <coreplugin/coreconstants.h>
#include <coreplugin/imode.h>

43 44
#include <extensionsystem/pluginmanager.h>

hjk's avatar
hjk committed
45 46
#include <utils/qtcassert.h>

47
#include <QAction>
48 49 50
#include <QDebug>
#include <QMap>
#include <QVector>
51 52 53

namespace Core {

54 55 56 57 58 59 60 61 62
/*!
    \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.
*/

63 64
struct ModeManagerPrivate
{
65 66 67
    Internal::MainWindow *m_mainWindow;
    Internal::FancyTabWidget *m_modeStack;
    Internal::FancyActionBar *m_actionBar;
68
    QMap<QAction*, int> m_actions;
69
    QVector<IMode*> m_modes;
70
    QVector<Command*> m_modeCommands;
71
    Context m_addedContexts;
72
    int m_oldCurrent;
73
    bool m_modeSelectorVisible;
74 75
};

76 77
static ModeManagerPrivate *d;
static ModeManager *m_instance = 0;
78

79
static int indexOf(Id id)
80
{
81 82 83 84
    for (int i = 0; i < d->m_modes.count(); ++i) {
        if (d->m_modes.at(i)->id() == id)
            return i;
    }
85
    qDebug() << "Warning, no such mode:" << id.toString();
86
    return -1;
87
}
88

89
ModeManager::ModeManager(Internal::MainWindow *mainWindow,
90
                         Internal::FancyTabWidget *modeStack)
con's avatar
con committed
91
{
92 93 94 95 96
    m_instance = this;
    d = new ModeManagerPrivate();
    d->m_mainWindow = mainWindow;
    d->m_modeStack = modeStack;
    d->m_oldCurrent = -1;
97 98
    d->m_actionBar = new Internal::FancyActionBar(modeStack);
    d->m_modeStack->addCornerWidget(d->m_actionBar);
99 100
    d->m_modeSelectorVisible = true;
    d->m_modeStack->setSelectionWidgetVisible(d->m_modeSelectorVisible);
con's avatar
con committed
101

102 103
    connect(d->m_modeStack, SIGNAL(currentAboutToShow(int)), SLOT(currentTabAboutToChange(int)));
    connect(d->m_modeStack, SIGNAL(currentChanged(int)), SLOT(currentTabChanged(int)));
con's avatar
con committed
104 105 106 107 108
}

void ModeManager::init()
{
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)),
109
                     m_instance, SLOT(objectAdded(QObject*)));
con's avatar
con committed
110
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)),
111
                     m_instance, SLOT(aboutToRemoveObject(QObject*)));
con's avatar
con committed
112 113
}

114 115 116
ModeManager::~ModeManager()
{
    delete d;
117 118
    d = 0;
    m_instance = 0;
119 120
}

121
IMode *ModeManager::currentMode()
con's avatar
con committed
122
{
123
    int currentIndex = d->m_modeStack->currentIndex();
con's avatar
con committed
124 125
    if (currentIndex < 0)
        return 0;
126
    return d->m_modes.at(currentIndex);
con's avatar
con committed
127 128
}

129
IMode *ModeManager::mode(Id id)
con's avatar
con committed
130 131 132
{
    const int index = indexOf(id);
    if (index >= 0)
133
        return d->m_modes.at(index);
con's avatar
con committed
134 135 136
    return 0;
}

137
void ModeManager::activateMode(Id id)
con's avatar
con committed
138 139 140
{
    const int index = indexOf(id);
    if (index >= 0)
141
        d->m_modeStack->setCurrentIndex(index);
con's avatar
con committed
142 143 144 145
}

void ModeManager::objectAdded(QObject *obj)
{
146
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
147 148 149
    if (!mode)
        return;

150
    d->m_mainWindow->addContextObject(mode);
con's avatar
con committed
151 152 153

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

158 159 160
    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
161 162

    // Register mode shortcut
163 164
    const Id actionId = mode->id().withPrefix("QtCreator.Mode.");
    QAction *action = new QAction(tr("Switch to <b>%1</b> mode").arg(mode->displayName()), this);
165
    Command *cmd = ActionManager::registerAction(action, actionId);
con's avatar
con committed
166

167
    d->m_modeCommands.insert(index, cmd);
168
    connect(cmd, SIGNAL(keySequenceChanged()), m_instance, SLOT(updateModeToolTip()));
169 170
    for (int i = 0; i < d->m_modeCommands.size(); ++i) {
        Command *currentCmd = d->m_modeCommands.at(i);
171 172 173
        // 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
174 175
        bool currentlyHasDefaultSequence = (currentCmd->keySequence()
                                            == currentCmd->defaultKeySequence());
176 177
        currentCmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? QString::fromLatin1("Meta+%1").arg(i+1)
                                                                       : QString::fromLatin1("Ctrl+%1").arg(i+1)));
con's avatar
con committed
178 179
        if (currentlyHasDefaultSequence)
            currentCmd->setKeySequence(currentCmd->defaultKeySequence());
con's avatar
con committed
180 181
    }

182 183 184 185 186 187
    Id id = mode->id();
    connect(action, &QAction::triggered, [id] {
        m_instance->activateMode(id);
        ICore::raiseWindow(d->m_modeStack);
    });

con's avatar
con committed
188
    connect(mode, SIGNAL(enabledStateChanged(bool)),
189
            m_instance, SLOT(enabledStateChanged()));
con's avatar
con committed
190 191 192 193
}

void ModeManager::updateModeToolTip()
{
con's avatar
con committed
194
    Command *cmd = qobject_cast<Command *>(sender());
con's avatar
con committed
195
    if (cmd) {
196
        int index = d->m_modeCommands.indexOf(cmd);
con's avatar
con committed
197
        if (index != -1)
198
            d->m_modeStack->setTabToolTip(index, cmd->action()->toolTip());
con's avatar
con committed
199 200 201
    }
}

con's avatar
con committed
202 203 204 205
void ModeManager::enabledStateChanged()
{
    IMode *mode = qobject_cast<IMode *>(sender());
    QTC_ASSERT(mode, return);
206
    int index = d->m_modes.indexOf(mode);
con's avatar
con committed
207
    QTC_ASSERT(index >= 0, return);
208
    d->m_modeStack->setTabEnabled(index, mode->isEnabled());
209 210 211 212 213 214 215 216 217 218 219 220

    // 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
221 222
}

con's avatar
con committed
223 224
void ModeManager::aboutToRemoveObject(QObject *obj)
{
225
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
226 227 228
    if (!mode)
        return;

229 230
    const int index = d->m_modes.indexOf(mode);
    d->m_modes.remove(index);
231
    d->m_modeCommands.remove(index);
232
    d->m_modeStack->removeTab(index);
con's avatar
con committed
233

234
    d->m_mainWindow->removeContextObject(mode);
con's avatar
con committed
235 236
}

237
void ModeManager::addAction(QAction *action, int priority)
con's avatar
con committed
238
{
239
    d->m_actions.insert(action, priority);
con's avatar
con committed
240 241 242

    // Count the number of commands with a higher priority
    int index = 0;
243
    foreach (int p, d->m_actions) {
con's avatar
con committed
244 245
        if (p > priority)
            ++index;
con's avatar
con committed
246
    }
con's avatar
con committed
247

248
    d->m_actionBar->insertAction(index, action);
con's avatar
con committed
249 250
}

251 252
void ModeManager::addProjectSelector(QAction *action)
{
253
    d->m_actionBar->addProjectSelector(action);
254
    d->m_actions.insert(0, INT_MAX);
255 256
}

con's avatar
con committed
257 258 259
void ModeManager::currentTabAboutToChange(int index)
{
    if (index >= 0) {
260
        IMode *mode = d->m_modes.at(index);
Eike Ziller's avatar
Eike Ziller committed
261
        if (mode)
con's avatar
con committed
262 263 264 265 266 267 268 269
            emit currentModeAboutToChange(mode);
    }
}

void ModeManager::currentTabChanged(int index)
{
    // Tab index changes to -1 when there is no tab left.
    if (index >= 0) {
270
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
271 272 273 274

        // 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
275
        ICore::updateAdditionalContexts(d->m_addedContexts, mode->context());
276
        d->m_addedContexts = mode->context();
277

278
        IMode *oldMode = 0;
279 280 281
        if (d->m_oldCurrent >= 0)
            oldMode = d->m_modes.at(d->m_oldCurrent);
        d->m_oldCurrent = index;
282
        emit currentModeChanged(mode, oldMode);
con's avatar
con committed
283 284 285 286 287 288
    }
}

void ModeManager::setFocusToCurrentMode()
{
    IMode *mode = currentMode();
hjk's avatar
hjk committed
289
    QTC_ASSERT(mode, return);
con's avatar
con committed
290 291 292
    QWidget *widget = mode->widget();
    if (widget) {
        QWidget *focusWidget = widget->focusWidget();
Eike Ziller's avatar
Eike Ziller committed
293 294 295
        if (!focusWidget)
            focusWidget = widget;
        focusWidget->setFocus();
con's avatar
con committed
296 297
    }
}
298

299
void ModeManager::setModeSelectorVisible(bool visible)
300
{
301
    d->m_modeSelectorVisible = visible;
302 303 304 305 306
    d->m_modeStack->setSelectionWidgetVisible(visible);
}

bool ModeManager::isModeSelectorVisible()
{
307
    return d->m_modeSelectorVisible;
308 309
}

310
ModeManager *ModeManager::instance()
311
{
312
    return m_instance;
313 314 315
}

} // namespace Core