kateconfigdialog.cpp 18.5 KB
Newer Older
1
/* This file is part of the KDE project
2
3
4
5
6
   SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
   SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
   SPDX-FileCopyrightText: 2007 Mirko Stocker <me@misto.ch>

   SPDX-License-Identifier: LGPL-2.0-only
7
8
9
10
11
*/

#include "kateconfigdialog.h"

#include "kateapp.h"
12
#include "kateconfigplugindialogpage.h"
13
#include "katedebug.h"
14
15
16
#include "katedocmanager.h"
#include "katemainwindow.h"
#include "katepluginmanager.h"
17
#include "katequickopenmodel.h"
18
19
#include "katesessionmanager.h"
#include "kateviewmanager.h"
20

Christoph Cullmann's avatar
Christoph Cullmann committed
21
#include <KTextEditor/ConfigPage>
22

23
#include <KConfigGroup>
Michal Humpula's avatar
Michal Humpula committed
24
#include <KLocalizedString>
25
#include <KMessageBox>
26
#include <KPluralHandlingSpinBox>
27
28
#include <KSharedConfig>
#include <KStandardAction>
Michal Humpula's avatar
Michal Humpula committed
29
30

#include <QCheckBox>
31
32
#include <QComboBox>
#include <QDesktopServices>
33
#include <QDialogButtonBox>
Michal Humpula's avatar
Michal Humpula committed
34
35
36
37
#include <QFrame>
#include <QGroupBox>
#include <QLabel>
#include <QVBoxLayout>
38

39
KateConfigDialog::KateConfigDialog(KateMainWindow *parent)
40
41
    : KPageDialog(parent)
    , m_mainWindow(parent)
42
{
43
44
45
    setWindowTitle(i18n("Configure"));
    setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Help);

46
47
48
    // first: add the KTextEditor config pages
    // rational: most people want to alter e.g. the fonts, the colors or some other editor stuff first
    addEditorPages();
49

50
51
52
53
54
55
56
57
    // second: add out own config pages
    // this includes all plugin config pages, added to the bottom
    addBehaviorPage();
    addSessionPage();
    addFeedbackPage();
    addPluginsPage();
    addPluginPages();

58
59
60
61
    // ensure no stray signals already set this!
    m_dataChanged = false;
    buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(false);

62
63
64
65
66
    // handle dialog actions
    connect(this, &KateConfigDialog::accepted, this, &KateConfigDialog::slotApply);
    connect(buttonBox()->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &KateConfigDialog::slotApply);
    connect(buttonBox()->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &KateConfigDialog::slotHelp);
}
67

68
69
70
71
void KateConfigDialog::addBehaviorPage()
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup cgGeneral = KConfigGroup(config, "General");
72
73

    QFrame *generalFrame = new QFrame;
74
75
    KPageWidgetItem *item = addPage(generalFrame, i18n("Behavior"));
    item->setHeader(i18n("Behavior Options"));
76
    item->setIcon(QIcon::fromTheme(QStringLiteral("preferences-system-windows-behavior")));
77
78

    QVBoxLayout *layout = new QVBoxLayout(generalFrame);
Laurent Montel's avatar
Laurent Montel committed
79
    layout->setContentsMargins(0, 0, 0, 0);
80
81
82
83
84
85
86

    // GROUP with the one below: "Behavior"
    QGroupBox *buttonGroup = new QGroupBox(i18n("&Behavior"), generalFrame);
    QVBoxLayout *vbox = new QVBoxLayout;
    layout->addWidget(buttonGroup);

    // modified files notification
87
    m_modNotifications = new QCheckBox(i18n("Wa&rn about files modified by foreign processes"), buttonGroup);
88
    m_modNotifications->setChecked(m_mainWindow->modNotificationEnabled());
89
90
91
92
93
    m_modNotifications->setWhatsThis(
        i18n("If enabled, when Kate receives focus you will be asked what to do with "
             "files that have been modified on the hard disk. If not enabled, you will "
             "be asked what to do with a file that has been modified on the hard disk only "
             "when that file is tried to be saved."));
