editorconfiguration.cpp 15.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

con's avatar
con committed
26
#include "editorconfiguration.h"
27
#include "project.h"
28 29
#include "projectexplorer.h"
#include "session.h"
30

31 32
#include <utils/algorithm.h>

33
#include <coreplugin/id.h>
jkobus's avatar
jkobus committed
34
#include <coreplugin/icore.h>
35
#include <coreplugin/editormanager/editormanager.h>
36
#include <texteditor/texteditor.h>
37
#include <texteditor/textdocument.h>
38
#include <texteditor/texteditorsettings.h>
Jarek Kobus's avatar
Jarek Kobus committed
39 40
#include <texteditor/simplecodestylepreferences.h>
#include <texteditor/typingsettings.h>
41 42 43
#include <texteditor/storagesettings.h>
#include <texteditor/behaviorsettings.h>
#include <texteditor/extraencodingsettings.h>
44
#include <texteditor/tabsettings.h>
45
#include <texteditor/marginsettings.h>
46
#include <texteditor/icodestylepreferencesfactory.h>
47

48 49 50 51
#include <QLatin1String>
#include <QByteArray>
#include <QTextCodec>
#include <QDebug>
con's avatar
con committed
52

53 54 55
static const QLatin1String kPrefix("EditorConfiguration.");
static const QLatin1String kUseGlobal("EditorConfiguration.UseGlobal");
static const QLatin1String kCodec("EditorConfiguration.Codec");
56 57
static const QLatin1String kCodeStylePrefix("EditorConfiguration.CodeStyle.");
static const QLatin1String kCodeStyleCount("EditorConfiguration.CodeStyle.Count");
58 59 60 61 62 63 64

using namespace TextEditor;

namespace ProjectExplorer {

struct EditorConfigurationPrivate
{
65 66 67 68 69 70 71 72 73
    EditorConfigurationPrivate() :
        m_typingSettings(TextEditorSettings::typingSettings()),
        m_storageSettings(TextEditorSettings::storageSettings()),
        m_behaviorSettings(TextEditorSettings::behaviorSettings()),
        m_extraEncodingSettings(TextEditorSettings::extraEncodingSettings()),
        m_textCodec(Core::EditorManager::defaultTextCodec())
    { }

