modemanager.cpp 10.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
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 12 13 14
** 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
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

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

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

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

41 42
#include <extensionsystem/pluginmanager.h>

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

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

49 50
#include <QSignalMapper>
#include <QAction>
con's avatar
con committed
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
    QSignalMapper *m_signalMapper;
72
    Context m_addedContexts;
73
    int m_oldCurrent;
74
    bool m_modeSelectorVisible;
75 76
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // Register mode shortcut
180 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);
    Command *cmd = ActionManager::registerAction(action, actionId, Context(Constants::C_GLOBAL));
con's avatar
con committed
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

} // namespace Core