Laurent Montel's avatar
Laurent Montel committed
94
    connect(m_modNotifications, &QCheckBox::toggled, this, &KateConfigDialog::slotChanged);
95
96

    vbox->addWidget(m_modNotifications);
97

98
99
    buttonGroup->setLayout(vbox);

100
101
    // quick search
    buttonGroup = new QGroupBox(i18n("&Quick Open"), generalFrame);
102
103
104
    vbox = new QVBoxLayout;
    buttonGroup->setLayout(vbox);
    // quick open match mode
105
106
    auto hlayout = new QHBoxLayout;
    auto label = new QLabel(i18n("&Match Mode:"), buttonGroup);
107
    hlayout->addWidget(label);
108
    m_cmbQuickOpenMatchMode = new QComboBox(buttonGroup);
109
110
111
112
113
114
    hlayout->addWidget(m_cmbQuickOpenMatchMode);
    label->setBuddy(m_cmbQuickOpenMatchMode);
    m_cmbQuickOpenMatchMode->addItem(i18n("Filename"), QVariant(KateQuickOpenModel::Columns::FileName));
    m_cmbQuickOpenMatchMode->addItem(i18n("Filepath"), QVariant(KateQuickOpenModel::Columns::FilePath));
    m_cmbQuickOpenMatchMode->setCurrentIndex(m_cmbQuickOpenMatchMode->findData(m_mainWindow->quickOpenMatchMode()));
    m_mainWindow->setQuickOpenMatchMode(m_cmbQuickOpenMatchMode->currentData().toInt());
115
    connect(m_cmbQuickOpenMatchMode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &KateConfigDialog::slotChanged);
116
117
118
119
120
121
122
123
124
125
126
    vbox->addLayout(hlayout);
    // quick open list mode
    hlayout = new QHBoxLayout;
    label = new QLabel(i18n("&List Mode:"), buttonGroup);
    hlayout->addWidget(label);
    m_cmbQuickOpenListMode = new QComboBox(buttonGroup);
    hlayout->addWidget(m_cmbQuickOpenListMode);
    label->setBuddy(m_cmbQuickOpenListMode);
    m_cmbQuickOpenListMode->addItem(i18n("Current Project Files"), QVariant(KateQuickOpenModel::List::CurrentProject));
    m_cmbQuickOpenListMode->addItem(i18n("All Projects Files"), QVariant(KateQuickOpenModel::List::AllProjects));
    m_cmbQuickOpenListMode->setCurrentIndex(m_cmbQuickOpenListMode->findData(m_mainWindow->quickOpenListMode()));
127
    m_mainWindow->setQuickOpenListMode(static_cast<KateQuickOpenModel::List>(m_cmbQuickOpenListMode->currentData().toInt()));
128
129
    connect(m_cmbQuickOpenListMode, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &KateConfigDialog::slotChanged);
    vbox->addLayout(hlayout);
130
131
    layout->addWidget(buttonGroup);

132
    // tabbar => we allow to configure some limit on number of tabs to show
133
    buttonGroup = new QGroupBox(i18n("&Tabs"), generalFrame);
134
135
136
    vbox = new QVBoxLayout;
    buttonGroup->setLayout(vbox);
    hlayout = new QHBoxLayout;
137
    label = new QLabel(i18n("&Limit number of tabs:"), buttonGroup);
138
139
140
141
142
143
144
145
146
    hlayout->addWidget(label);
    m_tabLimit = new QSpinBox(buttonGroup);
    hlayout->addWidget(m_tabLimit);
    label->setBuddy(m_tabLimit);
    m_tabLimit->setRange(0, 256);
    m_tabLimit->setSpecialValueText(i18n("Unlimited"));
    m_tabLimit->setValue(cgGeneral.readEntry("Tabbar Tab Limit", 0));
    connect(m_tabLimit, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &KateConfigDialog::slotChanged);
    vbox->addLayout(hlayout);