    ICodeStylePreferences *m_defaultCodeStyle = nullptr;
Jarek Kobus's avatar
Jarek Kobus committed
74
    TypingSettings m_typingSettings;
75 76
    StorageSettings m_storageSettings;
    BehaviorSettings m_behaviorSettings;
77
    bool m_useGlobal = true;
78
    ExtraEncodingSettings m_extraEncodingSettings;
79
    MarginSettings m_marginSettings;
80
    QTextCodec *m_textCodec;
81

Orgad Shaneh's avatar
Orgad Shaneh committed
82
    QMap<Core::Id, ICodeStylePreferences *> m_languageCodeStylePreferences;
83
    QList<BaseTextEditor *> m_editors;
84
};
con's avatar
con committed
85

hjk's avatar
hjk committed
86
EditorConfiguration::EditorConfiguration() : d(new EditorConfigurationPrivate)
87
{
88
    const QMap<Core::Id, ICodeStylePreferences *> languageCodeStylePreferences = TextEditorSettings::codeStyles();
Orgad Shaneh's avatar
Orgad Shaneh committed
89
    QMapIterator<Core::Id, ICodeStylePreferences *> itCodeStyle(languageCodeStylePreferences);
90 91
    while (itCodeStyle.hasNext()) {
        itCodeStyle.next();
Orgad Shaneh's avatar
Orgad Shaneh committed
92
        Core::Id languageId = itCodeStyle.key();
jkobus's avatar
jkobus committed
93
        // global prefs for language
Jarek Kobus's avatar
Jarek Kobus committed
94
        ICodeStylePreferences *originalPreferences = itCodeStyle.value();
95
        ICodeStylePreferencesFactory *factory = TextEditorSettings::codeStyleFactory(languageId);
jkobus's avatar
jkobus committed
96
        // clone of global prefs for language - it will became project prefs for language
Jarek Kobus's avatar
Jarek Kobus committed
97
        ICodeStylePreferences *preferences = factory->createCodeStyle();
jkobus's avatar
jkobus committed
98
        // project prefs can point to the global language pool, which contains also the global language prefs
99
        preferences->setDelegatingPool(TextEditorSettings::codeStylePool(languageId));
100
        preferences->setId(languageId.name() + "Project");
101
        preferences->setDisplayName(tr("Project %1", "Settings, %1 is a language (C++ or QML)").arg(factory->displayName()));
jkobus's avatar
jkobus committed
102
        // project prefs by default point to global prefs (which in turn can delegate to anything else or not)
Jarek Kobus's avatar
Jarek Kobus committed
103
        preferences->setCurrentDelegate(originalPreferences);
hjk's avatar
hjk committed
104
        d->m_languageCodeStylePreferences.insert(languageId, preferences);
105
    }
Jarek Kobus's avatar
Jarek Kobus committed
106

jkobus's avatar
jkobus committed
107
    // clone of global prefs (not language specific), for project scope
Jarek Kobus's avatar
Jarek Kobus committed
108
    d->m_defaultCodeStyle = new SimpleCodeStylePreferences(this);
109
    d->m_defaultCodeStyle->setDelegatingPool(TextEditorSettings::codeStylePool());
Jarek Kobus's avatar
Jarek Kobus committed
110
    d->m_defaultCodeStyle->setDisplayName(tr("Project", "Settings"));
111
    d->m_defaultCodeStyle->setId("Project");
jkobus's avatar
jkobus committed
112
    // if setCurrentDelegate is 0 values are read from *this prefs
113
    d->m_defaultCodeStyle->setCurrentDelegate(TextEditorSettings::codeStyle());
jkobus's avatar
jkobus committed
114

115 116
    connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
            this, &EditorConfiguration::slotAboutToRemoveProject);
117 118
}

119
EditorConfiguration::~EditorConfiguration()
con's avatar
con committed
120
{
121
    qDeleteAll(d->m_languageCodeStylePreferences);
122
    delete d;
con's avatar
con committed
123 124
}

125
bool EditorConfiguration::useGlobalSettings() const
con's avatar
con committed
126
{
hjk's avatar
hjk committed
127
    return d->m_useGlobal;
con's avatar
con committed
128 129
}

130
void EditorConfiguration::cloneGlobalSettings()
con's avatar
con committed
131
{
132 133 134 135 136
    d->m_defaultCodeStyle->setTabSettings(TextEditorSettings::codeStyle()->tabSettings());
    setTypingSettings(TextEditorSettings::typingSettings());
    setStorageSettings(TextEditorSettings::storageSettings());
    setBehaviorSettings(TextEditorSettings::behaviorSettings());
    setExtraEncodingSettings(TextEditorSettings::extraEncodingSettings());
137
    setMarginSettings(TextEditorSettings::marginSettings());
138
    d->m_textCodec = Core::EditorManager::defaultTextCodec();
139 140 141 142
}

QTextCodec *EditorConfiguration::textCodec() const
{
hjk's avatar
hjk committed
143
    return d->m_textCodec;
144 145
}

Jarek Kobus's avatar
Jarek Kobus committed
146
const TypingSettings &EditorConfiguration::typingSettings() const
147
{
Jarek Kobus's avatar
Jarek Kobus committed
148
    return d->m_typingSettings;
149 150 151 152
}

const StorageSettings &EditorConfiguration::storageSettings() const
{
hjk's avatar
hjk committed
153
    return d->m_storageSettings;
154 155 156 157
}

const BehaviorSettings &EditorConfiguration::behaviorSettings() const
{
hjk's avatar
hjk committed
158
    return d->m_behaviorSettings;
159 160 161 162
}

const ExtraEncodingSettings &EditorConfiguration::extraEncodingSettings() const
{
hjk's avatar
hjk committed
163
    return d->m_extraEncodingSettings;
con's avatar
con committed
164 165
}

166 167 168 169 170
const MarginSettings &EditorConfiguration::marginSettings() const
{
    return d->m_marginSettings;
}

