modemanager.cpp 10.9 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 51
#include <QSignalMapper>
#include <QShortcut>
#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 71 72
    QVector<IMode*> m_modes;
    QVector<Command*> m_modeShortcuts;
    QSignalMapper *m_signalMapper;
73
    Context m_addedContexts;
74
    int m_oldCurrent;
Tobias Hunger's avatar
Tobias Hunger committed
75
    bool m_saveSettingsOnModeChange;
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);
Tobias Hunger's avatar
Tobias Hunger committed
103
    d->m_saveSettingsOnModeChange = false;
104 105
    d->m_modeSelectorVisible = true;
    d->m_modeStack->setSelectionWidgetVisible(d->m_modeSelectorVisible);
con's avatar
con committed
106

107 108
    connect(d->m_modeStack, SIGNAL(currentAboutToShow(int)), SLOT(currentTabAboutToChange(int)));
    connect(d->m_modeStack, SIGNAL(currentChanged(int)), SLOT(currentTabChanged(int)));
109
    connect(d->m_signalMapper, SIGNAL(mapped(int)), this, SLOT(slotActivateMode(int)));
Tobias Hunger's avatar
Tobias Hunger committed
110 111
    connect(ExtensionSystem::PluginManager::instance(), SIGNAL(initializationDone()), this, SLOT(handleStartup()));
    connect(ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(handleShutdown()));
con's avatar
con committed
112 113 114 115 116
}

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

122 123 124
ModeManager::~ModeManager()
{
    delete d;
125 126
    d = 0;
    m_instance = 0;
127 128
}

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

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

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

153
void ModeManager::slotActivateMode(int id)
154
{
155
    m_instance->activateMode(Id::fromUniqueIdentifier(id));
156
    ICore::raiseWindow(d->m_modeStack);
157 158
}

159
void ModeManager::activateMode(Id id)
con's avatar
con committed
160 161 162
{
    const int index = indexOf(id);
    if (index >= 0)
163
        d->m_modeStack->setCurrentIndex(index);
con's avatar
con committed
164 165 166 167 168 169 170 171
}

void ModeManager::objectAdded(QObject *obj)
{
    IMode *mode = Aggregation::query<IMode>(obj);
    if (!mode)
        return;

172
    d->m_mainWindow->addContextObject(mode);
con's avatar
con committed
173 174 175

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

180 181 182
    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
183 184

    // Register mode shortcut
185
    const Id shortcutId = mode->id().withPrefix("QtCreator.Mode.");
186
    QShortcut *shortcut = new QShortcut(d->m_mainWindow);
187
    shortcut->setWhatsThis(tr("Switch to <b>%1</b> mode").arg(mode->displayName()));
Eike Ziller's avatar
Eike Ziller committed
188
    Command *cmd = ActionManager::registerShortcut(shortcut, shortcutId, Context(Constants::C_GLOBAL));
con's avatar
con committed
189

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

205
    d->m_signalMapper->setMapping(shortcut, mode->id().uniqueIdentifier());
206
    connect(shortcut, SIGNAL(activated()), d->m_signalMapper, SLOT(map()));
con's avatar
con committed
207
    connect(mode, SIGNAL(enabledStateChanged(bool)),
208
            m_instance, SLOT(enabledStateChanged()));
con's avatar
con committed
209 210 211 212
}

void ModeManager::updateModeToolTip()
{
con's avatar
con committed
213
    Command *cmd = qobject_cast<Command *>(sender());
con's avatar
con committed
214
    if (cmd) {
215
        int index = d->m_modeShortcuts.indexOf(cmd);
con's avatar
con committed
216
        if (index != -1)
217
            d->m_modeStack->setTabToolTip(index, cmd->stringWithAppendedShortcut(cmd->shortcut()->whatsThis()));
con's avatar
con committed
218 219 220
    }
}

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

    // 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
240 241
}

Tobias Hunger's avatar
Tobias Hunger committed
242 243 244 245 246 247
void ModeManager::handleStartup()
{ d->m_saveSettingsOnModeChange = true; }

void ModeManager::handleShutdown()
{ d->m_saveSettingsOnModeChange = false; }

con's avatar
con committed
248 249 250 251 252 253
void ModeManager::aboutToRemoveObject(QObject *obj)
{
    IMode *mode = Aggregation::query<IMode>(obj);
    if (!mode)
        return;

254 255 256 257
    const int index = d->m_modes.indexOf(mode);
    d->m_modes.remove(index);
    d->m_modeShortcuts.remove(index);
    d->m_modeStack->removeTab(index);
con's avatar
con committed
258

259
    d->m_mainWindow->removeContextObject(mode);
con's avatar
con committed
260 261
}

262
void ModeManager::addAction(QAction *action, int priority)
con's avatar
con committed
263
{
264
    d->m_actions.insert(action, priority);
con's avatar
con committed
265 266 267

    // Count the number of commands with a higher priority
    int index = 0;
268
    foreach (int p, d->m_actions) {
con's avatar
con committed
269 270
        if (p > priority)
            ++index;
con's avatar
con committed
271
    }
con's avatar
con committed
272

273
    d->m_actionBar->insertAction(index, action);
con's avatar
con committed
274 275
}

276 277
void ModeManager::addProjectSelector(QAction *action)
{
278
    d->m_actionBar->addProjectSelector(action);
279
    d->m_actions.insert(0, INT_MAX);
280 281
}

con's avatar
con committed
282 283 284
void ModeManager::currentTabAboutToChange(int index)
{
    if (index >= 0) {
285
        IMode *mode = d->m_modes.at(index);
Tobias Hunger's avatar
Tobias Hunger committed
286 287 288
        if (mode) {
            if (d->m_saveSettingsOnModeChange)
                ICore::saveSettings();
con's avatar
con committed
289
            emit currentModeAboutToChange(mode);
Tobias Hunger's avatar
Tobias Hunger committed
290
        }
con's avatar
con committed
291 292 293 294 295 296 297
    }
}

void ModeManager::currentTabChanged(int index)
{
    // Tab index changes to -1 when there is no tab left.
    if (index >= 0) {
298
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
299 300 301 302

        // 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
303
        ICore::updateAdditionalContexts(d->m_addedContexts, mode->context());
304
        d->m_addedContexts = mode->context();
305

306
        IMode *oldMode = 0;
307 308 309
        if (d->m_oldCurrent >= 0)
            oldMode = d->m_modes.at(d->m_oldCurrent);
        d->m_oldCurrent = index;
310
        emit currentModeChanged(mode, oldMode);
con's avatar
con committed
311 312 313 314 315 316
    }
}

void ModeManager::setFocusToCurrentMode()
{
    IMode *mode = currentMode();
hjk's avatar
hjk committed
317
    QTC_ASSERT(mode, return);
con's avatar
con committed
318 319 320
    QWidget *widget = mode->widget();
    if (widget) {
        QWidget *focusWidget = widget->focusWidget();
Eike Ziller's avatar
Eike Ziller committed
321 322 323
        if (!focusWidget)
            focusWidget = widget;
        focusWidget->setFocus();
con's avatar
con committed
324 325
    }
}
326

327
void ModeManager::setModeSelectorVisible(bool visible)
328
{
329
    d->m_modeSelectorVisible = visible;
330 331 332 333 334
    d->m_modeStack->setSelectionWidgetVisible(visible);
}

bool ModeManager::isModeSelectorVisible()
{
335
    return d->m_modeSelectorVisible;
336 337
}

338
QObject *ModeManager::instance()
339
{
340
    return m_instance;
341 342 343
}

} // namespace Core