147
148
149
150
151
152
153
154
155
156
157
158
159

    m_showTabCloseButton = new QCheckBox(i18n("&Show close button"), buttonGroup);
    m_showTabCloseButton->setChecked(cgGeneral.readEntry("Show Tabs Close Button", true));
    m_showTabCloseButton->setToolTip(i18n("When checked each tab will display a close button."));
    connect(m_showTabCloseButton, &QCheckBox::toggled, this, &KateConfigDialog::slotChanged);
    vbox->addWidget(m_showTabCloseButton);

    m_expandTabs = new QCheckBox(i18n("&Expand tabs"), buttonGroup);
    m_expandTabs->setChecked(cgGeneral.readEntry("Expand Tabs", true));
    m_expandTabs->setToolTip(i18n("When checked tabs take as much size as possible."));
    connect(m_expandTabs, &QCheckBox::toggled, this, &KateConfigDialog::slotChanged);
    vbox->addWidget(m_expandTabs);

160
161
    layout->addWidget(buttonGroup);

162
    layout->addStretch(1); // :-] works correct without autoadd
163
164
165
166
167
168
}

void KateConfigDialog::addSessionPage()
{
    KSharedConfig::Ptr config = KSharedConfig::openConfig();
    KConfigGroup cgGeneral = KConfigGroup(config, "General");
169

170
    QWidget *sessionsPage = new QWidget();
171
    auto item = addPage(sessionsPage, i18n("Session"));
172
173
174
    item->setHeader(i18n("Session Management"));
    item->setIcon(QIcon::fromTheme(QStringLiteral("view-history")));

175
176
177
178
179
180
181
182
183
184
185
186
    sessionConfigUi.setupUi(sessionsPage);

    // save meta infos
    sessionConfigUi.saveMetaInfos->setChecked(KateApp::self()->documentManager()->getSaveMetaInfos());
    connect(sessionConfigUi.saveMetaInfos, &QGroupBox::toggled, this, &KateConfigDialog::slotChanged);

    // meta infos days
    sessionConfigUi.daysMetaInfos->setMaximum(180);
    sessionConfigUi.daysMetaInfos->setSpecialValueText(i18nc("The special case of 'Delete unused meta-information after'", "(never)"));
    sessionConfigUi.daysMetaInfos->setSuffix(ki18ncp("The suffix of 'Delete unused meta-information after'", " day", " days"));
    sessionConfigUi.daysMetaInfos->setValue(KateApp::self()->documentManager()->getDaysMetaInfos());
    connect(sessionConfigUi.daysMetaInfos, static_cast<void (KPluralHandlingSpinBox::*)(int)>(&KPluralHandlingSpinBox::valueChanged), this, &KateConfigDialog::slotChanged);
187
188

    // restore view  config
189
190
    sessionConfigUi.restoreVC->setChecked(cgGeneral.readEntry("Restore Window Configuration", true));
    connect(sessionConfigUi.restoreVC, &QCheckBox::toggled, this, &KateConfigDialog::slotChanged);
191

192
193
    sessionConfigUi.spinBoxRecentFilesCount->setValue(recentFilesMaxCount());
    connect(sessionConfigUi.spinBoxRecentFilesCount, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &KateConfigDialog::slotChanged);
194

195
    QString sesStart(cgGeneral.readEntry("Startup Session", "manual"));
196
    if (sesStart == QLatin1String("new"))
197
        sessionConfigUi.startNewSessionRadioButton->setChecked(true);
198
    else if (sesStart == QLatin1String("last"))
199
        sessionConfigUi.loadLastUserSessionRadioButton->setChecked(true);
200
    else
201
        sessionConfigUi.manuallyChooseSessionRadioButton->setChecked(true);
202

203
204
205
    connect(sessionConfigUi.startNewSessionRadioButton, &QRadioButton::toggled, this, &KateConfigDialog::slotChanged);
    connect(sessionConfigUi.loadLastUserSessionRadioButton, &QRadioButton::toggled, this, &KateConfigDialog::slotChanged);
    connect(sessionConfigUi.manuallyChooseSessionRadioButton, &QRadioButton::toggled, this, &KateConfigDialog::slotChanged);
206
207
208
209

    // Closing last file closes Kate
    sessionConfigUi.modCloseAfterLast->setChecked(m_mainWindow->modCloseAfterLast());
    connect(sessionConfigUi.modCloseAfterLast, &QCheckBox::toggled, this, &KateConfigDialog::slotChanged);
210
}
211