Jarek Kobus's avatar
Jarek Kobus committed
171
ICodeStylePreferences *EditorConfiguration::codeStyle() const
172
{
Jarek Kobus's avatar
Jarek Kobus committed
173
    return d->m_defaultCodeStyle;
174 175
}

Orgad Shaneh's avatar
Orgad Shaneh committed
176
ICodeStylePreferences *EditorConfiguration::codeStyle(Core::Id languageId) const
177
{
Jarek Kobus's avatar
Jarek Kobus committed
178
    return d->m_languageCodeStylePreferences.value(languageId, codeStyle());
179 180
}

Orgad Shaneh's avatar
Orgad Shaneh committed
181
QMap<Core::Id, ICodeStylePreferences *> EditorConfiguration::codeStyles() const
182
{
hjk's avatar
hjk committed
183
    return d->m_languageCodeStylePreferences;
184 185
}

186 187 188
QVariantMap EditorConfiguration::toMap() const
{
    QVariantMap map;
hjk's avatar
hjk committed
189 190
    map.insert(kUseGlobal, d->m_useGlobal);
    map.insert(kCodec, d->m_textCodec->name());
191

hjk's avatar
hjk committed
192
    map.insert(kCodeStyleCount, d->m_languageCodeStylePreferences.count());
Orgad Shaneh's avatar
Orgad Shaneh committed
193
    QMapIterator<Core::Id, ICodeStylePreferences *> itCodeStyle(d->m_languageCodeStylePreferences);
Jarek Kobus's avatar
Jarek Kobus committed
194
    int i = 0;
195 196 197
    while (itCodeStyle.hasNext()) {
        itCodeStyle.next();
        QVariantMap settingsIdMap;
198
        settingsIdMap.insert(QLatin1String("language"), itCodeStyle.key().toSetting());
199 200 201 202 203 204 205
        QVariantMap value;
        itCodeStyle.value()->toMap(QString(), &value);
        settingsIdMap.insert(QLatin1String("value"), value);
        map.insert(kCodeStylePrefix + QString::number(i), settingsIdMap);
        i++;
    }

Jarek Kobus's avatar
Jarek Kobus committed
206 207
    d->m_defaultCodeStyle->tabSettings().toMap(kPrefix, &map);
    d->m_typingSettings.toMap(kPrefix, &map);
hjk's avatar
hjk committed
208 209 210
    d->m_storageSettings.toMap(kPrefix, &map);
    d->m_behaviorSettings.toMap(kPrefix, &map);
    d->m_extraEncodingSettings.toMap(kPrefix, &map);
211
    d->m_marginSettings.toMap(kPrefix, &map);
212

213 214 215
    return map;
}

216
void EditorConfiguration::fromMap(const QVariantMap &map)
217
{
hjk's avatar
hjk committed
218 219 220
    const QByteArray &codecName = map.value(kCodec, d->m_textCodec->name()).toByteArray();
    d->m_textCodec = QTextCodec::codecForName(codecName);
    if (!d->m_textCodec)
221
        d->m_textCodec = Core::EditorManager::defaultTextCodec();
222

223 224 225 226 227 228 229
    const int codeStyleCount = map.value(kCodeStyleCount, 0).toInt();
    for (int i = 0; i < codeStyleCount; ++i) {
        QVariantMap settingsIdMap = map.value(kCodeStylePrefix + QString::number(i)).toMap();
        if (settingsIdMap.isEmpty()) {
            qWarning() << "No data for code style settings list" << i << "found!";
            continue;
        }
230
        Core::Id languageId = Core::Id::fromSetting(settingsIdMap.value(QLatin1String("language")));
231
        QVariantMap value = settingsIdMap.value(QLatin1String("value")).toMap();
Jarek Kobus's avatar
Jarek Kobus committed
232
        ICodeStylePreferences *preferences = d->m_languageCodeStylePreferences.value(languageId);
233
        if (preferences)
234 235 236
             preferences->fromMap(QString(), value);
    }

Jarek Kobus's avatar
Jarek Kobus committed
237 238
    d->m_defaultCodeStyle->fromMap(kPrefix, map);
    d->m_typingSettings.fromMap(kPrefix, map);
hjk's avatar
hjk committed
239 240 241
    d->m_storageSettings.fromMap(kPrefix, map);
    d->m_behaviorSettings.fromMap(kPrefix, map);
    d->m_extraEncodingSettings.fromMap(kPrefix, map);
242
    d->m_marginSettings.fromMap(kPrefix, map);
243
    setUseGlobalSettings(map.value(kUseGlobal, d->m_useGlobal).toBool());
244 245
}

