modemanager.cpp 10.3 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>
con's avatar
con committed
39 40 41
#include <coreplugin/coreconstants.h>
#include <coreplugin/imode.h>

42 43
#include <extensionsystem/pluginmanager.h>

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

46 47 48
#include <QDebug>
#include <QMap>
#include <QVector>
49

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

53 54
namespace Core {

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

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

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

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

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

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

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

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

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

133
IMode *ModeManager::currentMode()
con's avatar
con committed
134
{
135
    int currentIndex = d->m_modeStack->currentIndex();
con's avatar
con committed
136 137
    if (currentIndex < 0)
        return 0;
138
    return d->m_modes.at(currentIndex);
con's avatar
con committed
139 140
}

141
IMode *ModeManager::mode(Id id)
con's avatar
con committed
142 143 144
{
    const int index = indexOf(id);
    if (index >= 0)
145
        return d->m_modes.at(index);
con's avatar
con committed
146 147 148
    return 0;
}

149
void ModeManager::slotActivateMode(int id)
150
{
151
    m_instance->activateMode(Id::fromUniqueIdentifier(id));
152
    ICore::raiseWindow(d->m_modeStack);
153 154
}

155
void ModeManager::activateMode(Id id)
con's avatar
con committed
156 157 158
{
    const int index = indexOf(id);
    if (index >= 0)
159
        d->m_modeStack->setCurrentIndex(index);
con's avatar
con committed
160 161 162 163
}

void ModeManager::objectAdded(QObject *obj)
{
164
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
165 166 167
    if (!mode)
        return;

168
    d->m_mainWindow->addContextObject(mode);
con's avatar
con committed
169 170 171

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

176 177 178
    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
179 180

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

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

200 201
    d->m_signalMapper->setMapping(action, mode->id().uniqueIdentifier());
    connect(action, SIGNAL(triggered()), d->m_signalMapper, SLOT(map()));
con's avatar
con committed
202
    connect(mode, SIGNAL(enabledStateChanged(bool)),
203
            m_instance, SLOT(enabledStateChanged()));
con's avatar
con committed
204 205 206 207
}

void ModeManager::updateModeToolTip()
{
con's avatar
con committed
208
    Command *cmd = qobject_cast<Command *>(sender());
con's avatar
con committed
209
    if (cmd) {
210
        int index = d->m_modeCommands.indexOf(cmd);
con's avatar
con committed
211
        if (index != -1)
212
            d->m_modeStack->setTabToolTip(index, cmd->action()->toolTip());
con's avatar
con committed
213 214 215
    }
}

con's avatar
con committed
216 217 218 219
void ModeManager::enabledStateChanged()
{
    IMode *mode = qobject_cast<IMode *>(sender());
    QTC_ASSERT(mode, return);
220
    int index = d->m_modes.indexOf(mode);
con's avatar
con committed
221
    QTC_ASSERT(index >= 0, return);
222
    d->m_modeStack->setTabEnabled(index, mode->isEnabled());
223 224 225 226 227 228 229 230 231 232 233 234

    // 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
235 236
}

con's avatar
con committed
237 238
void ModeManager::aboutToRemoveObject(QObject *obj)
{
239
    IMode *mode = qobject_cast<IMode *>(obj);
con's avatar
con committed
240 241 242
    if (!mode)
        return;

243 244
    const int index = d->m_modes.indexOf(mode);
    d->m_modes.remove(index);
245
    d->m_modeCommands.remove(index);
246
    d->m_modeStack->removeTab(index);
con's avatar
con committed
247

248
    d->m_mainWindow->removeContextObject(mode);
con's avatar
con committed
249 250
}

251
void ModeManager::addAction(QAction *action, int priority)
con's avatar
con committed
252
{
253
    d->m_actions.insert(action, priority);
con's avatar
con committed
254 255 256

    // Count the number of commands with a higher priority
    int index = 0;
257
    foreach (int p, d->m_actions) {
con's avatar
con committed
258 259
        if (p > priority)
            ++index;
con's avatar
con committed
260
    }
con's avatar
con committed
261

262
    d->m_actionBar->insertAction(index, action);
con's avatar
con committed
263 264
}

265 266
void ModeManager::addProjectSelector(QAction *action)
{
267
    d->m_actionBar->addProjectSelector(action);
268
    d->m_actions.insert(0, INT_MAX);
269 270
}

con's avatar
con committed
271 272 273
void ModeManager::currentTabAboutToChange(int index)
{
    if (index >= 0) {
274
        IMode *mode = d->m_modes.at(index);
Eike Ziller's avatar
Eike Ziller committed
275
        if (mode)
con's avatar
con committed
276 277 278 279 280 281 282 283
            emit currentModeAboutToChange(mode);
    }
}

void ModeManager::currentTabChanged(int index)
{
    // Tab index changes to -1 when there is no tab left.
    if (index >= 0) {
284
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
285 286 287 288

        // 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
289
        ICore::updateAdditionalContexts(d->m_addedContexts, mode->context());
290
        d->m_addedContexts = mode->context();
291

292
        IMode *oldMode = 0;
293 294 295
        if (d->m_oldCurrent >= 0)
            oldMode = d->m_modes.at(d->m_oldCurrent);
        d->m_oldCurrent = index;
296
        emit currentModeChanged(mode, oldMode);
con's avatar
con committed
297 298 299 300 301 302
    }
}

void ModeManager::setFocusToCurrentMode()
{
    IMode *mode = currentMode();
hjk's avatar
hjk committed
303
    QTC_ASSERT(mode, return);
con's avatar
con committed
304 305 306
    QWidget *widget = mode->widget();
    if (widget) {
        QWidget *focusWidget = widget->focusWidget();
Eike Ziller's avatar
Eike Ziller committed
307 308 309
        if (!focusWidget)
            focusWidget = widget;
        focusWidget->setFocus();
con's avatar
con committed
310 311
    }
}
312

313
void ModeManager::setModeSelectorVisible(bool visible)
314
{
315
    d->m_modeSelectorVisible = visible;
316 317 318 319 320
    d->m_modeStack->setSelectionWidgetVisible(visible);
}

bool ModeManager::isModeSelectorVisible()
{
321
    return d->m_modeSelectorVisible;
322 323
}

324
ModeManager *ModeManager::instance()
325
{
326
    return m_instance;
327 328 329
}

} // namespace Core