1 /*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2002, 2003 Anders Lund <anders.lund@lund.tdcadsl.dk>
4 SPDX-FileCopyrightText: 2003 Christoph Cullmann <cullmann@kde.org>
5 SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
6 SPDX-FileCopyrightText: 2006 Dominik Haumann <dhdev@gmx.de>
7 SPDX-FileCopyrightText: 2007 Mirko Stocker <me@misto.ch>
8 SPDX-FileCopyrightText: 2009 Michel Ludwig <michel.ludwig@kdemail.net>
9 SPDX-FileCopyrightText: 2009 Erlend Hamberg <ehamberg@gmail.com>
10
11 Based on work of:
12 SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
13
14 SPDX-License-Identifier: LGPL-2.0-only
15 */
16
17 // BEGIN Includes
18 #include "katedialogs.h"
19
20 #include <ktexteditor/message.h>
21 #include <ktexteditor_version.h>
22
23 #include "kateautoindent.h"
24 #include "katebuffer.h"
25 #include "kateconfig.h"
26 #include "katedocument.h"
27 #include "kateglobal.h"
28 #include "katemodeconfigpage.h"
29 #include "kateview.h"
30 #include "spellcheck/spellcheck.h"
31
32 // auto generated ui files
33 #include "ui_bordersappearanceconfigwidget.h"
34 #include "ui_completionconfigtab.h"
35 #include "ui_editconfigwidget.h"
36 #include "ui_indentationconfigwidget.h"
37 #include "ui_navigationconfigwidget.h"
38 #include "ui_opensaveconfigadvwidget.h"
39 #include "ui_opensaveconfigwidget.h"
40 #include "ui_spellcheckconfigwidget.h"
41 #include "ui_textareaappearanceconfigwidget.h"
42
43 #include <KIO/Job>
44 #include <KIO/JobUiDelegate>
45 #include <KIO/OpenUrlJob>
46
47 #include "kateabstractinputmodefactory.h"
48 #include "katepartdebug.h"
49 #include <KActionCollection>
50 #include <KCharsets>
51 #include <KColorCombo>
52 #include <KFontRequester>
53 #include <KMessageBox>
54 #include <KProcess>
55 #include <KSeparator>
56
57 #include <QCheckBox>
58 #include <QClipboard>
59 #include <QComboBox>
60 #include <QDomDocument>
61 #include <QFile>
62 #include <QFileDialog>
63 #include <QGroupBox>
64 #include <QKeyEvent>
65 #include <QLabel>
66 #include <QLayout>
67 #include <QMap>
68 #include <QPainter>
69 #include <QRadioButton>
70 #include <QSettings>
71 #include <QSlider>
72 #include <QSpinBox>
73 #include <QStringList>
74 #include <QTabBar>
75 #include <QTemporaryFile>
76 #include <QTextCodec>
77 #include <QTextStream>
78 #include <QToolButton>
79 #include <QWhatsThis>
80
81 // END
82
83 // BEGIN KateIndentConfigTab
KateIndentConfigTab(QWidget * parent)84 KateIndentConfigTab::KateIndentConfigTab(QWidget *parent)
85 : KateConfigPage(parent)
86 {
87 // This will let us have more separation between this page and
88 // the QTabWidget edge (ereslibre)
89 QVBoxLayout *layout = new QVBoxLayout(this);
90 QWidget *newWidget = new QWidget(this);
91
92 ui = new Ui::IndentationConfigWidget();
93 ui->setupUi(newWidget);
94
95 ui->cmbMode->addItems(KateAutoIndent::listModes());
96
97 // FIXME Give ui->label a more descriptive name, it's these "More..." info about tab key action
98 ui->label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
99 connect(ui->label, &QLabel::linkActivated, this, &KateIndentConfigTab::showWhatsThis);
100
101 // "What's This?" help can be found in the ui file
102
103 reload();
104
105 observeChanges(ui->chkBackspaceUnindents);
106 observeChanges(ui->chkIndentPaste);
107 observeChanges(ui->chkKeepExtraSpaces);
108 observeChanges(ui->cmbMode);
109 observeChanges(ui->rbIndentMixed);
110 observeChanges(ui->rbIndentWithSpaces);
111 observeChanges(ui->rbIndentWithTabs);
112 connect(ui->rbIndentWithTabs, &QAbstractButton::toggled, ui->sbIndentWidth, &QWidget::setDisabled);
113 connect(ui->rbIndentWithTabs, &QAbstractButton::toggled, this, &KateIndentConfigTab::slotChanged); // FIXME See slot below
114 observeChanges(ui->rbTabAdvances);
115 observeChanges(ui->rbTabIndents);
116 observeChanges(ui->rbTabSmart);
117 observeChanges(ui->sbIndentWidth);
118 observeChanges(ui->sbTabWidth);
119
120 layout->addWidget(newWidget);
121 }
122
~KateIndentConfigTab()123 KateIndentConfigTab::~KateIndentConfigTab()
124 {
125 delete ui;
126 }
127
slotChanged()128 void KateIndentConfigTab::slotChanged()
129 {
130 // FIXME Make it working without this quirk
131 // When the value is not copied it will silently set back to "Tabs & Spaces"
132 if (ui->rbIndentWithTabs->isChecked()) {
133 ui->sbIndentWidth->setValue(ui->sbTabWidth->value());
134 }
135 }
136
137 // NOTE Should we have more use of such info stuff, consider to make it part
138 // of KateConfigPage and add a similar function like observeChanges(..)
showWhatsThis(const QString & text)139 void KateIndentConfigTab::showWhatsThis(const QString &text)
140 {
141 QWhatsThis::showText(QCursor::pos(), text);
142 }
143
apply()144 void KateIndentConfigTab::apply()
145 {
146 // nothing changed, no need to apply stuff
147 if (!hasChanged()) {
148 return;
149 }
150 m_changed = false;
151
152 KateDocumentConfig::global()->configStart();
153
154 KateDocumentConfig::global()->setBackspaceIndents(ui->chkBackspaceUnindents->isChecked());
155 KateDocumentConfig::global()->setIndentPastedText(ui->chkIndentPaste->isChecked());
156 KateDocumentConfig::global()->setIndentationMode(KateAutoIndent::modeName(ui->cmbMode->currentIndex()));
157 KateDocumentConfig::global()->setIndentationWidth(ui->sbIndentWidth->value());
158 KateDocumentConfig::global()->setKeepExtraSpaces(ui->chkKeepExtraSpaces->isChecked());
159 KateDocumentConfig::global()->setReplaceTabsDyn(ui->rbIndentWithSpaces->isChecked());
160 KateDocumentConfig::global()->setTabWidth(ui->sbTabWidth->value());
161
162 if (ui->rbTabAdvances->isChecked()) {
163 KateDocumentConfig::global()->setTabHandling(KateDocumentConfig::tabInsertsTab);
164 } else if (ui->rbTabIndents->isChecked()) {
165 KateDocumentConfig::global()->setTabHandling(KateDocumentConfig::tabIndents);
166 } else {
167 KateDocumentConfig::global()->setTabHandling(KateDocumentConfig::tabSmart);
168 }
169
170 KateDocumentConfig::global()->configEnd();
171 }
172
reload()173 void KateIndentConfigTab::reload()
174 {
175 ui->chkBackspaceUnindents->setChecked(KateDocumentConfig::global()->backspaceIndents());
176 ui->chkIndentPaste->setChecked(KateDocumentConfig::global()->indentPastedText());
177 ui->chkKeepExtraSpaces->setChecked(KateDocumentConfig::global()->keepExtraSpaces());
178
179 ui->sbIndentWidth->setSuffix(ki18np(" character", " characters"));
180 ui->sbIndentWidth->setValue(KateDocumentConfig::global()->indentationWidth());
181 ui->sbTabWidth->setSuffix(ki18np(" character", " characters"));
182 ui->sbTabWidth->setValue(KateDocumentConfig::global()->tabWidth());
183
184 ui->rbTabAdvances->setChecked(KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabInsertsTab);
185 ui->rbTabIndents->setChecked(KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabIndents);
186 ui->rbTabSmart->setChecked(KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabSmart);
187
188 ui->cmbMode->setCurrentIndex(KateAutoIndent::modeNumber(KateDocumentConfig::global()->indentationMode()));
189
190 if (KateDocumentConfig::global()->replaceTabsDyn()) {
191 ui->rbIndentWithSpaces->setChecked(true);
192 } else {
193 if (KateDocumentConfig::global()->indentationWidth() == KateDocumentConfig::global()->tabWidth()) {
194 ui->rbIndentWithTabs->setChecked(true);
195 } else {
196 ui->rbIndentMixed->setChecked(true);
197 }
198 }
199
200 ui->sbIndentWidth->setEnabled(!ui->rbIndentWithTabs->isChecked());
201 }
202
name() const203 QString KateIndentConfigTab::name() const
204 {
205 return i18n("Indentation");
206 }
207
208 // END KateIndentConfigTab
209
210 // BEGIN KateCompletionConfigTab
KateCompletionConfigTab(QWidget * parent)211 KateCompletionConfigTab::KateCompletionConfigTab(QWidget *parent)
212 : KateConfigPage(parent)
213 {
214 // This will let us have more separation between this page and
215 // the QTabWidget edge (ereslibre)
216 QVBoxLayout *layout = new QVBoxLayout(this);
217 QWidget *newWidget = new QWidget(this);
218
219 ui = new Ui::CompletionConfigTab();
220 ui->setupUi(newWidget);
221
222 // "What's This?" help can be found in the ui file
223
224 reload();
225
226 observeChanges(ui->chkAutoCompletionEnabled);
227 observeChanges(ui->chkAutoSelectFirstEntry);
228 observeChanges(ui->gbKeywordCompletion);
229 observeChanges(ui->gbWordCompletion);
230 observeChanges(ui->minimalWordLength);
231 observeChanges(ui->removeTail);
232
233 layout->addWidget(newWidget);
234 }
235
~KateCompletionConfigTab()236 KateCompletionConfigTab::~KateCompletionConfigTab()
237 {
238 delete ui;
239 }
240
showWhatsThis(const QString & text)241 void KateCompletionConfigTab::showWhatsThis(const QString &text) // NOTE Not used atm, remove? See also KateIndentConfigTab::showWhatsThis
242 {
243 QWhatsThis::showText(QCursor::pos(), text);
244 }
245
apply()246 void KateCompletionConfigTab::apply()
247 {
248 // nothing changed, no need to apply stuff
249 if (!hasChanged()) {
250 return;
251 }
252 m_changed = false;
253
254 KateViewConfig::global()->configStart();
255
256 KateViewConfig::global()->setValue(KateViewConfig::AutomaticCompletionInvocation, ui->chkAutoCompletionEnabled->isChecked());
257 KateViewConfig::global()->setValue(KateViewConfig::AutomaticCompletionPreselectFirst, ui->chkAutoSelectFirstEntry->isChecked());
258 KateViewConfig::global()->setValue(KateViewConfig::KeywordCompletion, ui->gbKeywordCompletion->isChecked());
259 KateViewConfig::global()->setValue(KateViewConfig::WordCompletion, ui->gbWordCompletion->isChecked());
260 KateViewConfig::global()->setValue(KateViewConfig::WordCompletionMinimalWordLength, ui->minimalWordLength->value());
261 KateViewConfig::global()->setValue(KateViewConfig::WordCompletionRemoveTail, ui->removeTail->isChecked());
262
263 KateViewConfig::global()->configEnd();
264 }
265
reload()266 void KateCompletionConfigTab::reload()
267 {
268 ui->chkAutoCompletionEnabled->setChecked(KateViewConfig::global()->automaticCompletionInvocation());
269 ui->chkAutoSelectFirstEntry->setChecked(KateViewConfig::global()->automaticCompletionPreselectFirst());
270
271 ui->gbKeywordCompletion->setChecked(KateViewConfig::global()->keywordCompletion());
272 ui->gbWordCompletion->setChecked(KateViewConfig::global()->wordCompletion());
273
274 ui->minimalWordLength->setValue(KateViewConfig::global()->wordCompletionMinimalWordLength());
275 ui->removeTail->setChecked(KateViewConfig::global()->wordCompletionRemoveTail());
276 }
277
name() const278 QString KateCompletionConfigTab::name() const
279 {
280 return i18n("Auto Completion");
281 }
282
283 // END KateCompletionConfigTab
284
285 // BEGIN KateSpellCheckConfigTab
KateSpellCheckConfigTab(QWidget * parent)286 KateSpellCheckConfigTab::KateSpellCheckConfigTab(QWidget *parent)
287 : KateConfigPage(parent)
288 {
289 // This will let us have more separation between this page and
290 // the QTabWidget edge (ereslibre)
291 QVBoxLayout *layout = new QVBoxLayout(this);
292 QWidget *newWidget = new QWidget(this);
293
294 ui = new Ui::SpellCheckConfigWidget();
295 ui->setupUi(newWidget);
296
297 // "What's This?" help can be found in the ui file
298
299 reload();
300
301 m_sonnetConfigWidget = new Sonnet::ConfigWidget(this);
302 connect(m_sonnetConfigWidget, &Sonnet::ConfigWidget::configChanged, this, &KateSpellCheckConfigTab::slotChanged);
303 layout->addWidget(m_sonnetConfigWidget);
304
305 layout->addWidget(newWidget);
306 }
307
~KateSpellCheckConfigTab()308 KateSpellCheckConfigTab::~KateSpellCheckConfigTab()
309 {
310 delete ui;
311 }
312
showWhatsThis(const QString & text)313 void KateSpellCheckConfigTab::showWhatsThis(const QString &text) // NOTE Not used atm, remove? See also KateIndentConfigTab::showWhatsThis
314 {
315 QWhatsThis::showText(QCursor::pos(), text);
316 }
317
apply()318 void KateSpellCheckConfigTab::apply()
319 {
320 if (!hasChanged()) {
321 // nothing changed, no need to apply stuff
322 return;
323 }
324 m_changed = false;
325
326 // WARNING: this is slightly hackish, but it's currently the only way to
327 // do it, see also the KTextEdit class
328 KateDocumentConfig::global()->configStart();
329 m_sonnetConfigWidget->save();
330 QSettings settings(QStringLiteral("KDE"), QStringLiteral("Sonnet"));
331 KateDocumentConfig::global()->setOnTheFlySpellCheck(settings.value(QStringLiteral("checkerEnabledByDefault"), false).toBool());
332 KateDocumentConfig::global()->configEnd();
333
334 const auto docs = KTextEditor::EditorPrivate::self()->kateDocuments();
335 for (KTextEditor::DocumentPrivate *doc : docs) {
336 doc->refreshOnTheFlyCheck();
337 }
338 }
339
reload()340 void KateSpellCheckConfigTab::reload()
341 {
342 // does nothing
343 }
344
name() const345 QString KateSpellCheckConfigTab::name() const
346 {
347 return i18n("Spellcheck");
348 }
349
350 // END KateSpellCheckConfigTab
351
352 // BEGIN KateNavigationConfigTab
KateNavigationConfigTab(QWidget * parent)353 KateNavigationConfigTab::KateNavigationConfigTab(QWidget *parent)
354 : KateConfigPage(parent)
355 {
356 // This will let us having more separation between this page and
357 // the QTabWidget edge (ereslibre)
358 QVBoxLayout *layout = new QVBoxLayout(this);
359 QWidget *newWidget = new QWidget(this);
360
361 ui = new Ui::NavigationConfigWidget();
362 ui->setupUi(newWidget);
363
364 // "What's This?" help can be found in the ui file
365
366 reload();
367
368 observeChanges(ui->cbTextSelectionMode);
369 observeChanges(ui->chkBackspaceRemoveComposed);
370 observeChanges(ui->chkPagingMovesCursor);
371 observeChanges(ui->chkScrollPastEnd);
372 observeChanges(ui->chkSmartHome);
373 observeChanges(ui->sbAutoCenterCursor);
374 observeChanges(ui->chkCamelCursor);
375
376 layout->addWidget(newWidget);
377 }
378
~KateNavigationConfigTab()379 KateNavigationConfigTab::~KateNavigationConfigTab()
380 {
381 delete ui;
382 }
383
apply()384 void KateNavigationConfigTab::apply()
385 {
386 // nothing changed, no need to apply stuff
387 if (!hasChanged()) {
388 return;
389 }
390 m_changed = false;
391
392 KateViewConfig::global()->configStart();
393 KateDocumentConfig::global()->configStart();
394
395 KateDocumentConfig::global()->setPageUpDownMovesCursor(ui->chkPagingMovesCursor->isChecked());
396 KateDocumentConfig::global()->setSmartHome(ui->chkSmartHome->isChecked());
397 KateDocumentConfig::global()->setCamelCursor(ui->chkCamelCursor->isChecked());
398
399 KateViewConfig::global()->setValue(KateViewConfig::AutoCenterLines, ui->sbAutoCenterCursor->value());
400 KateViewConfig::global()->setValue(KateViewConfig::BackspaceRemoveComposedCharacters, ui->chkBackspaceRemoveComposed->isChecked());
401 KateViewConfig::global()->setValue(KateViewConfig::PersistentSelection, ui->cbTextSelectionMode->currentIndex() == 1);
402 KateViewConfig::global()->setValue(KateViewConfig::ScrollPastEnd, ui->chkScrollPastEnd->isChecked());
403
404 KateDocumentConfig::global()->configEnd();
405 KateViewConfig::global()->configEnd();
406 }
407
reload()408 void KateNavigationConfigTab::reload()
409 {
410 ui->cbTextSelectionMode->setCurrentIndex(KateViewConfig::global()->persistentSelection() ? 1 : 0);
411
412 ui->chkBackspaceRemoveComposed->setChecked(KateViewConfig::global()->backspaceRemoveComposed());
413 ui->chkPagingMovesCursor->setChecked(KateDocumentConfig::global()->pageUpDownMovesCursor());
414 ui->chkScrollPastEnd->setChecked(KateViewConfig::global()->scrollPastEnd());
415 ui->chkSmartHome->setChecked(KateDocumentConfig::global()->smartHome());
416 ui->chkCamelCursor->setChecked(KateDocumentConfig::global()->camelCursor());
417
418 ui->sbAutoCenterCursor->setValue(KateViewConfig::global()->autoCenterLines());
419 }
420
name() const421 QString KateNavigationConfigTab::name() const
422 {
423 return i18n("Text Navigation");
424 }
425
426 // END KateNavigationConfigTab
427
428 // BEGIN KateEditGeneralConfigTab
KateEditGeneralConfigTab(QWidget * parent)429 KateEditGeneralConfigTab::KateEditGeneralConfigTab(QWidget *parent)
430 : KateConfigPage(parent)
431 {
432 QVBoxLayout *layout = new QVBoxLayout(this);
433 QWidget *newWidget = new QWidget(this);
434 ui = new Ui::EditConfigWidget();
435 ui->setupUi(newWidget);
436
437 for (const auto &fact : KTextEditor::EditorPrivate::self()->inputModeFactories()) {
438 ui->cmbInputMode->addItem(fact->name(), static_cast<int>(fact->inputMode()));
439 }
440
441 // "What's This?" Help is in the ui-files
442
443 reload();
444
445 observeChanges(ui->chkAutoBrackets);
446 observeChanges(ui->chkMousePasteAtCursorPosition);
447 observeChanges(ui->chkShowStaticWordWrapMarker);
448 observeChanges(ui->chkTextDragAndDrop);
449 observeChanges(ui->chkSmartCopyCut);
450 observeChanges(ui->chkStaticWordWrap);
451 observeChanges(ui->cmbEncloseSelection);
452 ui->lblBracketHelp->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
453 connect(ui->cmbEncloseSelection->lineEdit(), &QLineEdit::editingFinished, [=] {
454 const int index = ui->cmbEncloseSelection->currentIndex();
455 const QString text = ui->cmbEncloseSelection->currentText();
456 // Text removed? Remove item, but don't remove default data!
457 if (index >= UserData && text.isEmpty()) {
458 ui->cmbEncloseSelection->removeItem(index);
459 slotChanged();
460
461 // Not already there? Add new item! For whatever reason it isn't done automatically
462 } else if (ui->cmbEncloseSelection->findText(text) < 0) {
463 ui->cmbEncloseSelection->addItem(text);
464 slotChanged();
465 }
466 ui->cmbEncloseSelection->setCurrentIndex(ui->cmbEncloseSelection->findText(text));
467 });
468 observeChanges(ui->cmbInputMode);
469 observeChanges(ui->sbWordWrap);
470
471 layout->addWidget(newWidget);
472 }
473
~KateEditGeneralConfigTab()474 KateEditGeneralConfigTab::~KateEditGeneralConfigTab()
475 {
476 delete ui;
477 }
478
apply()479 void KateEditGeneralConfigTab::apply()
480 {
481 // nothing changed, no need to apply stuff
482 if (!hasChanged()) {
483 return;
484 }
485 m_changed = false;
486
487 KateViewConfig::global()->configStart();
488 KateDocumentConfig::global()->configStart();
489
490 KateDocumentConfig::global()->setWordWrap(ui->chkStaticWordWrap->isChecked());
491 KateDocumentConfig::global()->setWordWrapAt(ui->sbWordWrap->value());
492
493 KateRendererConfig::global()->setWordWrapMarker(ui->chkShowStaticWordWrapMarker->isChecked());
494
495 KateViewConfig::global()->setValue(KateViewConfig::AutoBrackets, ui->chkAutoBrackets->isChecked());
496 KateViewConfig::global()->setValue(KateViewConfig::CharsToEncloseSelection, ui->cmbEncloseSelection->currentText());
497 QStringList userLetters;
498 for (int i = UserData; i < ui->cmbEncloseSelection->count(); ++i) {
499 userLetters.append(ui->cmbEncloseSelection->itemText(i));
500 }
501 KateViewConfig::global()->setValue(KateViewConfig::UserSetsOfCharsToEncloseSelection, userLetters);
502 KateViewConfig::global()->setValue(KateViewConfig::InputMode, ui->cmbInputMode->currentData().toInt());
503 KateViewConfig::global()->setValue(KateViewConfig::MousePasteAtCursorPosition, ui->chkMousePasteAtCursorPosition->isChecked());
504 KateViewConfig::global()->setValue(KateViewConfig::TextDragAndDrop, ui->chkTextDragAndDrop->isChecked());
505 KateViewConfig::global()->setValue(KateViewConfig::SmartCopyCut, ui->chkSmartCopyCut->isChecked());
506
507 KateDocumentConfig::global()->configEnd();
508 KateViewConfig::global()->configEnd();
509 }
510
reload()511 void KateEditGeneralConfigTab::reload()
512 {
513 ui->chkAutoBrackets->setChecked(KateViewConfig::global()->autoBrackets());
514 ui->chkMousePasteAtCursorPosition->setChecked(KateViewConfig::global()->mousePasteAtCursorPosition());
515 ui->chkShowStaticWordWrapMarker->setChecked(KateRendererConfig::global()->wordWrapMarker());
516 ui->chkTextDragAndDrop->setChecked(KateViewConfig::global()->textDragAndDrop());
517 ui->chkSmartCopyCut->setChecked(KateViewConfig::global()->smartCopyCut());
518 ui->chkStaticWordWrap->setChecked(KateDocumentConfig::global()->wordWrap());
519
520 ui->sbWordWrap->setSuffix(ki18ncp("Wrap words at (value is at 20 or larger)", " character", " characters"));
521 ui->sbWordWrap->setValue(KateDocumentConfig::global()->wordWrapAt());
522
523 ui->cmbEncloseSelection->clear();
524 ui->cmbEncloseSelection->lineEdit()->setClearButtonEnabled(true);
525 ui->cmbEncloseSelection->lineEdit()->setPlaceholderText(i18n("Feature is not active"));
526 ui->cmbEncloseSelection->addItem(QString(), None);
527 ui->cmbEncloseSelection->setItemData(0, i18n("Disable Feature"), Qt::ToolTipRole);
528 ui->cmbEncloseSelection->addItem(QStringLiteral("`*_~"), MarkDown);
529 ui->cmbEncloseSelection->setItemData(1, i18n("May be handy with Markdown"), Qt::ToolTipRole);
530 ui->cmbEncloseSelection->addItem(QStringLiteral("<>(){}[]'\""), MirrorChar);
531 ui->cmbEncloseSelection->setItemData(2, i18n("Mirror characters, similar but not exactly like auto brackets"), Qt::ToolTipRole);
532 ui->cmbEncloseSelection->addItem(QStringLiteral("´`_.:|#@~*!?$%/=,;-+^°§&"), NonLetters);
533 ui->cmbEncloseSelection->setItemData(3, i18n("Non letter character"), Qt::ToolTipRole);
534 const QStringList userLetters = KateViewConfig::global()->value(KateViewConfig::UserSetsOfCharsToEncloseSelection).toStringList();
535 for (int i = 0; i < userLetters.size(); ++i) {
536 ui->cmbEncloseSelection->addItem(userLetters.at(i), UserData + i);
537 }
538 ui->cmbEncloseSelection->setCurrentIndex(ui->cmbEncloseSelection->findText(KateViewConfig::global()->charsToEncloseSelection()));
539
540 const int id = static_cast<int>(KateViewConfig::global()->inputMode());
541 ui->cmbInputMode->setCurrentIndex(ui->cmbInputMode->findData(id));
542 }
543
name() const544 QString KateEditGeneralConfigTab::name() const
545 {
546 return i18n("General");
547 }
548
549 // END KateEditGeneralConfigTab
550
551 // BEGIN KateEditConfigTab
KateEditConfigTab(QWidget * parent)552 KateEditConfigTab::KateEditConfigTab(QWidget *parent)
553 : KateConfigPage(parent)
554 , editConfigTab(new KateEditGeneralConfigTab(this))
555 , navigationConfigTab(new KateNavigationConfigTab(this))
556 , indentConfigTab(new KateIndentConfigTab(this))
557 , completionConfigTab(new KateCompletionConfigTab(this))
558 , spellCheckConfigTab(new KateSpellCheckConfigTab(this))
559 {
560 QVBoxLayout *layout = new QVBoxLayout(this);
561 layout->setContentsMargins(0, 0, 0, 0);
562 QTabWidget *tabWidget = new QTabWidget(this);
563
564 // add all tabs
565 tabWidget->insertTab(0, editConfigTab, editConfigTab->name());
566 tabWidget->insertTab(1, navigationConfigTab, navigationConfigTab->name());
567 tabWidget->insertTab(2, indentConfigTab, indentConfigTab->name());
568 tabWidget->insertTab(3, completionConfigTab, completionConfigTab->name());
569 tabWidget->insertTab(4, spellCheckConfigTab, spellCheckConfigTab->name());
570
571 observeChanges(editConfigTab);
572 observeChanges(navigationConfigTab);
573 observeChanges(indentConfigTab);
574 observeChanges(completionConfigTab);
575 observeChanges(spellCheckConfigTab);
576
577 int i = tabWidget->count();
578 for (const auto &factory : KTextEditor::EditorPrivate::self()->inputModeFactories()) {
579 KateConfigPage *tab = factory->createConfigPage(this);
580 if (tab) {
581 m_inputModeConfigTabs << tab;
582 tabWidget->insertTab(i, tab, tab->name());
583 observeChanges(tab);
584 i++;
585 }
586 }
587
588 layout->addWidget(tabWidget);
589 }
590
~KateEditConfigTab()591 KateEditConfigTab::~KateEditConfigTab()
592 {
593 qDeleteAll(m_inputModeConfigTabs);
594 }
595
apply()596 void KateEditConfigTab::apply()
597 {
598 // try to update the rest of tabs
599 editConfigTab->apply();
600 navigationConfigTab->apply();
601 indentConfigTab->apply();
602 completionConfigTab->apply();
603 spellCheckConfigTab->apply();
604 for (KateConfigPage *tab : std::as_const(m_inputModeConfigTabs)) {
605 tab->apply();
606 }
607 }
608
reload()609 void KateEditConfigTab::reload()
610 {
611 editConfigTab->reload();
612 navigationConfigTab->reload();
613 indentConfigTab->reload();
614 completionConfigTab->reload();
615 spellCheckConfigTab->reload();
616 for (KateConfigPage *tab : std::as_const(m_inputModeConfigTabs)) {
617 tab->reload();
618 }
619 }
620
reset()621 void KateEditConfigTab::reset()
622 {
623 editConfigTab->reset();
624 navigationConfigTab->reset();
625 indentConfigTab->reset();
626 completionConfigTab->reset();
627 spellCheckConfigTab->reset();
628 for (KateConfigPage *tab : std::as_const(m_inputModeConfigTabs)) {
629 tab->reset();
630 }
631 }
632
defaults()633 void KateEditConfigTab::defaults()
634 {
635 editConfigTab->defaults();
636 navigationConfigTab->defaults();
637 indentConfigTab->defaults();
638 completionConfigTab->defaults();
639 spellCheckConfigTab->defaults();
640 for (KateConfigPage *tab : std::as_const(m_inputModeConfigTabs)) {
641 tab->defaults();
642 }
643 }
644
name() const645 QString KateEditConfigTab::name() const
646 {
647 return i18n("Editing");
648 }
649
fullName() const650 QString KateEditConfigTab::fullName() const
651 {
652 return i18n("Editing Options");
653 }
654
icon() const655 QIcon KateEditConfigTab::icon() const
656 {
657 return QIcon::fromTheme(QStringLiteral("accessories-text-editor"));
658 }
659
660 // END KateEditConfigTab
661
662 // BEGIN KateViewDefaultsConfig
KateViewDefaultsConfig(QWidget * parent)663 KateViewDefaultsConfig::KateViewDefaultsConfig(QWidget *parent)
664 : KateConfigPage(parent)
665 , textareaUi(new Ui::TextareaAppearanceConfigWidget())
666 , bordersUi(new Ui::BordersAppearanceConfigWidget())
667 {
668 QLayout *layout = new QVBoxLayout(this);
669 QTabWidget *tabWidget = new QTabWidget(this);
670 layout->addWidget(tabWidget);
671 layout->setContentsMargins(0, 0, 0, 0);
672
673 QWidget *textareaTab = new QWidget(tabWidget);
674 textareaUi->setupUi(textareaTab);
675 tabWidget->addTab(textareaTab, i18n("General"));
676
677 QWidget *bordersTab = new QWidget(tabWidget);
678 bordersUi->setupUi(bordersTab);
679 tabWidget->addTab(bordersTab, i18n("Borders"));
680
681 textareaUi->cmbDynamicWordWrapIndicator->addItem(i18n("Off"));
682 textareaUi->cmbDynamicWordWrapIndicator->addItem(i18n("Follow Line Numbers"));
683 textareaUi->cmbDynamicWordWrapIndicator->addItem(i18n("Always On"));
684
685 // "What's This?" help is in the ui-file
686
687 reload();
688
689 observeChanges(textareaUi->kfontrequester);
690
691 observeChanges(textareaUi->chkAnimateBracketMatching);
692 observeChanges(textareaUi->chkDynWrapAnywhere);
693 observeChanges(textareaUi->chkDynWrapAtStaticMarker);
694 observeChanges(textareaUi->chkFoldFirstLine);
695 observeChanges(textareaUi->chkShowBracketMatchPreview);
696 observeChanges(textareaUi->chkShowIndentationLines);
697 observeChanges(textareaUi->chkShowLineCount);
698 observeChanges(textareaUi->chkShowTabs);
699 observeChanges(textareaUi->chkShowWholeBracketExpression);
700 observeChanges(textareaUi->chkShowWordCount);
701 observeChanges(textareaUi->cmbDynamicWordWrapIndicator);
702 observeChanges(textareaUi->cbxWordWrap);
703 auto a = [ui = textareaUi, cbx = textareaUi->cbxWordWrap]() {
704 ui->chkDynWrapAtStaticMarker->setEnabled(cbx->isChecked());
705 ui->chkDynWrapAnywhere->setEnabled(cbx->isChecked());
706 ui->cmbDynamicWordWrapIndicator->setEnabled(cbx->isChecked());
707 ui->sbDynamicWordWrapDepth->setEnabled(cbx->isChecked());
708 };
709 connect(textareaUi->cbxWordWrap, &QCheckBox::stateChanged, this, a);
710 a();
711 auto b = [cbx = textareaUi->cbxIndentWrappedLines, sb = textareaUi->sbDynamicWordWrapDepth]() {
712 sb->setEnabled(cbx->isChecked());
713 };
714 b();
715 connect(textareaUi->cbxIndentWrappedLines, &QCheckBox::stateChanged, this, b);
716 observeChanges(textareaUi->cbxIndentWrappedLines);
717 observeChanges(textareaUi->sbDynamicWordWrapDepth);
718 observeChanges(textareaUi->sliSetMarkerSize);
719 observeChanges(textareaUi->spacesComboBox);
720
721 observeChanges(bordersUi->chkIconBorder);
722 observeChanges(bordersUi->chkLineNumbers);
723 observeChanges(bordersUi->chkScrollbarMarks);
724 observeChanges(bordersUi->chkScrollbarMiniMap);
725 observeChanges(bordersUi->chkScrollbarMiniMapAll);
726 bordersUi->chkScrollbarMiniMapAll->hide(); // this is temporary until the feature is done
727 observeChanges(bordersUi->chkScrollbarPreview);
728 observeChanges(bordersUi->chkShowFoldingMarkers);
729 observeChanges(bordersUi->chkShowFoldingPreview);
730 observeChanges(bordersUi->chkShowLineModification);
731 observeChanges(bordersUi->cmbShowScrollbars);
732 observeChanges(bordersUi->rbSortBookmarksByCreation);
733 observeChanges(bordersUi->rbSortBookmarksByPosition);
734 observeChanges(bordersUi->spBoxMiniMapWidth);
735 }
736
~KateViewDefaultsConfig()737 KateViewDefaultsConfig::~KateViewDefaultsConfig()
738 {
739 delete bordersUi;
740 delete textareaUi;
741 }
742
apply()743 void KateViewDefaultsConfig::apply()
744 {
745 // nothing changed, no need to apply stuff
746 if (!hasChanged()) {
747 return;
748 }
749 m_changed = false;
750
751 KateViewConfig::global()->configStart();
752 KateRendererConfig::global()->configStart();
753
754 KateDocumentConfig::global()->setMarkerSize(textareaUi->sliSetMarkerSize->value());
755 KateDocumentConfig::global()->setShowSpaces(KateDocumentConfig::WhitespaceRendering(textareaUi->spacesComboBox->currentIndex()));
756 KateDocumentConfig::global()->setShowTabs(textareaUi->chkShowTabs->isChecked());
757
758 KateRendererConfig::global()->setFont(textareaUi->kfontrequester->font());
759 KateRendererConfig::global()->setAnimateBracketMatching(textareaUi->chkAnimateBracketMatching->isChecked());
760 KateRendererConfig::global()->setShowIndentationLines(textareaUi->chkShowIndentationLines->isChecked());
761 KateRendererConfig::global()->setShowWholeBracketExpression(textareaUi->chkShowWholeBracketExpression->isChecked());
762
763 KateViewConfig::global()->setDynWordWrap(textareaUi->cbxWordWrap->isChecked());
764 KateViewConfig::global()->setShowWordCount(textareaUi->chkShowWordCount->isChecked());
765 KateViewConfig::global()->setValue(KateViewConfig::BookmarkSorting, bordersUi->rbSortBookmarksByPosition->isChecked() ? 0 : 1);
766 if (!textareaUi->cbxIndentWrappedLines->isChecked()) {
767 KateViewConfig::global()->setValue(KateViewConfig::DynWordWrapAlignIndent, 0);
768 } else {
769 KateViewConfig::global()->setValue(KateViewConfig::DynWordWrapAlignIndent, textareaUi->sbDynamicWordWrapDepth->value());
770 }
771 KateViewConfig::global()->setValue(KateViewConfig::DynWordWrapIndicators, textareaUi->cmbDynamicWordWrapIndicator->currentIndex());
772 KateViewConfig::global()->setValue(KateViewConfig::DynWrapAnywhere, textareaUi->chkDynWrapAnywhere->isChecked());
773 KateViewConfig::global()->setValue(KateViewConfig::DynWrapAtStaticMarker, textareaUi->chkDynWrapAtStaticMarker->isChecked());
774 KateViewConfig::global()->setValue(KateViewConfig::FoldFirstLine, textareaUi->chkFoldFirstLine->isChecked());
775 KateViewConfig::global()->setValue(KateViewConfig::ScrollBarMiniMapWidth, bordersUi->spBoxMiniMapWidth->value());
776 KateViewConfig::global()->setValue(KateViewConfig::ShowBracketMatchPreview, textareaUi->chkShowBracketMatchPreview->isChecked());
777 KateViewConfig::global()->setValue(KateViewConfig::ShowFoldingBar, bordersUi->chkShowFoldingMarkers->isChecked());
778 KateViewConfig::global()->setValue(KateViewConfig::ShowFoldingPreview, bordersUi->chkShowFoldingPreview->isChecked());
779 KateViewConfig::global()->setValue(KateViewConfig::ShowIconBar, bordersUi->chkIconBorder->isChecked());
780 KateViewConfig::global()->setValue(KateViewConfig::ShowLineCount, textareaUi->chkShowLineCount->isChecked());
781 KateViewConfig::global()->setValue(KateViewConfig::ShowLineModification, bordersUi->chkShowLineModification->isChecked());
782 KateViewConfig::global()->setValue(KateViewConfig::ShowLineNumbers, bordersUi->chkLineNumbers->isChecked());
783 KateViewConfig::global()->setValue(KateViewConfig::ShowScrollBarMarks, bordersUi->chkScrollbarMarks->isChecked());
784 KateViewConfig::global()->setValue(KateViewConfig::ShowScrollBarMiniMap, bordersUi->chkScrollbarMiniMap->isChecked());
785 KateViewConfig::global()->setValue(KateViewConfig::ShowScrollBarMiniMapAll, bordersUi->chkScrollbarMiniMapAll->isChecked());
786 KateViewConfig::global()->setValue(KateViewConfig::ShowScrollBarPreview, bordersUi->chkScrollbarPreview->isChecked());
787 KateViewConfig::global()->setValue(KateViewConfig::ShowScrollbars, bordersUi->cmbShowScrollbars->currentIndex());
788
789 KateRendererConfig::global()->configEnd();
790 KateViewConfig::global()->configEnd();
791 }
792
reload()793 void KateViewDefaultsConfig::reload()
794 {
795 bordersUi->chkIconBorder->setChecked(KateViewConfig::global()->iconBar());
796 bordersUi->chkLineNumbers->setChecked(KateViewConfig::global()->lineNumbers());
797 bordersUi->chkScrollbarMarks->setChecked(KateViewConfig::global()->scrollBarMarks());
798 bordersUi->chkScrollbarMiniMap->setChecked(KateViewConfig::global()->scrollBarMiniMap());
799 bordersUi->chkScrollbarMiniMapAll->setChecked(KateViewConfig::global()->scrollBarMiniMapAll());
800 bordersUi->chkScrollbarPreview->setChecked(KateViewConfig::global()->scrollBarPreview());
801 bordersUi->chkShowFoldingMarkers->setChecked(KateViewConfig::global()->foldingBar());
802 bordersUi->chkShowFoldingPreview->setChecked(KateViewConfig::global()->foldingPreview());
803 bordersUi->chkShowLineModification->setChecked(KateViewConfig::global()->lineModification());
804 bordersUi->cmbShowScrollbars->setCurrentIndex(KateViewConfig::global()->showScrollbars());
805 bordersUi->rbSortBookmarksByCreation->setChecked(KateViewConfig::global()->bookmarkSort() == 1);
806 bordersUi->rbSortBookmarksByPosition->setChecked(KateViewConfig::global()->bookmarkSort() == 0);
807 bordersUi->spBoxMiniMapWidth->setValue(KateViewConfig::global()->scrollBarMiniMapWidth());
808
809 textareaUi->kfontrequester->setFont(KateRendererConfig::global()->baseFont());
810
811 textareaUi->chkAnimateBracketMatching->setChecked(KateRendererConfig::global()->animateBracketMatching());
812 textareaUi->chkDynWrapAnywhere->setChecked(KateViewConfig::global()->dynWrapAnywhere());
813 textareaUi->chkDynWrapAtStaticMarker->setChecked(KateViewConfig::global()->dynWrapAtStaticMarker());
814 textareaUi->chkFoldFirstLine->setChecked(KateViewConfig::global()->foldFirstLine());
815 textareaUi->chkShowBracketMatchPreview->setChecked(KateViewConfig::global()->value(KateViewConfig::ShowBracketMatchPreview).toBool());
816 textareaUi->chkShowIndentationLines->setChecked(KateRendererConfig::global()->showIndentationLines());
817 textareaUi->chkShowLineCount->setChecked(KateViewConfig::global()->showLineCount());
818 textareaUi->chkShowTabs->setChecked(KateDocumentConfig::global()->showTabs());
819 textareaUi->chkShowWholeBracketExpression->setChecked(KateRendererConfig::global()->showWholeBracketExpression());
820 textareaUi->chkShowWordCount->setChecked(KateViewConfig::global()->showWordCount());
821 textareaUi->cmbDynamicWordWrapIndicator->setCurrentIndex(KateViewConfig::global()->dynWordWrapIndicators());
822 textareaUi->cbxWordWrap->setChecked(KateViewConfig::global()->dynWordWrap());
823 textareaUi->cbxIndentWrappedLines->setChecked(KateViewConfig::global()->dynWordWrapAlignIndent() != 0);
824 textareaUi->sbDynamicWordWrapDepth->setValue(KateViewConfig::global()->dynWordWrapAlignIndent());
825 textareaUi->sliSetMarkerSize->setValue(KateDocumentConfig::global()->markerSize());
826 textareaUi->spacesComboBox->setCurrentIndex(KateDocumentConfig::global()->showSpaces());
827 }
828
reset()829 void KateViewDefaultsConfig::reset()
830 {
831 }
832
defaults()833 void KateViewDefaultsConfig::defaults()
834 {
835 }
836
name() const837 QString KateViewDefaultsConfig::name() const
838 {
839 return i18n("Appearance");
840 }
841
fullName() const842 QString KateViewDefaultsConfig::fullName() const
843 {
844 return i18n("Appearance");
845 }
846
icon() const847 QIcon KateViewDefaultsConfig::icon() const
848 {
849 return QIcon::fromTheme(QStringLiteral("preferences-desktop-theme"));
850 }
851
852 // END KateViewDefaultsConfig
853
854 // BEGIN KateSaveConfigTab
KateSaveConfigTab(QWidget * parent)855 KateSaveConfigTab::KateSaveConfigTab(QWidget *parent)
856 : KateConfigPage(parent)
857 , modeConfigPage(new ModeConfigPage(this))
858 {
859 // FIXME: Is really needed to move all this code below to another class,
860 // since it is another tab itself on the config dialog. This means we should
861 // initialize, add and work with as we do with modeConfigPage (ereslibre)
862 QVBoxLayout *layout = new QVBoxLayout(this);
863 layout->setContentsMargins(0, 0, 0, 0);
864 QTabWidget *tabWidget = new QTabWidget(this);
865
866 QWidget *tmpWidget = new QWidget(tabWidget);
867 QVBoxLayout *internalLayout = new QVBoxLayout(tmpWidget);
868 QWidget *newWidget = new QWidget(tabWidget);
869 ui = new Ui::OpenSaveConfigWidget();
870 ui->setupUi(newWidget);
871
872 QWidget *tmpWidget2 = new QWidget(tabWidget);
873 QVBoxLayout *internalLayout2 = new QVBoxLayout(tmpWidget2);
874 QWidget *newWidget2 = new QWidget(tabWidget);
875 uiadv = new Ui::OpenSaveConfigAdvWidget();
876 uiadv->setupUi(newWidget2);
877 uiadv->lblExplanatory->setText(i18n("%1 backs up unsaved files to \"swap files.\" Swap files allow %1 to recover your work in the case of a system crash. Disabling swap files may cause data loss in case of a system crash.", QCoreApplication::applicationName()));
878 uiadv->lblExplanatory->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
879
880 // "What's This?" help can be found in the ui file
881
882 reload();
883
884 observeChanges(ui->cbRemoveTrailingSpaces);
885 observeChanges(ui->chkDetectEOL);
886 observeChanges(ui->chkEnableBOM);
887 observeChanges(ui->chkNewLineAtEof);
888 observeChanges(ui->cmbEOL);
889 observeChanges(ui->cmbEncoding);
890 observeChanges(ui->cmbEncodingDetection);
891 observeChanges(ui->cmbEncodingFallback);
892 observeChanges(ui->lineLengthLimit);
893
894 observeChanges(uiadv->chkBackupLocalFiles);
895 observeChanges(uiadv->chkBackupRemoteFiles);
896 observeChanges(uiadv->cmbSwapFileMode);
897 connect(uiadv->cmbSwapFileMode, qOverload<int>(&QComboBox::currentIndexChanged), this, &KateSaveConfigTab::swapFileModeChanged);
898
899 observeChanges(uiadv->edtBackupPrefix);
900 observeChanges(uiadv->edtBackupSuffix);
901 observeChanges(uiadv->kurlSwapDirectory);
902 observeChanges(uiadv->spbSwapFileSync);
903
904 internalLayout->addWidget(newWidget);
905 internalLayout2->addWidget(newWidget2);
906
907 // add all tabs
908 tabWidget->insertTab(0, tmpWidget, i18n("General"));
909 tabWidget->insertTab(1, tmpWidget2, i18n("Advanced"));
910 tabWidget->insertTab(2, modeConfigPage, modeConfigPage->name());
911
912 observeChanges(modeConfigPage);
913
914 layout->addWidget(tabWidget);
915
916 // support variable expansion in backup prefix/suffix
917 KTextEditor::Editor::instance()->addVariableExpansion({uiadv->edtBackupPrefix, uiadv->edtBackupSuffix},
918 {QStringLiteral("Date:Locale"),
919 QStringLiteral("Date:ISO"),
920 QStringLiteral("Date:"),
921 QStringLiteral("Time:Locale"),
922 QStringLiteral("Time:ISO"),
923 QStringLiteral("Time:"),
924 QStringLiteral("ENV:"),
925 QStringLiteral("JS:"),
926 QStringLiteral("UUID")});
927 }
928
~KateSaveConfigTab()929 KateSaveConfigTab::~KateSaveConfigTab()
930 {
931 delete uiadv;
932 delete ui;
933 }
934
swapFileModeChanged(int idx)935 void KateSaveConfigTab::swapFileModeChanged(int idx)
936 {
937 const KateDocumentConfig::SwapFileMode mode = static_cast<KateDocumentConfig::SwapFileMode>(idx);
938 switch (mode) {
939 case KateDocumentConfig::DisableSwapFile:
940 uiadv->lblSwapDirectory->setEnabled(false);
941 uiadv->kurlSwapDirectory->setEnabled(false);
942 uiadv->lblSwapFileSync->setEnabled(false);
943 uiadv->spbSwapFileSync->setEnabled(false);
944 break;
945 case KateDocumentConfig::EnableSwapFile:
946 uiadv->lblSwapDirectory->setEnabled(false);
947 uiadv->kurlSwapDirectory->setEnabled(false);
948 uiadv->lblSwapFileSync->setEnabled(true);
949 uiadv->spbSwapFileSync->setEnabled(true);
950 break;
951 case KateDocumentConfig::SwapFilePresetDirectory:
952 uiadv->lblSwapDirectory->setEnabled(true);
953 uiadv->kurlSwapDirectory->setEnabled(true);
954 uiadv->lblSwapFileSync->setEnabled(true);
955 uiadv->spbSwapFileSync->setEnabled(true);
956 break;
957 }
958 }
959
apply()960 void KateSaveConfigTab::apply()
961 {
962 modeConfigPage->apply();
963
964 // nothing changed, no need to apply stuff
965 if (!hasChanged()) {
966 return;
967 }
968 m_changed = false;
969
970 KateGlobalConfig::global()->configStart();
971 KateDocumentConfig::global()->configStart();
972
973 if (uiadv->edtBackupSuffix->text().isEmpty() && uiadv->edtBackupPrefix->text().isEmpty()) {
974 KMessageBox::information(this, i18n("You did not provide a backup suffix or prefix. Using default suffix: '~'"), i18n("No Backup Suffix or Prefix"));
975 uiadv->edtBackupSuffix->setText(QStringLiteral("~"));
976 }
977
978 KateDocumentConfig::global()->setBackupOnSaveLocal(uiadv->chkBackupLocalFiles->isChecked());
979 KateDocumentConfig::global()->setBackupOnSaveRemote(uiadv->chkBackupRemoteFiles->isChecked());
980 KateDocumentConfig::global()->setBackupPrefix(uiadv->edtBackupPrefix->text());
981 KateDocumentConfig::global()->setBackupSuffix(uiadv->edtBackupSuffix->text());
982
983 KateDocumentConfig::global()->setSwapFileMode(uiadv->cmbSwapFileMode->currentIndex());
984 KateDocumentConfig::global()->setSwapDirectory(uiadv->kurlSwapDirectory->url().toLocalFile());
985 KateDocumentConfig::global()->setSwapSyncInterval(uiadv->spbSwapFileSync->value());
986
987 KateDocumentConfig::global()->setRemoveSpaces(ui->cbRemoveTrailingSpaces->currentIndex());
988
989 KateDocumentConfig::global()->setNewLineAtEof(ui->chkNewLineAtEof->isChecked());
990
991 // set both standard and fallback encoding
992 KateDocumentConfig::global()->setEncoding(KCharsets::charsets()->encodingForName(ui->cmbEncoding->currentText()));
993
994 KateGlobalConfig::global()->setProberType((KEncodingProber::ProberType)ui->cmbEncodingDetection->currentIndex());
995 KateGlobalConfig::global()->setFallbackEncoding(KCharsets::charsets()->encodingForName(ui->cmbEncodingFallback->currentText()));
996
997 KateDocumentConfig::global()->setEol(ui->cmbEOL->currentIndex());
998 KateDocumentConfig::global()->setAllowEolDetection(ui->chkDetectEOL->isChecked());
999 KateDocumentConfig::global()->setBom(ui->chkEnableBOM->isChecked());
1000
1001 KateDocumentConfig::global()->setLineLengthLimit(ui->lineLengthLimit->value());
1002
1003 KateDocumentConfig::global()->configEnd();
1004 KateGlobalConfig::global()->configEnd();
1005 }
1006
reload()1007 void KateSaveConfigTab::reload()
1008 {
1009 modeConfigPage->reload();
1010
1011 // encodings
1012 ui->cmbEncoding->clear();
1013 ui->cmbEncodingFallback->clear();
1014 QStringList encodings(KCharsets::charsets()->descriptiveEncodingNames());
1015 int insert = 0;
1016 for (int i = 0; i < encodings.count(); i++) {
1017 bool found = false;
1018 QTextCodec *codecForEnc = KCharsets::charsets()->codecForName(KCharsets::charsets()->encodingForName(encodings[i]), found);
1019
1020 if (found) {
1021 ui->cmbEncoding->addItem(encodings[i]);
1022 ui->cmbEncodingFallback->addItem(encodings[i]);
1023
1024 if (codecForEnc == KateDocumentConfig::global()->codec()) {
1025 ui->cmbEncoding->setCurrentIndex(insert);
1026 }
1027
1028 if (codecForEnc == KateGlobalConfig::global()->fallbackCodec()) {
1029 // adjust index for fallback config, has no default!
1030 ui->cmbEncodingFallback->setCurrentIndex(insert);
1031 }
1032
1033 insert++;
1034 }
1035 }
1036
1037 // encoding detection
1038 ui->cmbEncodingDetection->clear();
1039 bool found = false;
1040 for (int i = 0; !KEncodingProber::nameForProberType((KEncodingProber::ProberType)i).isEmpty(); ++i) {
1041 ui->cmbEncodingDetection->addItem(KEncodingProber::nameForProberType((KEncodingProber::ProberType)i));
1042 if (i == KateGlobalConfig::global()->proberType()) {
1043 ui->cmbEncodingDetection->setCurrentIndex(ui->cmbEncodingDetection->count() - 1);
1044 found = true;
1045 }
1046 }
1047 if (!found) {
1048 ui->cmbEncodingDetection->setCurrentIndex(KEncodingProber::Universal);
1049 }
1050
1051 // eol
1052 ui->cmbEOL->setCurrentIndex(KateDocumentConfig::global()->eol());
1053 ui->chkDetectEOL->setChecked(KateDocumentConfig::global()->allowEolDetection());
1054 ui->chkEnableBOM->setChecked(KateDocumentConfig::global()->bom());
1055 ui->lineLengthLimit->setValue(KateDocumentConfig::global()->lineLengthLimit());
1056
1057 ui->cbRemoveTrailingSpaces->setCurrentIndex(KateDocumentConfig::global()->removeSpaces());
1058 ui->chkNewLineAtEof->setChecked(KateDocumentConfig::global()->newLineAtEof());
1059
1060 // other stuff
1061 uiadv->chkBackupLocalFiles->setChecked(KateDocumentConfig::global()->backupOnSaveLocal());
1062 uiadv->chkBackupRemoteFiles->setChecked(KateDocumentConfig::global()->backupOnSaveRemote());
1063 uiadv->edtBackupPrefix->setText(KateDocumentConfig::global()->backupPrefix());
1064 uiadv->edtBackupSuffix->setText(KateDocumentConfig::global()->backupSuffix());
1065
1066 uiadv->cmbSwapFileMode->setCurrentIndex(KateDocumentConfig::global()->swapFileMode());
1067 uiadv->kurlSwapDirectory->setUrl(QUrl::fromLocalFile(KateDocumentConfig::global()->swapDirectory()));
1068 uiadv->spbSwapFileSync->setValue(KateDocumentConfig::global()->swapSyncInterval());
1069 swapFileModeChanged(KateDocumentConfig::global()->swapFileMode());
1070 }
1071
reset()1072 void KateSaveConfigTab::reset()
1073 {
1074 modeConfigPage->reset();
1075 }
1076
defaults()1077 void KateSaveConfigTab::defaults()
1078 {
1079 modeConfigPage->defaults();
1080
1081 ui->cbRemoveTrailingSpaces->setCurrentIndex(0);
1082
1083 uiadv->chkBackupLocalFiles->setChecked(true);
1084 uiadv->chkBackupRemoteFiles->setChecked(false);
1085 uiadv->edtBackupPrefix->setText(QString());
1086 uiadv->edtBackupSuffix->setText(QStringLiteral("~"));
1087
1088 uiadv->cmbSwapFileMode->setCurrentIndex(1);
1089 uiadv->kurlSwapDirectory->setDisabled(true);
1090 uiadv->lblSwapDirectory->setDisabled(true);
1091 uiadv->spbSwapFileSync->setValue(15);
1092 }
1093
name() const1094 QString KateSaveConfigTab::name() const
1095 {
1096 return i18n("Open/Save");
1097 }
1098
fullName() const1099 QString KateSaveConfigTab::fullName() const
1100 {
1101 return i18n("File Opening & Saving");
1102 }
1103
icon() const1104 QIcon KateSaveConfigTab::icon() const
1105 {
1106 return QIcon::fromTheme(QStringLiteral("document-save"));
1107 }
1108
1109 // END KateSaveConfigTab
1110
1111 // BEGIN KateGotoBar
KateGotoBar(KTextEditor::View * view,QWidget * parent)1112 KateGotoBar::KateGotoBar(KTextEditor::View *view, QWidget *parent)
1113 : KateViewBarWidget(true, parent)
1114 , m_view(view)
1115 {
1116 Q_ASSERT(m_view != nullptr); // this bar widget is pointless w/o a view
1117
1118 QHBoxLayout *topLayout = new QHBoxLayout(centralWidget());
1119 topLayout->setContentsMargins(0, 0, 0, 0);
1120
1121 QToolButton *btn = new QToolButton(this);
1122 btn->setAutoRaise(true);
1123 btn->setMinimumSize(QSize(1, btn->minimumSizeHint().height()));
1124 btn->setText(i18n("&Line:"));
1125 btn->setToolTip(i18n("Go to line number from clipboard"));
1126 connect(btn, &QToolButton::clicked, this, &KateGotoBar::gotoClipboard);
1127 topLayout->addWidget(btn);
1128
1129 m_gotoRange = new QSpinBox(this);
1130 topLayout->addWidget(m_gotoRange, 1);
1131 topLayout->setStretchFactor(m_gotoRange, 0);
1132
1133 btn = new QToolButton(this);
1134 btn->setAutoRaise(true);
1135 btn->setMinimumSize(QSize(1, btn->minimumSizeHint().height()));
1136 btn->setText(i18n("Go to"));
1137 btn->setIcon(QIcon::fromTheme(QStringLiteral("go-jump")));
1138 btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
1139 connect(btn, &QToolButton::clicked, this, &KateGotoBar::gotoLine);
1140 topLayout->addWidget(btn);
1141
1142 btn = m_modifiedUp = new QToolButton(this);
1143 btn->setAutoRaise(true);
1144 btn->setMinimumSize(QSize(1, btn->minimumSizeHint().height()));
1145 btn->setDefaultAction(m_view->action("modified_line_up"));
1146 btn->setIcon(QIcon::fromTheme(QStringLiteral("go-up-search")));
1147 btn->setText(QString());
1148 btn->installEventFilter(this);
1149 topLayout->addWidget(btn);
1150
1151 btn = m_modifiedDown = new QToolButton(this);
1152 btn->setAutoRaise(true);
1153 btn->setMinimumSize(QSize(1, btn->minimumSizeHint().height()));
1154 btn->setDefaultAction(m_view->action("modified_line_down"));
1155 btn->setIcon(QIcon::fromTheme(QStringLiteral("go-down-search")));
1156 btn->setText(QString());
1157 btn->installEventFilter(this);
1158 topLayout->addWidget(btn);
1159
1160 topLayout->addStretch();
1161
1162 setFocusProxy(m_gotoRange);
1163 }
1164
showEvent(QShowEvent * event)1165 void KateGotoBar::showEvent(QShowEvent *event)
1166 {
1167 Q_UNUSED(event)
1168 // Catch rare cases where the bar is visible while document is edited
1169 connect(m_view->document(), &KTextEditor::Document::textChanged, this, &KateGotoBar::updateData);
1170 }
1171
closed()1172 void KateGotoBar::closed()
1173 {
1174 disconnect(m_view->document(), &KTextEditor::Document::textChanged, this, &KateGotoBar::updateData);
1175 }
1176
eventFilter(QObject * object,QEvent * event)1177 bool KateGotoBar::eventFilter(QObject *object, QEvent *event)
1178 {
1179 if (object == m_modifiedUp || object == m_modifiedDown) {
1180 if (event->type() != QEvent::Wheel) {
1181 return false;
1182 }
1183
1184 int delta = static_cast<QWheelEvent *>(event)->angleDelta().y();
1185 // Reset m_wheelDelta when scroll direction change
1186 if (m_wheelDelta != 0 && (m_wheelDelta < 0) != (delta < 0)) {
1187 m_wheelDelta = 0;
1188 }
1189
1190 m_wheelDelta += delta;
1191
1192 if (m_wheelDelta >= 120) {
1193 m_wheelDelta = 0;
1194 m_modifiedUp->click();
1195 } else if (m_wheelDelta <= -120) {
1196 m_wheelDelta = 0;
1197 m_modifiedDown->click();
1198 }
1199 }
1200
1201 return false;
1202 }
1203
gotoClipboard()1204 void KateGotoBar::gotoClipboard()
1205 {
1206 static const QRegularExpression rx(QStringLiteral("-?\\d+"));
1207 const int lineNo = rx.match(QApplication::clipboard()->text(QClipboard::Selection)).captured().toInt();
1208 if (lineNo >= m_gotoRange->minimum() && lineNo <= m_gotoRange->maximum()) {
1209 m_gotoRange->setValue(lineNo);
1210 gotoLine();
1211 } else {
1212 QPointer<KTextEditor::Message> message = new KTextEditor::Message(i18n("No valid line number found in clipboard"));
1213 message->setWordWrap(true);
1214 message->setAutoHide(2000);
1215 message->setPosition(KTextEditor::Message::BottomInView);
1216 message->setView(m_view), m_view->document()->postMessage(message);
1217 }
1218 }
1219
updateData()1220 void KateGotoBar::updateData()
1221 {
1222 int lines = m_view->document()->lines();
1223 m_gotoRange->setMinimum(-lines);
1224 m_gotoRange->setMaximum(lines);
1225 if (!isVisible()) {
1226 m_gotoRange->setValue(m_view->cursorPosition().line() + 1);
1227 m_gotoRange->adjustSize(); // ### does not respect the range :-(
1228 }
1229
1230 m_gotoRange->selectAll();
1231 }
1232
keyPressEvent(QKeyEvent * event)1233 void KateGotoBar::keyPressEvent(QKeyEvent *event)
1234 {
1235 int key = event->key();
1236 if (key == Qt::Key_Return || key == Qt::Key_Enter) {
1237 gotoLine();
1238 return;
1239 }
1240 KateViewBarWidget::keyPressEvent(event);
1241 }
1242
gotoLine()1243 void KateGotoBar::gotoLine()
1244 {
1245 KTextEditor::ViewPrivate *kv = qobject_cast<KTextEditor::ViewPrivate *>(m_view);
1246 if (kv && kv->selection() && !kv->config()->persistentSelection()) {
1247 kv->clearSelection();
1248 }
1249
1250 int gotoValue = m_gotoRange->value();
1251 if (gotoValue < 0) {
1252 gotoValue += m_view->document()->lines();
1253 } else if (gotoValue > 0) {
1254 gotoValue -= 1;
1255 }
1256
1257 m_view->setCursorPosition(KTextEditor::Cursor(gotoValue, 0));
1258 m_view->setFocus();
1259 Q_EMIT hideMe();
1260 }
1261 // END KateGotoBar
1262
1263 // BEGIN KateDictionaryBar
KateDictionaryBar(KTextEditor::ViewPrivate * view,QWidget * parent)1264 KateDictionaryBar::KateDictionaryBar(KTextEditor::ViewPrivate *view, QWidget *parent)
1265 : KateViewBarWidget(true, parent)
1266 , m_view(view)
1267 {
1268 Q_ASSERT(m_view != nullptr); // this bar widget is pointless w/o a view
1269
1270 QHBoxLayout *topLayout = new QHBoxLayout(centralWidget());
1271 topLayout->setContentsMargins(0, 0, 0, 0);
1272 // topLayout->setSpacing(spacingHint());
1273 m_dictionaryComboBox = new Sonnet::DictionaryComboBox(centralWidget());
1274 connect(m_dictionaryComboBox, &Sonnet::DictionaryComboBox::dictionaryChanged, this, &KateDictionaryBar::dictionaryChanged);
1275 connect(view->doc(), &KTextEditor::DocumentPrivate::defaultDictionaryChanged, this, &KateDictionaryBar::updateData);
1276 QLabel *label = new QLabel(i18n("Dictionary:"), centralWidget());
1277 label->setBuddy(m_dictionaryComboBox);
1278
1279 topLayout->addWidget(label);
1280 topLayout->addWidget(m_dictionaryComboBox, 1);
1281 topLayout->setStretchFactor(m_dictionaryComboBox, 0);
1282 topLayout->addStretch();
1283 }
1284
~KateDictionaryBar()1285 KateDictionaryBar::~KateDictionaryBar()
1286 {
1287 }
1288
updateData()1289 void KateDictionaryBar::updateData()
1290 {
1291 KTextEditor::DocumentPrivate *document = m_view->doc();
1292 QString dictionary = document->defaultDictionary();
1293 if (dictionary.isEmpty()) {
1294 dictionary = Sonnet::Speller().defaultLanguage();
1295 }
1296 m_dictionaryComboBox->setCurrentByDictionary(dictionary);
1297 }
1298
dictionaryChanged(const QString & dictionary)1299 void KateDictionaryBar::dictionaryChanged(const QString &dictionary)
1300 {
1301 const KTextEditor::Range selection = m_view->selectionRange();
1302 if (selection.isValid() && !selection.isEmpty()) {
1303 const bool blockmode = m_view->blockSelection();
1304 m_view->doc()->setDictionary(dictionary, selection, blockmode);
1305 } else {
1306 m_view->doc()->setDefaultDictionary(dictionary);
1307 }
1308 }
1309
1310 // END KateGotoBar
1311
1312 // BEGIN KateModOnHdPrompt
KateModOnHdPrompt(KTextEditor::DocumentPrivate * doc,KTextEditor::ModificationInterface::ModifiedOnDiskReason modtype,const QString & reason)1313 KateModOnHdPrompt::KateModOnHdPrompt(KTextEditor::DocumentPrivate *doc, KTextEditor::ModificationInterface::ModifiedOnDiskReason modtype, const QString &reason)
1314 : QObject(doc)
1315 , m_doc(doc)
1316 , m_modtype(modtype)
1317 , m_proc(nullptr)
1318 , m_diffFile(nullptr)
1319 , m_diffAction(nullptr)
1320 {
1321 m_message = new KTextEditor::Message(reason, KTextEditor::Message::Information);
1322 m_message->setPosition(KTextEditor::Message::AboveView);
1323 m_message->setWordWrap(true);
1324
1325 // If the file isn't deleted, present a diff button
1326 const bool onDiskDeleted = modtype == KTextEditor::ModificationInterface::OnDiskDeleted;
1327 if (!onDiskDeleted) {
1328 QAction *aAutoReload = new QAction(i18n("Enable Auto Reload"), this);
1329 aAutoReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
1330 aAutoReload->setToolTip(i18n("Will never again warn about on disk changes but always reload."));
1331 m_message->addAction(aAutoReload, false);
1332 connect(aAutoReload, &QAction::triggered, this, &KateModOnHdPrompt::autoReloadTriggered);
1333
1334 if (!QStandardPaths::findExecutable(QStringLiteral("diff")).isEmpty()) {
1335 m_diffAction = new QAction(i18n("View &Difference"), this);
1336 m_diffAction->setIcon(QIcon::fromTheme(QStringLiteral("document-multiple")));
1337 m_diffAction->setToolTip(i18n("Shows a diff of the changes"));
1338 m_message->addAction(m_diffAction, false);
1339 connect(m_diffAction, &QAction::triggered, this, &KateModOnHdPrompt::slotDiff);
1340 }
1341
1342 QAction *aReload = new QAction(i18n("&Reload"), this);
1343 aReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
1344 aReload->setToolTip(i18n("Reload the file from disk. Unsaved changes will be lost."));
1345 m_message->addAction(aReload);
1346 connect(aReload, &QAction::triggered, this, &KateModOnHdPrompt::reloadTriggered);
1347 } else {
1348 QAction *closeFile = new QAction(i18nc("@action:button closes the opened file", "&Close File"), this);
1349 closeFile->setIcon(QIcon::fromTheme(QStringLiteral("document-close")));
1350 closeFile->setToolTip(i18n("Close the file, discarding its content."));
1351 m_message->addAction(closeFile, false);
1352 connect(closeFile, &QAction::triggered, this, &KateModOnHdPrompt::closeTriggered);
1353
1354 QAction *aSaveAs = new QAction(i18n("&Save As..."), this);
1355 aSaveAs->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
1356 aSaveAs->setToolTip(i18n("Lets you select a location and save the file again."));
1357 m_message->addAction(aSaveAs, false);
1358 connect(aSaveAs, &QAction::triggered, this, &KateModOnHdPrompt::saveAsTriggered);
1359 }
1360
1361 QAction *aIgnore = new QAction(i18n("&Ignore"), this);
1362 aIgnore->setToolTip(i18n("Ignores the changes on disk without any action."));
1363 aIgnore->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
1364 m_message->addAction(aIgnore);
1365 connect(aIgnore, &QAction::triggered, this, &KateModOnHdPrompt::ignoreTriggered);
1366
1367 m_doc->postMessage(m_message);
1368 }
1369
~KateModOnHdPrompt()1370 KateModOnHdPrompt::~KateModOnHdPrompt()
1371 {
1372 delete m_proc;
1373 m_proc = nullptr;
1374 if (m_diffFile) {
1375 m_diffFile->setAutoRemove(true);
1376 delete m_diffFile;
1377 m_diffFile = nullptr;
1378 }
1379 delete m_message;
1380 }
1381
slotDiff()1382 void KateModOnHdPrompt::slotDiff()
1383 {
1384 if (m_diffFile) {
1385 return;
1386 }
1387
1388 m_diffFile = new QTemporaryFile(QLatin1String("XXXXXX.diff"));
1389 m_diffFile->open();
1390
1391 // Start a KProcess that creates a diff
1392 m_proc = new KProcess(this);
1393 m_proc->setOutputChannelMode(KProcess::MergedChannels);
1394 *m_proc << QStringLiteral("diff") << QStringLiteral("-u") << QStringLiteral("-") << m_doc->url().toLocalFile();
1395 connect(m_proc, &KProcess::readyRead, this, &KateModOnHdPrompt::slotDataAvailable);
1396 connect(m_proc, &KProcess::finished, this, &KateModOnHdPrompt::slotPDone);
1397
1398 // disable the diff button, to hinder the user to run it twice.
1399 m_diffAction->setEnabled(false);
1400
1401 m_proc->start();
1402
1403 QTextStream ts(m_proc);
1404 int lastln = m_doc->lines() - 1;
1405 for (int l = 0; l < lastln; ++l) {
1406 ts << m_doc->line(l) << '\n';
1407 }
1408 ts << m_doc->line(lastln);
1409 ts.flush();
1410 m_proc->closeWriteChannel();
1411 }
1412
slotDataAvailable()1413 void KateModOnHdPrompt::slotDataAvailable()
1414 {
1415 m_diffFile->write(m_proc->readAll());
1416 }
1417
slotPDone()1418 void KateModOnHdPrompt::slotPDone()
1419 {
1420 m_diffAction->setEnabled(true);
1421
1422 const QProcess::ExitStatus es = m_proc->exitStatus();
1423 delete m_proc;
1424 m_proc = nullptr;
1425
1426 if (es != QProcess::NormalExit) {
1427 KMessageBox::sorry(nullptr,
1428 i18n("The diff command failed. Please make sure that "
1429 "diff(1) is installed and in your PATH."),
1430 i18n("Error Creating Diff"));
1431 delete m_diffFile;
1432 m_diffFile = nullptr;
1433 return;
1434 }
1435
1436 if (m_diffFile->size() == 0) {
1437 KMessageBox::information(nullptr, i18n("The files are identical."), i18n("Diff Output"));
1438 delete m_diffFile;
1439 m_diffFile = nullptr;
1440 return;
1441 }
1442
1443 m_diffFile->setAutoRemove(false);
1444 QUrl url = QUrl::fromLocalFile(m_diffFile->fileName());
1445 delete m_diffFile;
1446 m_diffFile = nullptr;
1447
1448 KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url, QStringLiteral("text/x-patch"));
1449 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, nullptr /*TODO window*/));
1450 job->setDeleteTemporaryFile(true); // delete the file, once the client exits
1451 job->start();
1452 }
1453
1454 // END KateModOnHdPrompt
1455