File indexing completed on 2024-04-28 03:57:59

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2009 Michel Ludwig <michel.ludwig@kdemail.net>
0004     SPDX-FileCopyrightText: 2007 Mirko Stocker <me@misto.ch>
0005     SPDX-FileCopyrightText: 2003 Hamish Rodda <rodda@kde.org>
0006     SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org>
0007     SPDX-FileCopyrightText: 2001-2004 Christoph Cullmann <cullmann@kde.org>
0008     SPDX-FileCopyrightText: 2001-2010 Joseph Wenninger <jowenn@kde.org>
0009     SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
0010 
0011     SPDX-License-Identifier: LGPL-2.0-only
0012 */
0013 
0014 // BEGIN includes
0015 #include "kateview.h"
0016 
0017 #include "clipboardhistorydialog.h"
0018 #include "export/exporter.h"
0019 #include "inlinenotedata.h"
0020 #include "kateabstractinputmode.h"
0021 #include "kateautoindent.h"
0022 #include "katebookmarks.h"
0023 #include "katebuffer.h"
0024 #include "katecompletionwidget.h"
0025 #include "kateconfig.h"
0026 #include "katedialogs.h"
0027 #include "katedocument.h"
0028 #include "kateglobal.h"
0029 #include "katehighlight.h"
0030 #include "katehighlightmenu.h"
0031 #include "katekeywordcompletion.h"
0032 #include "katelayoutcache.h"
0033 #include "katemessagewidget.h"
0034 #include "katemodemenu.h"
0035 #include "katepartdebug.h"
0036 #include "katerenderer.h"
0037 #include "katestatusbar.h"
0038 #include "katetemplatehandler.h"
0039 #include "katetextline.h"
0040 #include "kateundomanager.h"
0041 #include "kateviewhelpers.h"
0042 #include "kateviewinternal.h"
0043 #include "katewordcompletion.h"
0044 #include "printing/kateprinter.h"
0045 #include "screenshotdialog.h"
0046 #include "script/katescriptaction.h"
0047 #include "script/katescriptmanager.h"
0048 #include "spellcheck/spellcheck.h"
0049 #include "spellcheck/spellcheckdialog.h"
0050 #include "spellcheck/spellingmenu.h"
0051 
0052 #include <KTextEditor/Message>
0053 #include <ktexteditor/annotationinterface.h>
0054 #include <ktexteditor/inlinenoteprovider.h>
0055 #include <ktexteditor/texthintinterface.h>
0056 
0057 #include <KActionCollection>
0058 #include <KConfig>
0059 #include <KConfigGroup>
0060 #include <KCursor>
0061 #include <KMessageBox>
0062 #include <KSelectAction>
0063 #include <KStandardAction>
0064 #include <KStandardShortcut>
0065 #include <KToggleAction>
0066 #include <KXMLGUIFactory>
0067 
0068 #include <QActionGroup>
0069 #include <QApplication>
0070 #include <QClipboard>
0071 #include <QFileDialog>
0072 #include <QFileInfo>
0073 #include <QFont>
0074 #include <QInputDialog>
0075 #include <QKeyEvent>
0076 #include <QLayout>
0077 #include <QMimeData>
0078 #include <QPainter>
0079 #include <QRegularExpression>
0080 #include <QTextToSpeech>
0081 #include <QToolTip>
0082 
0083 // #define VIEW_RANGE_DEBUG
0084 
0085 // END includes
0086 
0087 namespace
0088 {
0089 bool hasCommentInFirstLine(KTextEditor::DocumentPrivate *doc)
0090 {
0091     const Kate::TextLine line = doc->kateTextLine(0);
0092     return doc->isComment(0, line.firstChar());
0093 }
0094 
0095 }
0096 
0097 void KTextEditor::ViewPrivate::blockFix(KTextEditor::Range &range)
0098 {
0099     if (range.start().column() > range.end().column()) {
0100         int tmp = range.start().column();
0101         range.setStart(KTextEditor::Cursor(range.start().line(), range.end().column()));
0102         range.setEnd(KTextEditor::Cursor(range.end().line(), tmp));
0103     }
0104 }
0105 
0106 KTextEditor::ViewPrivate::ViewPrivate(KTextEditor::DocumentPrivate *doc, QWidget *parent, KTextEditor::MainWindow *mainWindow)
0107     : KTextEditor::View(this, parent)
0108     , m_completionWidget(nullptr)
0109     , m_annotationModel(nullptr)
0110     , m_markedSelection(false)
0111     , m_hasWrap(false)
0112     , m_doc(doc)
0113     , m_textFolding(doc->buffer())
0114     , m_config(new KateViewConfig(this))
0115     , m_renderer(new KateRenderer(doc, m_textFolding, this))
0116     , m_viewInternal(new KateViewInternal(this))
0117     , m_spell(new KateSpellCheckDialog(this))
0118     , m_bookmarks(new KateBookmarks(this))
0119     , m_topSpacer(new QSpacerItem(0, 0))
0120     , m_leftSpacer(new QSpacerItem(0, 0))
0121     , m_rightSpacer(new QSpacerItem(0, 0))
0122     , m_bottomSpacer(new QSpacerItem(0, 0))
0123     , m_startingUp(true)
0124     , m_updatingDocumentConfig(false)
0125     , m_selection(m_doc->buffer(), KTextEditor::Range::invalid(), Kate::TextRange::ExpandLeft, Kate::TextRange::AllowEmpty)
0126     , blockSelect(false)
0127     , m_bottomViewBar(nullptr)
0128     , m_gotoBar(nullptr)
0129     , m_dictionaryBar(nullptr)
0130     , m_spellingMenu(new KateSpellingMenu(this))
0131     , m_userContextMenuSet(false)
0132     , m_lineToUpdateRange(KTextEditor::LineRange::invalid())
0133     , m_mainWindow(mainWindow ? mainWindow : KTextEditor::EditorPrivate::self()->dummyMainWindow()) // use dummy window if no window there!
0134     , m_statusBar(nullptr)
0135     , m_temporaryAutomaticInvocationDisabled(false)
0136     , m_autoFoldedFirstLine(false)
0137 {
0138     // queued connect to collapse view updates for range changes, INIT THIS EARLY ENOUGH!
0139     connect(this, &KTextEditor::ViewPrivate::delayedUpdateOfView, this, &KTextEditor::ViewPrivate::slotDelayedUpdateOfView, Qt::QueuedConnection);
0140 
0141     m_delayedUpdateTimer.setSingleShot(true);
0142     m_delayedUpdateTimer.setInterval(0);
0143     connect(&m_delayedUpdateTimer, &QTimer::timeout, this, &KTextEditor::ViewPrivate::delayedUpdateOfView);
0144 
0145     KXMLGUIClient::setComponentName(KTextEditor::EditorPrivate::self()->aboutData().componentName(),
0146                                     KTextEditor::EditorPrivate::self()->aboutData().displayName());
0147 
0148     // selection if for this view only and will invalidate if becoming empty
0149     m_selection.setView(this);
0150 
0151     // use z depth defined in moving ranges interface
0152     m_selection.setZDepth(-100000.0);
0153 
0154     KTextEditor::EditorPrivate::self()->registerView(this);
0155 
0156     // try to let the main window, if any, create a view bar for this view
0157     QWidget *bottomBarParent = m_mainWindow->createViewBar(this);
0158 
0159     m_bottomViewBar = new KateViewBar(bottomBarParent != nullptr, bottomBarParent ? bottomBarParent : this, this);
0160 
0161     // ugly workaround:
0162     // Force the layout to be left-to-right even on RTL desktop, as discussed
0163     // on the mailing list. This will cause the lines and icons panel to be on
0164     // the left, even for Arabic/Hebrew/Farsi/whatever users.
0165     setLayoutDirection(Qt::LeftToRight);
0166 
0167     m_bottomViewBar->installEventFilter(m_viewInternal);
0168 
0169     // add KateMessageWidget for KTE::MessageInterface immediately above view
0170     m_messageWidgets[KTextEditor::Message::AboveView] = new KateMessageWidget(this);
0171     m_messageWidgets[KTextEditor::Message::AboveView]->setPosition(KateMessageWidget::Position::Header);
0172     m_messageWidgets[KTextEditor::Message::AboveView]->hide();
0173 
0174     // add KateMessageWidget for KTE::MessageInterface immediately above view
0175     m_messageWidgets[KTextEditor::Message::BelowView] = new KateMessageWidget(this);
0176     m_messageWidgets[KTextEditor::Message::BelowView]->setPosition(KateMessageWidget::Position::Footer);
0177     m_messageWidgets[KTextEditor::Message::BelowView]->hide();
0178 
0179     // add bottom viewbar...
0180     if (bottomBarParent) {
0181         m_mainWindow->addWidgetToViewBar(this, m_bottomViewBar);
0182     }
0183 
0184     // add layout for floating message widgets to KateViewInternal
0185     m_notificationLayout = new KateMessageLayout(m_viewInternal);
0186     m_notificationLayout->setContentsMargins(20, 20, 20, 20);
0187     m_viewInternal->setLayout(m_notificationLayout);
0188 
0189     // this really is needed :)
0190     m_viewInternal->updateView();
0191 
0192     doc->addView(this);
0193 
0194     setFocusProxy(m_viewInternal);
0195     setFocusPolicy(Qt::StrongFocus);
0196 
0197     setXMLFile(QStringLiteral("katepart5ui.rc"));
0198 
0199     setupConnections();
0200     setupActions();
0201 
0202     // auto word completion
0203     new KateWordCompletionView(this, actionCollection());
0204 
0205     // update the enabled state of the undo/redo actions...
0206     slotUpdateUndo();
0207 
0208     // create the status bar of this view
0209     // do this after action creation, we use some of them!
0210     toggleStatusBar();
0211 
0212     m_startingUp = false;
0213     updateConfig();
0214 
0215     slotHlChanged();
0216     KCursor::setAutoHideCursor(m_viewInternal, true);
0217 
0218     for (auto messageWidget : m_messageWidgets) {
0219         if (messageWidget) {
0220             // user interaction (scrolling) starts notification auto-hide timer
0221             connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
0222 
0223             // user interaction (cursor navigation) starts notification auto-hide timer
0224             connect(this, &KTextEditor::ViewPrivate::cursorPositionChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
0225         }
0226     }
0227 
0228     // folding restoration on reload
0229     connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::saveFoldingState);
0230     connect(m_doc, &KTextEditor::DocumentPrivate::reloaded, this, &KTextEditor::ViewPrivate::applyFoldingState);
0231 
0232     connect(m_doc, &KTextEditor::DocumentPrivate::reloaded, this, &KTextEditor::ViewPrivate::slotDocumentReloaded);
0233     connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::slotDocumentAboutToReload);
0234 
0235     // update highlights on scrolling and co
0236     connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, this, &KTextEditor::ViewPrivate::createHighlights);
0237 
0238     // clear highlights on reload
0239     connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::clearHighlights);
0240 
0241     // setup layout
0242     setupLayout();
0243 }
0244 
0245 KTextEditor::ViewPrivate::~ViewPrivate()
0246 {
0247     // de-register views early from global collections
0248     // otherwise we might "use" them again during destruction in a half-valid state
0249     // see e.g. bug 422546
0250     // Kate::TextBuffer::notifyAboutRangeChange will access views() in a chain during
0251     // deletion of m_viewInternal
0252     doc()->removeView(this);
0253     KTextEditor::EditorPrivate::self()->deregisterView(this);
0254 
0255     delete m_completionWidget;
0256 
0257     // remove from xmlgui factory, to be safe
0258     if (factory()) {
0259         factory()->removeClient(this);
0260     }
0261 
0262     // delete internal view before view bar!
0263     delete m_viewInternal;
0264 
0265     // remove view bar again, if needed
0266     m_mainWindow->deleteViewBar(this);
0267     m_bottomViewBar = nullptr;
0268 
0269     delete m_renderer;
0270 
0271     delete m_config;
0272 }
0273 
0274 void KTextEditor::ViewPrivate::toggleStatusBar()
0275 {
0276     // if there, delete it
0277     if (m_statusBar) {
0278         bottomViewBar()->removePermanentBarWidget(m_statusBar);
0279         delete m_statusBar;
0280         m_statusBar = nullptr;
0281         Q_EMIT statusBarEnabledChanged(this, false);
0282         return;
0283     }
0284 
0285     // else: create it
0286     m_statusBar = new KateStatusBar(this);
0287     bottomViewBar()->addPermanentBarWidget(m_statusBar);
0288     Q_EMIT statusBarEnabledChanged(this, true);
0289 }
0290 
0291 void KTextEditor::ViewPrivate::setupLayout()
0292 {
0293     // delete old layout if any
0294     if (layout()) {
0295         delete layout();
0296 
0297         //  need to recreate spacers because they are deleted with
0298         //  their belonging layout
0299         m_topSpacer = new QSpacerItem(0, 0);
0300         m_leftSpacer = new QSpacerItem(0, 0);
0301         m_rightSpacer = new QSpacerItem(0, 0);
0302         m_bottomSpacer = new QSpacerItem(0, 0);
0303     }
0304 
0305     // set margins
0306     QStyleOptionFrame opt;
0307     opt.initFrom(this);
0308     opt.frameShape = QFrame::StyledPanel;
0309     opt.state |= QStyle::State_Sunken;
0310     const int margin = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this);
0311     m_topSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
0312     m_leftSpacer->changeSize(margin, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
0313     m_rightSpacer->changeSize(margin, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
0314     m_bottomSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
0315 
0316     // define layout
0317     QGridLayout *layout = new QGridLayout(this);
0318     layout->setContentsMargins(0, 0, 0, 0);
0319     layout->setSpacing(0);
0320 
0321     const bool frameAroundContents = style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, this);
0322     if (frameAroundContents) {
0323         // top message widget
0324         layout->addWidget(m_messageWidgets[KTextEditor::Message::AboveView], 0, 0, 1, 5);
0325 
0326         // top spacer
0327         layout->addItem(m_topSpacer, 1, 0, 1, 4);
0328 
0329         // left spacer
0330         layout->addItem(m_leftSpacer, 2, 0, 1, 1);
0331 
0332         // left border
0333         layout->addWidget(m_viewInternal->m_leftBorder, 2, 1, 1, 1);
0334 
0335         // view
0336         layout->addWidget(m_viewInternal, 2, 2, 1, 1);
0337 
0338         // right spacer
0339         layout->addItem(m_rightSpacer, 2, 3, 1, 1);
0340 
0341         // bottom spacer
0342         layout->addItem(m_bottomSpacer, 3, 0, 1, 4);
0343 
0344         // vertical scrollbar
0345         layout->addWidget(m_viewInternal->m_lineScroll, 1, 4, 3, 1);
0346 
0347         // horizontal scrollbar
0348         layout->addWidget(m_viewInternal->m_columnScroll, 4, 0, 1, 4);
0349 
0350         // dummy
0351         layout->addWidget(m_viewInternal->m_dummy, 4, 4, 1, 1);
0352 
0353         // bottom message
0354         layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], 5, 0, 1, 5);
0355 
0356         // bottom viewbar
0357         if (m_bottomViewBar->parentWidget() == this) {
0358             layout->addWidget(m_bottomViewBar, 6, 0, 1, 5);
0359         }
0360 
0361         // stretch
0362         layout->setColumnStretch(2, 1);
0363         layout->setRowStretch(2, 1);
0364 
0365         // adjust scrollbar background
0366         m_viewInternal->m_lineScroll->setBackgroundRole(QPalette::Window);
0367         m_viewInternal->m_lineScroll->setAutoFillBackground(false);
0368 
0369         m_viewInternal->m_columnScroll->setBackgroundRole(QPalette::Window);
0370         m_viewInternal->m_columnScroll->setAutoFillBackground(false);
0371 
0372     } else {
0373         // top message widget
0374         layout->addWidget(m_messageWidgets[KTextEditor::Message::AboveView], 0, 0, 1, 5);
0375 
0376         // top spacer
0377         layout->addItem(m_topSpacer, 1, 0, 1, 5);
0378 
0379         // left spacer
0380         layout->addItem(m_leftSpacer, 2, 0, 1, 1);
0381 
0382         // left border
0383         layout->addWidget(m_viewInternal->m_leftBorder, 2, 1, 1, 1);
0384 
0385         // view
0386         layout->addWidget(m_viewInternal, 2, 2, 1, 1);
0387 
0388         // vertical scrollbar
0389         layout->addWidget(m_viewInternal->m_lineScroll, 2, 3, 1, 1);
0390 
0391         // right spacer
0392         layout->addItem(m_rightSpacer, 2, 4, 1, 1);
0393 
0394         // horizontal scrollbar
0395         layout->addWidget(m_viewInternal->m_columnScroll, 3, 1, 1, 2);
0396 
0397         // dummy
0398         layout->addWidget(m_viewInternal->m_dummy, 3, 3, 1, 1);
0399 
0400         // bottom spacer
0401         layout->addItem(m_bottomSpacer, 4, 0, 1, 5);
0402 
0403         // bottom message
0404         layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], 5, 0, 1, 5);
0405 
0406         // bottom viewbar
0407         if (m_bottomViewBar->parentWidget() == this) {
0408             layout->addWidget(m_bottomViewBar, 6, 0, 1, 5);
0409         }
0410 
0411         // stretch
0412         layout->setColumnStretch(2, 1);
0413         layout->setRowStretch(2, 1);
0414 
0415         // adjust scrollbar background
0416         m_viewInternal->m_lineScroll->setBackgroundRole(QPalette::Base);
0417         m_viewInternal->m_lineScroll->setAutoFillBackground(true);
0418 
0419         m_viewInternal->m_columnScroll->setBackgroundRole(QPalette::Base);
0420         m_viewInternal->m_columnScroll->setAutoFillBackground(true);
0421     }
0422 }
0423 
0424 void KTextEditor::ViewPrivate::setupConnections()
0425 {
0426     connect(m_doc, &KTextEditor::DocumentPrivate::undoChanged, this, &KTextEditor::ViewPrivate::slotUpdateUndo);
0427     connect(m_doc, &KTextEditor::DocumentPrivate::highlightingModeChanged, this, &KTextEditor::ViewPrivate::slotHlChanged);
0428     connect(m_doc, &KTextEditor::DocumentPrivate::canceled, this, &KTextEditor::ViewPrivate::slotSaveCanceled);
0429     connect(m_viewInternal, &KateViewInternal::dropEventPass, this, &KTextEditor::ViewPrivate::dropEventPass);
0430 
0431     connect(m_doc, &KTextEditor::DocumentPrivate::annotationModelChanged, m_viewInternal->m_leftBorder, &KateIconBorder::annotationModelChanged);
0432 }
0433 
0434 void KTextEditor::ViewPrivate::goToPreviousEditingPosition()
0435 {
0436     auto c = doc()->lastEditingPosition(KTextEditor::DocumentPrivate::Previous, cursorPosition());
0437     if (c.isValid()) {
0438         setCursorPosition(c);
0439     }
0440 }
0441 
0442 void KTextEditor::ViewPrivate::goToNextEditingPosition()
0443 {
0444     auto c = doc()->lastEditingPosition(KTextEditor::DocumentPrivate::Next, cursorPosition());
0445     if (c.isValid()) {
0446         setCursorPosition(c);
0447     }
0448 }
0449 void KTextEditor::ViewPrivate::setupActions()
0450 {
0451     KActionCollection *ac = actionCollection();
0452     QAction *a;
0453 
0454     m_toggleWriteLock = nullptr;
0455 
0456     m_cut = a = ac->addAction(KStandardAction::Cut, this, SLOT(cut()));
0457     a->setWhatsThis(i18n("Cut the selected text and move it to the clipboard"));
0458 
0459     m_paste = a = ac->addAction(KStandardAction::Paste, this, SLOT(paste()));
0460     a->setWhatsThis(i18n("Paste previously copied or cut clipboard contents"));
0461 
0462     m_copy = a = ac->addAction(KStandardAction::Copy, this, SLOT(copy()));
0463     a->setWhatsThis(i18n("Use this command to copy the currently selected text to the system clipboard."));
0464 
0465     m_clipboardHistory = a = ac->addAction(QStringLiteral("clipboard_history_paste"), this, [this] {
0466         ClipboardHistoryDialog chd(mainWindow()->window(), this);
0467         chd.openDialog(KTextEditor::EditorPrivate::self()->clipboardHistory());
0468     });
0469     a->setText(i18n("Clipboard &History Paste"));
0470     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::ALT | Qt::Key_V));
0471 
0472     if (QApplication::clipboard()->supportsSelection()) {
0473         m_pasteSelection = a = ac->addAction(QStringLiteral("edit_paste_selection"), this, SLOT(pasteSelection()));
0474         a->setText(i18n("Paste Selection"));
0475         ac->setDefaultShortcuts(a, KStandardShortcut::pasteSelection());
0476         a->setWhatsThis(i18n("Paste previously mouse selection contents"));
0477     }
0478 
0479     m_swapWithClipboard = a = ac->addAction(QStringLiteral("edit_swap_with_clipboard"), this, SLOT(swapWithClipboard()));
0480     a->setText(i18n("Swap with Clipboard Contents"));
0481     a->setWhatsThis(i18n("Swap the selected text with the clipboard contents"));
0482 
0483     m_screenshotSelection = a = ac->addAction(QStringLiteral("text_screenshot_selection"), this, &KTextEditor::ViewPrivate::screenshot);
0484     a->setText(i18n("Take Screenshot of Selection"));
0485 
0486     if (!doc()->readOnly()) {
0487         a = ac->addAction(KStandardAction::Save, m_doc, SLOT(documentSave()));
0488         a->setWhatsThis(i18n("Save the current document"));
0489 
0490         a = m_editUndo = ac->addAction(KStandardAction::Undo, m_doc, SLOT(undo()));
0491         a->setWhatsThis(i18n("Revert the most recent editing actions"));
0492 
0493         a = m_editRedo = ac->addAction(KStandardAction::Redo, m_doc, SLOT(redo()));
0494         a->setWhatsThis(i18n("Revert the most recent undo operation"));
0495 
0496         // Tools > Scripts
0497         // stored inside scoped pointer to ensure we destruct it early enough as it does internal cleanups of other child objects
0498         m_scriptActionMenu.reset(new KateScriptActionMenu(this, i18n("&Scripts")));
0499         ac->addAction(QStringLiteral("tools_scripts"), m_scriptActionMenu.get());
0500 
0501         a = ac->addAction(QStringLiteral("tools_apply_wordwrap"));
0502         a->setText(i18n("Apply &Word Wrap"));
0503         a->setWhatsThis(
0504             i18n("Use this to wrap the current line, or to reformat the selected lines as paragraph, "
0505                  "to fit the 'Wrap words at' setting in the configuration dialog.<br /><br />"
0506                  "This is a static word wrap, meaning the document is changed."));
0507         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::applyWordWrap);
0508 
0509         a = ac->addAction(QStringLiteral("tools_cleanIndent"));
0510         a->setText(i18n("&Clean Indentation"));
0511         a->setWhatsThis(
0512             i18n("Use this to clean the indentation of a selected block of text (only tabs/only spaces).<br /><br />"
0513                  "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
0514         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cleanIndent);
0515 
0516         a = ac->addAction(QStringLiteral("tools_formatIndent"));
0517         a->setText(i18n("&Format Indentation"));
0518         a->setWhatsThis(i18n("Use this to auto indent the current line or block of text to its proper indent level."));
0519         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::formatIndent);
0520 
0521         a = ac->addAction(QStringLiteral("tools_alignOn"));
0522         a->setText(i18n("&Align On..."));
0523         a->setWhatsThis(
0524             i18n("This command aligns lines in the selected block or whole document on the column given by a regular expression "
0525                  "that you will be prompted for.<br /><br />"
0526                  "If you give an empty pattern it will align on the first non-blank character by default.<br />"
0527                  "If the pattern has a capture it will indent on the captured match.<br /><br />"
0528                  "<i>Examples</i>:<br />"
0529                  "With '-' it will insert spaces before the first '-' of each lines to align them all on the same column.<br />"
0530                  "With ':\\s+(.)' it will insert spaces before the first non-blank character that occurs after a colon to align "
0531                  "them all on the same column."));
0532         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::alignOn);
0533 
0534         a = ac->addAction(QStringLiteral("tools_comment"));
0535         a->setText(i18n("C&omment"));
0536         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_D));
0537         a->setWhatsThis(
0538             i18n("This command comments out the current line or a selected block of text.<br /><br />"
0539                  "The characters for single/multiple line comments are defined within the language's highlighting."));
0540         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::comment);
0541 
0542         a = ac->addAction(QStringLiteral("Previous Editing Line"));
0543         a->setText(i18n("Go to Previous Editing Location"));
0544         a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
0545         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_E));
0546         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::goToPreviousEditingPosition);
0547 
0548         a = ac->addAction(QStringLiteral("Next Editing Line"));
0549         a->setText(i18n("Go to Next Editing Location"));
0550         a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
0551         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_E));
0552         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::goToNextEditingPosition);
0553 
0554         a = ac->addAction(QStringLiteral("tools_uncomment"));
0555         a->setText(i18n("Unco&mment"));
0556         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D));
0557         a->setWhatsThis(
0558             i18n("This command removes comments from the current line or a selected block of text.<br /><br />"
0559                  "The characters for single/multiple line comments are defined within the language's highlighting."));
0560         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::uncomment);
0561 
0562         a = ac->addAction(QStringLiteral("tools_toggle_comment"));
0563         a->setText(i18n("Toggle Comment"));
0564         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_Slash));
0565         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleComment);
0566 
0567         a = m_toggleWriteLock = new KToggleAction(i18n("&Read Only Mode"), this);
0568         a->setWhatsThis(i18n("Lock/unlock the document for writing"));
0569         a->setChecked(!doc()->isReadWrite());
0570         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleWriteLock);
0571         ac->addAction(QStringLiteral("tools_toggle_write_lock"), a);
0572 
0573         a = m_forceRTLDirection = new KToggleAction(i18n("&Force RTL Direction"), this);
0574         a->setWhatsThis(i18n("Force RTL Text Direction"));
0575         connect(a, &QAction::triggered, this, [this](bool checked) {
0576             m_forceRTL = checked;
0577             tagAll();
0578             updateView(true);
0579         });
0580         ac->addAction(QStringLiteral("force_rtl_direction"), a);
0581 
0582         a = ac->addAction(QStringLiteral("tools_uppercase"));
0583         a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-uppercase")));
0584         a->setText(i18n("Uppercase"));
0585         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_U));
0586         a->setWhatsThis(
0587             i18n("Convert the selection to uppercase, or the character to the "
0588                  "right of the cursor if no text is selected."));
0589         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::uppercase);
0590 
0591         a = ac->addAction(QStringLiteral("tools_lowercase"));
0592         a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-lowercase")));
0593         a->setText(i18n("Lowercase"));
0594         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_U));
0595         a->setWhatsThis(
0596             i18n("Convert the selection to lowercase, or the character to the "
0597                  "right of the cursor if no text is selected."));
0598         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::lowercase);
0599 
0600         a = ac->addAction(QStringLiteral("tools_capitalize"));
0601         a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-capitalize")));
0602         a->setText(i18n("Capitalize"));
0603         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_U));
0604         a->setWhatsThis(
0605             i18n("Capitalize the selection, or the word under the "
0606                  "cursor if no text is selected."));
0607         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::capitalize);
0608 
0609         a = ac->addAction(QStringLiteral("tools_join_lines"));
0610         a->setText(i18n("Join Lines"));
0611         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_J));
0612         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::joinLines);
0613 
0614         a = ac->addAction(QStringLiteral("tools_invoke_code_completion"));
0615         a->setText(i18n("Invoke Code Completion"));
0616         a->setWhatsThis(i18n("Manually invoke command completion, usually by using a shortcut bound to this action."));
0617         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_Space));
0618         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::userInvokedCompletion);
0619 
0620         a = ac->addAction(QStringLiteral("remove_trailing_spaces"));
0621         a->setText(i18n("Remove Trailing Spaces"));
0622         connect(a, &QAction::triggered, this, [this] {
0623             doc()->removeAllTrailingSpaces();
0624         });
0625     } else {
0626         for (auto *action : {m_cut, m_paste, m_clipboardHistory, m_swapWithClipboard}) {
0627             action->setEnabled(false);
0628         }
0629 
0630         if (m_pasteSelection) {
0631             m_pasteSelection->setEnabled(false);
0632         }
0633 
0634         m_editUndo = nullptr;
0635         m_editRedo = nullptr;
0636     }
0637 
0638     a = ac->addAction(KStandardAction::Print, this, SLOT(print()));
0639     a->setWhatsThis(i18n("Print the current document."));
0640 
0641     a = ac->addAction(KStandardAction::PrintPreview, this, SLOT(printPreview()));
0642     a->setWhatsThis(i18n("Show print preview of current document"));
0643 
0644     a = ac->addAction(QStringLiteral("file_reload"));
0645     a->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0646     a->setText(i18n("Reloa&d"));
0647     ac->setDefaultShortcuts(a, KStandardShortcut::reload());
0648     a->setWhatsThis(i18n("Reload the current document from disk."));
0649     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::reloadFile);
0650 
0651     a = ac->addAction(KStandardAction::SaveAs, m_doc, SLOT(documentSaveAs()));
0652     a->setWhatsThis(i18n("Save the current document to disk, with a name of your choice."));
0653 
0654     a = new KateViewEncodingAction(m_doc, this, i18n("Save As with Encoding..."), this, true /* special mode for save as */);
0655     a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
0656     ac->addAction(QStringLiteral("file_save_as_with_encoding"), a);
0657 
0658     a = ac->addAction(QStringLiteral("file_save_copy_as"));
0659     a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
0660     a->setText(i18n("Save &Copy As..."));
0661     a->setWhatsThis(i18n("Save a copy of the current document to disk."));
0662     connect(a, &QAction::triggered, m_doc, &KTextEditor::DocumentPrivate::documentSaveCopyAs);
0663 
0664     a = ac->addAction(KStandardAction::GotoLine, this, SLOT(gotoLine()));
0665     a->setWhatsThis(i18n("This command opens a dialog and lets you choose a line that you want the cursor to move to."));
0666 
0667     a = ac->addAction(QStringLiteral("modified_line_up"));
0668     a->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
0669     a->setText(i18n("Go to Previous Modified Line"));
0670     a->setWhatsThis(i18n("Move upwards to the previous modified line."));
0671     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toPrevModifiedLine);
0672 
0673     a = ac->addAction(QStringLiteral("modified_line_down"));
0674     a->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
0675     a->setText(i18n("Go to Next Modified Line"));
0676     a->setWhatsThis(i18n("Move downwards to the next modified line."));
0677     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toNextModifiedLine);
0678 
0679     a = ac->addAction(QStringLiteral("set_confdlg"));
0680     a->setText(i18n("&Configure Editor..."));
0681     a->setIcon(QIcon::fromTheme(QStringLiteral("preferences-other")));
0682     a->setWhatsThis(i18n("Configure various aspects of this editor."));
0683     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotConfigDialog);
0684 
0685     m_modeAction = new KateModeMenu(i18n("&Mode"), this);
0686     ac->addAction(QStringLiteral("tools_mode"), m_modeAction);
0687     m_modeAction->setWhatsThis(i18n(
0688         "Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example."));
0689     m_modeAction->updateMenu(m_doc);
0690 
0691     KateHighlightingMenu *menu = new KateHighlightingMenu(i18n("&Highlighting"), this);
0692     ac->addAction(QStringLiteral("tools_highlighting"), menu);
0693     menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted."));
0694     menu->updateMenu(m_doc);
0695 
0696     KateViewSchemaAction *schemaMenu = new KateViewSchemaAction(i18n("&Editor Color Theme"), this);
0697     schemaMenu->setIcon(QIcon::fromTheme(QStringLiteral("kcolorchooser")));
0698     ac->addAction(QStringLiteral("view_schemas"), schemaMenu);
0699     schemaMenu->updateMenu(this);
0700 
0701     // indentation menu
0702     KateViewIndentationAction *indentMenu = new KateViewIndentationAction(m_doc, i18n("&Indentation"), this);
0703     ac->addAction(QStringLiteral("tools_indentation"), indentMenu);
0704 
0705     m_selectAll = ac->addAction(KStandardAction::SelectAll);
0706     connect(m_selectAll, &QAction::triggered, this, [this] {
0707         selectAll();
0708         qApp->clipboard()->setText(selectionText(), QClipboard::Selection);
0709     });
0710     a->setWhatsThis(i18n("Select the entire text of the current document."));
0711 
0712     m_deSelect = a = ac->addAction(KStandardAction::Deselect, this, SLOT(clearSelection()));
0713     a->setWhatsThis(i18n("If you have selected something within the current document, this will no longer be selected."));
0714 
0715     a = ac->addAction(QStringLiteral("view_inc_font_sizes"));
0716     a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
0717     a->setText(i18n("Enlarge Font"));
0718     ac->setDefaultShortcuts(a, KStandardShortcut::zoomIn());
0719     a->setWhatsThis(i18n("This increases the display font size."));
0720     connect(a, &QAction::triggered, m_viewInternal, [this]() {
0721         m_viewInternal->slotIncFontSizes();
0722     });
0723 
0724     a = ac->addAction(QStringLiteral("view_dec_font_sizes"));
0725     a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
0726     a->setText(i18n("Shrink Font"));
0727     ac->setDefaultShortcuts(a, KStandardShortcut::zoomOut());
0728     a->setWhatsThis(i18n("This decreases the display font size."));
0729     connect(a, &QAction::triggered, m_viewInternal, [this]() {
0730         m_viewInternal->slotDecFontSizes();
0731     });
0732 
0733     a = ac->addAction(QStringLiteral("view_reset_font_sizes"));
0734     a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
0735     a->setText(i18n("Reset Font Size"));
0736     ac->setDefaultShortcuts(a, KStandardShortcut::shortcut(KStandardShortcut::ActualSize));
0737     a->setWhatsThis(i18n("This resets the display font size."));
0738     connect(a, &QAction::triggered, m_viewInternal, &KateViewInternal::slotResetFontSizes);
0739 
0740     a = m_toggleBlockSelection = new KToggleAction(i18n("Bl&ock Selection Mode"), this);
0741     ac->addAction(QStringLiteral("set_verticalSelect"), a);
0742     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_B));
0743     a->setWhatsThis(i18n("This command allows switching between the normal (line based) selection mode and the block selection mode."));
0744     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleBlockSelection);
0745 
0746     a = ac->addAction(QStringLiteral("switch_next_input_mode"));
0747     a->setText(i18n("Switch to Next Input Mode"));
0748     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_V));
0749     a->setWhatsThis(i18n("Switch to the next input mode."));
0750     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cycleInputMode);
0751 
0752     a = m_toggleInsert = new KToggleAction(i18n("Overwr&ite Mode"), this);
0753     ac->addAction(QStringLiteral("set_insert"), a);
0754     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Insert));
0755     a->setWhatsThis(i18n("Choose whether you want the text you type to be inserted or to overwrite existing text."));
0756     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleInsert);
0757 
0758     KToggleAction *toggleAction;
0759     a = m_toggleDynWrap = toggleAction = new KToggleAction(i18n("&Dynamic Word Wrap"), this);
0760     a->setIcon(QIcon::fromTheme(QStringLiteral("text-wrap")));
0761     ac->addAction(QStringLiteral("view_dynamic_word_wrap"), a);
0762     a->setWhatsThis(
0763         i18n("If this option is checked, the text lines will be wrapped at the view border on the screen.<br /><br />"
0764              "This is only a view option, meaning the document will not changed."));
0765     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleDynWordWrap);
0766 
0767     a = m_setDynWrapIndicators = new KSelectAction(i18n("Dynamic Word Wrap Indicators"), this);
0768     ac->addAction(QStringLiteral("dynamic_word_wrap_indicators"), a);
0769     a->setWhatsThis(i18n("Choose when the Dynamic Word Wrap Indicators should be displayed"));
0770     connect(m_setDynWrapIndicators, &KSelectAction::indexTriggered, this, &KTextEditor::ViewPrivate::setDynWrapIndicators);
0771     const QStringList list2{i18n("&Off"), i18n("Follow &Line Numbers"), i18n("&Always On")};
0772     m_setDynWrapIndicators->setItems(list2);
0773     m_setDynWrapIndicators->setEnabled(m_toggleDynWrap->isChecked()); // only synced on real change, later
0774 
0775     a = toggleAction = new KToggleAction(i18n("Static Word Wrap"), this);
0776     ac->addAction(QStringLiteral("view_static_word_wrap"), a);
0777     a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the column defined in the editing properties."));
0778     connect(a, &KToggleAction::triggered, [this] {
0779         if (m_doc) {
0780             m_doc->setWordWrap(!m_doc->wordWrap());
0781         }
0782     });
0783 
0784     a = toggleAction = m_toggleWWMarker = new KToggleAction(i18n("Show Static &Word Wrap Marker"), this);
0785     ac->addAction(QStringLiteral("view_word_wrap_marker"), a);
0786     a->setWhatsThis(
0787         i18n("Show/hide the Word Wrap Marker, a vertical line drawn at the word "
0788              "wrap column as defined in the editing properties"));
0789     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleWWMarker);
0790 
0791     a = toggleAction = m_toggleFoldingMarkers = new KToggleAction(i18n("Show Folding &Markers"), this);
0792     ac->addAction(QStringLiteral("view_folding_markers"), a);
0793     a->setWhatsThis(i18n("You can choose if the codefolding marks should be shown, if codefolding is possible."));
0794     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleFoldingMarkers);
0795 
0796     a = m_toggleIconBar = toggleAction = new KToggleAction(i18n("Show &Icon Border"), this);
0797     ac->addAction(QStringLiteral("view_border"), a);
0798     a->setWhatsThis(i18n("Show/hide the icon border.<br /><br />The icon border shows bookmark symbols, for instance."));
0799     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleIconBorder);
0800 
0801     a = toggleAction = m_toggleLineNumbers = new KToggleAction(i18n("Show &Line Numbers"), this);
0802     ac->addAction(QStringLiteral("view_line_numbers"), a);
0803     a->setWhatsThis(i18n("Show/hide the line numbers on the left hand side of the view."));
0804     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleLineNumbersOn);
0805 
0806     a = m_toggleScrollBarMarks = toggleAction = new KToggleAction(i18n("Show Scroll&bar Marks"), this);
0807     ac->addAction(QStringLiteral("view_scrollbar_marks"), a);
0808     a->setWhatsThis(i18n("Show/hide the marks on the vertical scrollbar.<br /><br />The marks show bookmarks, for instance."));
0809     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleScrollBarMarks);
0810 
0811     a = m_toggleScrollBarMiniMap = toggleAction = new KToggleAction(i18n("Show Scrollbar Mini-Map"), this);
0812     ac->addAction(QStringLiteral("view_scrollbar_minimap"), a);
0813     a->setWhatsThis(i18n("Show/hide the mini-map on the vertical scrollbar.<br /><br />The mini-map shows an overview of the whole document."));
0814     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleScrollBarMiniMap);
0815 
0816     a = m_doc->autoReloadToggleAction();
0817     ac->addAction(QStringLiteral("view_auto_reload"), a);
0818 
0819     //   a = m_toggleScrollBarMiniMapAll = toggleAction = new KToggleAction(i18n("Show the whole document in the Mini-Map"), this);
0820     //   ac->addAction(QLatin1String("view_scrollbar_minimap_all"), a);
0821     //   a->setWhatsThis(i18n("Display the whole document in the mini-map.<br /><br />With this option set the whole document will be visible in the
0822     //   mini-map.")); connect(a, SIGNAL(triggered(bool)), SLOT(toggleScrollBarMiniMapAll())); connect(m_toggleScrollBarMiniMap, SIGNAL(triggered(bool)),
0823     //   m_toggleScrollBarMiniMapAll, SLOT(setEnabled(bool)));
0824 
0825     a = m_toggleNPSpaces = new KToggleAction(i18n("Show Non-Printable Spaces"), this);
0826     ac->addAction(QStringLiteral("view_non_printable_spaces"), a);
0827     a->setWhatsThis(i18n("Show/hide bounding box around non-printable spaces"));
0828     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleNPSpaces);
0829 
0830     a = m_switchCmdLine = ac->addAction(QStringLiteral("switch_to_cmd_line"));
0831     a->setText(i18n("Switch to Command Line"));
0832     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_F7));
0833     a->setWhatsThis(i18n("Show/hide the command line on the bottom of the view."));
0834     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::switchToCmdLine);
0835 
0836     KActionMenu *am = new KActionMenu(i18n("Input Modes"), this);
0837     m_inputModeActions = new QActionGroup(am);
0838     ac->addAction(QStringLiteral("view_input_modes"), am);
0839     auto switchInputModeAction = ac->action(QStringLiteral("switch_next_input_mode"));
0840     am->addAction(switchInputModeAction);
0841     am->addSeparator();
0842     for (const auto &mode : m_viewInternal->m_inputModes) {
0843         a = new QAction(mode->viewInputModeHuman(), m_inputModeActions);
0844         am->addAction(a);
0845         a->setWhatsThis(i18n("Activate/deactivate %1", mode->viewInputModeHuman()));
0846         const InputMode im = mode->viewInputMode();
0847         a->setData(static_cast<int>(im));
0848         a->setCheckable(true);
0849         if (im == m_config->inputMode()) {
0850             a->setChecked(true);
0851         }
0852         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleInputMode);
0853     }
0854 
0855     a = m_setEndOfLine = new KSelectAction(i18n("&End of Line"), this);
0856     ac->addAction(QStringLiteral("set_eol"), a);
0857     a->setWhatsThis(i18n("Choose which line endings should be used, when you save the document"));
0858     const QStringList list{i18nc("@item:inmenu End of Line", "&UNIX"),
0859                            i18nc("@item:inmenu End of Line", "&Windows/DOS"),
0860                            i18nc("@item:inmenu End of Line", "&Macintosh")};
0861     m_setEndOfLine->setItems(list);
0862     m_setEndOfLine->setCurrentItem(doc()->config()->eol());
0863     connect(m_setEndOfLine, &KSelectAction::indexTriggered, this, &KTextEditor::ViewPrivate::setEol);
0864 
0865     a = m_addBom = new KToggleAction(i18n("Add &Byte Order Mark (BOM)"), this);
0866     m_addBom->setChecked(doc()->config()->bom());
0867     ac->addAction(QStringLiteral("add_bom"), a);
0868     a->setWhatsThis(i18n("Enable/disable adding of byte order marks for UTF-8/UTF-16 encoded files while saving"));
0869     connect(m_addBom, &KToggleAction::triggered, this, &KTextEditor::ViewPrivate::setAddBom);
0870 
0871     // encoding menu
0872     m_encodingAction = new KateViewEncodingAction(m_doc, this, i18n("E&ncoding"), this);
0873     ac->addAction(QStringLiteral("set_encoding"), m_encodingAction);
0874 
0875     a = ac->addAction(KStandardAction::Find, this, SLOT(find()));
0876     a->setWhatsThis(i18n("Look up the first occurrence of a piece of text or regular expression."));
0877     addAction(a);
0878 
0879     a = ac->addAction(QStringLiteral("edit_find_selected"));
0880     a->setText(i18n("Find Selected"));
0881     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_H));
0882     a->setWhatsThis(i18n("Finds next occurrence of selected text."));
0883     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findSelectedForwards);
0884 
0885     a = ac->addAction(QStringLiteral("edit_find_selected_backwards"));
0886     a->setText(i18n("Find Selected Backwards"));
0887     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_H));
0888     a->setWhatsThis(i18n("Finds previous occurrence of selected text."));
0889     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findSelectedBackwards);
0890 
0891     a = ac->addAction(QStringLiteral("edit_find_multicursor_next_occurrence"));
0892     a->setText(i18n("Find and Select Next Occurrence"));
0893     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::Key_J));
0894     a->setWhatsThis(i18n("Finds next occurrence of the word under cursor and add it to selection."));
0895     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findNextOccurunceAndSelect);
0896 
0897     a = ac->addAction(QStringLiteral("edit_skip_multicursor_current_occurrence"));
0898     a->setText(i18n("Mark Currently Selected Occurrence as Skipped"));
0899     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::Key_K));
0900     a->setWhatsThis(i18n("Marks the currently selected word as skipped."));
0901     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::skipCurrentOccurunceSelection);
0902 
0903     a = ac->addAction(QStringLiteral("edit_find_multicursor_all_occurrences"));
0904     a->setText(i18n("Find and Select All Occurrences"));
0905     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::CTRL | Qt::Key_J));
0906     a->setWhatsThis(i18n("Finds all occurrences of the word under cursor and selects them."));
0907     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findAllOccuruncesAndSelect);
0908 
0909     a = ac->addAction(KStandardAction::FindNext, this, SLOT(findNext()));
0910     a->setWhatsThis(i18n("Look up the next occurrence of the search phrase."));
0911     addAction(a);
0912 
0913     a = ac->addAction(KStandardAction::FindPrev, QStringLiteral("edit_find_prev"), this, SLOT(findPrevious()));
0914     a->setWhatsThis(i18n("Look up the previous occurrence of the search phrase."));
0915     addAction(a);
0916 
0917     a = ac->addAction(KStandardAction::Replace, this, SLOT(replace()));
0918     a->setWhatsThis(i18n("Look up a piece of text or regular expression and replace the result with some given text."));
0919 
0920     a = ac->addAction(QStringLiteral("edit_create_multi_cursor_from_sel"));
0921     a->setText(i18n("Add Cursors to Line Ends"));
0922     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_I));
0923     a->setWhatsThis(i18n("Creates a cursor at the end of every line in selection."));
0924     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::createMultiCursorsFromSelection);
0925 
0926     a = ac->addAction(QStringLiteral("edit_create_multi_cursor_down"));
0927     a->setText(i18n("Add Caret below Cursor"));
0928     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_Down));
0929     a->setWhatsThis(i18n("Adds a caret in the line below the current caret."));
0930     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::addSecondaryCursorDown);
0931 
0932     a = ac->addAction(QStringLiteral("edit_create_multi_cursor_up"));
0933     a->setText(i18n("Add Caret above Cursor"));
0934     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_Up));
0935     a->setWhatsThis(i18n("Adds a caret in the line above the current caret."));
0936     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::addSecondaryCursorUp);
0937 
0938     a = ac->addAction(QStringLiteral("edit_toggle_camel_case_cursor"));
0939     a->setText(i18n("Toggle Camel Case Cursor Movement"));
0940     a->setWhatsThis(i18n("Toggle between normal word movement and camel case cursor movement."));
0941     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleCamelCaseCursor);
0942 
0943     a = ac->addAction(QStringLiteral("edit_remove_cursors_from_empty_lines"));
0944     a->setText(i18n("Remove Cursors from Empty Lines"));
0945     a->setWhatsThis(i18n("Remove cursors from empty lines"));
0946     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::removeCursorsFromEmptyLines);
0947 
0948     m_spell->createActions(ac);
0949     m_toggleOnTheFlySpellCheck = new KToggleAction(i18n("Automatic Spell Checking"), this);
0950     m_toggleOnTheFlySpellCheck->setWhatsThis(i18n("Enable/disable automatic spell checking"));
0951     connect(m_toggleOnTheFlySpellCheck, &KToggleAction::triggered, this, &KTextEditor::ViewPrivate::toggleOnTheFlySpellCheck);
0952     ac->addAction(QStringLiteral("tools_toggle_automatic_spell_checking"), m_toggleOnTheFlySpellCheck);
0953     ac->setDefaultShortcut(m_toggleOnTheFlySpellCheck, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_O));
0954 
0955     a = ac->addAction(QStringLiteral("tools_change_dictionary"));
0956     a->setText(i18n("Change Dictionary..."));
0957     a->setWhatsThis(i18n("Change the dictionary that is used for spell checking."));
0958     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::changeDictionary);
0959 
0960     a = ac->addAction(QStringLiteral("tools_clear_dictionary_ranges"));
0961     a->setText(i18n("Clear Dictionary Ranges"));
0962     a->setEnabled(false);
0963     a->setWhatsThis(i18n("Remove all the separate dictionary ranges that were set for spell checking."));
0964     connect(a, &QAction::triggered, m_doc, &KTextEditor::DocumentPrivate::clearDictionaryRanges);
0965     connect(m_doc, &KTextEditor::DocumentPrivate::dictionaryRangesPresent, a, &QAction::setEnabled);
0966 
0967     m_copyHtmlAction = ac->addAction(QStringLiteral("edit_copy_html"), this, SLOT(exportHtmlToClipboard()));
0968     m_copyHtmlAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
0969     m_copyHtmlAction->setText(i18n("Copy as &HTML"));
0970     m_copyHtmlAction->setWhatsThis(i18n("Use this command to copy the currently selected text as HTML to the system clipboard."));
0971 
0972     a = ac->addAction(QStringLiteral("file_export_html"), this, SLOT(exportHtmlToFile()));
0973     a->setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
0974     a->setText(i18n("E&xport as HTML..."));
0975     a->setWhatsThis(
0976         i18n("This command allows you to export the current document"
0977              " with all highlighting information into a HTML document."));
0978 
0979     m_spellingMenu->createActions(ac);
0980 
0981     m_bookmarks->createActions(ac);
0982 
0983     slotSelectionChanged();
0984 
0985     // Now setup the editing actions before adding the associated
0986     // widget and setting the shortcut context
0987     setupEditActions();
0988     setupCodeFolding();
0989     setupSpeechActions();
0990 
0991     ac->addAssociatedWidget(m_viewInternal);
0992 
0993     const auto actions = ac->actions();
0994     for (QAction *action : actions) {
0995         action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
0996     }
0997 
0998     connect(this, &KTextEditor::ViewPrivate::selectionChanged, this, &KTextEditor::ViewPrivate::slotSelectionChanged);
0999 }
1000 
1001 void KTextEditor::ViewPrivate::slotConfigDialog()
1002 {
1003     // invoke config dialog, will auto-save configuration to katepartrc
1004     KTextEditor::EditorPrivate::self()->configDialog(this);
1005 }
1006 
1007 void KTextEditor::ViewPrivate::setupEditActions()
1008 {
1009     // If you add an editing action to this
1010     // function make sure to include the line
1011     // m_editActions << a after creating the action
1012     KActionCollection *ac = actionCollection();
1013 
1014     QAction *a = ac->addAction(QStringLiteral("word_left"));
1015     a->setText(i18n("Move Word Left"));
1016     ac->setDefaultShortcuts(a, KStandardShortcut::backwardWord());
1017     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::wordLeft);
1018     m_editActions.push_back(a);
1019 
1020     a = ac->addAction(QStringLiteral("select_char_left"));
1021     a->setText(i18n("Select Character Left"));
1022     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_Left));
1023     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftCursorLeft);
1024     m_editActions.push_back(a);
1025 
1026     a = ac->addAction(QStringLiteral("select_word_left"));
1027     a->setText(i18n("Select Word Left"));
1028     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Left));
1029     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftWordLeft);
1030     m_editActions.push_back(a);
1031 
1032     a = ac->addAction(QStringLiteral("word_right"));
1033     a->setText(i18n("Move Word Right"));
1034     ac->setDefaultShortcuts(a, KStandardShortcut::forwardWord());
1035     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::wordRight);
1036     m_editActions.push_back(a);
1037 
1038     a = ac->addAction(QStringLiteral("select_char_right"));
1039     a->setText(i18n("Select Character Right"));
1040     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_Right));
1041     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftCursorRight);
1042     m_editActions.push_back(a);
1043 
1044     a = ac->addAction(QStringLiteral("select_word_right"));
1045     a->setText(i18n("Select Word Right"));
1046     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Right));
1047     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftWordRight);
1048     m_editActions.push_back(a);
1049 
1050     a = ac->addAction(QStringLiteral("mark_selection"));
1051     a->setText(i18n("Start the Marked Selection"));
1052     a->setWhatsThis(i18n("Emulate the Emacs-like selection mode, where the beginning is marked and then the selection is continuously updated."));
1053     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::markSelection);
1054     m_editActions.push_back(a);
1055 
1056     a = ac->addAction(QStringLiteral("beginning_of_line"));
1057     a->setText(i18n("Move to Beginning of Line"));
1058     ac->setDefaultShortcuts(a, KStandardShortcut::beginningOfLine());
1059     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::home);
1060     m_editActions.push_back(a);
1061 
1062     a = ac->addAction(QStringLiteral("beginning_of_document"));
1063     a->setText(i18n("Move to Beginning of Document"));
1064     ac->setDefaultShortcuts(a, KStandardShortcut::begin());
1065     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::top);
1066     m_editActions.push_back(a);
1067 
1068     a = ac->addAction(QStringLiteral("select_beginning_of_line"));
1069     a->setText(i18n("Select to Beginning of Line"));
1070     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_Home));
1071     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftHome);
1072     m_editActions.push_back(a);
1073 
1074     a = ac->addAction(QStringLiteral("select_beginning_of_document"));
1075     a->setText(i18n("Select to Beginning of Document"));
1076     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Home));
1077     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftTop);
1078     m_editActions.push_back(a);
1079 
1080     a = ac->addAction(QStringLiteral("end_of_line"));
1081     a->setText(i18n("Move to End of Line"));
1082     ac->setDefaultShortcuts(a, KStandardShortcut::endOfLine());
1083     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::end);
1084     m_editActions.push_back(a);
1085 
1086     a = ac->addAction(QStringLiteral("end_of_document"));
1087     a->setText(i18n("Move to End of Document"));
1088     ac->setDefaultShortcuts(a, KStandardShortcut::end());
1089     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::bottom);
1090     m_editActions.push_back(a);
1091 
1092     a = ac->addAction(QStringLiteral("select_end_of_line"));
1093     a->setText(i18n("Select to End of Line"));
1094     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_End));
1095     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftEnd);
1096     m_editActions.push_back(a);
1097 
1098     a = ac->addAction(QStringLiteral("select_end_of_document"));
1099     a->setText(i18n("Select to End of Document"));
1100     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_End));
1101     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftBottom);
1102     m_editActions.push_back(a);
1103 
1104     a = ac->addAction(QStringLiteral("select_line_up"));
1105     a->setText(i18n("Select to Previous Line"));
1106     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_Up));
1107     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftUp);
1108     m_editActions.push_back(a);
1109 
1110     a = ac->addAction(QStringLiteral("scroll_line_up"));
1111     a->setText(i18n("Scroll Line Up"));
1112     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_Up));
1113     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::scrollUp);
1114     m_editActions.push_back(a);
1115 
1116     a = ac->addAction(QStringLiteral("move_line_down"));
1117     a->setText(i18n("Move to Next Line"));
1118     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Down));
1119     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::down);
1120     m_editActions.push_back(a);
1121 
1122     a = ac->addAction(QStringLiteral("move_line_up"));
1123     a->setText(i18n("Move to Previous Line"));
1124     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Up));
1125     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::up);
1126     m_editActions.push_back(a);
1127 
1128     a = ac->addAction(QStringLiteral("move_cursor_right"));
1129     a->setText(i18n("Move Cursor Right"));
1130     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Right));
1131     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cursorRight);
1132     m_editActions.push_back(a);
1133 
1134     a = ac->addAction(QStringLiteral("move_cursor_left"));
1135     a->setText(i18n("Move Cursor Left"));
1136     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Left));
1137     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cursorLeft);
1138     m_editActions.push_back(a);
1139 
1140     a = ac->addAction(QStringLiteral("select_line_down"));
1141     a->setText(i18n("Select to Next Line"));
1142     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_Down));
1143     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftDown);
1144     m_editActions.push_back(a);
1145 
1146     a = ac->addAction(QStringLiteral("scroll_line_down"));
1147     a->setText(i18n("Scroll Line Down"));
1148     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_Down));
1149     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::scrollDown);
1150     m_editActions.push_back(a);
1151 
1152     a = ac->addAction(QStringLiteral("scroll_page_up"));
1153     a->setText(i18n("Scroll Page Up"));
1154     ac->setDefaultShortcuts(a, KStandardShortcut::prior());
1155     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::pageUp);
1156     m_editActions.push_back(a);
1157 
1158     a = ac->addAction(QStringLiteral("select_page_up"));
1159     a->setText(i18n("Select Page Up"));
1160     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_PageUp));
1161     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftPageUp);
1162     m_editActions.push_back(a);
1163 
1164     a = ac->addAction(QStringLiteral("move_top_of_view"));
1165     a->setText(i18n("Move to Top of View"));
1166     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::Key_Home));
1167     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::topOfView);
1168     m_editActions.push_back(a);
1169 
1170     a = ac->addAction(QStringLiteral("select_top_of_view"));
1171     a->setText(i18n("Select to Top of View"));
1172     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_Home));
1173     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftTopOfView);
1174     m_editActions.push_back(a);
1175 
1176     a = ac->addAction(QStringLiteral("scroll_page_down"));
1177     a->setText(i18n("Scroll Page Down"));
1178     ac->setDefaultShortcuts(a, KStandardShortcut::next());
1179     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::pageDown);
1180     m_editActions.push_back(a);
1181 
1182     a = ac->addAction(QStringLiteral("select_page_down"));
1183     a->setText(i18n("Select Page Down"));
1184     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::Key_PageDown));
1185     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftPageDown);
1186     m_editActions.push_back(a);
1187 
1188     a = ac->addAction(QStringLiteral("move_bottom_of_view"));
1189     a->setText(i18n("Move to Bottom of View"));
1190     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::Key_End));
1191     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::bottomOfView);
1192     m_editActions.push_back(a);
1193 
1194     a = ac->addAction(QStringLiteral("select_bottom_of_view"));
1195     a->setText(i18n("Select to Bottom of View"));
1196     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_End));
1197     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftBottomOfView);
1198     m_editActions.push_back(a);
1199 
1200     a = ac->addAction(QStringLiteral("to_matching_bracket"));
1201     a->setText(i18n("Go to Matching Bracket"));
1202     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_6));
1203     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toMatchingBracket);
1204     // m_editActions << a;
1205 
1206     a = ac->addAction(QStringLiteral("select_matching_bracket"));
1207     a->setText(i18n("Select to Matching Bracket"));
1208     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_6));
1209     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftToMatchingBracket);
1210     // m_editActions << a;
1211 
1212     // anders: shortcuts doing any changes should not be created in read-only mode
1213     if (!doc()->readOnly()) {
1214         a = ac->addAction(QStringLiteral("transpose_char"));
1215         a->setText(i18n("Transpose Characters"));
1216         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::transpose);
1217         m_editActions.push_back(a);
1218 
1219         a = ac->addAction(QStringLiteral("transpose_word"));
1220         a->setText(i18n("Transpose Words"));
1221         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::transposeWord);
1222         m_editActions.push_back(a);
1223 
1224         a = ac->addAction(QStringLiteral("delete_line"));
1225         a->setText(i18n("Delete Line"));
1226         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_K));
1227         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::killLine);
1228         m_editActions.push_back(a);
1229 
1230         a = ac->addAction(QStringLiteral("delete_word_left"));
1231         a->setText(i18n("Delete Word Left"));
1232         ac->setDefaultShortcuts(a, KStandardShortcut::deleteWordBack());
1233         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::deleteWordLeft);
1234         m_editActions.push_back(a);
1235 
1236         a = ac->addAction(QStringLiteral("delete_word_right"));
1237         a->setText(i18n("Delete Word Right"));
1238         ac->setDefaultShortcuts(a, KStandardShortcut::deleteWordForward());
1239         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::deleteWordRight);
1240         m_editActions.push_back(a);
1241 
1242         a = ac->addAction(QStringLiteral("delete_next_character"));
1243         a->setText(i18n("Delete Next Character"));
1244         ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Delete));
1245         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::keyDelete);
1246         m_editActions.push_back(a);
1247 
1248         a = ac->addAction(QStringLiteral("backspace"));
1249         a->setText(i18n("Backspace"));
1250         QList<QKeySequence> scuts;
1251         scuts << QKeySequence(Qt::Key_Backspace) << QKeySequence(Qt::SHIFT | Qt::Key_Backspace);
1252         ac->setDefaultShortcuts(a, scuts);
1253         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::backspace);
1254         m_editActions.push_back(a);
1255 
1256         a = ac->addAction(QStringLiteral("insert_tabulator"));
1257         a->setText(i18n("Insert Tab Character"));
1258         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::insertTab);
1259         m_editActions.push_back(a);
1260 
1261         a = ac->addAction(QStringLiteral("smart_newline"));
1262         a->setText(i18n("Insert Smart Newline"));
1263         a->setWhatsThis(i18n("Insert newline including leading characters of the current line which are not letters or numbers."));
1264         scuts.clear();
1265         scuts << QKeySequence(Qt::SHIFT | Qt::Key_Return) << QKeySequence(Qt::SHIFT | Qt::Key_Enter);
1266         ac->setDefaultShortcuts(a, scuts);
1267         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::smartNewline);
1268         m_editActions.push_back(a);
1269 
1270         a = ac->addAction(QStringLiteral("no_indent_newline"));
1271         a->setText(i18n("Insert a Non-Indented Newline"));
1272         a->setWhatsThis(i18n("Insert a new line without indentation, regardless of indentation settings."));
1273         scuts.clear();
1274         scuts << QKeySequence(Qt::CTRL | Qt::Key_Return) << QKeySequence(Qt::CTRL | Qt::Key_Enter);
1275         ac->setDefaultShortcuts(a, scuts);
1276         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::noIndentNewline);
1277         m_editActions.push_back(a);
1278 
1279         a = ac->addAction(QStringLiteral("newline_above"));
1280         a->setText(i18n("Insert a Newline Above Current Line"));
1281         a->setWhatsThis(i18n("Insert a new line above current line without modifying the current line."));
1282         scuts.clear();
1283         scuts << QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Return) << QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Enter);
1284         ac->setDefaultShortcuts(a, scuts);
1285         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::newLineAbove);
1286         m_editActions.push_back(a);
1287 
1288         a = ac->addAction(QStringLiteral("newline_below"));
1289         a->setText(i18n("Insert a Newline Below Current Line"));
1290         a->setWhatsThis(i18n("Insert a new line below current line without modifying the current line."));
1291         scuts.clear();
1292         scuts << QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Return) << QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Enter);
1293         ac->setDefaultShortcuts(a, scuts);
1294         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::newLineBelow);
1295         m_editActions.push_back(a);
1296 
1297         a = ac->addAction(QStringLiteral("tools_indent"));
1298         a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-more")));
1299         a->setText(i18n("&Indent"));
1300         a->setWhatsThis(
1301             i18n("Use this to indent a selected block of text.<br /><br />"
1302                  "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
1303         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_I));
1304         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::indent);
1305 
1306         a = ac->addAction(QStringLiteral("tools_unindent"));
1307         a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-less")));
1308         a->setText(i18n("&Unindent"));
1309         a->setWhatsThis(i18n("Use this to unindent a selected block of text."));
1310         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_I));
1311         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::unIndent);
1312     }
1313 
1314     if (hasFocus()) {
1315         slotGotFocus();
1316     } else {
1317         slotLostFocus();
1318     }
1319 }
1320 
1321 void KTextEditor::ViewPrivate::setupCodeFolding()
1322 {
1323     KActionCollection *ac = this->actionCollection();
1324     QAction *a;
1325 
1326     a = ac->addAction(QStringLiteral("folding_toplevel"));
1327     a->setText(i18n("Fold Toplevel Nodes"));
1328     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotFoldToplevelNodes);
1329 
1330     a = ac->addAction(QStringLiteral("folding_expandtoplevel"));
1331     a->setText(i18n("Unfold Toplevel Nodes"));
1332     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotExpandToplevelNodes);
1333 
1334     a = ac->addAction(QStringLiteral("folding_toggle_current"));
1335     a->setText(i18n("Toggle Current Node"));
1336     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotToggleFolding);
1337 
1338     a = ac->addAction(QStringLiteral("folding_toggle_in_current"));
1339     a->setText(i18n("Toggle Contained Nodes"));
1340     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotToggleFoldingsInRange);
1341 }
1342 
1343 void KTextEditor::ViewPrivate::setupSpeechActions()
1344 {
1345     KActionCollection *ac = actionCollection();
1346 
1347     QAction *a = ac->addAction(QStringLiteral("tools_speech_say"));
1348     a->setText(i18n("Say current selection or document"));
1349     connect(a, &QAction::triggered, this, [this]() {
1350         if (selection()) {
1351             KTextEditor::EditorPrivate::self()->speechEngine(this)->say(selectionText());
1352         } else {
1353             KTextEditor::EditorPrivate::self()->speechEngine(this)->say(document()->text());
1354         }
1355     });
1356 
1357     a = ac->addAction(QStringLiteral("tools_speech_stop"));
1358     a->setText(i18n("Stop current output"));
1359     connect(a, &QAction::triggered, this, [this]() {
1360         KTextEditor::EditorPrivate::self()->speechEngine(this)->stop();
1361     });
1362 
1363     a = ac->addAction(QStringLiteral("tools_speech_pause"));
1364     a->setText(i18n("Pause current output"));
1365     connect(a, &QAction::triggered, this, [this]() {
1366         KTextEditor::EditorPrivate::self()->speechEngine(this)->pause();
1367     });
1368 
1369     a = ac->addAction(QStringLiteral("tools_speech_resume"));
1370     a->setText(i18n("Resume current output"));
1371     connect(a, &QAction::triggered, this, [this]() {
1372         KTextEditor::EditorPrivate::self()->speechEngine(this)->resume();
1373     });
1374 }
1375 
1376 void KTextEditor::ViewPrivate::slotFoldToplevelNodes()
1377 {
1378     for (int line = 0; line < doc()->lines(); ++line) {
1379         if (textFolding().isLineVisible(line)) {
1380             foldLine(line);
1381         }
1382     }
1383 }
1384 
1385 void KTextEditor::ViewPrivate::slotExpandToplevelNodes()
1386 {
1387     const auto topLevelRanges(textFolding().foldingRangesForParentRange());
1388     for (const auto &range : topLevelRanges) {
1389         textFolding().unfoldRange(range.first);
1390     }
1391 }
1392 
1393 void KTextEditor::ViewPrivate::slotToggleFolding()
1394 {
1395     int line = cursorPosition().line();
1396     bool actionDone = false;
1397     while (!actionDone && (line > -1)) {
1398         actionDone = unfoldLine(line);
1399         if (!actionDone) {
1400             actionDone = foldLine(line--).isValid();
1401         }
1402     }
1403 }
1404 
1405 void KTextEditor::ViewPrivate::slotToggleFoldingsInRange()
1406 {
1407     int line = cursorPosition().line();
1408     while (!toggleFoldingsInRange(line) && (line > -1)) {
1409         --line;
1410     }
1411 }
1412 
1413 KTextEditor::Range KTextEditor::ViewPrivate::foldLine(int line)
1414 {
1415     KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(line);
1416     if (!foldingRange.isValid()) {
1417         return foldingRange;
1418     }
1419 
1420     // Ensure not to fold the end marker to avoid a deceptive look, but only on token based folding
1421     // ensure we don't compute an invalid line by moving outside of the foldingRange range by checking onSingleLine(), see bug 417890
1422     if (!m_doc->buffer().isFoldingStartingOnLine(line).second && !foldingRange.onSingleLine()) {
1423         const int adjustedLine = foldingRange.end().line() - 1;
1424         foldingRange.setEnd(KTextEditor::Cursor(adjustedLine, doc()->buffer().plainLine(adjustedLine).length()));
1425     }
1426 
1427     // Don't try to fold a single line, which can happens due to adjustment above
1428     // FIXME Avoid to offer such a folding marker
1429     if (!foldingRange.onSingleLine()) {
1430         textFolding().newFoldingRange(foldingRange, Kate::TextFolding::Folded);
1431     }
1432 
1433     return foldingRange;
1434 }
1435 
1436 bool KTextEditor::ViewPrivate::unfoldLine(int line)
1437 {
1438     bool actionDone = false;
1439     const KTextEditor::Cursor currentCursor = cursorPosition();
1440 
1441     // ask the folding info for this line, if any folds are around!
1442     // auto = QList<QPair<qint64, Kate::TextFolding::FoldingRangeFlags>>
1443     auto startingRanges = textFolding().foldingRangesStartingOnLine(line);
1444     for (int i = 0; i < startingRanges.size() && !actionDone; ++i) {
1445         // Avoid jumping view in case of a big unfold and ensure nice highlight of folding marker
1446         setCursorPosition(textFolding().foldingRange(startingRanges[i].first).start());
1447 
1448         actionDone |= textFolding().unfoldRange(startingRanges[i].first);
1449     }
1450 
1451     if (!actionDone) {
1452         // Nothing unfolded? Restore old cursor position!
1453         setCursorPosition(currentCursor);
1454     }
1455 
1456     return actionDone;
1457 }
1458 
1459 bool KTextEditor::ViewPrivate::toggleFoldingOfLine(int line)
1460 {
1461     bool actionDone = unfoldLine(line);
1462     if (!actionDone) {
1463         actionDone = foldLine(line).isValid();
1464     }
1465 
1466     return actionDone;
1467 }
1468 
1469 bool KTextEditor::ViewPrivate::toggleFoldingsInRange(int line)
1470 {
1471     KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(line);
1472     if (!foldingRange.isValid()) {
1473         // Either line is not valid or there is no start range
1474         return false;
1475     }
1476 
1477     bool actionDone = false; // Track success
1478     const KTextEditor::Cursor currentCursor = cursorPosition();
1479 
1480     // Don't be too eager but obliging! Only toggle containing ranges which are
1481     // visible -> Be done when the range is folded
1482     actionDone |= unfoldLine(line);
1483 
1484     if (!actionDone) {
1485         // Unfold all in range, but not the range itself
1486         for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1487             actionDone |= unfoldLine(ln);
1488         }
1489 
1490         if (actionDone) {
1491             // In most cases we want now a not moved cursor
1492             setCursorPosition(currentCursor);
1493         }
1494     }
1495 
1496     if (!actionDone) {
1497         // Fold all in range, but not the range itself
1498         for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1499             KTextEditor::Range fr = foldLine(ln);
1500             if (fr.isValid()) {
1501                 // qMax to avoid infinite loop in case of range without content
1502                 ln = qMax(ln, fr.end().line() - 1);
1503                 actionDone = true;
1504             }
1505         }
1506     }
1507 
1508     if (!actionDone) {
1509         // At this point was an unfolded range clicked which contains no "childs"
1510         // We assume the user want to fold it by the wrong button, be obliging!
1511         actionDone |= foldLine(line).isValid();
1512     }
1513 
1514     // At this point we should be always true
1515     return actionDone;
1516 }
1517 
1518 KTextEditor::View::ViewMode KTextEditor::ViewPrivate::viewMode() const
1519 {
1520     return currentInputMode()->viewMode();
1521 }
1522 
1523 QString KTextEditor::ViewPrivate::viewModeHuman() const
1524 {
1525     QString currentMode = currentInputMode()->viewModeHuman();
1526 
1527     // append read-only if needed
1528     if (!doc()->isReadWrite()) {
1529         currentMode = i18n("(R/O) %1", currentMode);
1530     }
1531 
1532     // return full mode
1533     return currentMode;
1534 }
1535 
1536 KTextEditor::View::InputMode KTextEditor::ViewPrivate::viewInputMode() const
1537 {
1538     return currentInputMode()->viewInputMode();
1539 }
1540 
1541 QString KTextEditor::ViewPrivate::viewInputModeHuman() const
1542 {
1543     return currentInputMode()->viewInputModeHuman();
1544 }
1545 
1546 void KTextEditor::ViewPrivate::setInputMode(KTextEditor::View::InputMode mode, const bool rememberInConfig)
1547 {
1548     if (currentInputMode()->viewInputMode() == mode) {
1549         return;
1550     }
1551 
1552     // No multi cursors for vi
1553     if (mode == KTextEditor::View::InputMode::ViInputMode) {
1554         clearSecondaryCursors();
1555     }
1556 
1557     m_viewInternal->m_currentInputMode->deactivate();
1558     m_viewInternal->m_currentInputMode = m_viewInternal->m_inputModes[mode].get();
1559     m_viewInternal->m_currentInputMode->activate();
1560 
1561     // remember in local config if requested, we skip this for the calls in updateConfig
1562     if (rememberInConfig) {
1563         config()->setValue(KateViewConfig::InputMode, mode);
1564     }
1565 
1566     /* small duplication, but need to do this if not toggled by action */
1567     const auto inputModeActions = m_inputModeActions->actions();
1568     for (QAction *action : inputModeActions) {
1569         if (static_cast<InputMode>(action->data().toInt()) == mode) {
1570             action->setChecked(true);
1571             break;
1572         }
1573     }
1574 
1575     /* inform the rest of the system about the change */
1576     Q_EMIT viewInputModeChanged(this, mode);
1577     Q_EMIT viewModeChanged(this, viewMode());
1578 }
1579 
1580 void KTextEditor::ViewPrivate::slotDocumentAboutToReload()
1581 {
1582     if (doc()->isAutoReload()) {
1583         const int lastVisibleLine = m_viewInternal->endLine();
1584         const int currentLine = cursorPosition().line();
1585         m_gotoBottomAfterReload = (lastVisibleLine == currentLine) && (currentLine == doc()->lastLine());
1586         if (!m_gotoBottomAfterReload) {
1587             // Ensure the view jumps not back when user scrolls around
1588             const int firstVisibleLine = 1 + lastVisibleLine - m_viewInternal->linesDisplayed();
1589             const int newLine = qBound(firstVisibleLine, currentLine, lastVisibleLine);
1590             setCursorPositionVisual(KTextEditor::Cursor(newLine, cursorPosition().column()));
1591         }
1592     } else {
1593         m_gotoBottomAfterReload = false;
1594     }
1595 }
1596 
1597 void KTextEditor::ViewPrivate::slotDocumentReloaded()
1598 {
1599     if (m_gotoBottomAfterReload) {
1600         bottom();
1601     }
1602 }
1603 
1604 void KTextEditor::ViewPrivate::slotGotFocus()
1605 {
1606     // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotGotFocus";
1607     currentInputMode()->gotFocus();
1608 
1609     //  update current view and scrollbars
1610     //  it is needed for styles that implement different frame and scrollbar
1611     // rendering when focused
1612     update();
1613     if (m_viewInternal->m_lineScroll->isVisible()) {
1614         m_viewInternal->m_lineScroll->update();
1615     }
1616 
1617     if (m_viewInternal->m_columnScroll->isVisible()) {
1618         m_viewInternal->m_columnScroll->update();
1619     }
1620 
1621     Q_EMIT focusIn(this);
1622 }
1623 
1624 void KTextEditor::ViewPrivate::slotLostFocus()
1625 {
1626     // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotLostFocus";
1627     currentInputMode()->lostFocus();
1628 
1629     //  update current view and scrollbars
1630     //  it is needed for styles that implement different frame and scrollbar
1631     // rendering when focused
1632     update();
1633     if (m_viewInternal->m_lineScroll->isVisible()) {
1634         m_viewInternal->m_lineScroll->update();
1635     }
1636 
1637     if (m_viewInternal->m_columnScroll->isVisible()) {
1638         m_viewInternal->m_columnScroll->update();
1639     }
1640 
1641     if (doc()->config()->autoSave() && doc()->config()->autoSaveOnFocusOut() && doc()->isModified() && doc()->url().isLocalFile()) {
1642         doc()->documentSave();
1643     }
1644 
1645     Q_EMIT focusOut(this);
1646 }
1647 
1648 void KTextEditor::ViewPrivate::setDynWrapIndicators(int mode)
1649 {
1650     config()->setValue(KateViewConfig::DynWordWrapIndicators, mode);
1651 }
1652 
1653 bool KTextEditor::ViewPrivate::isOverwriteMode() const
1654 {
1655     return doc()->config()->ovr();
1656 }
1657 
1658 void KTextEditor::ViewPrivate::reloadFile()
1659 {
1660     // bookmarks and cursor positions are temporarily saved by the document
1661     doc()->documentReload();
1662 }
1663 
1664 void KTextEditor::ViewPrivate::slotReadWriteChanged()
1665 {
1666     if (m_toggleWriteLock) {
1667         m_toggleWriteLock->setChecked(!doc()->isReadWrite());
1668     }
1669 
1670     m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
1671     m_paste->setEnabled(doc()->isReadWrite());
1672     if (m_pasteSelection) {
1673         m_pasteSelection->setEnabled(doc()->isReadWrite());
1674     }
1675     m_swapWithClipboard->setEnabled(doc()->isReadWrite());
1676     m_setEndOfLine->setEnabled(doc()->isReadWrite());
1677 
1678     static const auto l = {QStringLiteral("edit_replace"),
1679                            QStringLiteral("tools_spelling"),
1680                            QStringLiteral("tools_indent"),
1681                            QStringLiteral("tools_unindent"),
1682                            QStringLiteral("tools_cleanIndent"),
1683                            QStringLiteral("tools_formatIndet"),
1684                            QStringLiteral("tools_alignOn"),
1685                            QStringLiteral("tools_comment"),
1686                            QStringLiteral("tools_uncomment"),
1687                            QStringLiteral("tools_toggle_comment"),
1688                            QStringLiteral("tools_uppercase"),
1689                            QStringLiteral("tools_lowercase"),
1690                            QStringLiteral("tools_capitalize"),
1691                            QStringLiteral("tools_join_lines"),
1692                            QStringLiteral("tools_apply_wordwrap"),
1693                            QStringLiteral("tools_spelling_from_cursor"),
1694                            QStringLiteral("tools_spelling_selection")};
1695 
1696     for (const auto &action : l) {
1697         QAction *a = actionCollection()->action(action);
1698         if (a) {
1699             a->setEnabled(doc()->isReadWrite());
1700         }
1701     }
1702     slotUpdateUndo();
1703 
1704     currentInputMode()->readWriteChanged(doc()->isReadWrite());
1705 
1706     // => view mode changed
1707     Q_EMIT viewModeChanged(this, viewMode());
1708     Q_EMIT viewInputModeChanged(this, viewInputMode());
1709 }
1710 
1711 void KTextEditor::ViewPrivate::toggleCamelCaseCursor()
1712 {
1713     const auto enabled = doc()->config()->camelCursor();
1714     doc()->config()->setCamelCursor(!enabled);
1715     KTextEditor::Message *m;
1716     if (enabled) {
1717         m = new KTextEditor::Message(i18n("Camel case movement disabled"));
1718     } else {
1719         m = new KTextEditor::Message(i18n("Camel case movement enabled"));
1720     }
1721     m->setPosition(KTextEditor::Message::TopInView);
1722     m->setAutoHide(1000);
1723     m->setAutoHideMode(KTextEditor::Message::Immediate);
1724     doc()->postMessage(m);
1725 }
1726 
1727 void KTextEditor::ViewPrivate::slotUpdateUndo()
1728 {
1729     if (doc()->readOnly()) {
1730         return;
1731     }
1732 
1733     m_editUndo->setEnabled(doc()->isReadWrite() && doc()->undoCount() > 0);
1734     m_editRedo->setEnabled(doc()->isReadWrite() && doc()->redoCount() > 0);
1735 }
1736 
1737 bool KTextEditor::ViewPrivate::setCursorPositionInternal(const KTextEditor::Cursor position, uint tabwidth, bool calledExternally)
1738 {
1739     if (position.line() < 0 || position.line() >= doc()->lines()) {
1740         return false;
1741     }
1742 
1743     Kate::TextLine l = doc()->kateTextLine(position.line());
1744     const QString line_str = l.text();
1745 
1746     int x = 0;
1747     int z = 0;
1748     for (; z < line_str.length() && z < position.column(); z++) {
1749         if (line_str[z] == QLatin1Char('\t')) {
1750             x += tabwidth - (x % tabwidth);
1751         } else {
1752             x++;
1753         }
1754     }
1755 
1756     if (blockSelection()) {
1757         if (z < position.column()) {
1758             x += position.column() - z;
1759         }
1760     }
1761 
1762     m_viewInternal->updateCursor(KTextEditor::Cursor(position.line(), x),
1763                                  false,
1764                                  calledExternally /* force center for external calls, see bug 408418 */,
1765                                  calledExternally);
1766 
1767     return true;
1768 }
1769 
1770 void KTextEditor::ViewPrivate::toggleInsert()
1771 {
1772     doc()->config()->setOvr(!doc()->config()->ovr());
1773     m_toggleInsert->setChecked(isOverwriteMode());
1774 
1775     // No multi cursors for overwrite mode
1776     if (isOverwriteMode()) {
1777         clearSecondaryCursors();
1778     }
1779 
1780     Q_EMIT viewModeChanged(this, viewMode());
1781     Q_EMIT viewInputModeChanged(this, viewInputMode());
1782 }
1783 
1784 void KTextEditor::ViewPrivate::slotSaveCanceled(const QString &error)
1785 {
1786     if (!error.isEmpty()) { // happens when canceling a job
1787         KMessageBox::error(this, error);
1788     }
1789 }
1790 
1791 void KTextEditor::ViewPrivate::gotoLine()
1792 {
1793     gotoBar()->updateData();
1794     bottomViewBar()->showBarWidget(gotoBar());
1795 }
1796 
1797 void KTextEditor::ViewPrivate::changeDictionary()
1798 {
1799     dictionaryBar()->updateData();
1800     bottomViewBar()->showBarWidget(dictionaryBar());
1801 }
1802 
1803 void KTextEditor::ViewPrivate::joinLines()
1804 {
1805     int first = selectionRange().start().line();
1806     int last = selectionRange().end().line();
1807     // int left = doc()->line( last ).length() - doc()->selEndCol();
1808     if (first == last) {
1809         first = cursorPosition().line();
1810         last = first + 1;
1811     }
1812     doc()->joinLines(first, last);
1813 }
1814 
1815 void KTextEditor::ViewPrivate::readSessionConfig(const KConfigGroup &config, const QSet<QString> &flags)
1816 {
1817     Q_UNUSED(flags)
1818 
1819     // cursor position
1820     KTextEditor::Cursor savedPosition(config.readEntry("CursorLine", 0), config.readEntry("CursorColumn", 0));
1821     setCursorPositionInternal(savedPosition);
1822 
1823     // scroll position
1824     const int scroll = config.readEntry("ScrollLine", -1);
1825     if (scroll >= 0 && scroll < doc()->lines() && savedPosition.line() < doc()->lines()) {
1826         setScrollPositionInternal(KTextEditor::Cursor(scroll, 0));
1827     }
1828 
1829     m_config->setDynWordWrap(config.readEntry("Dynamic Word Wrap", false));
1830 
1831     // restore text folding
1832     m_savedFoldingState = QJsonDocument::fromJson(config.readEntry("TextFolding", QByteArray()));
1833     applyFoldingState();
1834 
1835     m_forceRTL = config.readEntry("Force RTL Direction", false);
1836     m_forceRTLDirection->setChecked(m_forceRTL);
1837 
1838     for (const auto &mode : m_viewInternal->m_inputModes) {
1839         mode->readSessionConfig(config);
1840     }
1841 }
1842 
1843 void KTextEditor::ViewPrivate::writeSessionConfig(KConfigGroup &config, const QSet<QString> &flags)
1844 {
1845     Q_UNUSED(flags)
1846 
1847     // cursor position
1848     config.writeEntry("CursorLine", cursorPosition().line());
1849     config.writeEntry("CursorColumn", cursorPosition().column());
1850 
1851     // scroll position
1852     config.writeEntry("ScrollLine", firstDisplayedLineInternal(LineType::RealLine));
1853 
1854     config.writeEntry("Dynamic Word Wrap", m_config->dynWordWrap());
1855 
1856     // save text folding state
1857     saveFoldingState();
1858     config.writeEntry("TextFolding", m_savedFoldingState.toJson(QJsonDocument::Compact));
1859     m_savedFoldingState = QJsonDocument();
1860 
1861     config.writeEntry("Force RTL Direction", m_forceRTL);
1862 
1863     for (const auto &mode : m_viewInternal->m_inputModes) {
1864         mode->writeSessionConfig(config);
1865     }
1866 }
1867 
1868 int KTextEditor::ViewPrivate::getEol() const
1869 {
1870     return doc()->config()->eol();
1871 }
1872 
1873 QMenu *KTextEditor::ViewPrivate::getEolMenu()
1874 {
1875     return m_setEndOfLine->menu();
1876 }
1877 
1878 void KTextEditor::ViewPrivate::setEol(int eol)
1879 {
1880     if (!doc()->isReadWrite()) {
1881         return;
1882     }
1883 
1884     if (m_updatingDocumentConfig) {
1885         return;
1886     }
1887 
1888     if (eol != doc()->config()->eol()) {
1889         doc()->setModified(true); // mark modified (bug #143120)
1890         doc()->config()->setEol(eol);
1891     }
1892 }
1893 
1894 void KTextEditor::ViewPrivate::setAddBom(bool enabled)
1895 {
1896     if (!doc()->isReadWrite()) {
1897         return;
1898     }
1899 
1900     if (m_updatingDocumentConfig) {
1901         return;
1902     }
1903 
1904     doc()->config()->setBom(enabled);
1905     doc()->bomSetByUser();
1906 }
1907 
1908 void KTextEditor::ViewPrivate::setIconBorder(bool enable)
1909 {
1910     config()->setValue(KateViewConfig::ShowIconBar, enable);
1911 }
1912 
1913 void KTextEditor::ViewPrivate::toggleIconBorder()
1914 {
1915     config()->setValue(KateViewConfig::ShowIconBar, !config()->iconBar());
1916 }
1917 
1918 void KTextEditor::ViewPrivate::setLineNumbersOn(bool enable)
1919 {
1920     config()->setValue(KateViewConfig::ShowLineNumbers, enable);
1921 }
1922 
1923 void KTextEditor::ViewPrivate::toggleLineNumbersOn()
1924 {
1925     config()->setValue(KateViewConfig::ShowLineNumbers, !config()->lineNumbers());
1926 }
1927 
1928 void KTextEditor::ViewPrivate::setScrollBarMarks(bool enable)
1929 {
1930     config()->setValue(KateViewConfig::ShowScrollBarMarks, enable);
1931 }
1932 
1933 void KTextEditor::ViewPrivate::toggleScrollBarMarks()
1934 {
1935     config()->setValue(KateViewConfig::ShowScrollBarMarks, !config()->scrollBarMarks());
1936 }
1937 
1938 void KTextEditor::ViewPrivate::setScrollBarMiniMap(bool enable)
1939 {
1940     config()->setValue(KateViewConfig::ShowScrollBarMiniMap, enable);
1941 }
1942 
1943 void KTextEditor::ViewPrivate::toggleScrollBarMiniMap()
1944 {
1945     config()->setValue(KateViewConfig::ShowScrollBarMiniMap, !config()->scrollBarMiniMap());
1946 }
1947 
1948 void KTextEditor::ViewPrivate::setScrollBarMiniMapAll(bool enable)
1949 {
1950     config()->setValue(KateViewConfig::ShowScrollBarMiniMapAll, enable);
1951 }
1952 
1953 void KTextEditor::ViewPrivate::toggleScrollBarMiniMapAll()
1954 {
1955     config()->setValue(KateViewConfig::ShowScrollBarMiniMapAll, !config()->scrollBarMiniMapAll());
1956 }
1957 
1958 void KTextEditor::ViewPrivate::setScrollBarMiniMapWidth(int width)
1959 {
1960     config()->setValue(KateViewConfig::ScrollBarMiniMapWidth, width);
1961 }
1962 
1963 void KTextEditor::ViewPrivate::toggleDynWordWrap()
1964 {
1965     config()->setDynWordWrap(!config()->dynWordWrap());
1966 }
1967 
1968 void KTextEditor::ViewPrivate::toggleWWMarker()
1969 {
1970     m_renderer->config()->setWordWrapMarker(!m_renderer->config()->wordWrapMarker());
1971 }
1972 
1973 void KTextEditor::ViewPrivate::toggleNPSpaces()
1974 {
1975     m_renderer->setShowNonPrintableSpaces(!m_renderer->showNonPrintableSpaces());
1976     m_viewInternal->update(); // force redraw
1977 }
1978 
1979 void KTextEditor::ViewPrivate::toggleWordCount(bool on)
1980 {
1981     config()->setShowWordCount(on);
1982 }
1983 
1984 void KTextEditor::ViewPrivate::setFoldingMarkersOn(bool enable)
1985 {
1986     config()->setValue(KateViewConfig::ShowFoldingBar, enable);
1987 }
1988 
1989 void KTextEditor::ViewPrivate::toggleFoldingMarkers()
1990 {
1991     config()->setValue(KateViewConfig::ShowFoldingBar, !config()->foldingBar());
1992 }
1993 
1994 bool KTextEditor::ViewPrivate::iconBorder()
1995 {
1996     return m_viewInternal->m_leftBorder->iconBorderOn();
1997 }
1998 
1999 bool KTextEditor::ViewPrivate::lineNumbersOn()
2000 {
2001     return m_viewInternal->m_leftBorder->lineNumbersOn();
2002 }
2003 
2004 bool KTextEditor::ViewPrivate::scrollBarMarks()
2005 {
2006     return m_viewInternal->m_lineScroll->showMarks();
2007 }
2008 
2009 bool KTextEditor::ViewPrivate::scrollBarMiniMap()
2010 {
2011     return m_viewInternal->m_lineScroll->showMiniMap();
2012 }
2013 
2014 int KTextEditor::ViewPrivate::dynWrapIndicators()
2015 {
2016     return m_viewInternal->m_leftBorder->dynWrapIndicators();
2017 }
2018 
2019 bool KTextEditor::ViewPrivate::foldingMarkersOn()
2020 {
2021     return m_viewInternal->m_leftBorder->foldingMarkersOn();
2022 }
2023 
2024 bool KTextEditor::ViewPrivate::forceRTLDirection() const
2025 {
2026     return m_forceRTL;
2027 }
2028 
2029 void KTextEditor::ViewPrivate::toggleWriteLock()
2030 {
2031     doc()->setReadWrite(!doc()->isReadWrite());
2032 }
2033 
2034 void KTextEditor::ViewPrivate::registerTextHintProvider(KTextEditor::TextHintProvider *provider)
2035 {
2036     m_viewInternal->registerTextHintProvider(provider);
2037 }
2038 
2039 void KTextEditor::ViewPrivate::unregisterTextHintProvider(KTextEditor::TextHintProvider *provider)
2040 {
2041     m_viewInternal->unregisterTextHintProvider(provider);
2042 }
2043 
2044 void KTextEditor::ViewPrivate::setTextHintDelay(int delay)
2045 {
2046     m_viewInternal->setTextHintDelay(delay);
2047 }
2048 
2049 int KTextEditor::ViewPrivate::textHintDelay() const
2050 {
2051     return m_viewInternal->textHintDelay();
2052 }
2053 
2054 // NOLINTNEXTLINE(readability-make-member-function-const)
2055 void KTextEditor::ViewPrivate::find()
2056 {
2057     currentInputMode()->find();
2058 }
2059 
2060 // NOLINTNEXTLINE(readability-make-member-function-const)
2061 void KTextEditor::ViewPrivate::findSelectedForwards()
2062 {
2063     currentInputMode()->findSelectedForwards();
2064 }
2065 
2066 // NOLINTNEXTLINE(readability-make-member-function-const)
2067 void KTextEditor::ViewPrivate::findSelectedBackwards()
2068 {
2069     currentInputMode()->findSelectedBackwards();
2070 }
2071 
2072 void KTextEditor::ViewPrivate::skipCurrentOccurunceSelection()
2073 {
2074     if (isMulticursorNotAllowed()) {
2075         return;
2076     }
2077     m_skipCurrentSelection = true;
2078 }
2079 
2080 void KTextEditor::ViewPrivate::findNextOccurunceAndSelect()
2081 {
2082     if (isMulticursorNotAllowed()) {
2083         return;
2084     }
2085 
2086     const auto text = selection() ? doc()->text(selectionRange()) : QString();
2087     if (text.isEmpty()) {
2088         const auto selection = doc()->wordRangeAt(cursorPosition());
2089         // We don't want matching word highlights
2090         setSelection(selection);
2091         setCursorPosition(selection.end());
2092         clearHighlights();
2093 
2094         for (auto &c : m_secondaryCursors) {
2095             const auto range = doc()->wordRangeAt(c.cursor());
2096             if (!c.range && !c.anchor.isValid()) {
2097                 c.anchor = range.start();
2098                 c.range.reset(newSecondarySelectionRange(range));
2099                 c.pos->setPosition(range.end());
2100             }
2101             tagLines(range);
2102         }
2103         return;
2104     } else if (!m_rangesForHighlights.empty()) {
2105         clearHighlights();
2106     }
2107 
2108     // Use selection range end as starting point
2109     const auto lastSelectionRange = selectionRange();
2110 
2111     KTextEditor::Range searchRange(lastSelectionRange.end(), doc()->documentRange().end());
2112     QList<KTextEditor::Range> matches = doc()->searchText(searchRange, text, KTextEditor::Default);
2113     if (!matches.isEmpty() && !matches.constFirst().isValid()) {
2114         searchRange.setRange(doc()->documentRange().start(), lastSelectionRange.end());
2115         matches = doc()->searchText(searchRange, text, KTextEditor::Default);
2116     }
2117 
2118     // No match found or only one possible match
2119     if (matches.empty() || !matches.constFirst().isValid() || matches.constFirst() == selectionRange()) {
2120         return;
2121     }
2122 
2123     auto it = std::find_if(m_secondaryCursors.begin(), m_secondaryCursors.end(), [&](const SecondaryCursor &c) {
2124         return c.range && c.range->toRange() == matches.constFirst();
2125     });
2126 
2127     if (it != m_secondaryCursors.end()) {
2128         m_secondaryCursors.erase(it);
2129     }
2130 
2131     // Move our primary to cursor to this match and select it
2132     // Ensure we don't create occurence highlights
2133     setSelection(matches.constFirst());
2134     setCursorPosition(matches.constFirst().end());
2135     clearHighlights();
2136 
2137     // If we are skipping this selection, then we don't have to do anything
2138     if (!m_skipCurrentSelection) {
2139         PlainSecondaryCursor c;
2140         c.pos = lastSelectionRange.end();
2141         c.range = lastSelectionRange;
2142         // make our previous primary selection a secondary
2143         addSecondaryCursorsWithSelection({c});
2144     }
2145     // reset value
2146     m_skipCurrentSelection = false;
2147 }
2148 
2149 void KTextEditor::ViewPrivate::findAllOccuruncesAndSelect()
2150 {
2151     if (isMulticursorNotAllowed()) {
2152         return;
2153     }
2154 
2155     QString text = selection() ? doc()->text(selectionRange()) : QString();
2156     if (text.isEmpty()) {
2157         const auto selection = doc()->wordRangeAt(cursorPosition());
2158         setSelection(selection);
2159         setCursorPosition(selection.end());
2160         clearHighlights();
2161         text = doc()->text(selection);
2162 
2163         for (auto &c : m_secondaryCursors) {
2164             const auto range = doc()->wordRangeAt(c.cursor());
2165             if (!c.range && !c.anchor.isValid()) {
2166                 c.anchor = range.start();
2167                 c.range.reset(newSecondarySelectionRange(range));
2168                 c.pos->setPosition(range.end());
2169             }
2170             tagLines(range);
2171         }
2172     }
2173 
2174     KTextEditor::Range searchRange(doc()->documentRange());
2175     QList<KTextEditor::Range> matches;
2176     QList<PlainSecondaryCursor> resultRanges;
2177     do {
2178         matches = doc()->searchText(searchRange, text, KTextEditor::Default);
2179 
2180         if (matches.constFirst().isValid()) {
2181             // Dont add if matches primary selection
2182             if (matches.constFirst() != selectionRange()) {
2183                 PlainSecondaryCursor c;
2184                 c.pos = matches.constFirst().end();
2185                 c.range = matches.constFirst();
2186                 resultRanges.push_back(c);
2187             }
2188             searchRange.setStart(matches.constFirst().end());
2189         }
2190     } while (matches.first().isValid());
2191 
2192     // ensure to clear occurence highlights
2193     if (!resultRanges.empty()) {
2194         clearHighlights();
2195     }
2196 
2197     clearSecondaryCursors();
2198     addSecondaryCursorsWithSelection(resultRanges);
2199 }
2200 
2201 // NOLINTNEXTLINE(readability-make-member-function-const)
2202 void KTextEditor::ViewPrivate::replace()
2203 {
2204     currentInputMode()->findReplace();
2205 }
2206 
2207 // NOLINTNEXTLINE(readability-make-member-function-const)
2208 void KTextEditor::ViewPrivate::findNext()
2209 {
2210     currentInputMode()->findNext();
2211 }
2212 
2213 // NOLINTNEXTLINE(readability-make-member-function-const)
2214 void KTextEditor::ViewPrivate::findPrevious()
2215 {
2216     currentInputMode()->findPrevious();
2217 }
2218 
2219 void KTextEditor::ViewPrivate::showSearchWrappedHint(bool isReverseSearch)
2220 {
2221     // show message widget when wrapping
2222     const QIcon icon = isReverseSearch ? QIcon::fromTheme(QStringLiteral("go-up-search")) : QIcon::fromTheme(QStringLiteral("go-down-search"));
2223 
2224     if (!m_wrappedMessage || m_isLastSearchReversed != isReverseSearch) {
2225         m_isLastSearchReversed = isReverseSearch;
2226         m_wrappedMessage = new KTextEditor::Message(i18n("Search wrapped"), KTextEditor::Message::Information);
2227         m_wrappedMessage->setIcon(icon);
2228         m_wrappedMessage->setPosition(KTextEditor::Message::BottomInView);
2229         m_wrappedMessage->setAutoHide(2000);
2230         m_wrappedMessage->setAutoHideMode(KTextEditor::Message::Immediate);
2231         m_wrappedMessage->setView(this);
2232         this->doc()->postMessage(m_wrappedMessage);
2233     }
2234 }
2235 
2236 void KTextEditor::ViewPrivate::createMultiCursorsFromSelection()
2237 {
2238     if (!selection() || selectionRange().isEmpty()) {
2239         return;
2240     }
2241     // Is this really needed?
2242     // Lets just clear them now for simplicity
2243     clearSecondaryCursors();
2244 
2245     const auto range = selectionRange();
2246     QList<KTextEditor::Cursor> cursorsToAdd;
2247     const auto start = range.start().line() < 0 ? 0 : range.start().line();
2248     const auto end = range.end().line() > doc()->lines() ? doc()->lines() : range.end().line();
2249     const auto currentLine = cursorPosition().line();
2250     setCursorPosition({currentLine, doc()->lineLength(currentLine)});
2251     for (int line = start; line <= end; ++line) {
2252         if (line != currentLine) {
2253             cursorsToAdd.push_back({line, doc()->lineLength(line)});
2254         }
2255     }
2256     // clear selection
2257     setSelection({});
2258     setSecondaryCursors(cursorsToAdd);
2259 }
2260 
2261 void KTextEditor::ViewPrivate::removeCursorsFromEmptyLines()
2262 {
2263     if (!m_secondaryCursors.empty()) {
2264         std::vector<KTextEditor::Cursor> cursorsToRemove;
2265         for (const auto &c : m_secondaryCursors) {
2266             auto cursor = c.cursor();
2267             if (doc()->lineLength(cursor.line()) == 0) {
2268                 cursorsToRemove.push_back(cursor);
2269             }
2270         }
2271         removeSecondaryCursors(cursorsToRemove);
2272     }
2273 }
2274 
2275 void KTextEditor::ViewPrivate::slotSelectionChanged()
2276 {
2277     m_copy->setEnabled(selection() || m_config->smartCopyCut());
2278     m_deSelect->setEnabled(selection());
2279     m_copyHtmlAction->setEnabled(selection());
2280 
2281     // update highlighting of current selected word
2282     selectionChangedForHighlights();
2283 
2284     if (doc()->readOnly()) {
2285         return;
2286     }
2287 
2288     m_cut->setEnabled(selection() || m_config->smartCopyCut());
2289     m_screenshotSelection->setVisible(selection());
2290     m_screenshotSelection->setEnabled(selection());
2291 }
2292 
2293 // NOLINTNEXTLINE(readability-make-member-function-const)
2294 void KTextEditor::ViewPrivate::switchToCmdLine()
2295 {
2296     currentInputMode()->activateCommandLine();
2297 }
2298 
2299 KateRenderer *KTextEditor::ViewPrivate::renderer()
2300 {
2301     return m_renderer;
2302 }
2303 
2304 KateRendererConfig *KTextEditor::ViewPrivate::rendererConfig()
2305 {
2306     return m_renderer->config();
2307 }
2308 
2309 void KTextEditor::ViewPrivate::updateConfig()
2310 {
2311     if (m_startingUp) {
2312         return;
2313     }
2314 
2315     // dyn. word wrap & markers
2316     if (m_hasWrap != config()->dynWordWrap()) {
2317         m_hasWrap = config()->dynWordWrap();
2318 
2319         m_viewInternal->dynWrapChanged();
2320 
2321         m_setDynWrapIndicators->setEnabled(config()->dynWordWrap());
2322         m_toggleDynWrap->setChecked(config()->dynWordWrap());
2323     }
2324 
2325     m_viewInternal->m_leftBorder->setDynWrapIndicators(config()->dynWordWrapIndicators());
2326     m_setDynWrapIndicators->setCurrentItem(config()->dynWordWrapIndicators());
2327 
2328     // line numbers
2329     m_viewInternal->m_leftBorder->setLineNumbersOn(config()->lineNumbers());
2330     m_toggleLineNumbers->setChecked(config()->lineNumbers());
2331 
2332     // icon bar
2333     m_viewInternal->m_leftBorder->setIconBorderOn(config()->iconBar());
2334     m_toggleIconBar->setChecked(config()->iconBar());
2335 
2336     // scrollbar marks
2337     m_viewInternal->m_lineScroll->setShowMarks(config()->scrollBarMarks());
2338     m_toggleScrollBarMarks->setChecked(config()->scrollBarMarks());
2339 
2340     // scrollbar mini-map
2341     m_viewInternal->m_lineScroll->setShowMiniMap(config()->scrollBarMiniMap());
2342     m_toggleScrollBarMiniMap->setChecked(config()->scrollBarMiniMap());
2343 
2344     // scrollbar mini-map - (whole document)
2345     m_viewInternal->m_lineScroll->setMiniMapAll(config()->scrollBarMiniMapAll());
2346     // m_toggleScrollBarMiniMapAll->setChecked( config()->scrollBarMiniMapAll() );
2347 
2348     // scrollbar mini-map.width
2349     m_viewInternal->m_lineScroll->setMiniMapWidth(config()->scrollBarMiniMapWidth());
2350 
2351     // misc edit
2352     m_toggleBlockSelection->setChecked(blockSelection());
2353     m_toggleInsert->setChecked(isOverwriteMode());
2354 
2355     updateFoldingConfig();
2356 
2357     // bookmark
2358     m_bookmarks->setSorting((KateBookmarks::Sorting)config()->bookmarkSort());
2359 
2360     m_viewInternal->setAutoCenterLines(config()->autoCenterLines());
2361 
2362     for (const auto &input : m_viewInternal->m_inputModes) {
2363         input->updateConfig();
2364     }
2365 
2366     setInputMode(config()->inputMode(), false /* don't remember in config for these calls */);
2367 
2368     reflectOnTheFlySpellCheckStatus(doc()->isOnTheFlySpellCheckingEnabled());
2369 
2370     // register/unregister word completion...
2371     bool wc = config()->wordCompletion();
2372     if (wc != isCompletionModelRegistered(KTextEditor::EditorPrivate::self()->wordCompletionModel())) {
2373         if (wc) {
2374             registerCompletionModel(KTextEditor::EditorPrivate::self()->wordCompletionModel());
2375         } else {
2376             unregisterCompletionModel(KTextEditor::EditorPrivate::self()->wordCompletionModel());
2377         }
2378     }
2379 
2380     bool kc = config()->keywordCompletion();
2381     if (kc != isCompletionModelRegistered(KTextEditor::EditorPrivate::self()->keywordCompletionModel())) {
2382         if (kc) {
2383             registerCompletionModel(KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2384         } else {
2385             unregisterCompletionModel(KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2386         }
2387     }
2388 
2389     m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
2390     m_copy->setEnabled(selection() || m_config->smartCopyCut());
2391 
2392     m_accessibilityEnabled = m_config->value(KateViewConfig::EnableAccessibility).toBool();
2393 
2394     // if not disabled, update status bar
2395     if (m_statusBar) {
2396         m_statusBar->updateStatus();
2397     }
2398 
2399     // now redraw...
2400     m_viewInternal->cache()->clear();
2401     tagAll();
2402     updateView(true);
2403 
2404     Q_EMIT configChanged(this);
2405 }
2406 
2407 void KTextEditor::ViewPrivate::updateDocumentConfig()
2408 {
2409     if (m_startingUp) {
2410         return;
2411     }
2412 
2413     m_updatingDocumentConfig = true;
2414 
2415     m_setEndOfLine->setCurrentItem(doc()->config()->eol());
2416 
2417     m_addBom->setChecked(doc()->config()->bom());
2418 
2419     m_updatingDocumentConfig = false;
2420 
2421     // maybe block selection or wrap-cursor mode changed
2422     ensureCursorColumnValid();
2423 
2424     // first change this
2425     m_renderer->setTabWidth(doc()->config()->tabWidth());
2426     m_renderer->setIndentWidth(doc()->config()->indentationWidth());
2427 
2428     // now redraw...
2429     m_viewInternal->cache()->clear();
2430     tagAll();
2431     updateView(true);
2432 }
2433 
2434 void KTextEditor::ViewPrivate::updateRendererConfig()
2435 {
2436     if (m_startingUp) {
2437         return;
2438     }
2439 
2440     m_toggleWWMarker->setChecked(m_renderer->config()->wordWrapMarker());
2441 
2442     m_viewInternal->updateBracketMarkAttributes();
2443     m_viewInternal->updateBracketMarks();
2444 
2445     // now redraw...
2446     m_viewInternal->cache()->clear();
2447     tagAll();
2448     m_viewInternal->updateView(true);
2449 
2450     // update the left border right, for example linenumbers
2451     m_viewInternal->m_leftBorder->updateFont();
2452     m_viewInternal->m_leftBorder->repaint();
2453 
2454     m_viewInternal->m_lineScroll->queuePixmapUpdate();
2455 
2456     currentInputMode()->updateRendererConfig();
2457 
2458     // @@ showIndentLines is not cached anymore.
2459     //  m_renderer->setShowIndentLines (m_renderer->config()->showIndentationLines());
2460     Q_EMIT configChanged(this);
2461 }
2462 
2463 void KTextEditor::ViewPrivate::updateFoldingConfig()
2464 {
2465     // folding bar
2466     m_viewInternal->m_leftBorder->setFoldingMarkersOn(config()->foldingBar());
2467     m_toggleFoldingMarkers->setChecked(config()->foldingBar());
2468 
2469     if (hasCommentInFirstLine(m_doc)) {
2470         if (config()->foldFirstLine() && !m_autoFoldedFirstLine) {
2471             foldLine(0);
2472             m_autoFoldedFirstLine = true;
2473         } else if (!config()->foldFirstLine() && m_autoFoldedFirstLine) {
2474             unfoldLine(0);
2475             m_autoFoldedFirstLine = false;
2476         }
2477     } else {
2478         m_autoFoldedFirstLine = false;
2479     }
2480 
2481 #if 0
2482     // FIXME: FOLDING
2483     const QStringList l = {
2484           QStringLiteral("folding_toplevel")
2485         , QStringLiteral("folding_expandtoplevel")
2486         , QStringLiteral("folding_toggle_current")
2487         , QStringLiteral("folding_toggle_in_current")
2488     };
2489 
2490     QAction *a = 0;
2491     for (int z = 0; z < l.size(); z++)
2492         if ((a = actionCollection()->action(l[z].toAscii().constData()))) {
2493             a->setEnabled(doc()->highlight() && doc()->highlight()->allowsFolding());
2494         }
2495 #endif
2496 }
2497 
2498 void KTextEditor::ViewPrivate::ensureCursorColumnValid()
2499 {
2500     KTextEditor::Cursor c = m_viewInternal->cursorPosition();
2501 
2502     // make sure the cursor is valid:
2503     // - in block selection mode or if wrap cursor is off, the column is arbitrary
2504     // - otherwise: it's bounded by the line length
2505     if (!blockSelection() && wrapCursor() && (!c.isValid() || c.column() > doc()->lineLength(c.line()))) {
2506         c.setColumn(doc()->lineLength(cursorPosition().line()));
2507         setCursorPosition(c);
2508     }
2509 }
2510 
2511 // BEGIN EDIT STUFF
2512 void KTextEditor::ViewPrivate::editStart()
2513 {
2514     m_viewInternal->editStart();
2515 }
2516 
2517 void KTextEditor::ViewPrivate::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
2518 {
2519     m_viewInternal->editEnd(editTagLineStart, editTagLineEnd, tagFrom);
2520     textFolding().editEnd(editTagLineStart, editTagLineEnd, [this](int line) {
2521         return m_doc->buffer().isFoldingStartingOnLine(line).first;
2522     });
2523 }
2524 
2525 void KTextEditor::ViewPrivate::editSetCursor(const KTextEditor::Cursor cursor)
2526 {
2527     m_viewInternal->editSetCursor(cursor);
2528 }
2529 // END
2530 
2531 // BEGIN TAG & CLEAR
2532 bool KTextEditor::ViewPrivate::tagLine(const KTextEditor::Cursor virtualCursor)
2533 {
2534     return m_viewInternal->tagLine(virtualCursor);
2535 }
2536 
2537 bool KTextEditor::ViewPrivate::tagRange(KTextEditor::Range range, bool realLines)
2538 {
2539     return m_viewInternal->tagRange(range, realLines);
2540 }
2541 
2542 bool KTextEditor::ViewPrivate::tagLines(KTextEditor::LineRange lineRange, bool realLines)
2543 {
2544     return m_viewInternal->tagLines(lineRange.start(), lineRange.end(), realLines);
2545 }
2546 
2547 bool KTextEditor::ViewPrivate::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors)
2548 {
2549     return m_viewInternal->tagLines(start, end, realCursors);
2550 }
2551 
2552 void KTextEditor::ViewPrivate::tagAll()
2553 {
2554     m_viewInternal->tagAll();
2555 }
2556 
2557 void KTextEditor::ViewPrivate::clear()
2558 {
2559     m_viewInternal->clear();
2560 }
2561 
2562 void KTextEditor::ViewPrivate::repaintText(bool paintOnlyDirty)
2563 {
2564     if (paintOnlyDirty) {
2565         m_viewInternal->updateDirty();
2566     } else {
2567         m_viewInternal->update();
2568     }
2569 }
2570 
2571 void KTextEditor::ViewPrivate::updateView(bool changed)
2572 {
2573     // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::updateView";
2574     m_viewInternal->updateView(changed);
2575     m_viewInternal->m_leftBorder->update();
2576 }
2577 
2578 // END
2579 
2580 void KTextEditor::ViewPrivate::slotHlChanged()
2581 {
2582     KateHighlighting *hl = doc()->highlight();
2583     bool ok(!hl->getCommentStart(0).isEmpty() || !hl->getCommentSingleLineStart(0).isEmpty());
2584 
2585     if (actionCollection()->action(QStringLiteral("tools_comment"))) {
2586         actionCollection()->action(QStringLiteral("tools_comment"))->setEnabled(ok);
2587     }
2588 
2589     if (actionCollection()->action(QStringLiteral("tools_uncomment"))) {
2590         actionCollection()->action(QStringLiteral("tools_uncomment"))->setEnabled(ok);
2591     }
2592 
2593     if (actionCollection()->action(QStringLiteral("tools_toggle_comment"))) {
2594         actionCollection()->action(QStringLiteral("tools_toggle_comment"))->setEnabled(ok);
2595     }
2596 
2597     // show folding bar if "view defaults" says so, otherwise enable/disable only the menu entry
2598     updateFoldingConfig();
2599 }
2600 
2601 int KTextEditor::ViewPrivate::virtualCursorColumn() const
2602 {
2603     return doc()->toVirtualColumn(m_viewInternal->cursorPosition());
2604 }
2605 
2606 void KTextEditor::ViewPrivate::notifyMousePositionChanged(const KTextEditor::Cursor newPosition)
2607 {
2608     Q_EMIT mousePositionChanged(this, newPosition);
2609 }
2610 
2611 // BEGIN KTextEditor::SelectionInterface stuff
2612 
2613 bool KTextEditor::ViewPrivate::setSelection(KTextEditor::Range selection)
2614 {
2615     // anything to do?
2616     if (selection == m_selection) {
2617         return true;
2618     }
2619 
2620     // backup old range
2621     KTextEditor::Range oldSelection = m_selection;
2622 
2623     // set new range
2624     m_selection.setRange(selection.isEmpty() ? KTextEditor::Range::invalid() : selection);
2625 
2626     // trigger update of correct area
2627     tagSelection(oldSelection);
2628     repaintText(true);
2629 
2630     // emit holy signal
2631     Q_EMIT selectionChanged(this);
2632 
2633     // be done
2634     return true;
2635 }
2636 
2637 bool KTextEditor::ViewPrivate::clearSelection()
2638 {
2639     return clearSelection(true);
2640 }
2641 
2642 bool KTextEditor::ViewPrivate::clearSelection(bool redraw, bool finishedChangingSelection)
2643 {
2644     // no selection, nothing to do...
2645     if (!selection()) {
2646         return false;
2647     }
2648 
2649     // backup old range
2650     KTextEditor::Range oldSelection = m_selection;
2651 
2652     // invalidate current selection
2653     m_selection.setRange(KTextEditor::Range::invalid());
2654 
2655     // trigger update of correct area
2656     tagSelection(oldSelection);
2657     if (redraw) {
2658         repaintText(true);
2659     }
2660 
2661     // emit holy signal
2662     if (finishedChangingSelection) {
2663         Q_EMIT selectionChanged(this);
2664     }
2665 
2666     m_viewInternal->m_selChangedByUser = false;
2667     // be done
2668     return true;
2669 }
2670 
2671 bool KTextEditor::ViewPrivate::selection() const
2672 {
2673     if (!wrapCursor()) {
2674         return m_selection != KTextEditor::Range::invalid();
2675     } else {
2676         return m_selection.toRange().isValid();
2677     }
2678 }
2679 
2680 QString KTextEditor::ViewPrivate::selectionText() const
2681 {
2682     if (blockSelect) {
2683         return doc()->text(m_selection, blockSelect);
2684     }
2685 
2686     QVarLengthArray<KTextEditor::Range, 16> ranges;
2687     for (const auto &c : m_secondaryCursors) {
2688         if (c.range) {
2689             ranges.push_back(c.range->toRange());
2690         }
2691     }
2692     ranges.push_back(m_selection.toRange());
2693     std::sort(ranges.begin(), ranges.end());
2694 
2695     QString text;
2696     text.reserve(ranges.size() * m_selection.toRange().columnWidth());
2697     for (int i = 0; i < ranges.size() - 1; ++i) {
2698         text += doc()->text(ranges[i]) + QStringLiteral("\n");
2699     }
2700     text += doc()->text(ranges.last());
2701 
2702     return text;
2703 }
2704 
2705 bool KTextEditor::ViewPrivate::removeSelectedText()
2706 {
2707     if (!hasSelections()) {
2708         return false;
2709     }
2710 
2711     KTextEditor::Document::EditingTransaction t(doc());
2712 
2713     bool removed = false;
2714     // Handle multicursors selection removal
2715     if (!blockSelect) {
2716         completionWidget()->setIgnoreBufferSignals(true);
2717         for (auto &c : m_secondaryCursors) {
2718             if (c.range) {
2719                 removed = true;
2720                 doc()->removeText(c.range->toRange());
2721                 c.clearSelection();
2722             }
2723         }
2724         completionWidget()->setIgnoreBufferSignals(false);
2725     }
2726 
2727     // Optimization: clear selection before removing text
2728     KTextEditor::Range selection = m_selection;
2729     if (!selection.isValid()) {
2730         return removed;
2731     }
2732     doc()->removeText(selection, blockSelect);
2733     removed = true;
2734 
2735     // don't redraw the cleared selection - that's done in editEnd().
2736     if (blockSelect) {
2737         int selectionColumn = qMin(doc()->toVirtualColumn(selection.start()), doc()->toVirtualColumn(selection.end()));
2738         KTextEditor::Range newSelection = selection;
2739         newSelection.setStart(KTextEditor::Cursor(newSelection.start().line(), doc()->fromVirtualColumn(newSelection.start().line(), selectionColumn)));
2740         newSelection.setEnd(KTextEditor::Cursor(newSelection.end().line(), doc()->fromVirtualColumn(newSelection.end().line(), selectionColumn)));
2741         setSelection(newSelection);
2742         setCursorPositionInternal(newSelection.start());
2743     } else {
2744         clearSecondarySelections();
2745         clearSelection(false);
2746     }
2747 
2748     return removed;
2749 }
2750 
2751 bool KTextEditor::ViewPrivate::selectAll()
2752 {
2753     clearSecondaryCursors();
2754     setBlockSelection(false);
2755     // We use setSelection here to ensure we don't scroll anywhere
2756     // The cursor stays in place i.e., it doesn't move to end of selection
2757     // that is okay and expected.
2758     // The idea here is to maintain scroll position in case select all was
2759     // mistakenly triggered, and also to if you just want to copy text,
2760     // there is no need to scroll anywhere.
2761     setSelection(doc()->documentRange());
2762     m_viewInternal->moveCursorToSelectionEdge(/*scroll=*/false);
2763     m_viewInternal->updateMicroFocus();
2764     return true;
2765 }
2766 
2767 bool KTextEditor::ViewPrivate::cursorSelected(const KTextEditor::Cursor cursor)
2768 {
2769     KTextEditor::Cursor ret = cursor;
2770     if ((!blockSelect) && (ret.column() < 0)) {
2771         ret.setColumn(0);
2772     }
2773 
2774     if (blockSelect) {
2775         return cursor.line() >= m_selection.start().line() && ret.line() <= m_selection.end().line() && ret.column() >= m_selection.start().column()
2776             && ret.column() <= m_selection.end().column();
2777     } else {
2778         return m_selection.toRange().contains(cursor) || m_selection.end() == cursor;
2779     }
2780 }
2781 
2782 bool KTextEditor::ViewPrivate::lineSelected(int line)
2783 {
2784     return !blockSelect && m_selection.toRange().containsLine(line);
2785 }
2786 
2787 bool KTextEditor::ViewPrivate::lineEndSelected(const KTextEditor::Cursor lineEndPos)
2788 {
2789     return (!blockSelect)
2790         && (lineEndPos.line() > m_selection.start().line()
2791             || (lineEndPos.line() == m_selection.start().line() && (m_selection.start().column() < lineEndPos.column() || lineEndPos.column() == -1)))
2792         && (lineEndPos.line() < m_selection.end().line()
2793             || (lineEndPos.line() == m_selection.end().line() && (lineEndPos.column() <= m_selection.end().column() && lineEndPos.column() != -1)));
2794 }
2795 
2796 bool KTextEditor::ViewPrivate::lineHasSelected(int line)
2797 {
2798     return selection() && m_selection.toRange().containsLine(line);
2799 }
2800 
2801 bool KTextEditor::ViewPrivate::lineIsSelection(int line)
2802 {
2803     return (line == m_selection.start().line() && line == m_selection.end().line());
2804 }
2805 
2806 void KTextEditor::ViewPrivate::tagSelection(KTextEditor::Range oldSelection)
2807 {
2808     if (selection()) {
2809         if (oldSelection.start().line() == -1) {
2810             // We have to tag the whole lot if
2811             // 1) we have a selection, and:
2812             //  a) it's new; or
2813             tagLines(m_selection, true);
2814 
2815         } else if (blockSelection()
2816                    && (oldSelection.start().column() != m_selection.start().column() || oldSelection.end().column() != m_selection.end().column())) {
2817             //  b) we're in block selection mode and the columns have changed
2818             tagLines(m_selection, true);
2819             tagLines(oldSelection, true);
2820 
2821         } else {
2822             if (oldSelection.start() != m_selection.start()) {
2823                 tagLines(KTextEditor::LineRange(oldSelection.start().line(), m_selection.start().line()), true);
2824             }
2825 
2826             if (oldSelection.end() != m_selection.end()) {
2827                 tagLines(KTextEditor::LineRange(oldSelection.end().line(), m_selection.end().line()), true);
2828             }
2829         }
2830 
2831     } else {
2832         // No more selection, clean up
2833         tagLines(oldSelection, true);
2834     }
2835 }
2836 
2837 void KTextEditor::ViewPrivate::selectWord(const KTextEditor::Cursor cursor)
2838 {
2839     setSelection(doc()->wordRangeAt(cursor));
2840 }
2841 
2842 void KTextEditor::ViewPrivate::selectLine(const KTextEditor::Cursor cursor)
2843 {
2844     int line = cursor.line();
2845     if (line + 1 >= doc()->lines()) {
2846         setSelection(KTextEditor::Range(line, 0, line, doc()->lineLength(line)));
2847     } else {
2848         setSelection(KTextEditor::Range(line, 0, line + 1, 0));
2849     }
2850 }
2851 
2852 void KTextEditor::ViewPrivate::cut()
2853 {
2854     if (!selection() && !m_config->smartCopyCut()) {
2855         return;
2856     }
2857 
2858     // If markedSelection is true, copy() invalidates the selection,
2859     // which would obviate the removeSelectedText() here below.
2860     m_markedSelection = false;
2861 
2862     copy();
2863     if (!selection()) {
2864         selectLine(cursorPosition());
2865     }
2866     removeSelectedText();
2867 }
2868 
2869 void KTextEditor::ViewPrivate::copy()
2870 {
2871     QString text;
2872     KTextEditor::EditorPrivate::self()->copyToMulticursorClipboard({});
2873 
2874     if (!selection()) {
2875         if (!m_config->smartCopyCut()) {
2876             return;
2877         }
2878         text = doc()->line(cursorPosition().line()) + QLatin1Char('\n');
2879         m_viewInternal->moveEdge(KateViewInternal::left, false);
2880     } else {
2881         text = selectionText();
2882 
2883         // Multicursor copy
2884         if (!m_secondaryCursors.empty()) {
2885             QVarLengthArray<KTextEditor::Range, 16> ranges;
2886             for (const auto &c : m_secondaryCursors) {
2887                 if (c.range) {
2888                     ranges.push_back(c.range->toRange());
2889                 }
2890             }
2891             ranges.push_back(m_selection.toRange());
2892             std::sort(ranges.begin(), ranges.end());
2893             QStringList texts;
2894             for (auto range : ranges) {
2895                 texts.append(doc()->text(range));
2896             }
2897             KTextEditor::EditorPrivate::self()->copyToMulticursorClipboard(texts);
2898         }
2899 
2900         if (m_markedSelection) {
2901             setSelection(KTextEditor::Range::invalid());
2902             m_markedSelection = false;
2903         }
2904     }
2905 
2906     // copy to clipboard and our history!
2907     KTextEditor::EditorPrivate::self()->copyToClipboard(text, m_doc->url().fileName());
2908 }
2909 
2910 void KTextEditor::ViewPrivate::screenshot()
2911 {
2912     if (!selection()) {
2913         return;
2914     }
2915 
2916     ScreenshotDialog d(selectionRange(), this);
2917     d.renderScreenshot(m_renderer);
2918     d.exec();
2919 }
2920 
2921 void KTextEditor::ViewPrivate::pasteSelection()
2922 {
2923     m_temporaryAutomaticInvocationDisabled = true;
2924     doc()->paste(this, QApplication::clipboard()->text(QClipboard::Selection));
2925     m_temporaryAutomaticInvocationDisabled = false;
2926 }
2927 
2928 void KTextEditor::ViewPrivate::swapWithClipboard()
2929 {
2930     m_temporaryAutomaticInvocationDisabled = true;
2931 
2932     // get text to paste
2933     const auto text = QApplication::clipboard()->text(QClipboard::Clipboard);
2934 
2935     // do copy
2936     copy();
2937 
2938     // do paste of "previous" clipboard content we saved
2939     doc()->paste(this, text);
2940 
2941     m_temporaryAutomaticInvocationDisabled = false;
2942 }
2943 
2944 void KTextEditor::ViewPrivate::applyWordWrap()
2945 {
2946     int first = selectionRange().start().line();
2947     int last = selectionRange().end().line();
2948 
2949     if (first == last) {
2950         // Either no selection or only one line selected, wrap only the current line
2951         first = cursorPosition().line();
2952         last = first;
2953     }
2954 
2955     doc()->wrapParagraph(first, last);
2956 }
2957 
2958 // END
2959 
2960 // BEGIN KTextEditor::BlockSelectionInterface stuff
2961 
2962 bool KTextEditor::ViewPrivate::blockSelection() const
2963 {
2964     return blockSelect;
2965 }
2966 
2967 bool KTextEditor::ViewPrivate::setBlockSelection(bool on)
2968 {
2969     if (on != blockSelect) {
2970         blockSelect = on;
2971 
2972         KTextEditor::Range oldSelection = m_selection;
2973 
2974         const bool hadSelection = clearSelection(false, false);
2975 
2976         setSelection(oldSelection);
2977 
2978         m_toggleBlockSelection->setChecked(blockSelection());
2979 
2980         // when leaving block selection mode, if cursor is at an invalid position or past the end of the
2981         // line, move the cursor to the last column of the current line unless cursor wrapping is off
2982         ensureCursorColumnValid();
2983 
2984         if (!hadSelection) {
2985             // emit selectionChanged() according to the KTextEditor::View api
2986             // documentation also if there is no selection around. This is needed,
2987             // as e.g. the Kate App status bar uses this signal to update the state
2988             // of the selection mode (block selection, line based selection)
2989             Q_EMIT selectionChanged(this);
2990         }
2991     }
2992 
2993     return true;
2994 }
2995 
2996 bool KTextEditor::ViewPrivate::toggleBlockSelection()
2997 {
2998     // no multicursors for blockselect
2999     clearSecondaryCursors();
3000 
3001     m_toggleBlockSelection->setChecked(!blockSelect);
3002     return setBlockSelection(!blockSelect);
3003 }
3004 
3005 bool KTextEditor::ViewPrivate::wrapCursor() const
3006 {
3007     return !blockSelection();
3008 }
3009 
3010 // END
3011 
3012 void KTextEditor::ViewPrivate::slotTextInserted(KTextEditor::View *view, const KTextEditor::Cursor position, const QString &text)
3013 {
3014     Q_EMIT textInserted(view, position, text);
3015 }
3016 
3017 bool KTextEditor::ViewPrivate::insertTemplateInternal(const KTextEditor::Cursor c, const QString &templateString, const QString &script)
3018 {
3019     // no empty templates
3020     if (templateString.isEmpty()) {
3021         return false;
3022     }
3023 
3024     // not for read-only docs
3025     if (!doc()->isReadWrite()) {
3026         return false;
3027     }
3028 
3029     // only one handler maybe active at a time; store it in the document.
3030     // Clear it first to make sure at no time two handlers are active at once
3031     doc()->setActiveTemplateHandler(nullptr);
3032     doc()->setActiveTemplateHandler(new KateTemplateHandler(this, c, templateString, script, doc()->undoManager()));
3033     return true;
3034 }
3035 
3036 bool KTextEditor::ViewPrivate::tagLines(KTextEditor::Range range, bool realRange)
3037 {
3038     return tagLines(range.start(), range.end(), realRange);
3039 }
3040 
3041 void KTextEditor::ViewPrivate::deactivateEditActions()
3042 {
3043     for (QAction *action : std::as_const(m_editActions)) {
3044         action->setEnabled(false);
3045     }
3046 }
3047 
3048 void KTextEditor::ViewPrivate::activateEditActions()
3049 {
3050     for (QAction *action : std::as_const(m_editActions)) {
3051         action->setEnabled(true);
3052     }
3053 }
3054 
3055 bool KTextEditor::ViewPrivate::mouseTrackingEnabled() const
3056 {
3057     // FIXME support
3058     return true;
3059 }
3060 
3061 bool KTextEditor::ViewPrivate::setMouseTrackingEnabled(bool)
3062 {
3063     // FIXME support
3064     return true;
3065 }
3066 
3067 bool KTextEditor::ViewPrivate::isMulticursorNotAllowed() const
3068 {
3069     return blockSelection() || isOverwriteMode() || currentInputMode()->viewInputMode() == KTextEditor::View::InputMode::ViInputMode;
3070 }
3071 
3072 void KTextEditor::ViewPrivate::addSecondaryCursor(KTextEditor::Cursor pos)
3073 {
3074     auto primaryCursor = cursorPosition();
3075     const bool overlapsOrOnPrimary = pos == primaryCursor || (selection() && selectionRange().contains(pos));
3076     if (overlapsOrOnPrimary && m_secondaryCursors.empty()) {
3077         // Clicking on primary cursor while it is the only cursor,
3078         // we do nothing
3079         return;
3080     } else if (overlapsOrOnPrimary) {
3081         // Clicking on primary cursor, we have secondaries
3082         // so just make the last secondary cursor primary
3083         // and remove caret at current primary cursor position
3084         auto &last = m_secondaryCursors.back();
3085         setCursorPosition(last.cursor());
3086         if (last.range) {
3087             setSelection(last.range->toRange());
3088             Q_ASSERT(last.anchor.isValid());
3089             m_viewInternal->m_selectAnchor = last.anchor;
3090         }
3091         m_secondaryCursors.pop_back();
3092         return;
3093     }
3094 
3095     // If there are any existing cursors at this position
3096     // remove them and be done i.e., if you click on an
3097     // existing cursor it is removed.
3098     if (removeSecondaryCursors({pos}, /*removeIfSelectionOverlap=*/true)) {
3099         return;
3100     }
3101 
3102     // We are adding a new cursor!
3103     // - Move primary cursor to the position where the click happened
3104     // - Old primary cursor becomes a secondary cursor
3105     // Doing it like this makes multi mouse selections very easy
3106     setCursorPosition(pos);
3107     KTextEditor::ViewPrivate::PlainSecondaryCursor p;
3108     p.pos = primaryCursor;
3109     p.range = selection() ? selectionRange() : KTextEditor::Range::invalid();
3110     clearSelection();
3111     addSecondaryCursorsWithSelection({p});
3112 }
3113 
3114 void KTextEditor::ViewPrivate::setSecondaryCursors(const QList<KTextEditor::Cursor> &positions)
3115 {
3116     clearSecondaryCursors();
3117 
3118     if (positions.isEmpty() || isMulticursorNotAllowed()) {
3119         return;
3120     }
3121 
3122     const auto totalLines = doc()->lines();
3123     for (auto p : positions) {
3124         if (p != cursorPosition() && p.line() < totalLines) {
3125             SecondaryCursor c;
3126             c.pos.reset(static_cast<Kate::TextCursor *>(doc()->newMovingCursor(p)));
3127             m_secondaryCursors.push_back(std::move(c));
3128             tagLine(p);
3129         }
3130     }
3131     sortCursors();
3132     paintCursors();
3133 }
3134 
3135 void KTextEditor::ViewPrivate::clearSecondarySelections()
3136 {
3137     for (auto &c : m_secondaryCursors) {
3138         c.range.reset();
3139         c.anchor = KTextEditor::Cursor::invalid();
3140     }
3141 }
3142 
3143 void KTextEditor::ViewPrivate::clearSecondaryCursors()
3144 {
3145     if (m_secondaryCursors.empty()) {
3146         return;
3147     }
3148     for (const auto &c : m_secondaryCursors) {
3149         tagLine(c.cursor());
3150     }
3151     m_secondaryCursors.clear();
3152     m_viewInternal->updateDirty();
3153 }
3154 
3155 const std::vector<KTextEditor::ViewPrivate::SecondaryCursor> &KTextEditor::ViewPrivate::secondaryCursors() const
3156 {
3157     return m_secondaryCursors;
3158 }
3159 
3160 QList<KTextEditor::ViewPrivate::PlainSecondaryCursor> KTextEditor::ViewPrivate::plainSecondaryCursors() const
3161 {
3162     QList<PlainSecondaryCursor> cursors;
3163     cursors.reserve(m_secondaryCursors.size());
3164     std::transform(m_secondaryCursors.begin(), m_secondaryCursors.end(), std::back_inserter(cursors), [](const SecondaryCursor &c) {
3165         if (c.range) {
3166             return PlainSecondaryCursor{c.cursor(), c.range->toRange()};
3167         }
3168         return PlainSecondaryCursor{c.cursor(), KTextEditor::Range::invalid()};
3169     });
3170     return cursors;
3171 }
3172 
3173 bool KTextEditor::ViewPrivate::removeSecondaryCursors(const std::vector<KTextEditor::Cursor> &cursorsToRemove, bool removeIfOverlapsSelection)
3174 {
3175     Q_ASSERT(std::is_sorted(cursorsToRemove.begin(), cursorsToRemove.end()));
3176 
3177     QVarLengthArray<KTextEditor::Cursor, 8> linesToTag;
3178 
3179     if (removeIfOverlapsSelection) {
3180         m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3181                                                 m_secondaryCursors.end(),
3182                                                 [&](const SecondaryCursor &c) {
3183                                                     auto it = std::find_if(cursorsToRemove.begin(), cursorsToRemove.end(), [&c](KTextEditor::Cursor pos) {
3184                                                         return c.cursor() == pos || (c.range && c.range->contains(pos));
3185                                                     });
3186                                                     const bool match = it != cursorsToRemove.end();
3187                                                     if (match) {
3188                                                         linesToTag.push_back(c.cursor());
3189                                                     }
3190                                                     return match;
3191                                                 }),
3192                                  m_secondaryCursors.end());
3193     } else {
3194         m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3195                                                 m_secondaryCursors.end(),
3196                                                 [&](const SecondaryCursor &c) {
3197                                                     auto it = std::find_if(cursorsToRemove.begin(), cursorsToRemove.end(), [&c](KTextEditor::Cursor pos) {
3198                                                         return c.cursor() == pos;
3199                                                     });
3200                                                     const bool match = it != cursorsToRemove.end();
3201                                                     if (match) {
3202                                                         linesToTag.push_back(c.cursor());
3203                                                     }
3204                                                     return match;
3205                                                 }),
3206                                  m_secondaryCursors.end());
3207     }
3208 
3209     for (const auto &c : linesToTag) {
3210         tagLine(m_viewInternal->toVirtualCursor(c));
3211     }
3212     return !linesToTag.empty();
3213 
3214     for (auto cur : cursorsToRemove) {
3215         auto &sec = m_secondaryCursors;
3216         auto it = std::find_if(sec.begin(), sec.end(), [cur](const SecondaryCursor &c) {
3217             return c.cursor() == cur;
3218         });
3219         if (it != sec.end()) {
3220             //             removedAny = true;
3221             m_secondaryCursors.erase(it);
3222             tagLine(m_viewInternal->toVirtualCursor(cur));
3223         }
3224     }
3225 
3226     //     if (removedAny) {
3227     m_viewInternal->updateDirty();
3228     if (cursorPosition() == KTextEditor::Cursor(0, 0)) {
3229         m_viewInternal->paintCursor();
3230     }
3231     return !linesToTag.empty();
3232     //     }
3233     //     return removedAny;
3234 }
3235 
3236 void KTextEditor::ViewPrivate::ensureUniqueCursors(bool matchLine)
3237 {
3238     if (m_secondaryCursors.empty()) {
3239         return;
3240     }
3241 
3242     std::vector<SecondaryCursor>::iterator it;
3243     if (matchLine) {
3244         auto matchLine = [](const SecondaryCursor &l, const SecondaryCursor &r) {
3245             return l.cursor().line() == r.cursor().line();
3246         };
3247         it = std::unique(m_secondaryCursors.begin(), m_secondaryCursors.end(), matchLine);
3248     } else {
3249         it = std::unique(m_secondaryCursors.begin(), m_secondaryCursors.end());
3250     }
3251     if (it != m_secondaryCursors.end()) {
3252         m_secondaryCursors.erase(it, m_secondaryCursors.end());
3253     }
3254 
3255     if (matchLine) {
3256         const int ln = cursorPosition().line();
3257         m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3258                                                 m_secondaryCursors.end(),
3259                                                 [ln](const SecondaryCursor &c) {
3260                                                     return c.cursor().line() == ln;
3261                                                 }),
3262                                  m_secondaryCursors.end());
3263     } else {
3264         const auto cp = cursorPosition();
3265         m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3266                                                 m_secondaryCursors.end(),
3267                                                 [cp](const SecondaryCursor &c) {
3268                                                     return c.cursor() == cp;
3269                                                 }),
3270                                  m_secondaryCursors.end());
3271     }
3272 }
3273 
3274 void KTextEditor::ViewPrivate::addSecondaryCursorsWithSelection(const QList<PlainSecondaryCursor> &cursorsWithSelection)
3275 {
3276     if (isMulticursorNotAllowed() || cursorsWithSelection.isEmpty()) {
3277         return;
3278     }
3279 
3280     for (const auto &c : cursorsWithSelection) {
3281         // We don't want to add on top of primary cursor
3282         if (c.pos == cursorPosition()) {
3283             continue;
3284         }
3285         SecondaryCursor n;
3286         n.pos.reset(static_cast<Kate::TextCursor *>(doc()->newMovingCursor(c.pos)));
3287         if (c.range.isValid()) {
3288             n.range.reset(newSecondarySelectionRange(c.range));
3289             n.anchor = c.range.start() == c.pos ? c.range.end() : c.range.start();
3290         }
3291         m_secondaryCursors.push_back(std::move(n));
3292     }
3293     sortCursors();
3294     paintCursors();
3295 }
3296 
3297 Kate::TextRange *KTextEditor::ViewPrivate::newSecondarySelectionRange(KTextEditor::Range selRange)
3298 {
3299     constexpr auto expandBehaviour = KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight;
3300     auto range = new Kate::TextRange(doc()->buffer(), selRange, expandBehaviour);
3301     static KTextEditor::Attribute::Ptr selAttr;
3302     if (!selAttr) {
3303         selAttr = new KTextEditor::Attribute;
3304         auto color = QColor::fromRgba(theme().editorColor(KSyntaxHighlighting::Theme::TextSelection));
3305         selAttr->setBackground(color);
3306     }
3307     range->setZDepth(-999999.);
3308     range->setAttribute(selAttr);
3309     return range;
3310 }
3311 
3312 bool KTextEditor::ViewPrivate::hasSelections() const
3313 {
3314     if (selection())
3315         return true;
3316     return std::any_of(m_secondaryCursors.cbegin(), m_secondaryCursors.cend(), [](const SecondaryCursor &c) {
3317         return c.range && !c.range->isEmpty();
3318     });
3319 }
3320 
3321 void KTextEditor::ViewPrivate::addSecondaryCursorDown()
3322 {
3323     KTextEditor::Cursor last = cursorPosition();
3324     const auto &secondary = secondaryCursors();
3325     if (!secondary.empty()) {
3326         last = secondary.back().cursor();
3327         last = std::max(cursorPosition(), last);
3328     }
3329     if (last.line() >= doc()->lastLine()) {
3330         return;
3331     }
3332 
3333     auto nextRange = m_viewInternal->nextLayout(last);
3334     if (!nextRange.isValid()) {
3335         return;
3336     }
3337     auto primaryCursorLineLayout = m_viewInternal->currentLayout(cursorPosition());
3338     if (!primaryCursorLineLayout.isValid()) {
3339         return;
3340     }
3341 
3342     int x = renderer()->cursorToX(primaryCursorLineLayout, cursorPosition().column(), !wrapCursor());
3343     auto next = renderer()->xToCursor(nextRange, x, !wrapCursor());
3344     addSecondaryCursor(next);
3345 }
3346 
3347 void KTextEditor::ViewPrivate::addSecondaryCursorUp()
3348 {
3349     KTextEditor::Cursor last = cursorPosition();
3350     const auto &secondary = secondaryCursors();
3351     if (!secondary.empty()) {
3352         last = secondary.front().cursor();
3353         last = std::min(cursorPosition(), last);
3354     }
3355     if (last.line() == 0) {
3356         return;
3357     }
3358     auto nextRange = m_viewInternal->previousLayout(last);
3359     if (!nextRange.isValid()) {
3360         return;
3361     }
3362 
3363     auto primaryCursorLineLayout = m_viewInternal->currentLayout(cursorPosition());
3364     if (!primaryCursorLineLayout.isValid()) {
3365         return;
3366     }
3367 
3368     int x = renderer()->cursorToX(primaryCursorLineLayout, cursorPosition().column(), !wrapCursor());
3369     auto next = renderer()->xToCursor(nextRange, x, !wrapCursor());
3370     addSecondaryCursor(next);
3371 }
3372 
3373 QList<KTextEditor::Cursor> KTextEditor::ViewPrivate::cursors() const
3374 {
3375     QList<KTextEditor::Cursor> ret;
3376     ret.reserve(m_secondaryCursors.size() + 1);
3377     ret << cursorPosition();
3378     std::transform(m_secondaryCursors.begin(), m_secondaryCursors.end(), std::back_inserter(ret), [](const SecondaryCursor &c) {
3379         return c.cursor();
3380     });
3381     return ret;
3382 }
3383 
3384 QList<KTextEditor::Range> KTextEditor::ViewPrivate::selectionRanges() const
3385 {
3386     if (!selection()) {
3387         return {};
3388     }
3389 
3390     QList<KTextEditor::Range> ret;
3391     ret.reserve(m_secondaryCursors.size() + 1);
3392     ret << selectionRange();
3393     std::transform(m_secondaryCursors.begin(), m_secondaryCursors.end(), std::back_inserter(ret), [](const SecondaryCursor &c) {
3394         if (!c.range) {
3395             qWarning() << "selectionRanges(): Unexpected null selection range, please fix";
3396             return KTextEditor::Range::invalid();
3397         }
3398         return c.range->toRange();
3399     });
3400     return ret;
3401 }
3402 
3403 void KTextEditor::ViewPrivate::setCursors(const QList<KTextEditor::Cursor> &cursorPositions)
3404 {
3405     if (isMulticursorNotAllowed()) {
3406         qWarning() << "setCursors failed: Multicursors not allowed because one of the following is true"
3407                    << ", blockSelection: " << blockSelection() << ", overwriteMode: " << isOverwriteMode()
3408                    << ", viMode: " << (currentInputMode()->viewInputMode() == KTextEditor::View::InputMode::ViInputMode);
3409         return;
3410     }
3411 
3412     clearSecondaryCursors();
3413     if (cursorPositions.empty()) {
3414         return;
3415     }
3416 
3417     const auto primary = cursorPositions.front();
3418     // We clear primary selection because primary and secondary
3419     // cursors should always have same selection state
3420     setSelection({});
3421     setCursorPosition(primary);
3422     // First will be auto ignored because it equals cursorPosition()
3423     setSecondaryCursors(cursorPositions);
3424 }
3425 
3426 void KTextEditor::ViewPrivate::setSelections(const QList<KTextEditor::Range> &selectionRanges)
3427 {
3428     if (isMulticursorNotAllowed()) {
3429         qWarning() << "setSelections failed: Multicursors not allowed because one of the following is true"
3430                    << ", blockSelection: " << blockSelection() << ", overwriteMode: " << isOverwriteMode()
3431                    << ", viMode: " << (currentInputMode()->viewInputMode() == KTextEditor::View::InputMode::ViInputMode);
3432         return;
3433     }
3434 
3435     clearSecondaryCursors();
3436     setSelection({});
3437     if (selectionRanges.isEmpty()) {
3438         return;
3439     }
3440 
3441     auto first = selectionRanges.front();
3442     setCursorPosition(first.end());
3443     setSelection(first);
3444 
3445     if (selectionRanges.size() == 1) {
3446         return;
3447     }
3448 
3449     const auto docRange = doc()->documentRange();
3450     for (auto it = selectionRanges.begin() + 1; it != selectionRanges.end(); ++it) {
3451         KTextEditor::Range r = *it;
3452         KTextEditor::Cursor c = r.end();
3453         if (c == cursorPosition() || !r.isValid() || r.isEmpty() || !docRange.contains(r)) {
3454             continue;
3455         }
3456 
3457         SecondaryCursor n;
3458         n.pos.reset(static_cast<Kate::TextCursor *>(doc()->newMovingCursor(c)));
3459         n.range.reset(newSecondarySelectionRange(r));
3460         n.anchor = r.start();
3461         m_secondaryCursors.push_back(std::move(n));
3462     }
3463     m_viewInternal->mergeSelections();
3464 
3465     sortCursors();
3466     paintCursors();
3467 }
3468 
3469 void KTextEditor::ViewPrivate::sortCursors()
3470 {
3471     std::sort(m_secondaryCursors.begin(), m_secondaryCursors.end());
3472     ensureUniqueCursors();
3473 }
3474 
3475 void KTextEditor::ViewPrivate::paintCursors()
3476 {
3477     if (m_viewInternal->m_cursorTimer.isActive()) {
3478         if (QApplication::cursorFlashTime() > 0) {
3479             m_viewInternal->m_cursorTimer.start(QApplication::cursorFlashTime() / 2);
3480         }
3481         renderer()->setDrawCaret(true);
3482     }
3483     m_viewInternal->paintCursor();
3484 }
3485 
3486 bool KTextEditor::ViewPrivate::isCompletionActive() const
3487 {
3488     return completionWidget()->isCompletionActive();
3489 }
3490 
3491 KateCompletionWidget *KTextEditor::ViewPrivate::completionWidget() const
3492 {
3493     if (!m_completionWidget) {
3494         m_completionWidget = new KateCompletionWidget(const_cast<KTextEditor::ViewPrivate *>(this));
3495     }
3496 
3497     return m_completionWidget;
3498 }
3499 
3500 void KTextEditor::ViewPrivate::startCompletion(KTextEditor::Range word, KTextEditor::CodeCompletionModel *model)
3501 {
3502     completionWidget()->startCompletion(word, model);
3503 }
3504 
3505 void KTextEditor::ViewPrivate::startCompletion(const Range &word,
3506                                                const QList<KTextEditor::CodeCompletionModel *> &models,
3507                                                KTextEditor::CodeCompletionModel::InvocationType invocationType)
3508 {
3509     completionWidget()->startCompletion(word, models, invocationType);
3510 }
3511 
3512 void KTextEditor::ViewPrivate::abortCompletion()
3513 {
3514     completionWidget()->abortCompletion();
3515 }
3516 
3517 void KTextEditor::ViewPrivate::forceCompletion()
3518 {
3519     completionWidget()->execute();
3520 }
3521 
3522 void KTextEditor::ViewPrivate::registerCompletionModel(KTextEditor::CodeCompletionModel *model)
3523 {
3524     completionWidget()->registerCompletionModel(model);
3525 }
3526 
3527 void KTextEditor::ViewPrivate::unregisterCompletionModel(KTextEditor::CodeCompletionModel *model)
3528 {
3529     completionWidget()->unregisterCompletionModel(model);
3530 }
3531 
3532 bool KTextEditor::ViewPrivate::isCompletionModelRegistered(KTextEditor::CodeCompletionModel *model) const
3533 {
3534     return completionWidget()->isCompletionModelRegistered(model);
3535 }
3536 
3537 QList<KTextEditor::CodeCompletionModel *> KTextEditor::ViewPrivate::codeCompletionModels() const
3538 {
3539     return completionWidget()->codeCompletionModels();
3540 }
3541 
3542 bool KTextEditor::ViewPrivate::isAutomaticInvocationEnabled() const
3543 {
3544     return !m_temporaryAutomaticInvocationDisabled && m_config->automaticCompletionInvocation();
3545 }
3546 
3547 void KTextEditor::ViewPrivate::setAutomaticInvocationEnabled(bool enabled)
3548 {
3549     config()->setValue(KateViewConfig::AutomaticCompletionInvocation, enabled);
3550 }
3551 
3552 void KTextEditor::ViewPrivate::sendCompletionExecuted(const KTextEditor::Cursor position, KTextEditor::CodeCompletionModel *model, const QModelIndex &index)
3553 {
3554     Q_EMIT completionExecuted(this, position, model, index);
3555 }
3556 
3557 void KTextEditor::ViewPrivate::sendCompletionAborted()
3558 {
3559     Q_EMIT completionAborted(this);
3560 }
3561 
3562 void KTextEditor::ViewPrivate::paste(const QString *textToPaste)
3563 {
3564     const int cursorCount = m_secondaryCursors.size() + 1; // 1 primary cursor
3565     const auto multicursorClipboard = KTextEditor::EditorPrivate::self()->multicursorClipboard();
3566     if (cursorCount == multicursorClipboard.size() && !textToPaste) {
3567         if (doc()->multiPaste(this, multicursorClipboard)) {
3568             return;
3569         }
3570     } else if (!textToPaste && cursorCount > 1) {
3571         // We still have multiple cursors, but the amount
3572         // of multicursors doesn't match the entry count in clipboard
3573         QStringList texts;
3574         texts.reserve(cursorCount);
3575         QString clipboard = QApplication::clipboard()->text(QClipboard::Clipboard);
3576         for (int i = 0; i < cursorCount; ++i) {
3577             texts << clipboard;
3578         }
3579         // It might still fail for e.g., if we are in block mode,
3580         // in that case we will fallback to normal pasting below
3581         if (doc()->multiPaste(this, texts)) {
3582             return;
3583         }
3584     }
3585 
3586     m_temporaryAutomaticInvocationDisabled = true;
3587     doc()->paste(this, textToPaste ? *textToPaste : QApplication::clipboard()->text(QClipboard::Clipboard));
3588     m_temporaryAutomaticInvocationDisabled = false;
3589 }
3590 
3591 bool KTextEditor::ViewPrivate::setCursorPosition(KTextEditor::Cursor position)
3592 {
3593     return setCursorPositionInternal(position, 1, true);
3594 }
3595 
3596 KTextEditor::Cursor KTextEditor::ViewPrivate::cursorPosition() const
3597 {
3598     return m_viewInternal->cursorPosition();
3599 }
3600 
3601 KTextEditor::Cursor KTextEditor::ViewPrivate::cursorPositionVirtual() const
3602 {
3603     return KTextEditor::Cursor(m_viewInternal->cursorPosition().line(), virtualCursorColumn());
3604 }
3605 
3606 QPoint KTextEditor::ViewPrivate::cursorToCoordinate(KTextEditor::Cursor cursor) const
3607 {
3608     // map from ViewInternal to View coordinates
3609     const QPoint pt = m_viewInternal->cursorToCoordinate(cursor, true, false);
3610     return pt == QPoint(-1, -1) ? pt : m_viewInternal->mapToParent(pt);
3611 }
3612 
3613 KTextEditor::Cursor KTextEditor::ViewPrivate::coordinatesToCursor(const QPoint &coords) const
3614 {
3615     // map from View to ViewInternal coordinates
3616     return m_viewInternal->coordinatesToCursor(m_viewInternal->mapFromParent(coords), false);
3617 }
3618 
3619 QPoint KTextEditor::ViewPrivate::cursorPositionCoordinates() const
3620 {
3621     // map from ViewInternal to View coordinates
3622     const QPoint pt = m_viewInternal->cursorCoordinates(false);
3623     return pt == QPoint(-1, -1) ? pt : m_viewInternal->mapToParent(pt);
3624 }
3625 
3626 void KTextEditor::ViewPrivate::setScrollPositionInternal(KTextEditor::Cursor cursor)
3627 {
3628     m_viewInternal->scrollPos(cursor, false, true, false);
3629 }
3630 
3631 void KTextEditor::ViewPrivate::setHorizontalScrollPositionInternal(int x)
3632 {
3633     m_viewInternal->scrollColumns(x);
3634 }
3635 
3636 KTextEditor::Cursor KTextEditor::ViewPrivate::maxScrollPositionInternal() const
3637 {
3638     return m_viewInternal->maxStartPos(true);
3639 }
3640 
3641 int KTextEditor::ViewPrivate::firstDisplayedLineInternal(LineType lineType) const
3642 {
3643     if (lineType == RealLine) {
3644         return m_textFolding.visibleLineToLine(m_viewInternal->startLine());
3645     } else {
3646         return m_viewInternal->startLine();
3647     }
3648 }
3649 
3650 int KTextEditor::ViewPrivate::lastDisplayedLineInternal(LineType lineType) const
3651 {
3652     if (lineType == RealLine) {
3653         return m_textFolding.visibleLineToLine(m_viewInternal->endLine());
3654     } else {
3655         return m_viewInternal->endLine();
3656     }
3657 }
3658 
3659 QRect KTextEditor::ViewPrivate::textAreaRectInternal() const
3660 {
3661     const auto sourceRect = m_viewInternal->rect();
3662     const auto topLeft = m_viewInternal->mapTo(this, sourceRect.topLeft());
3663     const auto bottomRight = m_viewInternal->mapTo(this, sourceRect.bottomRight());
3664     return {topLeft, bottomRight};
3665 }
3666 
3667 bool KTextEditor::ViewPrivate::setCursorPositionVisual(const KTextEditor::Cursor position)
3668 {
3669     return setCursorPositionInternal(position, doc()->config()->tabWidth(), true);
3670 }
3671 
3672 QScrollBar *KTextEditor::ViewPrivate::verticalScrollBar() const
3673 {
3674     return m_viewInternal->m_lineScroll;
3675 }
3676 
3677 QScrollBar *KTextEditor::ViewPrivate::horizontalScrollBar() const
3678 {
3679     return m_viewInternal->m_columnScroll;
3680 }
3681 
3682 bool KTextEditor::ViewPrivate::isLineRTL(int line) const
3683 {
3684     const QString s = doc()->line(line);
3685     if (s.isEmpty()) {
3686         int line = cursorPosition().line();
3687         if (line == 0) {
3688             const int count = doc()->lines();
3689             for (int i = 1; i < count; ++i) {
3690                 const QString ln = doc()->line(i);
3691                 if (ln.isEmpty()) {
3692                     continue;
3693                 }
3694                 return ln.isRightToLeft();
3695             }
3696         } else {
3697             int line = cursorPosition().line();
3698             for (; line >= 0; --line) {
3699                 const QString s = doc()->line(line);
3700                 if (s.isEmpty()) {
3701                     continue;
3702                 }
3703                 return s.isRightToLeft();
3704             }
3705         }
3706         return false;
3707     } else {
3708         return s.isRightToLeft();
3709     }
3710 }
3711 
3712 QTextLayout *KTextEditor::ViewPrivate::textLayout(const KTextEditor::Cursor pos) const
3713 {
3714     KateLineLayout *thisLine = m_viewInternal->cache()->line(pos.line());
3715     return thisLine && thisLine->isValid() ? thisLine->layout() : nullptr;
3716 }
3717 
3718 void KTextEditor::ViewPrivate::indent()
3719 {
3720     KTextEditor::Cursor c(cursorPosition().line(), 0);
3721     KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
3722     doc()->indent(r, 1);
3723 }
3724 
3725 void KTextEditor::ViewPrivate::unIndent()
3726 {
3727     KTextEditor::Cursor c(cursorPosition().line(), 0);
3728     KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
3729     doc()->indent(r, -1);
3730 }
3731 
3732 void KTextEditor::ViewPrivate::cleanIndent()
3733 {
3734     KTextEditor::Cursor c(cursorPosition().line(), 0);
3735     KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
3736     doc()->indent(r, 0);
3737 }
3738 
3739 void KTextEditor::ViewPrivate::formatIndent()
3740 {
3741     // no selection: align current line; selection: use selection range
3742     const int line = cursorPosition().line();
3743     KTextEditor::Range formatRange(KTextEditor::Cursor(line, 0), KTextEditor::Cursor(line, 0));
3744     if (selection()) {
3745         formatRange = selectionRange();
3746     }
3747 
3748     doc()->align(this, formatRange);
3749 }
3750 
3751 // alias of formatIndent, for backward compatibility
3752 void KTextEditor::ViewPrivate::align()
3753 {
3754     formatIndent();
3755 }
3756 
3757 void KTextEditor::ViewPrivate::alignOn()
3758 {
3759     static QString pattern;
3760     KTextEditor::Range range;
3761     if (!selection()) {
3762         range = doc()->documentRange();
3763     } else {
3764         range = selectionRange();
3765     }
3766     bool ok;
3767     pattern = QInputDialog::getText(window(), i18n("Align On"), i18n("Alignment pattern:"), QLineEdit::Normal, pattern, &ok);
3768     if (!ok) {
3769         return;
3770     }
3771     doc()->alignOn(range, pattern, this->blockSelection());
3772 }
3773 
3774 void KTextEditor::ViewPrivate::comment()
3775 {
3776     m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight);
3777     doc()->comment(this, cursorPosition().line(), cursorPosition().column(), DocumentPrivate::Comment);
3778     m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight);
3779 }
3780 
3781 void KTextEditor::ViewPrivate::uncomment()
3782 {
3783     doc()->comment(this, cursorPosition().line(), cursorPosition().column(), DocumentPrivate::UnComment);
3784 }
3785 
3786 void KTextEditor::ViewPrivate::toggleComment()
3787 {
3788     m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight);
3789     doc()->comment(this, cursorPosition().line(), cursorPosition().column(), DocumentPrivate::ToggleComment);
3790     m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight);
3791 }
3792 
3793 void KTextEditor::ViewPrivate::uppercase()
3794 {
3795     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Uppercase);
3796 }
3797 
3798 void KTextEditor::ViewPrivate::killLine()
3799 {
3800     std::vector<int> linesToRemove;
3801     if (m_selection.isEmpty()) {
3802         // collect lines of all cursors
3803         linesToRemove.reserve(m_secondaryCursors.size() + 1);
3804         for (const auto &c : m_secondaryCursors) {
3805             linesToRemove.push_back(c.pos->line());
3806         }
3807         // add primary cursor line
3808         linesToRemove.push_back(cursorPosition().line());
3809     } else {
3810         linesToRemove.reserve(m_secondaryCursors.size() + 1);
3811         for (const auto &c : m_secondaryCursors) {
3812             const auto &range = c.range;
3813             if (!range) {
3814                 continue;
3815             }
3816             for (int line = range->end().line(); line >= range->start().line(); line--) {
3817                 linesToRemove.push_back(line);
3818             }
3819         }
3820 
3821         // cache endline, else that moves and we might delete complete document if last line is selected!
3822         for (int line = m_selection.end().line(), endLine = m_selection.start().line(); line >= endLine; line--) {
3823             linesToRemove.push_back(line);
3824         }
3825     }
3826 
3827     std::sort(linesToRemove.begin(), linesToRemove.end(), std::greater{});
3828     linesToRemove.erase(std::unique(linesToRemove.begin(), linesToRemove.end()), linesToRemove.end());
3829 
3830     doc()->editStart();
3831     std::for_each(linesToRemove.begin(), linesToRemove.end(), [this](int line) {
3832         doc()->removeLine(line);
3833     });
3834     doc()->editEnd();
3835 
3836     ensureUniqueCursors();
3837 }
3838 
3839 void KTextEditor::ViewPrivate::lowercase()
3840 {
3841     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Lowercase);
3842 }
3843 
3844 void KTextEditor::ViewPrivate::capitalize()
3845 {
3846     doc()->editStart();
3847     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Lowercase);
3848     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Capitalize);
3849     doc()->editEnd();
3850 }
3851 
3852 void KTextEditor::ViewPrivate::keyReturn()
3853 {
3854     doc()->newLine(this);
3855     m_viewInternal->iconBorder()->updateForCursorLineChange();
3856     m_viewInternal->updateView();
3857 }
3858 
3859 void KTextEditor::ViewPrivate::smartNewline()
3860 {
3861     const KTextEditor::Cursor cursor = cursorPosition();
3862     const int ln = cursor.line();
3863     Kate::TextLine line = doc()->kateTextLine(ln);
3864     int col = qMin(cursor.column(), line.firstChar());
3865     if (col != -1) {
3866         while (line.length() > col && !(line.at(col).isLetterOrNumber() || line.at(col) == QLatin1Char('_')) && col < cursor.column()) {
3867             ++col;
3868         }
3869     } else {
3870         col = line.length(); // stay indented
3871     }
3872     doc()->editStart();
3873     doc()->editWrapLine(ln, cursor.column());
3874     doc()->insertText(KTextEditor::Cursor(ln + 1, 0), line.string(0, col));
3875     doc()->editEnd();
3876 
3877     m_viewInternal->updateView();
3878 }
3879 
3880 void KTextEditor::ViewPrivate::noIndentNewline()
3881 {
3882     doc()->newLine(this, KTextEditor::DocumentPrivate::NoIndent);
3883     m_viewInternal->iconBorder()->updateForCursorLineChange();
3884     m_viewInternal->updateView();
3885 }
3886 
3887 void KTextEditor::ViewPrivate::newLineAbove()
3888 {
3889     doc()->newLine(this, KTextEditor::DocumentPrivate::Indent, KTextEditor::DocumentPrivate::Above);
3890     m_viewInternal->iconBorder()->updateForCursorLineChange();
3891     m_viewInternal->updateView();
3892 }
3893 
3894 void KTextEditor::ViewPrivate::newLineBelow()
3895 {
3896     doc()->newLine(this, KTextEditor::DocumentPrivate::Indent, KTextEditor::DocumentPrivate::Below);
3897     m_viewInternal->iconBorder()->updateForCursorLineChange();
3898     m_viewInternal->updateView();
3899 }
3900 
3901 void KTextEditor::ViewPrivate::backspace()
3902 {
3903     // Will take care of both multi and primary cursors
3904     doc()->backspace(this);
3905 }
3906 
3907 void KTextEditor::ViewPrivate::insertTab()
3908 {
3909     doc()->insertTab(this, cursorPosition());
3910 }
3911 
3912 void KTextEditor::ViewPrivate::deleteWordLeft()
3913 {
3914     doc()->editStart();
3915     m_viewInternal->wordPrev(true);
3916     KTextEditor::Range selection = selectionRange();
3917     removeSelectedText();
3918     doc()->editEnd();
3919 
3920     ensureUniqueCursors();
3921 
3922     m_viewInternal->tagRange(selection, true);
3923     m_viewInternal->updateDirty();
3924 }
3925 
3926 void KTextEditor::ViewPrivate::keyDelete()
3927 {
3928     KTextEditor::Document::EditingTransaction t(doc());
3929 
3930     if (blockSelect) {
3931         KTextEditor::Range selection = m_selection;
3932         if (selection.isValid() && selection.start().column() == selection.end().column()) {
3933             KTextEditor::Cursor end = {selection.end().line(), selection.end().column() + 1};
3934             selection = {selection.start(), end};
3935             doc()->removeText(selection, blockSelect);
3936             return;
3937         }
3938     }
3939 
3940     // multi cursor
3941 
3942     if (removeSelectedText()) {
3943         return;
3944     }
3945 
3946     for (const auto &c : m_secondaryCursors) {
3947         if (c.range) {
3948             doc()->removeText(c.range->toRange());
3949         } else {
3950             doc()->del(this, c.cursor());
3951         }
3952     }
3953 
3954     // primary cursor
3955     doc()->del(this, cursorPosition());
3956 
3957     ensureUniqueCursors();
3958 }
3959 
3960 void KTextEditor::ViewPrivate::deleteWordRight()
3961 {
3962     doc()->editStart();
3963     m_viewInternal->wordNext(true);
3964     KTextEditor::Range selection = selectionRange();
3965     removeSelectedText();
3966     doc()->editEnd();
3967 
3968     ensureUniqueCursors();
3969 
3970     m_viewInternal->tagRange(selection, true);
3971     m_viewInternal->updateDirty();
3972 }
3973 
3974 void KTextEditor::ViewPrivate::transpose()
3975 {
3976     doc()->editStart();
3977     for (const auto &c : m_secondaryCursors) {
3978         doc()->transpose(c.cursor());
3979     }
3980     doc()->transpose(cursorPosition());
3981     doc()->editEnd();
3982 }
3983 
3984 void KTextEditor::ViewPrivate::transposeWord()
3985 {
3986     const KTextEditor::Cursor originalCurPos = cursorPosition();
3987     const KTextEditor::Range firstWord = doc()->wordRangeAt(originalCurPos);
3988     if (!firstWord.isValid()) {
3989         return;
3990     }
3991 
3992     auto wordIsInvalid = [](QStringView word) {
3993         for (const QChar &character : word) {
3994             if (character.isLetterOrNumber()) {
3995                 return false;
3996             }
3997         }
3998         return true;
3999     };
4000 
4001     if (wordIsInvalid(doc()->text(firstWord))) {
4002         return;
4003     }
4004 
4005     setCursorPosition(firstWord.end());
4006     wordRight();
4007     KTextEditor::Cursor curPos = cursorPosition();
4008     // swap with the word to the right if it exists, otherwise try to swap with word to the left
4009     if (curPos.line() != firstWord.end().line() || curPos.column() == firstWord.end().column()) {
4010         setCursorPosition(firstWord.start());
4011         wordLeft();
4012         curPos = cursorPosition();
4013         // if there is still no next word in this line, no swapping will be done
4014         if (curPos.line() != firstWord.start().line() || curPos.column() == firstWord.start().column() || wordIsInvalid(doc()->wordAt(curPos))) {
4015             setCursorPosition(originalCurPos);
4016             return;
4017         }
4018     }
4019 
4020     if (wordIsInvalid(doc()->wordAt(curPos))) {
4021         setCursorPosition(originalCurPos);
4022         return;
4023     }
4024 
4025     const KTextEditor::Range secondWord = doc()->wordRangeAt(curPos);
4026     doc()->swapTextRanges(firstWord, secondWord);
4027 
4028     // return cursor to its original position inside the word before swap
4029     // after the swap, the cursor will be at the end of the word, so we compute the position relative to the end of the word
4030     const int offsetFromWordEnd = firstWord.end().column() - originalCurPos.column();
4031     setCursorPosition(cursorPosition() - KTextEditor::Cursor(0, offsetFromWordEnd));
4032 }
4033 
4034 void KTextEditor::ViewPrivate::cursorLeft()
4035 {
4036     if (selection() && !config()->persistentSelection() && !m_markedSelection) {
4037         if (isLineRTL(cursorPosition().line())) {
4038             m_viewInternal->updateCursor(selectionRange().end());
4039             setSelection(KTextEditor::Range::invalid());
4040         } else {
4041             m_viewInternal->updateCursor(selectionRange().start());
4042             setSelection(KTextEditor::Range::invalid());
4043         }
4044 
4045         for (const auto &c : m_secondaryCursors) {
4046             if (!c.range) {
4047                 continue;
4048             }
4049             const bool rtl = isLineRTL(c.cursor().line());
4050             c.pos->setPosition(rtl ? c.range->end() : c.range->start());
4051         }
4052         clearSecondarySelections();
4053     } else {
4054         if (isLineRTL(cursorPosition().line())) {
4055             m_viewInternal->cursorNextChar(m_markedSelection);
4056         } else {
4057             m_viewInternal->cursorPrevChar(m_markedSelection);
4058         }
4059     }
4060 }
4061 
4062 void KTextEditor::ViewPrivate::shiftCursorLeft()
4063 {
4064     if (isLineRTL(cursorPosition().line())) {
4065         m_viewInternal->cursorNextChar(true);
4066     } else {
4067         m_viewInternal->cursorPrevChar(true);
4068     }
4069 }
4070 
4071 void KTextEditor::ViewPrivate::cursorRight()
4072 {
4073     if (selection() && !config()->persistentSelection() && !m_markedSelection) {
4074         if (isLineRTL(cursorPosition().line())) {
4075             m_viewInternal->updateCursor(selectionRange().start());
4076             setSelection(KTextEditor::Range::invalid());
4077         } else {
4078             m_viewInternal->updateCursor(selectionRange().end());
4079             setSelection(KTextEditor::Range::invalid());
4080         }
4081 
4082         for (const auto &c : m_secondaryCursors) {
4083             if (!c.range) {
4084                 continue;
4085             }
4086             const bool rtl = doc()->line(c.cursor().line()).isRightToLeft();
4087             c.pos->setPosition(rtl ? c.range->start() : c.range->end());
4088         }
4089         clearSecondarySelections();
4090     } else {
4091         if (isLineRTL(cursorPosition().line())) {
4092             m_viewInternal->cursorPrevChar(m_markedSelection);
4093         } else {
4094             m_viewInternal->cursorNextChar(m_markedSelection);
4095         }
4096     }
4097 }
4098 
4099 void KTextEditor::ViewPrivate::shiftCursorRight()
4100 {
4101     if (isLineRTL(cursorPosition().line())) {
4102         m_viewInternal->cursorPrevChar(true);
4103     } else {
4104         m_viewInternal->cursorNextChar(true);
4105     }
4106 }
4107 
4108 void KTextEditor::ViewPrivate::wordLeft()
4109 {
4110     if (isLineRTL(cursorPosition().line())) {
4111         m_viewInternal->wordNext(m_markedSelection);
4112     } else {
4113         m_viewInternal->wordPrev(m_markedSelection);
4114     }
4115 }
4116 
4117 void KTextEditor::ViewPrivate::shiftWordLeft()
4118 {
4119     if (isLineRTL(cursorPosition().line())) {
4120         m_viewInternal->wordNext(true);
4121     } else {
4122         m_viewInternal->wordPrev(true);
4123     }
4124 }
4125 
4126 void KTextEditor::ViewPrivate::wordRight()
4127 {
4128     if (isLineRTL(cursorPosition().line())) {
4129         m_viewInternal->wordPrev(m_markedSelection);
4130     } else {
4131         m_viewInternal->wordNext(m_markedSelection);
4132     }
4133 }
4134 
4135 void KTextEditor::ViewPrivate::shiftWordRight()
4136 {
4137     if (isLineRTL(cursorPosition().line())) {
4138         m_viewInternal->wordPrev(true);
4139     } else {
4140         m_viewInternal->wordNext(true);
4141     }
4142 }
4143 
4144 void KTextEditor::ViewPrivate::markSelection()
4145 {
4146     if (m_markedSelection && selection()) {
4147         setSelection(KTextEditor::Range::invalid());
4148         clearSecondarySelections();
4149     } else {
4150         m_markedSelection = !m_markedSelection;
4151     }
4152 }
4153 
4154 void KTextEditor::ViewPrivate::home()
4155 {
4156     m_viewInternal->home(m_markedSelection);
4157 }
4158 
4159 void KTextEditor::ViewPrivate::shiftHome()
4160 {
4161     m_viewInternal->home(true);
4162 }
4163 
4164 void KTextEditor::ViewPrivate::end()
4165 {
4166     m_viewInternal->end(m_markedSelection);
4167 }
4168 
4169 void KTextEditor::ViewPrivate::shiftEnd()
4170 {
4171     m_viewInternal->end(true);
4172 }
4173 
4174 void KTextEditor::ViewPrivate::up()
4175 {
4176     m_viewInternal->cursorUp(m_markedSelection);
4177 }
4178 
4179 void KTextEditor::ViewPrivate::shiftUp()
4180 {
4181     m_viewInternal->cursorUp(true);
4182 }
4183 
4184 void KTextEditor::ViewPrivate::down()
4185 {
4186     m_viewInternal->cursorDown(m_markedSelection);
4187 }
4188 
4189 void KTextEditor::ViewPrivate::shiftDown()
4190 {
4191     m_viewInternal->cursorDown(true);
4192 }
4193 
4194 void KTextEditor::ViewPrivate::scrollUp()
4195 {
4196     m_viewInternal->scrollUp();
4197 }
4198 
4199 void KTextEditor::ViewPrivate::scrollDown()
4200 {
4201     m_viewInternal->scrollDown();
4202 }
4203 
4204 void KTextEditor::ViewPrivate::topOfView()
4205 {
4206     m_viewInternal->topOfView();
4207 }
4208 
4209 void KTextEditor::ViewPrivate::shiftTopOfView()
4210 {
4211     m_viewInternal->topOfView(true);
4212 }
4213 
4214 void KTextEditor::ViewPrivate::bottomOfView()
4215 {
4216     m_viewInternal->bottomOfView();
4217 }
4218 
4219 void KTextEditor::ViewPrivate::shiftBottomOfView()
4220 {
4221     m_viewInternal->bottomOfView(true);
4222 }
4223 
4224 void KTextEditor::ViewPrivate::pageUp()
4225 {
4226     m_viewInternal->pageUp(m_markedSelection);
4227 }
4228 
4229 void KTextEditor::ViewPrivate::shiftPageUp()
4230 {
4231     m_viewInternal->pageUp(true);
4232 }
4233 
4234 void KTextEditor::ViewPrivate::pageDown()
4235 {
4236     m_viewInternal->pageDown(m_markedSelection);
4237 }
4238 
4239 void KTextEditor::ViewPrivate::shiftPageDown()
4240 {
4241     m_viewInternal->pageDown(true);
4242 }
4243 
4244 void KTextEditor::ViewPrivate::top()
4245 {
4246     m_viewInternal->top_home(m_markedSelection);
4247 }
4248 
4249 void KTextEditor::ViewPrivate::shiftTop()
4250 {
4251     m_viewInternal->top_home(true);
4252 }
4253 
4254 void KTextEditor::ViewPrivate::bottom()
4255 {
4256     m_viewInternal->bottom_end(m_markedSelection);
4257 }
4258 
4259 void KTextEditor::ViewPrivate::shiftBottom()
4260 {
4261     m_viewInternal->bottom_end(true);
4262 }
4263 
4264 void KTextEditor::ViewPrivate::toMatchingBracket()
4265 {
4266     m_viewInternal->cursorToMatchingBracket();
4267 }
4268 
4269 void KTextEditor::ViewPrivate::shiftToMatchingBracket()
4270 {
4271     m_viewInternal->cursorToMatchingBracket(true);
4272 }
4273 
4274 void KTextEditor::ViewPrivate::toPrevModifiedLine()
4275 {
4276     const int startLine = cursorPosition().line() - 1;
4277     const int line = doc()->findTouchedLine(startLine, false);
4278     if (line >= 0) {
4279         KTextEditor::Cursor c(line, 0);
4280         m_viewInternal->updateSelection(c, false);
4281         m_viewInternal->updateCursor(c);
4282     }
4283 }
4284 
4285 void KTextEditor::ViewPrivate::toNextModifiedLine()
4286 {
4287     const int startLine = cursorPosition().line() + 1;
4288     const int line = doc()->findTouchedLine(startLine, true);
4289     if (line >= 0) {
4290         KTextEditor::Cursor c(line, 0);
4291         m_viewInternal->updateSelection(c, false);
4292         m_viewInternal->updateCursor(c);
4293     }
4294 }
4295 
4296 KTextEditor::Range KTextEditor::ViewPrivate::selectionRange() const
4297 {
4298     return m_selection;
4299 }
4300 
4301 KTextEditor::Document *KTextEditor::ViewPrivate::document() const
4302 {
4303     return m_doc;
4304 }
4305 
4306 void KTextEditor::ViewPrivate::setContextMenu(QMenu *menu)
4307 {
4308     if (m_contextMenu) {
4309         disconnect(m_contextMenu.data(), &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
4310         disconnect(m_contextMenu.data(), &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
4311     }
4312     m_contextMenu = menu;
4313     m_userContextMenuSet = true;
4314 
4315     if (m_contextMenu) {
4316         connect(m_contextMenu.data(), &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
4317         connect(m_contextMenu.data(), &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
4318     }
4319 }
4320 
4321 QMenu *KTextEditor::ViewPrivate::contextMenu() const
4322 {
4323     if (m_userContextMenuSet) {
4324         return m_contextMenu;
4325     } else {
4326         KXMLGUIClient *client = const_cast<KTextEditor::ViewPrivate *>(this);
4327         while (client->parentClient()) {
4328             client = client->parentClient();
4329         }
4330 
4331         // qCDebug(LOG_KTE) << "looking up all menu containers";
4332         if (client->factory()) {
4333             const QList<QWidget *> menuContainers = client->factory()->containers(QStringLiteral("menu"));
4334             for (QWidget *w : menuContainers) {
4335                 if (w->objectName() == QLatin1String("ktexteditor_popup")) {
4336                     // perhaps optimize this block
4337                     QMenu *menu = (QMenu *)w;
4338                     // menu is a reusable instance shared among all views. Therefore,
4339                     // disconnect the current receiver(s) from the menu show/hide signals
4340                     // before connecting `this` view. This ensures that only the current
4341                     // view gets a signal when the menu is about to be shown or hidden,
4342                     // and not also the view(s) that previously had the menu open.
4343                     disconnect(menu, &QMenu::aboutToShow, nullptr, nullptr);
4344                     disconnect(menu, &QMenu::aboutToHide, nullptr, nullptr);
4345                     connect(menu, &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
4346                     connect(menu, &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
4347                     return menu;
4348                 }
4349             }
4350         }
4351     }
4352     return nullptr;
4353 }
4354 
4355 QMenu *KTextEditor::ViewPrivate::defaultContextMenu(QMenu *menu) const
4356 {
4357     if (!menu) {
4358         menu = new QMenu(const_cast<KTextEditor::ViewPrivate *>(this));
4359     }
4360 
4361     if (m_editUndo) {
4362         menu->addAction(m_editUndo);
4363     }
4364     if (m_editRedo) {
4365         menu->addAction(m_editRedo);
4366     }
4367 
4368     menu->addSeparator();
4369     menu->addAction(m_cut);
4370     menu->addAction(m_copy);
4371     menu->addAction(m_paste);
4372     if (m_pasteSelection) {
4373         menu->addAction(m_pasteSelection);
4374     }
4375 
4376     menu->addAction(m_screenshotSelection);
4377     menu->addAction(m_swapWithClipboard);
4378     menu->addSeparator();
4379     menu->addAction(m_selectAll);
4380     menu->addAction(m_deSelect);
4381     QAction *editing = actionCollection()->action(QStringLiteral("tools_scripts_Editing"));
4382     if (editing) {
4383         menu->addAction(editing);
4384     }
4385     if (QAction *spellingSuggestions = actionCollection()->action(QStringLiteral("spelling_suggestions"))) {
4386         menu->addSeparator();
4387         menu->addAction(spellingSuggestions);
4388     }
4389     if (QAction *bookmark = actionCollection()->action(QStringLiteral("bookmarks"))) {
4390         menu->addSeparator();
4391         menu->addAction(bookmark);
4392     }
4393 
4394     return menu;
4395 }
4396 
4397 void KTextEditor::ViewPrivate::aboutToShowContextMenu()
4398 {
4399     QMenu *menu = qobject_cast<QMenu *>(sender());
4400 
4401     if (menu) {
4402         Q_EMIT contextMenuAboutToShow(this, menu);
4403     }
4404 }
4405 
4406 void KTextEditor::ViewPrivate::aboutToHideContextMenu()
4407 {
4408     m_spellingMenu->cleanUpAfterShown();
4409 }
4410 
4411 // BEGIN ConfigInterface stff
4412 QStringList KTextEditor::ViewPrivate::configKeys() const
4413 {
4414     static const QStringList keys = {QStringLiteral("icon-bar"),
4415                                      QStringLiteral("line-numbers"),
4416                                      QStringLiteral("dynamic-word-wrap"),
4417                                      QStringLiteral("background-color"),
4418                                      QStringLiteral("selection-color"),
4419                                      QStringLiteral("search-highlight-color"),
4420                                      QStringLiteral("replace-highlight-color"),
4421                                      QStringLiteral("default-mark-type"),
4422                                      QStringLiteral("allow-mark-menu"),
4423                                      QStringLiteral("folding-bar"),
4424                                      QStringLiteral("folding-preview"),
4425                                      QStringLiteral("icon-border-color"),
4426                                      QStringLiteral("folding-marker-color"),
4427                                      QStringLiteral("line-number-color"),
4428                                      QStringLiteral("current-line-number-color"),
4429                                      QStringLiteral("modification-markers"),
4430                                      QStringLiteral("keyword-completion"),
4431                                      QStringLiteral("word-count"),
4432                                      QStringLiteral("line-count"),
4433                                      QStringLiteral("scrollbar-minimap"),
4434                                      QStringLiteral("scrollbar-preview"),
4435                                      QStringLiteral("font"),
4436                                      QStringLiteral("theme")};
4437     return keys;
4438 }
4439 
4440 QVariant KTextEditor::ViewPrivate::configValue(const QString &key)
4441 {
4442     if (key == QLatin1String("icon-bar")) {
4443         return config()->iconBar();
4444     } else if (key == QLatin1String("line-numbers")) {
4445         return config()->lineNumbers();
4446     } else if (key == QLatin1String("dynamic-word-wrap")) {
4447         return config()->dynWordWrap();
4448     } else if (key == QLatin1String("background-color")) {
4449         return rendererConfig()->backgroundColor();
4450     } else if (key == QLatin1String("selection-color")) {
4451         return rendererConfig()->selectionColor();
4452     } else if (key == QLatin1String("search-highlight-color")) {
4453         return rendererConfig()->searchHighlightColor();
4454     } else if (key == QLatin1String("replace-highlight-color")) {
4455         return rendererConfig()->replaceHighlightColor();
4456     } else if (key == QLatin1String("default-mark-type")) {
4457         return config()->defaultMarkType();
4458     } else if (key == QLatin1String("allow-mark-menu")) {
4459         return config()->allowMarkMenu();
4460     } else if (key == QLatin1String("folding-bar")) {
4461         return config()->foldingBar();
4462     } else if (key == QLatin1String("folding-preview")) {
4463         return config()->foldingPreview();
4464     } else if (key == QLatin1String("icon-border-color")) {
4465         return rendererConfig()->iconBarColor();
4466     } else if (key == QLatin1String("folding-marker-color")) {
4467         return rendererConfig()->foldingColor();
4468     } else if (key == QLatin1String("line-number-color")) {
4469         return rendererConfig()->lineNumberColor();
4470     } else if (key == QLatin1String("current-line-number-color")) {
4471         return rendererConfig()->currentLineNumberColor();
4472     } else if (key == QLatin1String("modification-markers")) {
4473         return config()->lineModification();
4474     } else if (key == QLatin1String("keyword-completion")) {
4475         return config()->keywordCompletion();
4476     } else if (key == QLatin1String("word-count")) {
4477         return config()->showWordCount();
4478     } else if (key == QLatin1String("line-count")) {
4479         return config()->showLineCount();
4480     } else if (key == QLatin1String("scrollbar-minimap")) {
4481         return config()->scrollBarMiniMap();
4482     } else if (key == QLatin1String("scrollbar-preview")) {
4483         return config()->scrollBarPreview();
4484     } else if (key == QLatin1String("font")) {
4485         return rendererConfig()->baseFont();
4486     } else if (key == QLatin1String("theme")) {
4487         return rendererConfig()->schema();
4488     }
4489 
4490     // return invalid variant
4491     return QVariant();
4492 }
4493 
4494 void KTextEditor::ViewPrivate::setConfigValue(const QString &key, const QVariant &value)
4495 {
4496     // First, try the new config interface
4497     if (config()->setValue(key, value)) {
4498         return;
4499 
4500     } else if (rendererConfig()->setValue(key, value)) {
4501         return;
4502     }
4503 
4504     // No success? Go the old way
4505     if (value.canConvert<QColor>()) {
4506         if (key == QLatin1String("background-color")) {
4507             rendererConfig()->setBackgroundColor(value.value<QColor>());
4508         } else if (key == QLatin1String("selection-color")) {
4509             rendererConfig()->setSelectionColor(value.value<QColor>());
4510         } else if (key == QLatin1String("search-highlight-color")) {
4511             rendererConfig()->setSearchHighlightColor(value.value<QColor>());
4512         } else if (key == QLatin1String("replace-highlight-color")) {
4513             rendererConfig()->setReplaceHighlightColor(value.value<QColor>());
4514         } else if (key == QLatin1String("icon-border-color")) {
4515             rendererConfig()->setIconBarColor(value.value<QColor>());
4516         } else if (key == QLatin1String("folding-marker-color")) {
4517             rendererConfig()->setFoldingColor(value.value<QColor>());
4518         } else if (key == QLatin1String("line-number-color")) {
4519             rendererConfig()->setLineNumberColor(value.value<QColor>());
4520         } else if (key == QLatin1String("current-line-number-color")) {
4521             rendererConfig()->setCurrentLineNumberColor(value.value<QColor>());
4522         }
4523     }
4524     if (value.userType() == QMetaType::Bool) {
4525         // Note explicit type check above. If we used canConvert, then
4526         // values of type UInt will be trapped here.
4527         if (key == QLatin1String("dynamic-word-wrap")) {
4528             config()->setDynWordWrap(value.toBool());
4529         } else if (key == QLatin1String("word-count")) {
4530             config()->setShowWordCount(value.toBool());
4531         } else if (key == QLatin1String("line-count")) {
4532             config()->setShowLineCount(value.toBool());
4533         }
4534     } else if (key == QLatin1String("font") && value.canConvert<QFont>()) {
4535         rendererConfig()->setFont(value.value<QFont>());
4536     } else if (key == QLatin1String("theme") && value.userType() == QMetaType::QString) {
4537         rendererConfig()->setSchema(value.toString());
4538     }
4539 }
4540 
4541 // END ConfigInterface
4542 
4543 // NOLINTNEXTLINE(readability-make-member-function-const)
4544 void KTextEditor::ViewPrivate::userInvokedCompletion()
4545 {
4546     completionWidget()->userInvokedCompletion();
4547 }
4548 
4549 KateViewBar *KTextEditor::ViewPrivate::bottomViewBar() const
4550 {
4551     return m_bottomViewBar;
4552 }
4553 
4554 KateGotoBar *KTextEditor::ViewPrivate::gotoBar()
4555 {
4556     if (!m_gotoBar) {
4557         m_gotoBar = new KateGotoBar(this);
4558         bottomViewBar()->addBarWidget(m_gotoBar);
4559     }
4560 
4561     return m_gotoBar;
4562 }
4563 
4564 KateDictionaryBar *KTextEditor::ViewPrivate::dictionaryBar()
4565 {
4566     if (!m_dictionaryBar) {
4567         m_dictionaryBar = new KateDictionaryBar(this);
4568         bottomViewBar()->addBarWidget(m_dictionaryBar);
4569     }
4570 
4571     return m_dictionaryBar;
4572 }
4573 
4574 void KTextEditor::ViewPrivate::setAnnotationModel(KTextEditor::AnnotationModel *model)
4575 {
4576     KTextEditor::AnnotationModel *oldmodel = m_annotationModel;
4577     m_annotationModel = model;
4578     m_viewInternal->m_leftBorder->annotationModelChanged(oldmodel, m_annotationModel);
4579 }
4580 
4581 KTextEditor::AnnotationModel *KTextEditor::ViewPrivate::annotationModel() const
4582 {
4583     return m_annotationModel;
4584 }
4585 
4586 void KTextEditor::ViewPrivate::setAnnotationBorderVisible(bool visible)
4587 {
4588     m_viewInternal->m_leftBorder->setAnnotationBorderOn(visible);
4589 }
4590 
4591 bool KTextEditor::ViewPrivate::isAnnotationBorderVisible() const
4592 {
4593     return m_viewInternal->m_leftBorder->annotationBorderOn();
4594 }
4595 
4596 KTextEditor::AbstractAnnotationItemDelegate *KTextEditor::ViewPrivate::annotationItemDelegate() const
4597 {
4598     return m_viewInternal->m_leftBorder->annotationItemDelegate();
4599 }
4600 
4601 void KTextEditor::ViewPrivate::setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate)
4602 {
4603     m_viewInternal->m_leftBorder->setAnnotationItemDelegate(delegate);
4604 }
4605 
4606 bool KTextEditor::ViewPrivate::uniformAnnotationItemSizes() const
4607 {
4608     return m_viewInternal->m_leftBorder->uniformAnnotationItemSizes();
4609 }
4610 
4611 void KTextEditor::ViewPrivate::setAnnotationUniformItemSizes(bool enable)
4612 {
4613     m_viewInternal->m_leftBorder->setAnnotationUniformItemSizes(enable);
4614 }
4615 
4616 KTextEditor::Range KTextEditor::ViewPrivate::visibleRange()
4617 {
4618     // ensure that the view is up-to-date, otherwise 'endPos()' might fail!
4619     if (!m_viewInternal->endPos().isValid()) {
4620         m_viewInternal->updateView();
4621     }
4622     return KTextEditor::Range(m_viewInternal->toRealCursor(m_viewInternal->startPos()), m_viewInternal->toRealCursor(m_viewInternal->endPos()));
4623 }
4624 
4625 bool KTextEditor::ViewPrivate::event(QEvent *e)
4626 {
4627     switch (e->type()) {
4628     case QEvent::StyleChange:
4629         setupLayout();
4630         return true;
4631     default:
4632         return KTextEditor::View::event(e);
4633     }
4634 }
4635 
4636 void KTextEditor::ViewPrivate::paintEvent(QPaintEvent *e)
4637 {
4638     // base class
4639     KTextEditor::View::paintEvent(e);
4640 
4641     if (!config()->showFocusFrame()) {
4642         return;
4643     }
4644 
4645     const QRect contentsRect = m_topSpacer->geometry() | m_bottomSpacer->geometry() | m_leftSpacer->geometry() | m_rightSpacer->geometry();
4646 
4647     if (contentsRect.isValid()) {
4648         QStyleOptionFrame opt;
4649         opt.initFrom(this);
4650         opt.frameShape = QFrame::StyledPanel;
4651         opt.state |= QStyle::State_Sunken;
4652 
4653         // clear mouseOver and focus state
4654         // update from relevant widgets
4655         opt.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
4656         const QList<QWidget *> widgets = QList<QWidget *>()
4657             << m_viewInternal << m_viewInternal->m_leftBorder << m_viewInternal->m_lineScroll << m_viewInternal->m_columnScroll;
4658         for (const QWidget *w : widgets) {
4659             if (w->hasFocus()) {
4660                 opt.state |= QStyle::State_HasFocus;
4661             }
4662             if (w->underMouse()) {
4663                 opt.state |= QStyle::State_MouseOver;
4664             }
4665         }
4666 
4667         // update rect
4668         opt.rect = contentsRect;
4669 
4670         // render
4671         QPainter paint(this);
4672         paint.setClipRegion(e->region());
4673         paint.setRenderHints(QPainter::Antialiasing);
4674         style()->drawControl(QStyle::CE_ShapedFrame, &opt, &paint, this);
4675     }
4676 }
4677 
4678 void KTextEditor::ViewPrivate::toggleOnTheFlySpellCheck(bool b)
4679 {
4680     doc()->onTheFlySpellCheckingEnabled(b);
4681 }
4682 
4683 void KTextEditor::ViewPrivate::reflectOnTheFlySpellCheckStatus(bool enabled)
4684 {
4685     m_spellingMenu->setVisible(enabled);
4686     m_toggleOnTheFlySpellCheck->setChecked(enabled);
4687 }
4688 
4689 KateSpellingMenu *KTextEditor::ViewPrivate::spellingMenu()
4690 {
4691     return m_spellingMenu;
4692 }
4693 
4694 void KTextEditor::ViewPrivate::notifyAboutRangeChange(KTextEditor::LineRange lineRange, bool needsRepaint)
4695 {
4696 #ifdef VIEW_RANGE_DEBUG
4697     // output args
4698     qCDebug(LOG_KTE) << "trigger attribute changed in line range " << lineRange << "needsRepaint" << needsRepaint;
4699 #endif
4700 
4701     // if we need repaint, we will need to collect the line ranges we will update
4702     if (needsRepaint && lineRange.isValid()) {
4703         if (m_lineToUpdateRange.isValid()) {
4704             m_lineToUpdateRange.expandToRange(lineRange);
4705         } else {
4706             m_lineToUpdateRange = lineRange;
4707         }
4708     }
4709 
4710     // first call => trigger later update of view via delayed signal to group updates
4711     if (!m_delayedUpdateTimer.isActive()) {
4712         m_delayedUpdateTimer.start();
4713     }
4714 }
4715 
4716 void KTextEditor::ViewPrivate::slotDelayedUpdateOfView()
4717 {
4718 #ifdef VIEW_RANGE_DEBUG
4719     // output args
4720     qCDebug(LOG_KTE) << "delayed attribute changed in line range" << m_lineToUpdateRange;
4721 #endif
4722     // update ranges in
4723     updateRangesIn(KTextEditor::Attribute::ActivateMouseIn);
4724     updateRangesIn(KTextEditor::Attribute::ActivateCaretIn);
4725 
4726     // update view, if valid line range, else only feedback update wanted anyway
4727     if (m_lineToUpdateRange.isValid()) {
4728         tagLines(m_lineToUpdateRange, true);
4729         updateView(true);
4730     }
4731 
4732     // reset flags
4733     m_lineToUpdateRange = KTextEditor::LineRange::invalid();
4734 }
4735 
4736 void KTextEditor::ViewPrivate::updateRangesIn(KTextEditor::Attribute::ActivationType activationType)
4737 {
4738     // new ranges with cursor in, default none
4739     QSet<Kate::TextRange *> newRangesIn;
4740 
4741     // on which range set we work?
4742     QSet<Kate::TextRange *> &oldSet = (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_rangesMouseIn : m_rangesCaretIn;
4743 
4744     // which cursor position to honor?
4745     KTextEditor::Cursor currentCursor =
4746         (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_viewInternal->mousePosition() : m_viewInternal->cursorPosition();
4747 
4748     // first: validate the remembered ranges
4749     QSet<Kate::TextRange *> validRanges;
4750     for (Kate::TextRange *range : std::as_const(oldSet)) {
4751         if (doc()->buffer().rangePointerValid(range)) {
4752             validRanges.insert(range);
4753         }
4754     }
4755 
4756     // cursor valid? else no new ranges can be found
4757     if (currentCursor.isValid() && currentCursor.line() < doc()->buffer().lines()) {
4758         // now: get current ranges for the line of cursor with an attribute
4759         const QList<Kate::TextRange *> rangesForCurrentCursor = doc()->buffer().rangesForLine(currentCursor.line(), this, false);
4760 
4761         // match which ranges really fit the given cursor
4762         for (Kate::TextRange *range : rangesForCurrentCursor) {
4763             // range has no dynamic attribute of right type and no feedback object
4764             auto attribute = range->attribute();
4765             if ((!attribute || !attribute->dynamicAttribute(activationType)) && !range->feedback()) {
4766                 continue;
4767             }
4768 
4769             // range doesn't contain cursor, not interesting
4770             if ((range->startInternal().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) ? (currentCursor < range->toRange().start())
4771                                                                                                      : (currentCursor <= range->toRange().start())) {
4772                 continue;
4773             }
4774 
4775             if ((range->endInternal().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) ? (range->toRange().end() <= currentCursor)
4776                                                                                                    : (range->toRange().end() < currentCursor)) {
4777                 continue;
4778             }
4779 
4780             // range contains cursor, was it already in old set?
4781             auto it = validRanges.find(range);
4782             if (it != validRanges.end()) {
4783                 // insert in new, remove from old, be done with it
4784                 newRangesIn.insert(range);
4785                 validRanges.erase(it);
4786                 continue;
4787             }
4788 
4789             // oh, new range, trigger update and insert into new set
4790             newRangesIn.insert(range);
4791 
4792             if (attribute && attribute->dynamicAttribute(activationType)) {
4793                 notifyAboutRangeChange(range->toLineRange(), true);
4794             }
4795 
4796             // feedback
4797             if (range->feedback()) {
4798                 if (activationType == KTextEditor::Attribute::ActivateMouseIn) {
4799                     range->feedback()->mouseEnteredRange(range, this);
4800                 } else {
4801                     range->feedback()->caretEnteredRange(range, this);
4802                     Q_EMIT caretChangedRange(this);
4803                 }
4804             }
4805 
4806 #ifdef VIEW_RANGE_DEBUG
4807             // found new range for activation
4808             qCDebug(LOG_KTE) << "activated new range" << range << "by" << activationType;
4809 #endif
4810         }
4811     }
4812 
4813     // now: notify for left ranges!
4814     for (Kate::TextRange *range : std::as_const(validRanges)) {
4815         // range valid + right dynamic attribute, trigger update
4816         if (range->toRange().isValid() && range->attribute() && range->attribute()->dynamicAttribute(activationType)) {
4817             notifyAboutRangeChange(range->toLineRange(), true);
4818         }
4819 
4820         // feedback
4821         if (range->feedback()) {
4822             if (activationType == KTextEditor::Attribute::ActivateMouseIn) {
4823                 range->feedback()->mouseExitedRange(range, this);
4824             } else {
4825                 range->feedback()->caretExitedRange(range, this);
4826                 Q_EMIT caretChangedRange(this);
4827             }
4828         }
4829     }
4830 
4831     // set new ranges
4832     oldSet = newRangesIn;
4833 }
4834 
4835 void KTextEditor::ViewPrivate::postMessage(KTextEditor::Message *message, QList<std::shared_ptr<QAction>> actions)
4836 {
4837     // just forward to KateMessageWidget :-)
4838     auto messageWidget = m_messageWidgets[message->position()];
4839     if (!messageWidget) {
4840         // this branch is used for: TopInView, CenterInView, and BottomInView
4841         messageWidget = new KateMessageWidget(m_viewInternal, true);
4842         m_messageWidgets[message->position()] = messageWidget;
4843         m_notificationLayout->addWidget(messageWidget, message->position());
4844         connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
4845         connect(this, &KTextEditor::ViewPrivate::cursorPositionChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
4846     }
4847     messageWidget->postMessage(message, std::move(actions));
4848 }
4849 
4850 KateMessageWidget *KTextEditor::ViewPrivate::messageWidget()
4851 {
4852     return m_messageWidgets[KTextEditor::Message::TopInView];
4853 }
4854 
4855 void KTextEditor::ViewPrivate::saveFoldingState()
4856 {
4857     m_savedFoldingState = m_textFolding.exportFoldingRanges();
4858 }
4859 
4860 void KTextEditor::ViewPrivate::clearFoldingState()
4861 {
4862     m_savedFoldingState = {};
4863 }
4864 
4865 void KTextEditor::ViewPrivate::applyFoldingState()
4866 {
4867     m_textFolding.importFoldingRanges(m_savedFoldingState);
4868     m_savedFoldingState = QJsonDocument();
4869 }
4870 
4871 void KTextEditor::ViewPrivate::exportHtmlToFile(const QString &file)
4872 {
4873     KateExporter(this).exportToFile(file);
4874 }
4875 
4876 void KTextEditor::ViewPrivate::exportHtmlToClipboard()
4877 {
4878     KateExporter(this).exportToClipboard();
4879 }
4880 
4881 void KTextEditor::ViewPrivate::exportHtmlToFile()
4882 {
4883     const QString file = QFileDialog::getSaveFileName(this, i18n("Export File as HTML"), doc()->documentName());
4884     if (!file.isEmpty()) {
4885         KateExporter(this).exportToFile(file);
4886     }
4887 }
4888 
4889 void KTextEditor::ViewPrivate::clearHighlights()
4890 {
4891     m_rangesForHighlights.clear();
4892     m_currentTextForHighlights.clear();
4893 }
4894 
4895 void KTextEditor::ViewPrivate::selectionChangedForHighlights()
4896 {
4897     QString text;
4898     // if text of selection is still the same, abort
4899     if (selection() && selectionRange().onSingleLine()) {
4900         text = selectionText();
4901         if (text == m_currentTextForHighlights) {
4902             return;
4903         }
4904     }
4905 
4906     // text changed: remove all highlights + create new ones
4907     // (do not call clearHighlights(), since this also resets the m_currentTextForHighlights
4908     m_rangesForHighlights.clear();
4909 
4910     // do not highlight strings with leading and trailing spaces
4911     if (!text.isEmpty() && (text.at(0).isSpace() || text.at(text.length() - 1).isSpace())) {
4912         return;
4913     }
4914 
4915     // trigger creation of ranges for current view range
4916     m_currentTextForHighlights = text;
4917     createHighlights();
4918 }
4919 
4920 void KTextEditor::ViewPrivate::createHighlights()
4921 {
4922     // do nothing if no text to highlight
4923     if (m_currentTextForHighlights.isEmpty()) {
4924         return;
4925     }
4926 
4927     // clear existing highlighting ranges, otherwise we stack over and over the same ones eventually
4928     m_rangesForHighlights.clear();
4929 
4930     KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute());
4931     attr->setBackground(Qt::yellow);
4932 
4933     // set correct highlight color from Kate's color schema
4934     QColor fgColor = defaultStyleAttribute(KSyntaxHighlighting::Theme::TextStyle::Normal)->foreground().color();
4935     QColor bgColor = rendererConfig()->searchHighlightColor();
4936     attr->setForeground(fgColor);
4937     attr->setBackground(bgColor);
4938 
4939     KTextEditor::Cursor start(visibleRange().start());
4940     KTextEditor::Range searchRange;
4941 
4942     // only add word boundary if we can find the text then
4943     // fixes $lala hl
4944     QString pattern = QRegularExpression::escape(m_currentTextForHighlights);
4945     if (m_currentTextForHighlights.contains(QRegularExpression(QLatin1String("\\b") + pattern, QRegularExpression::UseUnicodePropertiesOption))) {
4946         pattern.prepend(QLatin1String("\\b"));
4947     }
4948 
4949     if (m_currentTextForHighlights.contains(QRegularExpression(pattern + QLatin1String("\\b"), QRegularExpression::UseUnicodePropertiesOption))) {
4950         pattern += QLatin1String("\\b");
4951     }
4952 
4953     QList<KTextEditor::Range> matches;
4954     do {
4955         searchRange.setRange(start, visibleRange().end());
4956 
4957         matches = doc()->searchText(searchRange, pattern, KTextEditor::Regex);
4958 
4959         if (matches.first().isValid()) {
4960             if (matches.first() != selectionRange()) {
4961                 std::unique_ptr<KTextEditor::MovingRange> mr(doc()->newMovingRange(matches.first()));
4962                 mr->setZDepth(-90000.0); // Set the z-depth to slightly worse than the selection
4963                 mr->setAttribute(attr);
4964                 mr->setView(this);
4965                 mr->setAttributeOnlyForViews(true);
4966                 m_rangesForHighlights.push_back(std::move(mr));
4967             }
4968             start = matches.first().end();
4969         }
4970     } while (matches.first().isValid());
4971 }
4972 
4973 KateAbstractInputMode *KTextEditor::ViewPrivate::currentInputMode() const
4974 {
4975     return m_viewInternal->m_currentInputMode;
4976 }
4977 
4978 void KTextEditor::ViewPrivate::toggleInputMode()
4979 {
4980     if (QAction *a = qobject_cast<QAction *>(sender())) {
4981         setInputMode(static_cast<KTextEditor::View::InputMode>(a->data().toInt()));
4982     }
4983 }
4984 
4985 void KTextEditor::ViewPrivate::cycleInputMode()
4986 {
4987     InputMode current = currentInputMode()->viewInputMode();
4988     InputMode to = (current == KTextEditor::View::NormalInputMode) ? KTextEditor::View::ViInputMode : KTextEditor::View::NormalInputMode;
4989     setInputMode(to);
4990 }
4991 
4992 // BEGIN KTextEditor::PrintInterface stuff
4993 bool KTextEditor::ViewPrivate::print()
4994 {
4995     return KatePrinter::print(this);
4996 }
4997 
4998 void KTextEditor::ViewPrivate::printPreview()
4999 {
5000     KatePrinter::printPreview(this);
5001 }
5002 
5003 // END
5004 
5005 // BEGIN Inline Note Interface
5006 void KTextEditor::ViewPrivate::registerInlineNoteProvider(KTextEditor::InlineNoteProvider *provider)
5007 {
5008     if (std::find(m_inlineNoteProviders.cbegin(), m_inlineNoteProviders.cend(), provider) == m_inlineNoteProviders.cend()) {
5009         m_inlineNoteProviders.push_back(provider);
5010 
5011         connect(provider, &KTextEditor::InlineNoteProvider::inlineNotesReset, this, &KTextEditor::ViewPrivate::inlineNotesReset);
5012         connect(provider, &KTextEditor::InlineNoteProvider::inlineNotesChanged, this, &KTextEditor::ViewPrivate::inlineNotesLineChanged);
5013 
5014         inlineNotesReset();
5015     }
5016 }
5017 
5018 void KTextEditor::ViewPrivate::unregisterInlineNoteProvider(KTextEditor::InlineNoteProvider *provider)
5019 {
5020     auto it = std::find(m_inlineNoteProviders.cbegin(), m_inlineNoteProviders.cend(), provider);
5021     if (it != m_inlineNoteProviders.cend()) {
5022         m_inlineNoteProviders.erase(it);
5023         provider->disconnect(this);
5024 
5025         inlineNotesReset();
5026     }
5027 }
5028 
5029 QVarLengthArray<KateInlineNoteData, 8> KTextEditor::ViewPrivate::inlineNotes(int line) const
5030 {
5031     QVarLengthArray<KateInlineNoteData, 8> allInlineNotes;
5032     for (KTextEditor::InlineNoteProvider *provider : m_inlineNoteProviders) {
5033         int index = 0;
5034         const auto columns = provider->inlineNotes(line);
5035         for (int column : columns) {
5036             const bool underMouse = Cursor(line, column) == m_viewInternal->m_activeInlineNote.m_position;
5037             KateInlineNoteData note =
5038                 {provider, this, {line, column}, index, underMouse, m_viewInternal->renderer()->currentFont(), m_viewInternal->renderer()->lineHeight()};
5039             allInlineNotes.append(note);
5040             index++;
5041         }
5042     }
5043     return allInlineNotes;
5044 }
5045 
5046 QRect KTextEditor::ViewPrivate::inlineNoteRect(const KateInlineNoteData &note) const
5047 {
5048     return m_viewInternal->inlineNoteRect(note);
5049 }
5050 
5051 void KTextEditor::ViewPrivate::inlineNotesReset()
5052 {
5053     m_viewInternal->m_activeInlineNote = {};
5054     tagLines(KTextEditor::LineRange(0, doc()->lastLine()), true);
5055 }
5056 
5057 void KTextEditor::ViewPrivate::inlineNotesLineChanged(int line)
5058 {
5059     if (line == m_viewInternal->m_activeInlineNote.m_position.line()) {
5060         m_viewInternal->m_activeInlineNote = {};
5061     }
5062     tagLines({line, line}, true);
5063 }
5064 
5065 // END Inline Note Interface
5066 
5067 KTextEditor::Attribute::Ptr KTextEditor::ViewPrivate::defaultStyleAttribute(KSyntaxHighlighting::Theme::TextStyle defaultStyle) const
5068 {
5069     KateRendererConfig *renderConfig = const_cast<KTextEditor::ViewPrivate *>(this)->rendererConfig();
5070 
5071     KTextEditor::Attribute::Ptr style = doc()->highlight()->attributes(renderConfig->schema()).at(defaultStyle);
5072     if (!style->hasProperty(QTextFormat::BackgroundBrush)) {
5073         // make sure the returned style has the default background color set
5074         style = new KTextEditor::Attribute(*style);
5075         style->setBackground(QBrush(renderConfig->backgroundColor()));
5076     }
5077     return style;
5078 }
5079 
5080 QList<KTextEditor::AttributeBlock> KTextEditor::ViewPrivate::lineAttributes(int line)
5081 {
5082     QList<KTextEditor::AttributeBlock> attribs;
5083 
5084     if (line < 0 || line >= doc()->lines()) {
5085         return attribs;
5086     }
5087 
5088     const Kate::TextLine kateLine = doc()->kateTextLine(line);
5089     const auto &intAttrs = kateLine.attributesList();
5090     for (qsizetype i = 0; i < intAttrs.size(); ++i) {
5091         if (intAttrs[i].length > 0 && intAttrs[i].attributeValue > 0) {
5092             attribs << KTextEditor::AttributeBlock(intAttrs.at(i).offset, intAttrs.at(i).length, renderer()->attribute(intAttrs.at(i).attributeValue));
5093         }
5094     }
5095 
5096     return attribs;
5097 }
5098 
5099 #include "moc_kateview.cpp"