modemanager.cpp 9.37 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

con's avatar
con committed
26
#include "modemanager.h"
hjk's avatar
hjk committed
27

con's avatar
con committed
28 29
#include "fancytabwidget.h"
#include "fancyactionbar.h"
30
#include "icore.h"
con's avatar
con committed
31 32
#include "mainwindow.h"

33
#include <coreplugin/actionmanager/actionmanager.h>
34
#include <coreplugin/actionmanager/command.h>
con's avatar
con committed
35 36 37
#include <coreplugin/coreconstants.h>
#include <coreplugin/imode.h>

38 39
#include <extensionsystem/pluginmanager.h>

hjk's avatar
hjk committed
40 41
#include <utils/qtcassert.h>

42
#include <QAction>
43 44 45
#include <QDebug>
#include <QMap>
#include <QVector>
46 47 48

namespace Core {

49 50 51 52 53 54 55 56 57
/*!
    \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.
*/

58 59
struct ModeManagerPrivate
{
60 61 62
    Internal::MainWindow *m_mainWindow;
    Internal::FancyTabWidget *m_modeStack;
    Internal::FancyActionBar *m_actionBar;
63
    QMap<QAction*, int> m_actions;
64
    QVector<IMode*> m_modes;
65
    QVector<Command*> m_modeCommands;
66
    Context m_addedContexts;
67
    int m_oldCurrent;
68
    bool m_modeSelectorVisible;
69 70
};

71 72
static ModeManagerPrivate *d;
static ModeManager *m_instance = 0;
73

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

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

97 98
    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
99 100 101 102 103
}

void ModeManager::init()
{
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)),
104
                     m_instance, SLOT(objectAdded(QObject*)));
con's avatar
con committed
105
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)),
106
                     m_instance, SLOT(aboutToRemoveObject(QObject*)));
con's avatar
con committed
107 108
}

109 110 111
ModeManager::~ModeManager()
{
    delete d;
112 113
    d = 0;
    m_instance = 0;
114 115
}

116
IMode *ModeManager::currentMode()
con's avatar
con committed
117
{
118
    int currentIndex = d->m_modeStack->currentIndex();
con's avatar
con committed
119 120
    if (currentIndex < 0)
        return 0;
121
    return d->m_modes.at(currentIndex);
con's avatar
con committed
122 123
}

124
IMode *ModeManager::mode(Id id)
con's avatar
con committed
125 126 127
{
    const int index = indexOf(id);
    if (index >= 0)
128
        return d->m_modes.at(index);
con's avatar
con committed
129 130 131
    return 0;
}

132
void ModeManager::activateMode(Id id)
con's avatar
con committed
133 134 135
{
    const int index = indexOf(id);
    if (index >= 0)
136
        d->m_modeStack->setCurrentIndex(index);
con's avatar
con committed
137 138 139 140
}

void ModeManager::objectAdded(QObject *obj)
{
141
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
142 143 144
    if (!mode)
        return;

145
    d->m_mainWindow->addContextObject(mode);
con's avatar
con committed
146 147 148

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

153 154 155
    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
156 157

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

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

177 178 179 180 181 182
    Id id = mode->id();
    connect(action, &QAction::triggered, [id] {
        m_instance->activateMode(id);
        ICore::raiseWindow(d->m_modeStack);
    });

con's avatar
con committed
183
    connect(mode, SIGNAL(enabledStateChanged(bool)),
184
            m_instance, SLOT(enabledStateChanged()));
con's avatar
con committed
185 186 187 188
}

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

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

    // 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
216 217
}

con's avatar
con committed
218 219
void ModeManager::aboutToRemoveObject(QObject *obj)
{
220
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
221 222 223
    if (!mode)
        return;

224 225
    const int index = d->m_modes.indexOf(mode);
    d->m_modes.remove(index);
226
    d->m_modeCommands.remove(index);
227
    d->m_modeStack->removeTab(index);
con's avatar
con committed
228

229
    d->m_mainWindow->removeContextObject(mode);
con's avatar
con committed
230 231
}

232
void ModeManager::addAction(QAction *action, int priority)
con's avatar
con committed
233
{
234
    d->m_actions.insert(action, priority);
con's avatar
con committed
235 236 237

    // Count the number of commands with a higher priority
    int index = 0;
238
    foreach (int p, d->m_actions) {
con's avatar
con committed
239 240
        if (p > priority)
            ++index;
con's avatar
con committed
241
    }
con's avatar
con committed
242

243
    d->m_actionBar->insertAction(index, action);
con's avatar
con committed
244 245
}

246 247
void ModeManager::addProjectSelector(QAction *action)
{
248
    d->m_actionBar->addProjectSelector(action);
249
    d->m_actions.insert(0, INT_MAX);
250 251
}

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

void ModeManager::currentTabChanged(int index)
{
    // Tab index changes to -1 when there is no tab left.
    if (index >= 0) {
265
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
266 267 268 269

        // 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
270
        ICore::updateAdditionalContexts(d->m_addedContexts, mode->context());
271
        d->m_addedContexts = mode->context();
272

273
        IMode *oldMode = 0;
274 275 276
        if (d->m_oldCurrent >= 0)
            oldMode = d->m_modes.at(d->m_oldCurrent);
        d->m_oldCurrent = index;
277
        emit currentModeChanged(mode, oldMode);
con's avatar
con committed
278 279 280 281 282 283
    }
}

void ModeManager::setFocusToCurrentMode()
{
    IMode *mode = currentMode();
hjk's avatar
hjk committed
284
    QTC_ASSERT(mode, return);
con's avatar
con committed
285 286 287
    QWidget *widget = mode->widget();
    if (widget) {
        QWidget *focusWidget = widget->focusWidget();
Eike Ziller's avatar
Eike Ziller committed
288 289 290
        if (!focusWidget)
            focusWidget = widget;
        focusWidget->setFocus();
con's avatar
con committed
291 292
    }
}
293

294
void ModeManager::setModeSelectorVisible(bool visible)
295
{
296
    d->m_modeSelectorVisible = visible;
297 298 299 300 301
    d->m_modeStack->setSelectionWidgetVisible(visible);
}

bool ModeManager::isModeSelectorVisible()
{
302
    return d->m_modeSelectorVisible;
303 304
}

305
ModeManager *ModeManager::instance()
306
{
307
    return m_instance;
308 309 310
}

} // namespace Core