1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "behaviorsettingswidget.h"
27 #include "ui_behaviorsettingswidget.h"
28 
29 #include "tabsettingswidget.h"
30 
31 #include <coreplugin/coreconstants.h>
32 #include <coreplugin/icore.h>
33 
34 #include <texteditor/typingsettings.h>
35 #include <texteditor/storagesettings.h>
36 #include <texteditor/behaviorsettings.h>
37 #include <texteditor/extraencodingsettings.h>
38 #include <utils/algorithm.h>
39 
40 #include <QList>
41 #include <QString>
42 #include <QByteArray>
43 #include <QTextCodec>
44 #include <QTextStream>
45 
46 #include <functional>
47 
48 namespace TextEditor {
49 
50 struct BehaviorSettingsWidgetPrivate
51 {
52     Internal::Ui::BehaviorSettingsWidget m_ui;
53     QList<QTextCodec *> m_codecs;
54 };
55 
BehaviorSettingsWidget(QWidget * parent)56 BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
57     : QWidget(parent)
58     , d(new BehaviorSettingsWidgetPrivate)
59 {
60     d->m_ui.setupUi(this);
61 
62     QList<int> mibs = QTextCodec::availableMibs();
63     Utils::sort(mibs);
64     QList<int>::iterator firstNonNegative =
65         std::find_if(mibs.begin(), mibs.end(), [](int n) { return n >=0; });
66     if (firstNonNegative != mibs.end())
67         std::rotate(mibs.begin(), firstNonNegative, mibs.end());
68     foreach (int mib, mibs) {
69         if (QTextCodec *codec = QTextCodec::codecForMib(mib)) {
70             QString compoundName = QLatin1String(codec->name());
71             foreach (const QByteArray &alias, codec->aliases()) {
72                 compoundName += QLatin1String(" / ");
73                 compoundName += QString::fromLatin1(alias);
74             }
75             d->m_ui.encodingBox->addItem(compoundName);
76             d->m_codecs.append(codec);
77         }
78     }
79 
80     // Qt5 doesn't list the system locale (QTBUG-34283), so add it manually
81     const QString system(QLatin1String("System"));
82     if (d->m_ui.encodingBox->findText(system) == -1) {
83         d->m_ui.encodingBox->insertItem(0, system);
84         d->m_codecs.prepend(QTextCodec::codecForLocale());
85     }
86 
87     d->m_ui.defaultLineEndings->addItems(ExtraEncodingSettings::lineTerminationModeNames());
88 
89     auto currentIndexChanged = QOverload<int>::of(&QComboBox::currentIndexChanged);
90     connect(d->m_ui.autoIndent, &QAbstractButton::toggled,
91             this, &BehaviorSettingsWidget::slotTypingSettingsChanged);
92     connect(d->m_ui.smartBackspaceBehavior, currentIndexChanged,
93             this, &BehaviorSettingsWidget::slotTypingSettingsChanged);
94     connect(d->m_ui.tabKeyBehavior, currentIndexChanged,
95             this, &BehaviorSettingsWidget::slotTypingSettingsChanged);
96     connect(d->m_ui.cleanWhitespace, &QAbstractButton::clicked,
97             this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
98     connect(d->m_ui.inEntireDocument, &QAbstractButton::clicked,
99             this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
100     connect(d->m_ui.addFinalNewLine, &QAbstractButton::clicked,
101             this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
102     connect(d->m_ui.cleanIndentation, &QAbstractButton::clicked,
103             this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
104     connect(d->m_ui.skipTrailingWhitespace, &QAbstractButton::clicked,
105             this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
106     connect(d->m_ui.mouseHiding, &QAbstractButton::clicked,
107             this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
108     connect(d->m_ui.mouseNavigation, &QAbstractButton::clicked,
109             this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
110     connect(d->m_ui.scrollWheelZooming, &QAbstractButton::clicked,
111             this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
112     connect(d->m_ui.camelCaseNavigation, &QAbstractButton::clicked,
113             this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
114     connect(d->m_ui.utf8BomBox, currentIndexChanged,
115             this, &BehaviorSettingsWidget::slotExtraEncodingChanged);
116     connect(d->m_ui.encodingBox, currentIndexChanged,
117             this, &BehaviorSettingsWidget::slotEncodingBoxChanged);
118     connect(d->m_ui.constrainTooltipsBox, currentIndexChanged,
119             this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
120     connect(d->m_ui.keyboardTooltips, &QAbstractButton::clicked,
121             this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
122     connect(d->m_ui.smartSelectionChanging, &QAbstractButton::clicked,
123             this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
124 }
125 
~BehaviorSettingsWidget()126 BehaviorSettingsWidget::~BehaviorSettingsWidget()
127 {
128     delete d;
129 }
130 
setActive(bool active)131 void BehaviorSettingsWidget::setActive(bool active)
132 {
133     d->m_ui.tabPreferencesWidget->setEnabled(active);
134     d->m_ui.groupBoxTyping->setEnabled(active);
135     d->m_ui.groupBoxEncodings->setEnabled(active);
136     d->m_ui.groupBoxMouse->setEnabled(active);
137     d->m_ui.groupBoxStorageSettings->setEnabled(active);
138 }
139 
setAssignedCodec(QTextCodec * codec)140 void BehaviorSettingsWidget::setAssignedCodec(QTextCodec *codec)
141 {
142     const QString codecName = Core::ICore::settings()->value(
143                 Core::Constants::SETTINGS_DEFAULTTEXTENCODING).toString();
144 
145     int rememberedSystemPosition = -1;
146     for (int i = 0; i < d->m_codecs.size(); ++i) {
147         if (codec == d->m_codecs.at(i)) {
148             if (d->m_ui.encodingBox->itemText(i) == codecName) {
149                 d->m_ui.encodingBox->setCurrentIndex(i);
150                 return;
151             } else { // we've got System matching encoding - but have explicitly set the codec
152                 rememberedSystemPosition = i;
153             }
154         }
155     }
156     if (rememberedSystemPosition != -1)
157         d->m_ui.encodingBox->setCurrentIndex(rememberedSystemPosition);
158 }
159 
assignedCodecName() const160 QByteArray BehaviorSettingsWidget::assignedCodecName() const
161 {
162     return d->m_ui.encodingBox->currentIndex() == 0
163             ? QByteArray("System")   // we prepend System to the available codecs
164             : d->m_codecs.at(d->m_ui.encodingBox->currentIndex())->name();
165 
166 }
167 
setCodeStyle(ICodeStylePreferences * preferences)168 void BehaviorSettingsWidget::setCodeStyle(ICodeStylePreferences *preferences)
169 {
170     d->m_ui.tabPreferencesWidget->setPreferences(preferences);
171 }
172 
setAssignedTypingSettings(const TypingSettings & typingSettings)173 void BehaviorSettingsWidget::setAssignedTypingSettings(const TypingSettings &typingSettings)
174 {
175     d->m_ui.autoIndent->setChecked(typingSettings.m_autoIndent);
176     d->m_ui.smartBackspaceBehavior->setCurrentIndex(typingSettings.m_smartBackspaceBehavior);
177     d->m_ui.tabKeyBehavior->setCurrentIndex(typingSettings.m_tabKeyBehavior);
178 
179     d->m_ui.preferSingleLineComments->setChecked(typingSettings.m_preferSingleLineComments);
180 }
181 
assignedTypingSettings(TypingSettings * typingSettings) const182 void BehaviorSettingsWidget::assignedTypingSettings(TypingSettings *typingSettings) const
183 {
184     typingSettings->m_autoIndent = d->m_ui.autoIndent->isChecked();
185     typingSettings->m_smartBackspaceBehavior =
186         (TypingSettings::SmartBackspaceBehavior)(d->m_ui.smartBackspaceBehavior->currentIndex());
187     typingSettings->m_tabKeyBehavior =
188         (TypingSettings::TabKeyBehavior)(d->m_ui.tabKeyBehavior->currentIndex());
189 
190     typingSettings->m_preferSingleLineComments = d->m_ui.preferSingleLineComments->isChecked();
191 }
192 
setAssignedStorageSettings(const StorageSettings & storageSettings)193 void BehaviorSettingsWidget::setAssignedStorageSettings(const StorageSettings &storageSettings)
194 {
195     d->m_ui.cleanWhitespace->setChecked(storageSettings.m_cleanWhitespace);
196     d->m_ui.inEntireDocument->setChecked(storageSettings.m_inEntireDocument);
197     d->m_ui.cleanIndentation->setChecked(storageSettings.m_cleanIndentation);
198     d->m_ui.addFinalNewLine->setChecked(storageSettings.m_addFinalNewLine);
199     d->m_ui.skipTrailingWhitespace->setChecked(storageSettings.m_skipTrailingWhitespace);
200     d->m_ui.ignoreFileTypes->setText(storageSettings.m_ignoreFileTypes);
201     d->m_ui.ignoreFileTypes->setEnabled(d->m_ui.skipTrailingWhitespace->isChecked());
202 }
203 
assignedStorageSettings(StorageSettings * storageSettings) const204 void BehaviorSettingsWidget::assignedStorageSettings(StorageSettings *storageSettings) const
205 {
206     storageSettings->m_cleanWhitespace = d->m_ui.cleanWhitespace->isChecked();
207     storageSettings->m_inEntireDocument = d->m_ui.inEntireDocument->isChecked();
208     storageSettings->m_cleanIndentation = d->m_ui.cleanIndentation->isChecked();
209     storageSettings->m_addFinalNewLine = d->m_ui.addFinalNewLine->isChecked();
210     storageSettings->m_skipTrailingWhitespace = d->m_ui.skipTrailingWhitespace->isChecked();
211     storageSettings->m_ignoreFileTypes = d->m_ui.ignoreFileTypes->text();
212 }
213 
updateConstrainTooltipsBoxTooltip() const214 void BehaviorSettingsWidget::updateConstrainTooltipsBoxTooltip() const
215 {
216     if (d->m_ui.constrainTooltipsBox->currentIndex() == 0) {
217         d->m_ui.constrainTooltipsBox->setToolTip(
218             tr("Displays context-sensitive help or type information on mouseover."));
219     } else {
220         d->m_ui.constrainTooltipsBox->setToolTip(
221             tr("Displays context-sensitive help or type information on Shift+Mouseover."));
222     }
223 }
224 
setAssignedBehaviorSettings(const BehaviorSettings & behaviorSettings)225 void BehaviorSettingsWidget::setAssignedBehaviorSettings(const BehaviorSettings &behaviorSettings)
226 {
227     d->m_ui.mouseHiding->setChecked(behaviorSettings.m_mouseHiding);
228     d->m_ui.mouseNavigation->setChecked(behaviorSettings.m_mouseNavigation);
229     d->m_ui.scrollWheelZooming->setChecked(behaviorSettings.m_scrollWheelZooming);
230     d->m_ui.constrainTooltipsBox->setCurrentIndex(behaviorSettings.m_constrainHoverTooltips ? 1 : 0);
231     d->m_ui.camelCaseNavigation->setChecked(behaviorSettings.m_camelCaseNavigation);
232     d->m_ui.keyboardTooltips->setChecked(behaviorSettings.m_keyboardTooltips);
233     d->m_ui.smartSelectionChanging->setChecked(behaviorSettings.m_smartSelectionChanging);
234     updateConstrainTooltipsBoxTooltip();
235 }
236 
assignedBehaviorSettings(BehaviorSettings * behaviorSettings) const237 void BehaviorSettingsWidget::assignedBehaviorSettings(BehaviorSettings *behaviorSettings) const
238 {
239     behaviorSettings->m_mouseHiding = d->m_ui.mouseHiding->isChecked();
240     behaviorSettings->m_mouseNavigation = d->m_ui.mouseNavigation->isChecked();
241     behaviorSettings->m_scrollWheelZooming = d->m_ui.scrollWheelZooming->isChecked();
242     behaviorSettings->m_constrainHoverTooltips = (d->m_ui.constrainTooltipsBox->currentIndex() == 1);
243     behaviorSettings->m_camelCaseNavigation = d->m_ui.camelCaseNavigation->isChecked();
244     behaviorSettings->m_keyboardTooltips = d->m_ui.keyboardTooltips->isChecked();
245     behaviorSettings->m_smartSelectionChanging = d->m_ui.smartSelectionChanging->isChecked();
246 }
247 
setAssignedExtraEncodingSettings(const ExtraEncodingSettings & encodingSettings)248 void BehaviorSettingsWidget::setAssignedExtraEncodingSettings(
249     const ExtraEncodingSettings &encodingSettings)
250 {
251     d->m_ui.utf8BomBox->setCurrentIndex(encodingSettings.m_utf8BomSetting);
252 }
253 
assignedExtraEncodingSettings(ExtraEncodingSettings * encodingSettings) const254 void BehaviorSettingsWidget::assignedExtraEncodingSettings(
255     ExtraEncodingSettings *encodingSettings) const
256 {
257     encodingSettings->m_utf8BomSetting =
258         (ExtraEncodingSettings::Utf8BomSetting)d->m_ui.utf8BomBox->currentIndex();
259 }
260 
setAssignedLineEnding(int lineEnding)261 void BehaviorSettingsWidget::setAssignedLineEnding(int lineEnding)
262 {
263     d->m_ui.defaultLineEndings->setCurrentIndex(lineEnding);
264 }
265 
assignedLineEnding() const266 int BehaviorSettingsWidget::assignedLineEnding() const
267 {
268     return d->m_ui.defaultLineEndings->currentIndex();
269 }
270 
tabSettingsWidget() const271 TabSettingsWidget *BehaviorSettingsWidget::tabSettingsWidget() const
272 {
273     return d->m_ui.tabPreferencesWidget->tabSettingsWidget();
274 }
275 
slotTypingSettingsChanged()276 void BehaviorSettingsWidget::slotTypingSettingsChanged()
277 {
278     TypingSettings settings;
279     assignedTypingSettings(&settings);
280     emit typingSettingsChanged(settings);
281 }
282 
slotStorageSettingsChanged()283 void BehaviorSettingsWidget::slotStorageSettingsChanged()
284 {
285     StorageSettings settings;
286     assignedStorageSettings(&settings);
287 
288     bool ignoreFileTypesEnabled = d->m_ui.cleanWhitespace->isChecked() && d->m_ui.skipTrailingWhitespace->isChecked();
289     d->m_ui.ignoreFileTypes->setEnabled(ignoreFileTypesEnabled);
290 
291     emit storageSettingsChanged(settings);
292 }
293 
slotBehaviorSettingsChanged()294 void BehaviorSettingsWidget::slotBehaviorSettingsChanged()
295 {
296     BehaviorSettings settings;
297     assignedBehaviorSettings(&settings);
298     updateConstrainTooltipsBoxTooltip();
299     emit behaviorSettingsChanged(settings);
300 }
301 
slotExtraEncodingChanged()302 void BehaviorSettingsWidget::slotExtraEncodingChanged()
303 {
304     ExtraEncodingSettings settings;
305     assignedExtraEncodingSettings(&settings);
306     emit extraEncodingSettingsChanged(settings);
307 }
308 
slotEncodingBoxChanged(int index)309 void BehaviorSettingsWidget::slotEncodingBoxChanged(int index)
310 {
311     emit textCodecChanged(d->m_codecs.at(index));
312 }
313 
314 } // TextEditor
315