246
void EditorConfiguration::configureEditor(BaseTextEditor *textEditor) const
247
{
248
    TextEditorWidget *widget = textEditor->editorWidget();
249 250
    if (widget)
        widget->setCodeStyle(codeStyle(widget->languageSettingsId()));
hjk's avatar
hjk committed
251
    if (!d->m_useGlobal) {
252
        textEditor->textDocument()->setCodec(d->m_textCodec);
253 254
        if (widget)
            switchSettings(widget);
255
    }
256
    d->m_editors.append(textEditor);
257 258 259
    connect(textEditor, &BaseTextEditor::destroyed, this, [this, textEditor]() {
        d->m_editors.removeOne(textEditor);
    });
260 261
}

262
void EditorConfiguration::deconfigureEditor(BaseTextEditor *textEditor) const
jkobus's avatar
jkobus committed
263
{
264
    TextEditorWidget *widget = textEditor->editorWidget();
265 266
    if (widget)
        widget->setCodeStyle(TextEditorSettings::codeStyle(widget->languageSettingsId()));
jkobus's avatar
jkobus committed
267

268 269
    d->m_editors.removeOne(textEditor);

jkobus's avatar
jkobus committed
270 271 272
    // TODO: what about text codec and switching settings?
}

273 274
void EditorConfiguration::setUseGlobalSettings(bool use)
{
hjk's avatar
hjk committed
275
    d->m_useGlobal = use;
276
    d->m_defaultCodeStyle->setCurrentDelegate(use ? TextEditorSettings::codeStyle() : 0);
hjk's avatar
hjk committed
277
    foreach (Core::IEditor *editor, Core::DocumentModel::editorsForOpenedDocuments()) {
278
        if (auto widget = qobject_cast<TextEditorWidget *>(editor->widget())) {
279
            Project *project = SessionManager::projectForFile(editor->document()->filePath());
280
            if (project && project->editorConfiguration() == this)
281
                switchSettings(widget);
282 283 284 285
        }
    }
}

286 287
template<typename New, typename Old>
static void switchSettings_helper(const New *newSender, const Old *oldSender,
288
                                  TextEditorWidget *widget)
289
{
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
    QObject::disconnect(oldSender, &Old::marginSettingsChanged,
                        widget, &TextEditorWidget::setMarginSettings);
    QObject::disconnect(oldSender, &Old::typingSettingsChanged,
                        widget, &TextEditorWidget::setTypingSettings);
    QObject::disconnect(oldSender, &Old::storageSettingsChanged,
                        widget, &TextEditorWidget::setStorageSettings);
    QObject::disconnect(oldSender, &Old::behaviorSettingsChanged,
                        widget, &TextEditorWidget::setBehaviorSettings);
    QObject::disconnect(oldSender, &Old::extraEncodingSettingsChanged,
                        widget, &TextEditorWidget::setExtraEncodingSettings);

    QObject::connect(newSender, &New::marginSettingsChanged,
                     widget, &TextEditorWidget::setMarginSettings);
    QObject::connect(newSender, &New::typingSettingsChanged,
                     widget, &TextEditorWidget::setTypingSettings);
    QObject::connect(newSender, &New::storageSettingsChanged,
                     widget, &TextEditorWidget::setStorageSettings);
    QObject::connect(newSender, &New::behaviorSettingsChanged,
                     widget, &TextEditorWidget::setBehaviorSettings);
    QObject::connect(newSender, &New::extraEncodingSettingsChanged,
                     widget, &TextEditorWidget::setExtraEncodingSettings);
311 312
}