212
213
void KateConfigDialog::addPluginsPage()
{
214
215
    QFrame *page = new QFrame(this);
    QVBoxLayout *vlayout = new QVBoxLayout(page);
Laurent Montel's avatar
Laurent Montel committed
216
    vlayout->setContentsMargins(0, 0, 0, 0);
217
218
219
220
    vlayout->setSpacing(0);

    KateConfigPluginPage *configPluginPage = new KateConfigPluginPage(page, this);
    vlayout->addWidget(configPluginPage);
Laurent Montel's avatar
Laurent Montel committed
221
    connect(configPluginPage, &KateConfigPluginPage::changed, this, &KateConfigDialog::slotChanged);
222

223
    auto item = addPage(page, i18n("Plugins"));
224
225
    item->setHeader(i18n("Plugin Manager"));
    item->setIcon(QIcon::fromTheme(QStringLiteral("preferences-plugin")));
226
}
227

228
229
void KateConfigDialog::addFeedbackPage()
{
230
231
#ifdef WITH_KUSERFEEDBACK
    // KUserFeedback Config
232
233
    auto page = new QFrame(this);
    auto vlayout = new QVBoxLayout(page);
234
235
236
237
238
239
240
241
    vlayout->setContentsMargins(0, 0, 0, 0);
    vlayout->setSpacing(0);

    m_userFeedbackWidget = new KUserFeedback::FeedbackConfigWidget(page);
    m_userFeedbackWidget->setFeedbackProvider(&KateApp::self()->userFeedbackProvider());
    connect(m_userFeedbackWidget, &KUserFeedback::FeedbackConfigWidget::configurationChanged, this, &KateConfigDialog::slotChanged);
    vlayout->addWidget(m_userFeedbackWidget);

242
    auto item = addPage(page, i18n("User Feedback"));
243
244
245
    item->setHeader(i18n("User Feedback"));
    item->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-locale")));
#endif
246
247
}

248
void KateConfigDialog::addPluginPages()
249
{
250
251
252
253
254
255
    const KatePluginList &pluginList(KateApp::self()->pluginManager()->pluginList());
    for (const KatePluginInfo &plugin : pluginList) {
        if (plugin.load) {
            addPluginPage(plugin.plugin);
        }
    }
256
}
257

258
259
260
261
void KateConfigDialog::addEditorPages()
{
    for (int i = 0; i < KTextEditor::Editor::instance()->configPages(); ++i) {
        KTextEditor::ConfigPage *page = KTextEditor::Editor::instance()->configPage(i, this);
Laurent Montel's avatar
Laurent Montel committed
262
        connect(page, &KTextEditor::ConfigPage::changed, this, &KateConfigDialog::slotChanged);
263
        m_editorPages.push_back(page);
264
        KPageWidgetItem *item = addPage(page, page->name());
265
266
        item->setHeader(page->fullName());
        item->setIcon(page->icon());
267
    }
Christoph Cullmann's avatar
jowenn:    
Christoph Cullmann committed
268
269
}

270
void KateConfigDialog::addPluginPage(KTextEditor::Plugin *plugin)
271
{
272
    for (int i = 0; i < plugin->configPages(); i++) {
273
274
275
        QFrame *page = new QFrame();
        QVBoxLayout *layout = new QVBoxLayout(page);
        layout->setSpacing(0);
Laurent Montel's avatar
Laurent Montel committed
276
        layout->setContentsMargins(0, 0, 0, 0);
277

278
        KTextEditor::ConfigPage *cp = plugin->configPage(i, page);
279
280
        page->layout()->addWidget(cp);

281
        KPageWidgetItem *item = addPage(page, cp->name());
282
283
        item->setHeader(cp->fullName());
        item->setIcon(cp->icon());
284
285
286
287

        PluginPageListItem *info = new PluginPageListItem;
        info->plugin = plugin;
        info->pageParent = page;
288
        info->pluginPage = cp;
289
290
        info->idInPlugin = i;
        info->pageWidgetItem = item;
Laurent Montel's avatar
Laurent Montel committed
291
        connect(info->pluginPage, &KTextEditor::ConfigPage::changed, this, &KateConfigDialog::slotChanged);
292
293
        m_pluginPages.insert(item, info);
    }
Christoph Cullmann's avatar
jowenn:    
Christoph Cullmann committed
294
295
}

