File indexing completed on 2024-04-28 11:45:34

0001 /*
0002     SPDX-FileCopyrightText: 2001-2010 Christoph Cullmann <cullmann@kde.org>
0003     SPDX-FileCopyrightText: 2009 Erlend Hamberg <ehamberg@gmail.com>
0004 
0005     For the addScrollablePage original
0006     SPDX-FileCopyrightText: 2003 Benjamin C Meyer <ben+kdelibs at meyerhome dot net>
0007     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
0008     SPDX-FileCopyrightText: 2004 Michael Brade <brade@kde.org>
0009     SPDX-FileCopyrightText: 2021 Ahmad Samir <a.samirh78@gmail.com>
0010 
0011     SPDX-License-Identifier: LGPL-2.0-or-later
0012 */
0013 
0014 #include "kateglobal.h"
0015 #include "config.h"
0016 
0017 #include <ktexteditor_version.h>
0018 
0019 #include "katebuffer.h"
0020 #include "katecmd.h"
0021 #include "katecmds.h"
0022 #include "kateconfig.h"
0023 #include "katedialogs.h"
0024 #include "katedocument.h"
0025 #include "katehighlightingcmds.h"
0026 #include "katekeywordcompletion.h"
0027 #include "katemodemanager.h"
0028 #include "katescriptmanager.h"
0029 #include "katesedcmd.h"
0030 #include "katesyntaxmanager.h"
0031 #include "katethemeconfig.h"
0032 #include "katevariableexpansionmanager.h"
0033 #include "kateview.h"
0034 #include "katewordcompletion.h"
0035 #include "spellcheck/spellcheck.h"
0036 
0037 #include "katenormalinputmodefactory.h"
0038 #include "kateviinputmodefactory.h"
0039 
0040 #include <KConfigGroup>
0041 #include <KDirWatch>
0042 #include <KLocalizedString>
0043 #include <KPageDialog>
0044 
0045 #include <QApplication>
0046 #include <QBoxLayout>
0047 #include <QClipboard>
0048 #include <QFrame>
0049 #include <QPushButton>
0050 #include <QScreen>
0051 #include <QScrollArea>
0052 #include <QScrollBar>
0053 #include <QStringListModel>
0054 #include <QTimer>
0055 
0056 // BEGIN unit test mode
0057 static bool kateUnitTestMode = false;
0058 
0059 void KTextEditor::EditorPrivate::enableUnitTestMode()
0060 {
0061     kateUnitTestMode = true;
0062 }
0063 
0064 bool KTextEditor::EditorPrivate::unitTestMode()
0065 {
0066     return kateUnitTestMode;
0067 }
0068 // END unit test mode
0069 
0070 KTextEditor::EditorPrivate::EditorPrivate(QPointer<KTextEditor::EditorPrivate> &staticInstance)
0071     : KTextEditor::Editor(this)
0072     , m_aboutData(QStringLiteral("katepart"),
0073                   i18n("Kate Part"),
0074                   QStringLiteral(KTEXTEDITOR_VERSION_STRING),
0075                   i18n("Embeddable editor component"),
0076                   KAboutLicense::LGPL_V2,
0077                   i18n("(c) 2000-2022 The Kate Authors"),
0078                   QString(),
0079                   QStringLiteral("https://kate-editor.org"))
0080     , m_dummyApplication(nullptr)
0081     , m_application(&m_dummyApplication)
0082     , m_dummyMainWindow(nullptr)
0083     , m_searchHistoryModel(nullptr)
0084     , m_replaceHistoryModel(nullptr)
0085 {
0086     // remember this
0087     staticInstance = this;
0088 
0089     // register some datatypes
0090     qRegisterMetaType<KTextEditor::Cursor>("KTextEditor::Cursor");
0091     qRegisterMetaType<KTextEditor::Document *>("KTextEditor::Document*");
0092     qRegisterMetaType<KTextEditor::View *>("KTextEditor::View*");
0093 
0094     //
0095     // fill about data
0096     //
0097     m_aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("https://cullmann.io"));
0098     m_aboutData.addAuthor(i18n("Dominik Haumann"), i18n("Core Developer"), QStringLiteral("dhaumann@kde.org"));
0099     m_aboutData.addAuthor(i18n("Milian Wolff"), i18n("Core Developer"), QStringLiteral("mail@milianw.de"), QStringLiteral("https://milianw.de/"));
0100     m_aboutData.addAuthor(i18n("Joseph Wenninger"),
0101                           i18n("Core Developer"),
0102                           QStringLiteral("jowenn@kde.org"),
0103                           QStringLiteral("http://stud3.tuwien.ac.at/~e9925371"));
0104     m_aboutData.addAuthor(i18n("Erlend Hamberg"), i18n("Vi Input Mode"), QStringLiteral("ehamberg@gmail.com"), QStringLiteral("https://hamberg.no/erlend"));
0105     m_aboutData.addAuthor(i18n("Bernhard Beschow"),
0106                           i18n("Developer"),
0107                           QStringLiteral("bbeschow@cs.tu-berlin.de"),
0108                           QStringLiteral("https://user.cs.tu-berlin.de/~bbeschow"));
0109     m_aboutData.addAuthor(i18n("Anders Lund"), i18n("Core Developer"), QStringLiteral("anders@alweb.dk"), QStringLiteral("https://alweb.dk"));
0110     m_aboutData.addAuthor(i18n("Michel Ludwig"), i18n("On-the-fly spell checking"), QStringLiteral("michel.ludwig@kdemail.net"));
0111     m_aboutData.addAuthor(i18n("Pascal Létourneau"), i18n("Large scale bug fixing"), QStringLiteral("pascal.letourneau@gmail.com"));
0112     m_aboutData.addAuthor(i18n("Hamish Rodda"), i18n("Core Developer"), QStringLiteral("rodda@kde.org"));
0113     m_aboutData.addAuthor(i18n("Waldo Bastian"), i18n("The cool buffersystem"), QStringLiteral("bastian@kde.org"));
0114     m_aboutData.addAuthor(i18n("Charles Samuels"), i18n("The Editing Commands"), QStringLiteral("charles@kde.org"));
0115     m_aboutData.addAuthor(i18n("Matt Newell"), i18n("Testing, ..."), QStringLiteral("newellm@proaxis.com"));
0116     m_aboutData.addAuthor(i18n("Michael Bartl"), i18n("Former Core Developer"), QStringLiteral("michael.bartl1@chello.at"));
0117     m_aboutData.addAuthor(i18n("Michael McCallum"), i18n("Core Developer"), QStringLiteral("gholam@xtra.co.nz"));
0118     m_aboutData.addAuthor(i18n("Michael Koch"), i18n("KWrite port to KParts"), QStringLiteral("koch@kde.org"));
0119     m_aboutData.addAuthor(i18n("Christian Gebauer"), QString(), QStringLiteral("gebauer@kde.org"));
0120     m_aboutData.addAuthor(i18n("Simon Hausmann"), QString(), QStringLiteral("hausmann@kde.org"));
0121     m_aboutData.addAuthor(i18n("Glen Parker"), i18n("KWrite Undo History, Kspell integration"), QStringLiteral("glenebob@nwlink.com"));
0122     m_aboutData.addAuthor(i18n("Scott Manson"), i18n("KWrite XML Syntax highlighting support"), QStringLiteral("sdmanson@alltel.net"));
0123     m_aboutData.addAuthor(i18n("John Firebaugh"), i18n("Patches and more"), QStringLiteral("jfirebaugh@kde.org"));
0124     m_aboutData.addAuthor(i18n("Andreas Kling"), i18n("Developer"), QStringLiteral("kling@impul.se"));
0125     m_aboutData.addAuthor(i18n("Mirko Stocker"), i18n("Various bugfixes"), QStringLiteral("me@misto.ch"), QStringLiteral("https://misto.ch/"));
0126     m_aboutData.addAuthor(i18n("Matthew Woehlke"), i18n("Selection, KColorScheme integration"), QStringLiteral("mw_triad@users.sourceforge.net"));
0127     m_aboutData.addAuthor(i18n("Sebastian Pipping"),
0128                           i18n("Search bar back- and front-end"),
0129                           QStringLiteral("webmaster@hartwork.org"),
0130                           QStringLiteral("https://hartwork.org/"));
0131     m_aboutData.addAuthor(i18n("Jochen Wilhelmy"), i18n("Original KWrite Author"), QStringLiteral("digisnap@cs.tu-berlin.de"));
0132     m_aboutData.addAuthor(i18n("Gerald Senarclens de Grancy"),
0133                           i18n("QA and Scripting"),
0134                           QStringLiteral("oss@senarclens.eu"),
0135                           QStringLiteral("http://find-santa.eu/"));
0136 
0137     m_aboutData.addCredit(i18n("Matteo Merli"), i18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), QStringLiteral("merlim@libero.it"));
0138     m_aboutData.addCredit(i18n("Rocky Scaletta"), i18n("Highlighting for VHDL"), QStringLiteral("rocky@purdue.edu"));
0139     m_aboutData.addCredit(i18n("Yury Lebedev"), i18n("Highlighting for SQL"), QString());
0140     m_aboutData.addCredit(i18n("Chris Ross"), i18n("Highlighting for Ferite"), QString());
0141     m_aboutData.addCredit(i18n("Nick Roux"), i18n("Highlighting for ILERPG"), QString());
0142     m_aboutData.addCredit(i18n("Carsten Niehaus"), i18n("Highlighting for LaTeX"), QString());
0143     m_aboutData.addCredit(i18n("Per Wigren"), i18n("Highlighting for Makefiles, Python"), QString());
0144     m_aboutData.addCredit(i18n("Jan Fritz"), i18n("Highlighting for Python"), QString());
0145     m_aboutData.addCredit(i18n("Daniel Naber"));
0146     m_aboutData.addCredit(i18n("Roland Pabel"), i18n("Highlighting for Scheme"), QString());
0147     m_aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("PHP Keyword/Datatype list"), QString());
0148     m_aboutData.addCredit(i18n("Carsten Pfeiffer"), i18n("Very nice help"), QString());
0149     m_aboutData.addCredit(i18n("Bruno Massa"), i18n("Highlighting for Lua"), QStringLiteral("brmassa@gmail.com"));
0150 
0151     m_aboutData.addCredit(i18n("All people who have contributed and I have forgotten to mention"));
0152 
0153     m_aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
0154 
0155     // set proper Kate icon for our about dialog
0156     m_aboutData.setProgramLogo(QIcon(QStringLiteral(":/ktexteditor/kate.svg")));
0157 
0158     //
0159     // dir watch
0160     //
0161     m_dirWatch = new KDirWatch();
0162 
0163     //
0164     // command manager
0165     //
0166     m_cmdManager = new KateCmd();
0167 
0168     //
0169     // variable expansion manager
0170     //
0171     m_variableExpansionManager = new KateVariableExpansionManager(this);
0172 
0173     //
0174     // hl manager
0175     //
0176     m_hlManager = new KateHlManager();
0177 
0178     //
0179     // mode man
0180     //
0181     m_modeManager = new KateModeManager();
0182 
0183     //
0184     // input mode factories
0185     //
0186     Q_ASSERT(m_inputModeFactories.size() == KTextEditor::View::ViInputMode + 1);
0187     m_inputModeFactories[KTextEditor::View::NormalInputMode].reset(new KateNormalInputModeFactory());
0188     m_inputModeFactories[KTextEditor::View::ViInputMode].reset(new KateViInputModeFactory());
0189 
0190     //
0191     // spell check manager
0192     //
0193     m_spellCheckManager = new KateSpellCheckManager();
0194 
0195     // config objects
0196     m_globalConfig = new KateGlobalConfig();
0197     m_documentConfig = new KateDocumentConfig();
0198     m_viewConfig = new KateViewConfig();
0199     m_rendererConfig = new KateRendererConfig();
0200 
0201     // create script manager (search scripts)
0202     m_scriptManager = KateScriptManager::self();
0203 
0204     //
0205     // init the cmds
0206     //
0207     m_cmds.push_back(KateCommands::CoreCommands::self());
0208     m_cmds.push_back(KateCommands::Character::self());
0209     m_cmds.push_back(KateCommands::Date::self());
0210     m_cmds.push_back(KateCommands::SedReplace::self());
0211     m_cmds.push_back(KateCommands::Highlighting::self());
0212 
0213     // global word completion model
0214     m_wordCompletionModel = new KateWordCompletionModel(this);
0215 
0216     // global keyword completion model
0217     m_keywordCompletionModel = new KateKeywordCompletionModel(this);
0218 
0219     // tap to QApplication object for color palette changes
0220     qApp->installEventFilter(this);
0221 }
0222 
0223 KTextEditor::EditorPrivate::~EditorPrivate()
0224 {
0225     delete m_globalConfig;
0226     delete m_documentConfig;
0227     delete m_viewConfig;
0228     delete m_rendererConfig;
0229 
0230     delete m_modeManager;
0231 
0232     delete m_dirWatch;
0233 
0234     // cu managers
0235     delete m_scriptManager;
0236     delete m_hlManager;
0237 
0238     delete m_spellCheckManager;
0239 
0240     // cu model
0241     delete m_wordCompletionModel;
0242 
0243     // delete variable expansion manager
0244     delete m_variableExpansionManager;
0245     m_variableExpansionManager = nullptr;
0246 
0247     // delete the commands before we delete the cmd manager
0248     qDeleteAll(m_cmds);
0249     delete m_cmdManager;
0250 }
0251 
0252 KTextEditor::Document *KTextEditor::EditorPrivate::createDocument(QObject *parent)
0253 {
0254     KTextEditor::DocumentPrivate *doc = new KTextEditor::DocumentPrivate(false, false, nullptr, parent);
0255 
0256     Q_EMIT documentCreated(this, doc);
0257 
0258     return doc;
0259 }
0260 
0261 // END KTextEditor::Editor config stuff
0262 
0263 // config dialog with improved sizing and that allows scrolling
0264 class KTextEditorConfigDialog : public KPageDialog
0265 {
0266 public:
0267     QList<KTextEditor::ConfigPage *> editorPages;
0268 
0269     KTextEditorConfigDialog(KTextEditor::EditorPrivate *editor, QWidget *parent)
0270         : KPageDialog(parent)
0271     {
0272         setWindowTitle(i18n("Configure"));
0273         setFaceType(KPageDialog::List);
0274         setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Help);
0275 
0276         // create pages already in construct to have proper layout for sizeHint
0277         editorPages.reserve(editor->configPages());
0278         for (int i = 0; i < editor->configPages(); ++i) {
0279             KTextEditor::ConfigPage *page = editor->configPage(i, this);
0280             KPageWidgetItem *item = addScrollablePage(page, page->name());
0281             item->setHeader(page->fullName());
0282             item->setIcon(page->icon());
0283 
0284             connect(button(QDialogButtonBox::Apply), &QPushButton::clicked, page, &KTextEditor::ConfigPage::apply);
0285             editorPages.append(page);
0286         }
0287     }
0288 
0289     QSize sizeHint() const override
0290     {
0291         // start with a bit enlarged default size hint to minimize changes of useless scrollbars
0292         QSize size = KPageDialog::sizeHint() * 1.3;
0293 
0294         // enlarge it to half of the main window size, if that is larger
0295         if (parentWidget() && parentWidget()->window()) {
0296             size = size.expandedTo(parentWidget()->window()->size() * 0.5);
0297         }
0298 
0299         // return bounded size to available real screen space
0300         return size.boundedTo(screen()->availableSize() * 0.9);
0301     }
0302 
0303     KPageWidgetItem *addScrollablePage(QWidget *page, const QString &itemName)
0304     {
0305         // inspired by KPageWidgetItem *KConfigDialogPrivate::addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString
0306         // &header)
0307         QWidget *frame = new QWidget;
0308         QVBoxLayout *boxLayout = new QVBoxLayout(frame);
0309         boxLayout->setContentsMargins(0, 0, 0, 0);
0310         boxLayout->setContentsMargins(0, 0, 0, 0);
0311 
0312         QScrollArea *scroll = new QScrollArea;
0313         scroll->setFrameShape(QFrame::NoFrame);
0314         scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0315         scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
0316         scroll->setWidget(page);
0317         scroll->setWidgetResizable(true);
0318         scroll->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
0319 
0320         if (page->minimumSizeHint().height() > scroll->sizeHint().height() - 2) {
0321             if (page->sizeHint().width() < scroll->sizeHint().width() + 2) {
0322                 // QScrollArea is planning only a vertical scroll bar,
0323                 // try to avoid the horizontal one by reserving space for the vertical one.
0324                 // Currently KPageViewPrivate::_k_modelChanged() queries the minimumSizeHint().
0325                 // We can only set the minimumSize(), so this approach relies on QStackedWidget size calculation.
0326                 scroll->setMinimumWidth(scroll->sizeHint().width() + qBound(0, scroll->verticalScrollBar()->sizeHint().width(), 200) + 4);
0327             }
0328         }
0329 
0330         boxLayout->addWidget(scroll);
0331         return addPage(frame, itemName);
0332     }
0333 };
0334 
0335 void KTextEditor::EditorPrivate::configDialog(QWidget *parent)
0336 {
0337     QPointer<KTextEditorConfigDialog> kd = new KTextEditorConfigDialog(this, parent);
0338     if (kd->exec() && kd) {
0339         KateGlobalConfig::global()->configStart();
0340         KateDocumentConfig::global()->configStart();
0341         KateViewConfig::global()->configStart();
0342         KateRendererConfig::global()->configStart();
0343 
0344         for (int i = 0; i < kd->editorPages.count(); ++i) {
0345             kd->editorPages.at(i)->apply();
0346         }
0347 
0348         KateGlobalConfig::global()->configEnd();
0349         KateDocumentConfig::global()->configEnd();
0350         KateViewConfig::global()->configEnd();
0351         KateRendererConfig::global()->configEnd();
0352     }
0353     delete kd;
0354 }
0355 
0356 int KTextEditor::EditorPrivate::configPages() const
0357 {
0358     return 4;
0359 }
0360 
0361 KTextEditor::ConfigPage *KTextEditor::EditorPrivate::configPage(int number, QWidget *parent)
0362 {
0363     switch (number) {
0364     case 0:
0365         return new KateViewDefaultsConfig(parent);
0366 
0367     case 1:
0368         return new KateThemeConfigPage(parent);
0369 
0370     case 2:
0371         return new KateEditConfigTab(parent);
0372 
0373     case 3:
0374         return new KateSaveConfigTab(parent);
0375 
0376     default:
0377         break;
0378     }
0379 
0380     return nullptr;
0381 }
0382 
0383 /**
0384  * Cleanup the KTextEditor::EditorPrivate during QCoreApplication shutdown
0385  */
0386 static void cleanupGlobal()
0387 {
0388     // delete if there
0389     delete KTextEditor::EditorPrivate::self();
0390 }
0391 
0392 KTextEditor::EditorPrivate *KTextEditor::EditorPrivate::self()
0393 {
0394     // remember the static instance in a QPointer
0395     static bool inited = false;
0396     static QPointer<KTextEditor::EditorPrivate> staticInstance;
0397 
0398     // just return it, if already inited
0399     if (inited) {
0400         return staticInstance.data();
0401     }
0402 
0403     // start init process
0404     inited = true;
0405 
0406     // now create the object and store it
0407     new KTextEditor::EditorPrivate(staticInstance);
0408 
0409     // register cleanup
0410     // let use be deleted during QCoreApplication shutdown
0411     qAddPostRoutine(cleanupGlobal);
0412 
0413     // return instance
0414     return staticInstance.data();
0415 }
0416 
0417 void KTextEditor::EditorPrivate::registerDocument(KTextEditor::DocumentPrivate *doc)
0418 {
0419     Q_ASSERT(!m_documents.contains(doc));
0420     m_documents.insert(doc, doc);
0421 }
0422 
0423 void KTextEditor::EditorPrivate::deregisterDocument(KTextEditor::DocumentPrivate *doc)
0424 {
0425     Q_ASSERT(m_documents.contains(doc));
0426     m_documents.remove(doc);
0427 }
0428 
0429 void KTextEditor::EditorPrivate::registerView(KTextEditor::ViewPrivate *view)
0430 {
0431     Q_ASSERT(!m_views.contains(view));
0432     m_views.insert(view);
0433 }
0434 
0435 void KTextEditor::EditorPrivate::deregisterView(KTextEditor::ViewPrivate *view)
0436 {
0437     Q_ASSERT(m_views.contains(view));
0438     m_views.remove(view);
0439 }
0440 
0441 KTextEditor::Command *KTextEditor::EditorPrivate::queryCommand(const QString &cmd) const
0442 {
0443     return m_cmdManager->queryCommand(cmd);
0444 }
0445 
0446 QList<KTextEditor::Command *> KTextEditor::EditorPrivate::commands() const
0447 {
0448     return m_cmdManager->commands();
0449 }
0450 
0451 QStringList KTextEditor::EditorPrivate::commandList() const
0452 {
0453     return m_cmdManager->commandList();
0454 }
0455 
0456 KateVariableExpansionManager *KTextEditor::EditorPrivate::variableExpansionManager()
0457 {
0458     return m_variableExpansionManager;
0459 }
0460 
0461 void KTextEditor::EditorPrivate::updateColorPalette()
0462 {
0463     // reload the global schema (triggers reload for every view as well)
0464     // might trigger selection of better matching theme for new palette
0465     m_rendererConfig->reloadSchema();
0466 
0467     // force full update of all view caches and colors
0468     m_rendererConfig->updateConfig();
0469 }
0470 
0471 void KTextEditor::EditorPrivate::copyToClipboard(const QString &text, const QString &fileName)
0472 {
0473     // empty => nop
0474     if (text.isEmpty()) {
0475         return;
0476     }
0477 
0478     // move to clipboard
0479     QApplication::clipboard()->setText(text, QClipboard::Clipboard);
0480 
0481     // LRU, kill potential duplicated, move new entry to top
0482     // cut after X entries
0483     auto entry = ClipboardEntry{text, fileName};
0484     m_clipboardHistory.removeOne(entry);
0485     m_clipboardHistory.prepend(entry);
0486     if (m_clipboardHistory.size() > m_viewConfig->clipboardHistoryEntries()) {
0487         m_clipboardHistory.removeLast();
0488     }
0489 
0490     // notify about change
0491     Q_EMIT clipboardHistoryChanged();
0492 }
0493 
0494 bool KTextEditor::EditorPrivate::eventFilter(QObject *obj, QEvent *event)
0495 {
0496     if (obj == qApp && event->type() == QEvent::ApplicationPaletteChange) {
0497         // only update the color once for the event that belongs to the qApp
0498         updateColorPalette();
0499     }
0500 
0501     return false; // always continue processing
0502 }
0503 
0504 QStringListModel *KTextEditor::EditorPrivate::searchHistoryModel()
0505 {
0506     if (!m_searchHistoryModel) {
0507         KConfigGroup cg(KSharedConfig::openConfig(), "KTextEditor::Search");
0508         const QStringList history = cg.readEntry(QStringLiteral("Search History"), QStringList());
0509         m_searchHistoryModel = new QStringListModel(history, this);
0510     }
0511     return m_searchHistoryModel;
0512 }
0513 
0514 QStringListModel *KTextEditor::EditorPrivate::replaceHistoryModel()
0515 {
0516     if (!m_replaceHistoryModel) {
0517         KConfigGroup cg(KSharedConfig::openConfig(), "KTextEditor::Search");
0518         const QStringList history = cg.readEntry(QStringLiteral("Replace History"), QStringList());
0519         m_replaceHistoryModel = new QStringListModel(history, this);
0520     }
0521     return m_replaceHistoryModel;
0522 }
0523 
0524 void KTextEditor::EditorPrivate::saveSearchReplaceHistoryModels()
0525 {
0526     KConfigGroup cg(KSharedConfig::openConfig(), "KTextEditor::Search");
0527     if (m_searchHistoryModel) {
0528         cg.writeEntry(QStringLiteral("Search History"), m_searchHistoryModel->stringList());
0529     }
0530     if (m_replaceHistoryModel) {
0531         cg.writeEntry(QStringLiteral("Replace History"), m_replaceHistoryModel->stringList());
0532     }
0533 }
0534 
0535 KSharedConfigPtr KTextEditor::EditorPrivate::config()
0536 {
0537     // use dummy config for unit tests!
0538     if (KTextEditor::EditorPrivate::unitTestMode()) {
0539         return KSharedConfig::openConfig(QStringLiteral("katepartrc-unittest"), KConfig::SimpleConfig, QStandardPaths::TempLocation);
0540     }
0541 
0542     // else: use application configuration, but try to transfer global settings on first use
0543     auto applicationConfig = KSharedConfig::openConfig();
0544     if (!KConfigGroup(applicationConfig, QStringLiteral("KTextEditor Editor")).exists()) {
0545         auto globalConfig = KSharedConfig::openConfig(QStringLiteral("katepartrc"));
0546         for (const auto &group : {QStringLiteral("Editor"), QStringLiteral("Document"), QStringLiteral("View"), QStringLiteral("Renderer")}) {
0547             KConfigGroup origin(globalConfig, group);
0548             KConfigGroup destination(applicationConfig, QStringLiteral("KTextEditor ") + group);
0549             origin.copyTo(&destination);
0550         }
0551     }
0552     return applicationConfig;
0553 }
0554 
0555 void KTextEditor::EditorPrivate::triggerConfigChanged()
0556 {
0557     // trigger delayed emission, will collapse multiple events to one signal emission
0558     m_configWasChanged = true;
0559     QTimer::singleShot(0, this, &KTextEditor::EditorPrivate::emitConfigChanged);
0560 }
0561 
0562 void KTextEditor::EditorPrivate::emitConfigChanged()
0563 {
0564     // emit only once, if still needed
0565     if (m_configWasChanged) {
0566         m_configWasChanged = false;
0567         Q_EMIT configChanged(this);
0568     }
0569 }
0570 
0571 void KTextEditor::EditorPrivate::copyToMulticursorClipboard(const QStringList &texts)
0572 {
0573     if (texts.size() == 1) {
0574         qWarning() << "Unexpected size 1 of multicursorClipboard. It should either be empty or greater than 1";
0575         m_multicursorClipboard = QStringList();
0576         Q_ASSERT(false);
0577         return;
0578     }
0579     m_multicursorClipboard = texts;
0580 }
0581 
0582 QStringList KTextEditor::EditorPrivate::multicursorClipboard() const
0583 {
0584     return m_multicursorClipboard;
0585 }
0586 
0587 #include "moc_kateglobal.cpp"