modemanager.cpp 11.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 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::activateModeType(Id type)
154
{
155
156
    if (currentMode() && currentMode()->type() == type)
        return;
157
158
159
160
161
162
163
164
165
166
167
    int index = -1;
    for (int i = 0; i < d->m_modes.count(); ++i) {
        if (d->m_modes.at(i)->type() == type) {
            index = i;
            break;
        }
    }
    if (index != -1)
        d->m_modeStack->setCurrentIndex(index);
}

168
void ModeManager::slotActivateMode(int id)
169
{
170
    m_instance->activateMode(Id::fromUniqueIdentifier(id));
171
    ICore::raiseWindow(d->m_modeStack);
172
173
}

174
void ModeManager::activateMode(Id id)
con's avatar
con committed
175
176
177
{
    const int index = indexOf(id);
    if (index >= 0)
178
        d->m_modeStack->setCurrentIndex(index);
con's avatar
con committed
179
180
181
182
183
184
185
186
}

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

187
    d->m_mainWindow->addContextObject(mode);
con's avatar
con committed
188
189
190

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

195
196
197
    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
198
199

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

205
    d->m_modeShortcuts.insert(index, cmd);
con's avatar
con committed
206
    connect(cmd, SIGNAL(keySequenceChanged()), this, SLOT(updateModeToolTip()));
207
208
    for (int i = 0; i < d->m_modeShortcuts.size(); ++i) {
        Command *currentCmd = d->m_modeShortcuts.at(i);
209
210
211
        // 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
212
213
        bool currentlyHasDefaultSequence = (currentCmd->keySequence()
                                            == currentCmd->defaultKeySequence());
214
215
        currentCmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? QString::fromLatin1("Meta+%1").arg(i+1)
                                                                       : QString::fromLatin1("Ctrl+%1").arg(i+1)));
con's avatar
con committed
216
217
        if (currentlyHasDefaultSequence)
            currentCmd->setKeySequence(currentCmd->defaultKeySequence());
con's avatar
con committed
218
219
    }

220
    d->m_signalMapper->setMapping(shortcut, mode->id().uniqueIdentifier());
221
    connect(shortcut, SIGNAL(activated()), d->m_signalMapper, SLOT(map()));
con's avatar
con committed
222
223
    connect(mode, SIGNAL(enabledStateChanged(bool)),
            this, SLOT(enabledStateChanged()));
con's avatar
con committed
224
225
226
227
}

void ModeManager::updateModeToolTip()
{
con's avatar
con committed
228
    Command *cmd = qobject_cast<Command *>(sender());
con's avatar
con committed
229
    if (cmd) {
230
        int index = d->m_modeShortcuts.indexOf(cmd);
con's avatar
con committed
231
        if (index != -1)
232
            d->m_modeStack->setTabToolTip(index, cmd->stringWithAppendedShortcut(cmd->shortcut()->whatsThis()));
con's avatar
con committed
233
234
235
    }
}

con's avatar
con committed
236
237
238
239
void ModeManager::enabledStateChanged()
{
    IMode *mode = qobject_cast<IMode *>(sender());
    QTC_ASSERT(mode, return);
240
    int index = d->m_modes.indexOf(mode);
con's avatar
con committed
241
    QTC_ASSERT(index >= 0, return);
242
    d->m_modeStack->setTabEnabled(index, mode->isEnabled());
243
244
245
246
247
248
249
250
251
252
253
254

    // 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
255
256
}

Tobias Hunger's avatar
Tobias Hunger committed
257
258
259
260
261
262
void ModeManager::handleStartup()
{ d->m_saveSettingsOnModeChange = true; }

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

con's avatar
con committed
263
264
265
266
267
268
void ModeManager::aboutToRemoveObject(QObject *obj)
{
    IMode *mode = Aggregation::query<IMode>(obj);
    if (!mode)
        return;

269
270
271
272
    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
273

274
    d->m_mainWindow->removeContextObject(mode);
con's avatar
con committed
275
276
}

277
void ModeManager::addAction(QAction *action, int priority)
con's avatar
con committed
278
{
279
    d->m_actions.insert(action, priority);
con's avatar
con committed
280
281
282

    // Count the number of commands with a higher priority
    int index = 0;
283
    foreach (int p, d->m_actions) {
con's avatar
con committed
284
285
        if (p > priority)
            ++index;
con's avatar
con committed
286
    }
con's avatar
con committed
287

288
    d->m_actionBar->insertAction(index, action);
con's avatar
con committed
289
290
}

291
292
void ModeManager::addProjectSelector(QAction *action)
{
293
    d->m_actionBar->addProjectSelector(action);
294
    d->m_actions.insert(0, INT_MAX);
295
296
}

con's avatar
con committed
297
298
299
void ModeManager::currentTabAboutToChange(int index)
{
    if (index >= 0) {
300
        IMode *mode = d->m_modes.at(index);
Tobias Hunger's avatar
Tobias Hunger committed
301
302
303
        if (mode) {
            if (d->m_saveSettingsOnModeChange)
                ICore::saveSettings();
con's avatar
con committed
304
            emit currentModeAboutToChange(mode);
Tobias Hunger's avatar
Tobias Hunger committed
305
        }
con's avatar
con committed
306
307
308
309
310
311
312
    }
}

void ModeManager::currentTabChanged(int index)
{
    // Tab index changes to -1 when there is no tab left.
    if (index >= 0) {
313
        IMode *mode = d->m_modes.at(index);
con's avatar
con committed
314
315
316
317

        // 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
318
        ICore::updateAdditionalContexts(d->m_addedContexts, mode->context());
319
        d->m_addedContexts = mode->context();
320

321
        IMode *oldMode = 0;
322
323
324
        if (d->m_oldCurrent >= 0)
            oldMode = d->m_modes.at(d->m_oldCurrent);
        d->m_oldCurrent = index;
325
        emit currentModeChanged(mode, oldMode);
con's avatar
con committed
326
327
328
329
330
331
    }
}

void ModeManager::setFocusToCurrentMode()
{
    IMode *mode = currentMode();
hjk's avatar
hjk committed
332
    QTC_ASSERT(mode, return);
con's avatar
con committed
333
334
335
336
337
338
339
340
341
    QWidget *widget = mode->widget();
    if (widget) {
        QWidget *focusWidget = widget->focusWidget();
        if (focusWidget)
            focusWidget->setFocus();
        else
            widget->setFocus();
    }
}
342

343
void ModeManager::setModeSelectorVisible(bool visible)
344
{
345
    d->m_modeSelectorVisible = visible;
346
347
348
349
350
    d->m_modeStack->setSelectionWidgetVisible(visible);
}

bool ModeManager::isModeSelectorVisible()
{
351
    return d->m_modeSelectorVisible;
352
353
}

354
355
ModeManager *ModeManager::instance()
{
356
    return m_instance;
357
358
359
}

} // namespace Core