296
void KateConfigDialog::removePluginPage(KTextEditor::Plugin *plugin)
297
{
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
    QList<KPageWidgetItem *> remove;
    for (QHash<KPageWidgetItem *, PluginPageListItem *>::const_iterator it = m_pluginPages.constBegin(); it != m_pluginPages.constEnd(); ++it) {
        PluginPageListItem *pli = it.value();
        if (!pli) {
            continue;
        }
        if (pli->plugin == plugin) {
            remove.append(it.key());
        }
    }

    qCDebug(LOG_KATE) << remove.count();
    while (!remove.isEmpty()) {
        KPageWidgetItem *wItem = remove.takeLast();
        PluginPageListItem *pItem = m_pluginPages.take(wItem);
        delete pItem->pluginPage;
        delete pItem->pageParent;
        removePage(wItem);
        delete pItem;
    }
318
319
}

320
321
void KateConfigDialog::slotApply()
{
322
323
324
325
326
327
    KSharedConfig::Ptr config = KSharedConfig::openConfig();

    // if data changed apply the kate app stuff
    if (m_dataChanged) {
        KConfigGroup cg = KConfigGroup(config, "General");

328
        cg.writeEntry("Restore Window Configuration", sessionConfigUi.restoreVC->isChecked());
329

330
        cg.writeEntry("Recent File List Entry Count", sessionConfigUi.spinBoxRecentFilesCount->value());
331

332
        if (sessionConfigUi.startNewSessionRadioButton->isChecked()) {
333
            cg.writeEntry("Startup Session", "new");
334
        } else if (sessionConfigUi.loadLastUserSessionRadioButton->isChecked()) {
335
336
337
338
339
            cg.writeEntry("Startup Session", "last");
        } else {
            cg.writeEntry("Startup Session", "manual");
        }

340
341
        cg.writeEntry("Save Meta Infos", sessionConfigUi.saveMetaInfos->isChecked());
        KateApp::self()->documentManager()->setSaveMetaInfos(sessionConfigUi.saveMetaInfos->isChecked());
342

343
344
        cg.writeEntry("Days Meta Infos", sessionConfigUi.daysMetaInfos->value());
        KateApp::self()->documentManager()->setDaysMetaInfos(sessionConfigUi.daysMetaInfos->value());
345
346
347
348

        cg.writeEntry("Modified Notification", m_modNotifications->isChecked());
        m_mainWindow->setModNotificationEnabled(m_modNotifications->isChecked());

349
350
        cg.writeEntry("Close After Last", sessionConfigUi.modCloseAfterLast->isChecked());
        m_mainWindow->setModCloseAfterLast(sessionConfigUi.modCloseAfterLast->isChecked());
351

352
353
354
        cg.writeEntry("Quick Open Search Mode", m_cmbQuickOpenMatchMode->currentData().toInt());
        m_mainWindow->setQuickOpenMatchMode(m_cmbQuickOpenMatchMode->currentData().toInt());

355
        cg.writeEntry("Quick Open List Mode", m_cmbQuickOpenListMode->currentData().toInt());
356
        m_mainWindow->setQuickOpenListMode(static_cast<KateQuickOpenModel::List>(m_cmbQuickOpenListMode->currentData().toInt()));
357

358
359
        cg.writeEntry("Tabbar Tab Limit", m_tabLimit->value());

360
361
362
363
        cg.writeEntry("Show Tabs Close Button", m_showTabCloseButton->isChecked());

        cg.writeEntry("Expand Tabs", m_expandTabs->isChecked());

364
        // patch document modified warn state
Christoph Cullmann's avatar
Christoph Cullmann committed
365
        const QList<KTextEditor::Document *> &docs = KateApp::self()->documentManager()->documentList();
366
        for (KTextEditor::Document *doc : docs)
367
368
369
            if (qobject_cast<KTextEditor::ModificationInterface *>(doc)) {
                qobject_cast<KTextEditor::ModificationInterface *>(doc)->setModifiedOnDiskWarning(!m_modNotifications->isChecked());
            }
370
371
372
373

        m_mainWindow->saveOptions();

        // save plugin config !!
Christoph Cullmann's avatar
Christoph Cullmann committed
374
        KateSessionManager *sessionmanager = KateApp::self()->sessionManager();
375
376
        KConfig *sessionConfig = sessionmanager->activeSession()->config();
        KateApp::self()->pluginManager()->writeConfig(sessionConfig);
Christoph Cullmann's avatar
Christoph Cullmann committed
377
378
379
380

#ifdef WITH_KUSERFEEDBACK
        // set current active mode + write back the config for future starts
        KateApp::self()->userFeedbackProvider().setTelemetryMode(m_userFeedbackWidget->telemetryMode());
381
        KateApp::self()->userFeedbackProvider().setSurveyInterval(m_userFeedbackWidget->surveyInterval());
Christoph Cullmann's avatar
Christoph Cullmann committed
382
#endif
383
384
    }

385
    for (PluginPageListItem *plugin : qAsConst(m_pluginPages)) {
386
387
388
389
390
391
392
393
394
        if (!plugin) {
            continue;
        }
        if (plugin->pluginPage) {
            plugin->pluginPage->apply();
        }
    }

    // apply ktexteditor pages
395
    for (KTextEditor::ConfigPage *page : qAsConst(m_editorPages))
396
        page->apply();
Christoph Cullmann's avatar
Christoph Cullmann committed
397

398
    config->sync();
399

400
401
    // emit config change
    if (m_dataChanged) {
Christoph Cullmann's avatar
Christoph Cullmann committed
402
        KateApp::self()->configurationChanged();
403
404
    }

405
406
    m_dataChanged = false;
    buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(false);
407
408
409
410
}

