File indexing completed on 2024-04-14 03:55:30
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 0016 #include <ktexteditor_version.h> 0017 0018 #include "katebuffer.h" 0019 #include "katecmd.h" 0020 #include "katecmds.h" 0021 #include "kateconfig.h" 0022 #include "katedialogs.h" 0023 #include "katedocument.h" 0024 #include "katehighlightingcmds.h" 0025 #include "katekeywordcompletion.h" 0026 #include "katemodemanager.h" 0027 #include "katescriptmanager.h" 0028 #include "katesedcmd.h" 0029 #include "katesyntaxmanager.h" 0030 #include "katethemeconfig.h" 0031 #include "katevariableexpansionmanager.h" 0032 #include "kateview.h" 0033 #include "katewordcompletion.h" 0034 #include "spellcheck/spellcheck.h" 0035 0036 #include "katenormalinputmodefactory.h" 0037 #include "kateviinputmodefactory.h" 0038 0039 #include <KConfigGroup> 0040 #include <KDirWatch> 0041 #include <KLocalizedString> 0042 #include <KPageDialog> 0043 0044 #include <QApplication> 0045 #include <QBoxLayout> 0046 #include <QClipboard> 0047 #include <QFrame> 0048 #include <QPushButton> 0049 #include <QScreen> 0050 #include <QScrollArea> 0051 #include <QScrollBar> 0052 #include <QStringListModel> 0053 #include <QTextToSpeech> 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 = { 0208 KateCommands::CoreCommands::self(), 0209 KateCommands::Character::self(), 0210 KateCommands::Date::self(), 0211 KateCommands::SedReplace::self(), 0212 KateCommands::Highlighting::self(), 0213 }; 0214 0215 // global word completion model 0216 m_wordCompletionModel = new KateWordCompletionModel(this); 0217 0218 // global keyword completion model 0219 m_keywordCompletionModel = new KateKeywordCompletionModel(this); 0220 0221 // tap to QApplication object for color palette changes 0222 qApp->installEventFilter(this); 0223 } 0224 0225 KTextEditor::EditorPrivate::~EditorPrivate() 0226 { 0227 delete m_globalConfig; 0228 delete m_documentConfig; 0229 delete m_viewConfig; 0230 delete m_rendererConfig; 0231 0232 delete m_modeManager; 0233 0234 delete m_dirWatch; 0235 0236 // cu managers 0237 delete m_scriptManager; 0238 delete m_hlManager; 0239 0240 delete m_spellCheckManager; 0241 0242 // cu model 0243 delete m_wordCompletionModel; 0244 0245 // delete variable expansion manager 0246 delete m_variableExpansionManager; 0247 m_variableExpansionManager = nullptr; 0248 0249 // delete the commands before we delete the cmd manager 0250 qDeleteAll(m_cmds); 0251 delete m_cmdManager; 0252 } 0253 0254 KTextEditor::Document *KTextEditor::EditorPrivate::createDocument(QObject *parent) 0255 { 0256 KTextEditor::DocumentPrivate *doc = new KTextEditor::DocumentPrivate(false, false, nullptr, parent); 0257 0258 Q_EMIT documentCreated(this, doc); 0259 0260 return doc; 0261 } 0262 0263 // END KTextEditor::Editor config stuff 0264 0265 // config dialog with improved sizing and that allows scrolling 0266 class KTextEditorConfigDialog : public KPageDialog 0267 { 0268 public: 0269 std::vector<KTextEditor::ConfigPage *> editorPages; 0270 0271 KTextEditorConfigDialog(KTextEditor::EditorPrivate *editor, QWidget *parent) 0272 : KPageDialog(parent) 0273 { 0274 setWindowTitle(i18n("Configure")); 0275 setFaceType(KPageDialog::List); 0276 setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Help); 0277 0278 // create pages already in construct to have proper layout for sizeHint 0279 editorPages.reserve(editor->configPages()); 0280 for (int i = 0; i < editor->configPages(); ++i) { 0281 KTextEditor::ConfigPage *page = editor->configPage(i, this); 0282 KPageWidgetItem *item = addScrollablePage(page, page->name()); 0283 item->setHeader(page->fullName()); 0284 item->setIcon(page->icon()); 0285 0286 connect(button(QDialogButtonBox::Apply), &QPushButton::clicked, page, &KTextEditor::ConfigPage::apply); 0287 editorPages.push_back(page); 0288 } 0289 } 0290 0291 QSize sizeHint() const override 0292 { 0293 // start with a bit enlarged default size hint to minimize changes of useless scrollbars 0294 QSize size = KPageDialog::sizeHint() * 1.3; 0295 0296 // enlarge it to half of the main window size, if that is larger 0297 if (parentWidget() && parentWidget()->window()) { 0298 size = size.expandedTo(parentWidget()->window()->size() * 0.5); 0299 } 0300 0301 // return bounded size to available real screen space 0302 return size.boundedTo(screen()->availableSize() * 0.9); 0303 } 0304 0305 KPageWidgetItem *addScrollablePage(QWidget *page, const QString &itemName) 0306 { 0307 // inspired by KPageWidgetItem *KConfigDialogPrivate::addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString 0308 // &header) 0309 QWidget *frame = new QWidget; 0310 QVBoxLayout *boxLayout = new QVBoxLayout(frame); 0311 boxLayout->setContentsMargins(0, 0, 0, 0); 0312 boxLayout->setContentsMargins(0, 0, 0, 0); 0313 0314 QScrollArea *scroll = new QScrollArea; 0315 scroll->setFrameShape(QFrame::NoFrame); 0316 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0317 scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0318 scroll->setWidget(page); 0319 scroll->setWidgetResizable(true); 0320 scroll->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0321 0322 if (page->minimumSizeHint().height() > scroll->sizeHint().height() - 2) { 0323 if (page->sizeHint().width() < scroll->sizeHint().width() + 2) { 0324 // QScrollArea is planning only a vertical scroll bar, 0325 // try to avoid the horizontal one by reserving space for the vertical one. 0326 // Currently KPageViewPrivate::_k_modelChanged() queries the minimumSizeHint(). 0327 // We can only set the minimumSize(), so this approach relies on QStackedWidget size calculation. 0328 scroll->setMinimumWidth(scroll->sizeHint().width() + qBound(0, scroll->verticalScrollBar()->sizeHint().width(), 200) + 4); 0329 } 0330 } 0331 0332 boxLayout->addWidget(scroll); 0333 return addPage(frame, itemName); 0334 } 0335 }; 0336 0337 void KTextEditor::EditorPrivate::configDialog(QWidget *parent) 0338 { 0339 QPointer<KTextEditorConfigDialog> kd = new KTextEditorConfigDialog(this, parent); 0340 if (kd->exec() && kd) { 0341 KateGlobalConfig::global()->configStart(); 0342 KateDocumentConfig::global()->configStart(); 0343 KateViewConfig::global()->configStart(); 0344 KateRendererConfig::global()->configStart(); 0345 0346 for (auto *page : kd->editorPages) { 0347 page->apply(); 0348 } 0349 0350 KateGlobalConfig::global()->configEnd(); 0351 KateDocumentConfig::global()->configEnd(); 0352 KateViewConfig::global()->configEnd(); 0353 KateRendererConfig::global()->configEnd(); 0354 } 0355 delete kd; 0356 } 0357 0358 int KTextEditor::EditorPrivate::configPages() const 0359 { 0360 return 4; 0361 } 0362 0363 KTextEditor::ConfigPage *KTextEditor::EditorPrivate::configPage(int number, QWidget *parent) 0364 { 0365 switch (number) { 0366 case 0: 0367 return new KateViewDefaultsConfig(parent); 0368 0369 case 1: 0370 return new KateThemeConfigPage(parent); 0371 0372 case 2: 0373 return new KateEditConfigTab(parent); 0374 0375 case 3: 0376 return new KateSaveConfigTab(parent); 0377 0378 default: 0379 break; 0380 } 0381 0382 return nullptr; 0383 } 0384 0385 /** 0386 * Cleanup the KTextEditor::EditorPrivate during QCoreApplication shutdown 0387 */ 0388 static void cleanupGlobal() 0389 { 0390 // delete if there 0391 delete KTextEditor::EditorPrivate::self(); 0392 } 0393 0394 KTextEditor::EditorPrivate *KTextEditor::EditorPrivate::self() 0395 { 0396 // remember the static instance in a QPointer 0397 static bool inited = false; 0398 static QPointer<KTextEditor::EditorPrivate> staticInstance; 0399 0400 // just return it, if already inited 0401 if (inited) { 0402 return staticInstance.data(); 0403 } 0404 0405 // start init process 0406 inited = true; 0407 0408 // now create the object and store it 0409 new KTextEditor::EditorPrivate(staticInstance); 0410 0411 // register cleanup 0412 // let use be deleted during QCoreApplication shutdown 0413 qAddPostRoutine(cleanupGlobal); 0414 0415 // return instance 0416 return staticInstance.data(); 0417 } 0418 0419 void KTextEditor::EditorPrivate::registerDocument(KTextEditor::DocumentPrivate *doc) 0420 { 0421 Q_ASSERT(!m_documents.contains(doc)); 0422 m_documents.push_back(doc); 0423 } 0424 0425 void KTextEditor::EditorPrivate::deregisterDocument(KTextEditor::DocumentPrivate *doc) 0426 { 0427 int i = m_documents.indexOf(doc); 0428 Q_ASSERT(i != -1); 0429 m_documents.removeAt(i); 0430 } 0431 0432 void KTextEditor::EditorPrivate::registerView(KTextEditor::ViewPrivate *view) 0433 { 0434 Q_ASSERT(std::find(m_views.begin(), m_views.end(), view) == m_views.end()); 0435 m_views.push_back(view); 0436 } 0437 0438 void KTextEditor::EditorPrivate::deregisterView(KTextEditor::ViewPrivate *view) 0439 { 0440 auto it = std::find(m_views.begin(), m_views.end(), view); 0441 Q_ASSERT(it != m_views.end()); 0442 m_views.erase(it); 0443 } 0444 0445 KTextEditor::Command *KTextEditor::EditorPrivate::queryCommand(const QString &cmd) const 0446 { 0447 return m_cmdManager->queryCommand(cmd); 0448 } 0449 0450 QList<KTextEditor::Command *> KTextEditor::EditorPrivate::commands() const 0451 { 0452 return m_cmdManager->commands(); 0453 } 0454 0455 QStringList KTextEditor::EditorPrivate::commandList() const 0456 { 0457 return m_cmdManager->commandList(); 0458 } 0459 0460 KateVariableExpansionManager *KTextEditor::EditorPrivate::variableExpansionManager() 0461 { 0462 return m_variableExpansionManager; 0463 } 0464 0465 void KTextEditor::EditorPrivate::updateColorPalette() 0466 { 0467 // reload the global schema (triggers reload for every view as well) 0468 // might trigger selection of better matching theme for new palette 0469 m_rendererConfig->reloadSchema(); 0470 0471 // force full update of all view caches and colors 0472 m_rendererConfig->updateConfig(); 0473 } 0474 0475 void KTextEditor::EditorPrivate::copyToClipboard(const QString &text, const QString &fileName) 0476 { 0477 // empty => nop 0478 if (text.isEmpty()) { 0479 return; 0480 } 0481 0482 // move to clipboard 0483 QApplication::clipboard()->setText(text, QClipboard::Clipboard); 0484 0485 // LRU, kill potential duplicated, move new entry to top 0486 // cut after X entries 0487 auto entry = ClipboardEntry{text, fileName}; 0488 m_clipboardHistory.removeOne(entry); 0489 m_clipboardHistory.prepend(entry); 0490 if (m_clipboardHistory.size() > m_viewConfig->clipboardHistoryEntries()) { 0491 m_clipboardHistory.removeLast(); 0492 } 0493 0494 // notify about change 0495 Q_EMIT clipboardHistoryChanged(); 0496 } 0497 0498 bool KTextEditor::EditorPrivate::eventFilter(QObject *obj, QEvent *event) 0499 { 0500 if (obj == qApp && event->type() == QEvent::ApplicationPaletteChange) { 0501 // only update the color once for the event that belongs to the qApp 0502 updateColorPalette(); 0503 } 0504 0505 return false; // always continue processing 0506 } 0507 0508 QStringListModel *KTextEditor::EditorPrivate::searchHistoryModel() 0509 { 0510 if (!m_searchHistoryModel) { 0511 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KTextEditor::Search")); 0512 const QStringList history = cg.readEntry(QStringLiteral("Search History"), QStringList()); 0513 m_searchHistoryModel = new QStringListModel(history, this); 0514 } 0515 return m_searchHistoryModel; 0516 } 0517 0518 QStringListModel *KTextEditor::EditorPrivate::replaceHistoryModel() 0519 { 0520 if (!m_replaceHistoryModel) { 0521 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KTextEditor::Search")); 0522 const QStringList history = cg.readEntry(QStringLiteral("Replace History"), QStringList()); 0523 m_replaceHistoryModel = new QStringListModel(history, this); 0524 } 0525 return m_replaceHistoryModel; 0526 } 0527 0528 void KTextEditor::EditorPrivate::saveSearchReplaceHistoryModels() 0529 { 0530 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KTextEditor::Search")); 0531 if (m_searchHistoryModel) { 0532 cg.writeEntry(QStringLiteral("Search History"), m_searchHistoryModel->stringList()); 0533 } 0534 if (m_replaceHistoryModel) { 0535 cg.writeEntry(QStringLiteral("Replace History"), m_replaceHistoryModel->stringList()); 0536 } 0537 } 0538 0539 KSharedConfigPtr KTextEditor::EditorPrivate::config() 0540 { 0541 // use dummy config for unit tests! 0542 if (KTextEditor::EditorPrivate::unitTestMode()) { 0543 return KSharedConfig::openConfig(QStringLiteral("katepartrc-unittest"), KConfig::SimpleConfig, QStandardPaths::TempLocation); 0544 } 0545 0546 // else: use application configuration, but try to transfer global settings on first use 0547 auto applicationConfig = KSharedConfig::openConfig(); 0548 if (!KConfigGroup(applicationConfig, QStringLiteral("KTextEditor Editor")).exists()) { 0549 auto globalConfig = KSharedConfig::openConfig(QStringLiteral("katepartrc")); 0550 for (const auto &group : {QStringLiteral("Editor"), QStringLiteral("Document"), QStringLiteral("View"), QStringLiteral("Renderer")}) { 0551 KConfigGroup origin(globalConfig, group); 0552 KConfigGroup destination(applicationConfig, QStringLiteral("KTextEditor ") + group); 0553 origin.copyTo(&destination); 0554 } 0555 } 0556 return applicationConfig; 0557 } 0558 0559 void KTextEditor::EditorPrivate::triggerConfigChanged() 0560 { 0561 // trigger delayed emission, will collapse multiple events to one signal emission 0562 m_configWasChanged = true; 0563 QTimer::singleShot(0, this, &KTextEditor::EditorPrivate::emitConfigChanged); 0564 } 0565 0566 void KTextEditor::EditorPrivate::emitConfigChanged() 0567 { 0568 // emit only once, if still needed 0569 if (m_configWasChanged) { 0570 m_configWasChanged = false; 0571 Q_EMIT configChanged(this); 0572 } 0573 } 0574 0575 void KTextEditor::EditorPrivate::copyToMulticursorClipboard(const QStringList &texts) 0576 { 0577 if (texts.size() == 1) { 0578 qWarning() << "Unexpected size 1 of multicursorClipboard. It should either be empty or greater than 1"; 0579 m_multicursorClipboard = QStringList(); 0580 Q_ASSERT(false); 0581 return; 0582 } 0583 m_multicursorClipboard = texts; 0584 } 0585 0586 QStringList KTextEditor::EditorPrivate::multicursorClipboard() const 0587 { 0588 return m_multicursorClipboard; 0589 } 0590 0591 QTextToSpeech *KTextEditor::EditorPrivate::speechEngine(KTextEditor::ViewPrivate *view) 0592 { 0593 Q_ASSERT(view); 0594 if (!m_speechEngine) { 0595 m_speechEngine = new QTextToSpeech(this); 0596 0597 // error handler for errors happening during speech output 0598 connect(m_speechEngine, &QTextToSpeech::errorOccurred, this, [this](QTextToSpeech::ErrorReason, const QString &errorString) { 0599 if (m_speechEngineLastUser) { 0600 speechError(m_speechEngineLastUser, errorString); 0601 } 0602 }); 0603 0604 // handle init errors, that will not emit errorOccurred later 0605 if (m_speechEngine->errorReason() != QTextToSpeech::ErrorReason::NoError) { 0606 speechError(view, m_speechEngine->errorString()); 0607 } 0608 } 0609 0610 // register current view, handle error output and stopping of the speaking 0611 if (view != m_speechEngineLastUser) { 0612 if (m_speechEngineLastUser) { 0613 disconnect(m_speechEngineLastUser, &QObject::destroyed, this, &KTextEditor::EditorPrivate::speechEngineUserDestoyed); 0614 } 0615 m_speechEngineLastUser = view; 0616 connect(m_speechEngineLastUser, &QObject::destroyed, this, &KTextEditor::EditorPrivate::speechEngineUserDestoyed); 0617 } 0618 0619 return m_speechEngine; 0620 } 0621 0622 void KTextEditor::EditorPrivate::speechEngineUserDestoyed() 0623 { 0624 Q_ASSERT(m_speechEngine); 0625 m_speechEngine->stop(); 0626 } 0627 0628 void KTextEditor::EditorPrivate::speechError(KTextEditor::ViewPrivate *view, const QString &errorString) 0629 { 0630 Q_ASSERT(view); 0631 auto message = new KTextEditor::Message(errorString, Message::Error); 0632 message->setPosition(KTextEditor::Message::TopInView); 0633 message->setView(view); 0634 view->document()->postMessage(message); 0635 } 0636 0637 #include "moc_kateglobal.cpp"