313
void EditorConfiguration::switchSettings(TextEditorWidget *widget) const
314 315
{
    if (d->m_useGlobal) {
316 317 318 319 320 321
        widget->setMarginSettings(TextEditorSettings::marginSettings());
        widget->setTypingSettings(TextEditorSettings::typingSettings());
        widget->setStorageSettings(TextEditorSettings::storageSettings());
        widget->setBehaviorSettings(TextEditorSettings::behaviorSettings());
        widget->setExtraEncodingSettings(TextEditorSettings::extraEncodingSettings());
        switchSettings_helper(TextEditorSettings::instance(), this, widget);
322
    } else {
323 324 325 326 327 328
        widget->setMarginSettings(marginSettings());
        widget->setTypingSettings(typingSettings());
        widget->setStorageSettings(storageSettings());
        widget->setBehaviorSettings(behaviorSettings());
        widget->setExtraEncodingSettings(extraEncodingSettings());
        switchSettings_helper(this, TextEditorSettings::instance(), widget);
329 330 331
    }
}

332
void EditorConfiguration::setTypingSettings(const TypingSettings &settings)
Jarek Kobus's avatar
Jarek Kobus committed
333 334 335 336 337
{
    d->m_typingSettings = settings;
    emit typingSettingsChanged(d->m_typingSettings);
}

338
void EditorConfiguration::setStorageSettings(const StorageSettings &settings)
339
{
hjk's avatar
hjk committed
340 341
    d->m_storageSettings = settings;
    emit storageSettingsChanged(d->m_storageSettings);
342 343
}

344
void EditorConfiguration::setBehaviorSettings(const BehaviorSettings &settings)
345
{
hjk's avatar
hjk committed
346 347
    d->m_behaviorSettings = settings;
    emit behaviorSettingsChanged(d->m_behaviorSettings);
348 349
}

350
void EditorConfiguration::setExtraEncodingSettings(const ExtraEncodingSettings &settings)
351
{
hjk's avatar
hjk committed
352 353
    d->m_extraEncodingSettings = settings;
    emit extraEncodingSettingsChanged(d->m_extraEncodingSettings);
354 355
}

356 357 358 359 360 361 362 363
void EditorConfiguration::setMarginSettings(const MarginSettings &settings)
{
    if (d->m_marginSettings != settings) {
        d->m_marginSettings = settings;
        emit marginSettingsChanged(d->m_marginSettings);
    }
}

364 365
void EditorConfiguration::setTextCodec(QTextCodec *textCodec)
{
hjk's avatar
hjk committed
366
    d->m_textCodec = textCodec;
367 368
}

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
void EditorConfiguration::setShowWrapColumn(bool onoff)
{
    if (d->m_marginSettings.m_showMargin != onoff) {
        d->m_marginSettings.m_showMargin = onoff;
        emit marginSettingsChanged(d->m_marginSettings);
    }
}

void EditorConfiguration::setWrapColumn(int column)
{
    if (d->m_marginSettings.m_marginColumn != column) {
        d->m_marginSettings.m_marginColumn = column;
        emit marginSettingsChanged(d->m_marginSettings);
    }
}

385
void EditorConfiguration::slotAboutToRemoveProject(Project *project)
jkobus's avatar
jkobus committed
386 387 388 389
{
    if (project->editorConfiguration() != this)
        return;

390
    foreach (BaseTextEditor *editor, d->m_editors)
391 392 393
        deconfigureEditor(editor);
}

jkobus's avatar
jkobus committed
394
TabSettings actualTabSettings(const QString &fileName,
395
                              const TextDocument *baseTextdocument)
396
{
397 398
    if (baseTextdocument)
        return baseTextdocument->tabSettings();
399
    if (Project *project = SessionManager::projectForFile(Utils::FileName::fromString(fileName)))
hjk's avatar
hjk committed
400
        return project->editorConfiguration()->codeStyle()->tabSettings();
401
    return TextEditorSettings::codeStyle()->tabSettings();
402 403 404
}

} // ProjectExplorer