void KateConfigDialog::slotChanged()
{
411
412
    m_dataChanged = true;
    buttonBox()->button(QDialogButtonBox::Apply)->setEnabled(true);
413
}
Adam Treat's avatar
Adam Treat committed
414

415
void KateConfigDialog::showAppPluginPage(KTextEditor::Plugin *p, int id)
416
{
417
    for (PluginPageListItem *plugin : qAsConst(m_pluginPages)) {
418
        if ((plugin->plugin == p) && (id == plugin->idInPlugin)) {
419
420
421
            setCurrentPage(plugin->pageWidgetItem);
            break;
        }
422
423
424
    }
}

Christoph Cullmann's avatar
Christoph Cullmann committed
425
426
void KateConfigDialog::slotHelp()
{
427
    QDesktopServices::openUrl(QUrl(QStringLiteral("help:/")));
Christoph Cullmann's avatar
Christoph Cullmann committed
428
}
429

430
int KateConfigDialog::recentFilesMaxCount()
431
{
432
    int maxItems = KConfigGroup(KSharedConfig::openConfig(), "General").readEntry("Recent File List Entry Count", 10);
433
434
435
    return maxItems;
}

436
437
438
439
440
441
442
void KateConfigDialog::closeEvent(QCloseEvent *event)
{
    if (!m_dataChanged) {
        event->accept();
        return;
    }

443
444
    const auto response =
        KMessageBox::warningYesNoCancel(this, i18n("You have unsaved changes. Do you want to apply the changes or discard them?"), i18n("Warning"), KStandardGuiItem::save(), KStandardGuiItem::discard(), KStandardGuiItem::cancel());
445
    switch (response) {
446
447
448
449
450
451
452
453
454
455
456
    case KMessageBox::Yes:
        slotApply();
        Q_FALLTHROUGH();
    case KMessageBox::No:
        event->accept();
        break;
    case KMessageBox::Cancel:
        event->ignore();
        break;
    default:
        break;
457
458
    }
}