modemanager.cpp 10 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 48 49
#include <QDebug>
#include <QMap>
#include <QVector>
50

51 52
#include <QSignalMapper>
#include <QAction>
con's avatar
con committed
53

54 55
namespace Core {

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

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

79 80
static ModeManagerPrivate *d;
static ModeManager *m_instance = 0;
81

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

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

106 107
    connect(d->m_modeStack, SIGNAL(currentAboutToShow(int)), SLOT(currentTabAboutToChange(int)));
    connect(d->m_modeStack, SIGNAL(currentChanged(int)), SLOT(currentTabChanged(int)));
108
    connect(d->m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotActivateMode(int)));
con's avatar
con committed
109 110 111 112 113
}

void ModeManager::init()
{
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(objectAdded(QObject*)),
114
                     m_instance, SLOT(objectAdded(QObject*)));
con's avatar
con committed
115
    QObject::connect(ExtensionSystem::PluginManager::instance(), SIGNAL(aboutToRemoveObject(QObject*)),
116
                     m_instance, SLOT(aboutToRemoveObject(QObject*)));
con's avatar
con committed
117 118
}

119 120 121
ModeManager::~ModeManager()
{
    delete d;
122 123
    d = 0;
    m_instance = 0;
124 125
}

126
IMode *ModeManager::currentMode()
con's avatar
con committed
127
{
128
    int currentIndex = d->m_modeStack->currentIndex();
con's avatar
con committed
129 130
    if (currentIndex < 0)
        return 0;
131
    return d->m_modes.at(currentIndex);
con's avatar
con committed
132 133
}

134
IMode *ModeManager::mode(Id id)
con's avatar
con committed
135 136 137
{
    const int index = indexOf(id);
    if (index >= 0)
138
        return d->m_modes.at(index);
con's avatar
con committed
139 140 141
    return 0;
}

142
void ModeManager::slotActivateMode(int id)
143
{
144
    m_instance->activateMode(Id::fromUniqueIdentifier(id));
145
    ICore::raiseWindow(d->m_modeStack);
146 147
}

148
void ModeManager::activateMode(Id id)
con's avatar
con committed
149 150 151
{
    const int index = indexOf(id);
    if (index >= 0)
152
        d->m_modeStack->setCurrentIndex(index);
con's avatar
con committed
153 154 155 156
}

void ModeManager::objectAdded(QObject *obj)
{
157
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
158 159 160
    if (!mode)
        return;

161
    d->m_mainWindow->addContextObject(mode);
con's avatar
con committed
162 163 164

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

169 170 171
    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
172 173

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

178
    d->m_modeCommands.insert(index, cmd);
179
    connect(cmd, SIGNAL(keySequenceChanged()), m_instance, SLOT(updateModeToolTip()));
180 181
    for (int i = 0; i < d->m_modeCommands.size(); ++i) {
        Command *currentCmd = d->m_modeCommands.at(i);
182 183 184
        // 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
185 186
        bool currentlyHasDefaultSequence = (currentCmd->keySequence()
                                            == currentCmd->defaultKeySequence());
187 188
        currentCmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? QString::fromLatin1("Meta+%1").arg(i+1)
                                                                       : QString::fromLatin1("Ctrl+%1").arg(i+1)));
con's avatar
con committed
189 190
        if (currentlyHasDefaultSequence)
            currentCmd->setKeySequence(currentCmd->defaultKeySequence());
con's avatar
con committed
191 192
    }

193 194
    d->m_signalMapper->setMapping(action, mode->id().uniqueIdentifier());
    connect(action, SIGNAL(triggered()), d->m_signalMapper, SLOT(map()));
con's avatar
con committed
195
    connect(mode, SIGNAL(enabledStateChanged(bool)),
196
            m_instance, SLOT(enabledStateChanged()));
con's avatar
con committed
197 198 199 200
}

