modemanager.cpp 9.54 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

Orgad Shaneh's avatar
Orgad Shaneh committed
97 98 99 100
    connect(d->m_modeStack, &Internal::FancyTabWidget::currentAboutToShow,
            this, &ModeManager::currentTabAboutToChange);
    connect(d->m_modeStack, &Internal::FancyTabWidget::currentChanged,
            this, &ModeManager::currentTabChanged);
con's avatar
con committed
101 102 103 104
}

void ModeManager::init()
{
Orgad Shaneh's avatar
Orgad Shaneh committed
105 106 107 108
    QObject::connect(ExtensionSystem::PluginManager::instance(), &ExtensionSystem::PluginManager::objectAdded,
                     m_instance, &ModeManager::objectAdded);
    QObject::connect(ExtensionSystem::PluginManager::instance(), &ExtensionSystem::PluginManager::aboutToRemoveObject,
                     m_instance, &ModeManager::aboutToRemoveObject);
con's avatar
con committed
109 110
}

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

hjk's avatar
hjk committed
118
Id ModeManager::currentMode()
con's avatar
con committed
119
{
120
    int currentIndex = d->m_modeStack->currentIndex();
con's avatar
con committed
121 122
    if (currentIndex < 0)
        return 0;
hjk's avatar
hjk committed
123
    return d->m_modes.at(currentIndex)->id();
con's avatar
con committed
124 125
}

hjk's avatar
hjk committed
126
static IMode *findMode(Id id)
con's avatar
con committed
127 128 129
{
    const int index = indexOf(id);
    if (index >= 0)
130
        return d->m_modes.at(index);
con's avatar
con committed
131 132 133
    return 0;
}

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

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

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

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

155 156 157
    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
158 159

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

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

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

Orgad Shaneh's avatar
Orgad Shaneh committed
185 186
    connect(mode, &IMode::enabledStateChanged,
            m_instance, &ModeManager::enabledStateChanged);
con's avatar
con committed
187 188 189 190
}

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

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

    // Make sure we leave any disabled mode to prevent possible crashes:
hjk's avatar
hjk committed
208
    if (mode->id() == currentMode() && !mode->isEnabled()) {
209 210 211 212 213 214 215 216 217
        // 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
218 219
}

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

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

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

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

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

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

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

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

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

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

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

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

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

bool ModeManager::isModeSelectorVisible()
{
304
    return d->m_modeSelectorVisible;
305 306
}

307
ModeManager *ModeManager::instance()
308
{
309
    return m_instance;
310 311 312
}

} // namespace Core