void ModeManager::updateModeToolTip()
{
con's avatar
con committed
201
    Command *cmd = qobject_cast<Command *>(sender());
con's avatar
con committed
202
    if (cmd) {
203
        int index = d->m_modeCommands.indexOf(cmd);
con's avatar
con committed
204
        if (index != -1)
205
            d->m_modeStack->setTabToolTip(index, cmd->action()->toolTip());
con's avatar
con committed
206 207 208
    }
}

con's avatar
con committed
209 210 211 212
void ModeManager::enabledStateChanged()
{
    IMode *mode = qobject_cast<IMode *>(sender());
    QTC_ASSERT(mode, return);
213
    int index = d->m_modes.indexOf(mode);
con's avatar
con committed
214
    QTC_ASSERT(index >= 0, return);
215
    d->m_modeStack->setTabEnabled(index, mode->isEnabled());
216 217 218 219 220 221 222 223 224 225 226 227

    // 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
228 229
}

con's avatar
con committed
230 231
void ModeManager::aboutToRemoveObject(QObject *obj)
{
232
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
233 234 235
    if (!mode)
        return;

236 237
    const int index = d->m_modes.indexOf(mode);
    d->m_modes.remove(index);
238
    d->m_modeCommands.remove(index);
239
    d->m_modeStack->removeTab(index);
con's avatar
con committed
240

241
    d->m_mainWindow->removeContextObject(mode);
con's avatar
con committed
242 243
}

244
void ModeManager::addAction(QAction *action, int priority)
con's avatar
con committed
245
{
246
    d->m_actions.insert(action, priority);
con's avatar
con committed
247 248 249

    // Count the number of commands with a higher priority
    int index = 0;
250
    foreach (int p, d->m_actions) {
con's avatar
con committed
251 252
        if (p > priority)
            ++index;
con's avatar
con committed
253
    }
con's avatar
con committed
254

255
    d->m_actionBar->insertAction(index, action);
con's avatar
con committed
256 257
}

258 259
void ModeManager::addProjectSelector(QAction *action)
{
260
    d->m_actionBar->addProjectSelector(action);
261
    d->m_actions.insert(0, INT_MAX);
262 263
}

con's avatar
con committed
264 265 266
void ModeManager::currentTabAboutToChange(int index)
{
    if (index >= 0) {
267
        IMode *mode = d->m_modes.at(index);
Eike Ziller's avatar
Eike Ziller committed
268
        if (mode)
con's avatar
con committed
269 270 271 272 273 274 275 276
            emit currentModeAboutToChange(mode);
    }
}

void ModeManager::currentTabChanged(int index)
{
    // Tab index changes to -1 when there is no tab left.
    if (index >= 0) {
277
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
278 279 280 281

        // 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
282
        ICore::updateAdditionalContexts(d->m_addedContexts, mode->context());
283
        d->m_addedContexts = mode->context();
284

285
        IMode *oldMode = 0;
286 287 288
        if (d->m_oldCurrent >= 0)
            oldMode = d->m_modes.at(d->m_oldCurrent);
        d->m_oldCurrent = index;
289
        emit currentModeChanged(mode, oldMode);
con's avatar
con committed
290 291 292 293 294 295
    }
}

void ModeManager::setFocusToCurrentMode()
{
    IMode *mode = currentMode();
hjk's avatar
hjk committed
296
    QTC_ASSERT(mode, return);
con's avatar
con committed
297 298 299
    QWidget *widget = mode->widget();
    if (widget) {
        QWidget *focusWidget = widget->focusWidget();
Eike Ziller's avatar
Eike Ziller committed
300 301 302
        if (!focusWidget)
            focusWidget = widget;
        focusWidget->setFocus();
con's avatar
con committed
303 304
    }
}
305

306
void ModeManager::setModeSelectorVisible(bool visible)
307
{
308
    d->m_modeSelectorVisible = visible;
309 310 311 312 313
    d->m_modeStack->setSelectionWidgetVisible(visible);
}

bool ModeManager::isModeSelectorVisible()
{
314
    return d->m_modeSelectorVisible;
315 316
}

317
ModeManager *ModeManager::instance()
318
{
319
    return m_instance;
320 321 322
}

} // namespace Core