1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 2009 Michel Ludwig <michel.ludwig@kdemail.net>
4     SPDX-FileCopyrightText: 2007 Mirko Stocker <me@misto.ch>
5     SPDX-FileCopyrightText: 2003 Hamish Rodda <rodda@kde.org>
6     SPDX-FileCopyrightText: 2002 John Firebaugh <jfirebaugh@kde.org>
7     SPDX-FileCopyrightText: 2001-2004 Christoph Cullmann <cullmann@kde.org>
8     SPDX-FileCopyrightText: 2001-2010 Joseph Wenninger <jowenn@kde.org>
9     SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
10 
11     SPDX-License-Identifier: LGPL-2.0-only
12 */
13 
14 // BEGIN includes
15 #include "kateview.h"
16 
17 #include "export/exporter.h"
18 #include "inlinenotedata.h"
19 #include "kateabstractinputmode.h"
20 #include "kateautoindent.h"
21 #include "katebookmarks.h"
22 #include "katebuffer.h"
23 #include "katecompletionwidget.h"
24 #include "kateconfig.h"
25 #include "katedialogs.h"
26 #include "katedocument.h"
27 #include "kateglobal.h"
28 #include "katehighlight.h"
29 #include "katehighlightmenu.h"
30 #include "katekeywordcompletion.h"
31 #include "katelayoutcache.h"
32 #include "katemessagewidget.h"
33 #include "katemodemenu.h"
34 #include "katepartdebug.h"
35 #include "katestatusbar.h"
36 #include "katetemplatehandler.h"
37 #include "katetextline.h"
38 #include "kateundomanager.h"
39 #include "kateviewhelpers.h"
40 #include "kateviewinternal.h"
41 #include "katewordcompletion.h"
42 #include "printing/kateprinter.h"
43 #include "script/katescriptaction.h"
44 #include "script/katescriptmanager.h"
45 #include "spellcheck/spellcheck.h"
46 #include "spellcheck/spellcheckdialog.h"
47 #include "spellcheck/spellingmenu.h"
48 
49 #include <KTextEditor/Message>
50 #include <ktexteditor/inlinenoteprovider.h>
51 
52 #include <KParts/Event>
53 
54 #include <KActionCollection>
55 #include <KCharsets>
56 #include <KConfig>
57 #include <KConfigGroup>
58 #include <KCursor>
59 #include <KMessageBox>
60 #include <KSelectAction>
61 #include <KStandardAction>
62 #include <KStandardShortcut>
63 #include <KToggleAction>
64 #include <KXMLGUIFactory>
65 
66 #include <QApplication>
67 #include <QClipboard>
68 #include <QFileDialog>
69 #include <QFileInfo>
70 #include <QFont>
71 #include <QKeyEvent>
72 #include <QLayout>
73 #include <QMimeData>
74 #include <QPainter>
75 #include <QRegularExpression>
76 #include <QToolTip>
77 
78 //#define VIEW_RANGE_DEBUG
79 
80 // END includes
81 
82 namespace
83 {
hasCommentInFirstLine(KTextEditor::DocumentPrivate * doc)84 bool hasCommentInFirstLine(KTextEditor::DocumentPrivate *doc)
85 {
86     const Kate::TextLine &line = doc->kateTextLine(0);
87     Q_ASSERT(line);
88     return doc->isComment(0, line->firstChar());
89 }
90 
91 }
92 
blockFix(KTextEditor::Range & range)93 void KTextEditor::ViewPrivate::blockFix(KTextEditor::Range &range)
94 {
95     if (range.start().column() > range.end().column()) {
96         int tmp = range.start().column();
97         range.setStart(KTextEditor::Cursor(range.start().line(), range.end().column()));
98         range.setEnd(KTextEditor::Cursor(range.end().line(), tmp));
99     }
100 }
101 
ViewPrivate(KTextEditor::DocumentPrivate * doc,QWidget * parent,KTextEditor::MainWindow * mainWindow)102 KTextEditor::ViewPrivate::ViewPrivate(KTextEditor::DocumentPrivate *doc, QWidget *parent, KTextEditor::MainWindow *mainWindow)
103     : KTextEditor::View(this, parent)
104     , m_completionWidget(nullptr)
105     , m_annotationModel(nullptr)
106     , m_hasWrap(false)
107     , m_doc(doc)
108     , m_textFolding(doc->buffer())
109     , m_config(new KateViewConfig(this))
110     , m_renderer(new KateRenderer(doc, m_textFolding, this))
111     , m_viewInternal(new KateViewInternal(this))
112     , m_spell(new KateSpellCheckDialog(this))
113     , m_bookmarks(new KateBookmarks(this))
114     , m_topSpacer(new QSpacerItem(0, 0))
115     , m_leftSpacer(new QSpacerItem(0, 0))
116     , m_rightSpacer(new QSpacerItem(0, 0))
117     , m_bottomSpacer(new QSpacerItem(0, 0))
118     , m_startingUp(true)
119     , m_updatingDocumentConfig(false)
120     , m_selection(m_doc->buffer(), KTextEditor::Range::invalid(), Kate::TextRange::ExpandLeft, Kate::TextRange::AllowEmpty)
121     , blockSelect(false)
122     , m_bottomViewBar(nullptr)
123     , m_gotoBar(nullptr)
124     , m_dictionaryBar(nullptr)
125     , m_spellingMenu(new KateSpellingMenu(this))
126     , m_userContextMenuSet(false)
127     , m_lineToUpdateRange(KTextEditor::LineRange::invalid())
128     , m_mainWindow(mainWindow ? mainWindow : KTextEditor::EditorPrivate::self()->dummyMainWindow()) // use dummy window if no window there!
129     , m_statusBar(nullptr)
130     , m_temporaryAutomaticInvocationDisabled(false)
131     , m_autoFoldedFirstLine(false)
132 {
133     // queued connect to collapse view updates for range changes, INIT THIS EARLY ENOUGH!
134     connect(this, &KTextEditor::ViewPrivate::delayedUpdateOfView, this, &KTextEditor::ViewPrivate::slotDelayedUpdateOfView, Qt::QueuedConnection);
135 
136     m_delayedUpdateTimer.setSingleShot(true);
137     m_delayedUpdateTimer.setInterval(0);
138     connect(&m_delayedUpdateTimer, &QTimer::timeout, this, &KTextEditor::ViewPrivate::delayedUpdateOfView);
139 
140     KXMLGUIClient::setComponentName(KTextEditor::EditorPrivate::self()->aboutData().componentName(),
141                                     KTextEditor::EditorPrivate::self()->aboutData().displayName());
142 
143     // selection if for this view only and will invalidate if becoming empty
144     m_selection.setView(this);
145 
146     // use z depth defined in moving ranges interface
147     m_selection.setZDepth(-100000.0);
148 
149     KTextEditor::EditorPrivate::self()->registerView(this);
150 
151     // try to let the main window, if any, create a view bar for this view
152     QWidget *bottomBarParent = m_mainWindow->createViewBar(this);
153 
154     m_bottomViewBar = new KateViewBar(bottomBarParent != nullptr, bottomBarParent ? bottomBarParent : this, this);
155 
156     // ugly workaround:
157     // Force the layout to be left-to-right even on RTL desktop, as discussed
158     // on the mailing list. This will cause the lines and icons panel to be on
159     // the left, even for Arabic/Hebrew/Farsi/whatever users.
160     setLayoutDirection(Qt::LeftToRight);
161 
162     m_bottomViewBar->installEventFilter(m_viewInternal);
163 
164     // add KateMessageWidget for KTE::MessageInterface immediately above view
165     m_messageWidgets[KTextEditor::Message::AboveView] = new KateMessageWidget(this);
166     m_messageWidgets[KTextEditor::Message::AboveView]->hide();
167 
168     // add KateMessageWidget for KTE::MessageInterface immediately above view
169     m_messageWidgets[KTextEditor::Message::BelowView] = new KateMessageWidget(this);
170     m_messageWidgets[KTextEditor::Message::BelowView]->hide();
171 
172     // add bottom viewbar...
173     if (bottomBarParent) {
174         m_mainWindow->addWidgetToViewBar(this, m_bottomViewBar);
175     }
176 
177     // add layout for floating message widgets to KateViewInternal
178     m_notificationLayout = new KateMessageLayout(m_viewInternal);
179     m_notificationLayout->setContentsMargins(20, 20, 20, 20);
180     m_viewInternal->setLayout(m_notificationLayout);
181 
182     // this really is needed :)
183     m_viewInternal->updateView();
184 
185     doc->addView(this);
186 
187     setFocusProxy(m_viewInternal);
188     setFocusPolicy(Qt::StrongFocus);
189 
190     setXMLFile(QStringLiteral("katepart5ui.rc"));
191 
192     setupConnections();
193     setupActions();
194 
195     // auto word completion
196     new KateWordCompletionView(this, actionCollection());
197 
198     // update the enabled state of the undo/redo actions...
199     slotUpdateUndo();
200 
201     // create the status bar of this view
202     // do this after action creation, we use some of them!
203     toggleStatusBar();
204 
205     m_startingUp = false;
206     updateConfig();
207 
208     slotHlChanged();
209     KCursor::setAutoHideCursor(m_viewInternal, true);
210 
211     for (auto messageWidget : m_messageWidgets) {
212         if (messageWidget) {
213             // user interaction (scrolling) starts notification auto-hide timer
214             connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
215 
216             // user interaction (cursor navigation) starts notification auto-hide timer
217             connect(this, &KTextEditor::ViewPrivate::cursorPositionChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
218         }
219     }
220 
221     // folding restoration on reload
222     connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::saveFoldingState);
223     connect(m_doc, &KTextEditor::DocumentPrivate::reloaded, this, &KTextEditor::ViewPrivate::applyFoldingState);
224 
225     connect(m_doc, &KTextEditor::DocumentPrivate::reloaded, this, &KTextEditor::ViewPrivate::slotDocumentReloaded);
226     connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::slotDocumentAboutToReload);
227 
228     // update highlights on scrolling and co
229     connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, this, &KTextEditor::ViewPrivate::createHighlights);
230 
231     // clear highlights on reload
232     connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::clearHighlights);
233 
234     // setup layout
235     setupLayout();
236 }
237 
~ViewPrivate()238 KTextEditor::ViewPrivate::~ViewPrivate()
239 {
240     // de-register views early from global collections
241     // otherwise we might "use" them again during destruction in a half-valid state
242     // see e.g. bug 422546
243     // Kate::TextBuffer::notifyAboutRangeChange will access views() in a chain during
244     // deletion of m_viewInternal
245     doc()->removeView(this);
246     KTextEditor::EditorPrivate::self()->deregisterView(this);
247 
248     // remove from xmlgui factory, to be safe
249     if (factory()) {
250         factory()->removeClient(this);
251     }
252 
253     // delete internal view before view bar!
254     delete m_viewInternal;
255 
256     // remove view bar again, if needed
257     m_mainWindow->deleteViewBar(this);
258     m_bottomViewBar = nullptr;
259 
260     delete m_renderer;
261 
262     delete m_config;
263 }
264 
toggleStatusBar()265 void KTextEditor::ViewPrivate::toggleStatusBar()
266 {
267     // if there, delete it
268     if (m_statusBar) {
269         bottomViewBar()->removePermanentBarWidget(m_statusBar);
270         delete m_statusBar;
271         m_statusBar = nullptr;
272         Q_EMIT statusBarEnabledChanged(this, false);
273         return;
274     }
275 
276     // else: create it
277     m_statusBar = new KateStatusBar(this);
278     bottomViewBar()->addPermanentBarWidget(m_statusBar);
279     Q_EMIT statusBarEnabledChanged(this, true);
280 }
281 
setupLayout()282 void KTextEditor::ViewPrivate::setupLayout()
283 {
284     // delete old layout if any
285     if (layout()) {
286         delete layout();
287 
288         //  need to recreate spacers because they are deleted with
289         //  their belonging layout
290         m_topSpacer = new QSpacerItem(0, 0);
291         m_leftSpacer = new QSpacerItem(0, 0);
292         m_rightSpacer = new QSpacerItem(0, 0);
293         m_bottomSpacer = new QSpacerItem(0, 0);
294     }
295 
296     // set margins
297     QStyleOptionFrame opt;
298     opt.initFrom(this);
299     opt.frameShape = QFrame::StyledPanel;
300     opt.state |= QStyle::State_Sunken;
301     const int margin = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this);
302     m_topSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
303     m_leftSpacer->changeSize(margin, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
304     m_rightSpacer->changeSize(margin, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
305     m_bottomSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
306 
307     // define layout
308     QGridLayout *layout = new QGridLayout(this);
309     layout->setContentsMargins(0, 0, 0, 0);
310     layout->setSpacing(0);
311 
312     const bool frameAroundContents = style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, this);
313     if (frameAroundContents) {
314         // top message widget
315         layout->addWidget(m_messageWidgets[KTextEditor::Message::AboveView], 0, 0, 1, 5);
316 
317         // top spacer
318         layout->addItem(m_topSpacer, 1, 0, 1, 4);
319 
320         // left spacer
321         layout->addItem(m_leftSpacer, 2, 0, 1, 1);
322 
323         // left border
324         layout->addWidget(m_viewInternal->m_leftBorder, 2, 1, 1, 1);
325 
326         // view
327         layout->addWidget(m_viewInternal, 2, 2, 1, 1);
328 
329         // right spacer
330         layout->addItem(m_rightSpacer, 2, 3, 1, 1);
331 
332         // bottom spacer
333         layout->addItem(m_bottomSpacer, 3, 0, 1, 4);
334 
335         // vertical scrollbar
336         layout->addWidget(m_viewInternal->m_lineScroll, 1, 4, 3, 1);
337 
338         // horizontal scrollbar
339         layout->addWidget(m_viewInternal->m_columnScroll, 4, 0, 1, 4);
340 
341         // dummy
342         layout->addWidget(m_viewInternal->m_dummy, 4, 4, 1, 1);
343 
344         // bottom message
345         layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], 5, 0, 1, 5);
346 
347         // bottom viewbar
348         if (m_bottomViewBar->parentWidget() == this) {
349             layout->addWidget(m_bottomViewBar, 6, 0, 1, 5);
350         }
351 
352         // stretch
353         layout->setColumnStretch(2, 1);
354         layout->setRowStretch(2, 1);
355 
356         // adjust scrollbar background
357         m_viewInternal->m_lineScroll->setBackgroundRole(QPalette::Window);
358         m_viewInternal->m_lineScroll->setAutoFillBackground(false);
359 
360         m_viewInternal->m_columnScroll->setBackgroundRole(QPalette::Window);
361         m_viewInternal->m_columnScroll->setAutoFillBackground(false);
362 
363     } else {
364         // top message widget
365         layout->addWidget(m_messageWidgets[KTextEditor::Message::AboveView], 0, 0, 1, 5);
366 
367         // top spacer
368         layout->addItem(m_topSpacer, 1, 0, 1, 5);
369 
370         // left spacer
371         layout->addItem(m_leftSpacer, 2, 0, 1, 1);
372 
373         // left border
374         layout->addWidget(m_viewInternal->m_leftBorder, 2, 1, 1, 1);
375 
376         // view
377         layout->addWidget(m_viewInternal, 2, 2, 1, 1);
378 
379         // vertical scrollbar
380         layout->addWidget(m_viewInternal->m_lineScroll, 2, 3, 1, 1);
381 
382         // right spacer
383         layout->addItem(m_rightSpacer, 2, 4, 1, 1);
384 
385         // horizontal scrollbar
386         layout->addWidget(m_viewInternal->m_columnScroll, 3, 1, 1, 2);
387 
388         // dummy
389         layout->addWidget(m_viewInternal->m_dummy, 3, 3, 1, 1);
390 
391         // bottom spacer
392         layout->addItem(m_bottomSpacer, 4, 0, 1, 5);
393 
394         // bottom message
395         layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], 5, 0, 1, 5);
396 
397         // bottom viewbar
398         if (m_bottomViewBar->parentWidget() == this) {
399             layout->addWidget(m_bottomViewBar, 6, 0, 1, 5);
400         }
401 
402         // stretch
403         layout->setColumnStretch(2, 1);
404         layout->setRowStretch(2, 1);
405 
406         // adjust scrollbar background
407         m_viewInternal->m_lineScroll->setBackgroundRole(QPalette::Base);
408         m_viewInternal->m_lineScroll->setAutoFillBackground(true);
409 
410         m_viewInternal->m_columnScroll->setBackgroundRole(QPalette::Base);
411         m_viewInternal->m_columnScroll->setAutoFillBackground(true);
412     }
413 }
414 
setupConnections()415 void KTextEditor::ViewPrivate::setupConnections()
416 {
417     connect(m_doc, &KTextEditor::DocumentPrivate::undoChanged, this, &KTextEditor::ViewPrivate::slotUpdateUndo);
418     connect(m_doc, &KTextEditor::DocumentPrivate::highlightingModeChanged, this, &KTextEditor::ViewPrivate::slotHlChanged);
419     connect(m_doc, &KTextEditor::DocumentPrivate::canceled, this, &KTextEditor::ViewPrivate::slotSaveCanceled);
420     connect(m_viewInternal, &KateViewInternal::dropEventPass, this, &KTextEditor::ViewPrivate::dropEventPass);
421 
422     connect(m_doc, &KTextEditor::DocumentPrivate::annotationModelChanged, m_viewInternal->m_leftBorder, &KateIconBorder::annotationModelChanged);
423 }
424 
goToPreviousEditingPosition()425 void KTextEditor::ViewPrivate::goToPreviousEditingPosition()
426 {
427     auto c = doc()->lastEditingPosition(KTextEditor::DocumentPrivate::Previous, cursorPosition());
428     if (c.isValid()) {
429         setCursorPosition(c);
430     }
431 }
432 
goToNextEditingPosition()433 void KTextEditor::ViewPrivate::goToNextEditingPosition()
434 {
435     auto c = doc()->lastEditingPosition(KTextEditor::DocumentPrivate::Next, cursorPosition());
436     if (c.isValid()) {
437         setCursorPosition(c);
438     }
439 }
setupActions()440 void KTextEditor::ViewPrivate::setupActions()
441 {
442     KActionCollection *ac = actionCollection();
443     QAction *a;
444 
445     m_toggleWriteLock = nullptr;
446 
447     m_cut = a = ac->addAction(KStandardAction::Cut, this, SLOT(cut()));
448     a->setWhatsThis(i18n("Cut the selected text and move it to the clipboard"));
449 
450     m_paste = a = ac->addAction(KStandardAction::Paste, this, SLOT(paste()));
451     a->setWhatsThis(i18n("Paste previously copied or cut clipboard contents"));
452 
453     m_copy = a = ac->addAction(KStandardAction::Copy, this, SLOT(copy()));
454     a->setWhatsThis(i18n("Use this command to copy the currently selected text to the system clipboard."));
455 
456     m_pasteMenu = ac->addAction(QStringLiteral("edit_paste_menu"), new KatePasteMenu(i18n("Clipboard &History"), this));
457     connect(KTextEditor::EditorPrivate::self(),
458             &KTextEditor::EditorPrivate::clipboardHistoryChanged,
459             this,
460             &KTextEditor::ViewPrivate::slotClipboardHistoryChanged);
461 
462     if (QApplication::clipboard()->supportsSelection()) {
463         m_pasteSelection = a = ac->addAction(QStringLiteral("edit_paste_selection"), this, SLOT(pasteSelection()));
464         a->setText(i18n("Paste Selection"));
465         ac->setDefaultShortcuts(a, KStandardShortcut::pasteSelection());
466         a->setWhatsThis(i18n("Paste previously mouse selection contents"));
467     }
468 
469     m_swapWithClipboard = a = ac->addAction(QStringLiteral("edit_swap_with_clipboard"), this, SLOT(swapWithClipboard()));
470     a->setText(i18n("Swap with clipboard contents"));
471     a->setWhatsThis(i18n("Swap the selected text with the clipboard contents"));
472 
473     if (!doc()->readOnly()) {
474         a = ac->addAction(KStandardAction::Save, m_doc, SLOT(documentSave()));
475         a->setWhatsThis(i18n("Save the current document"));
476 
477         a = m_editUndo = ac->addAction(KStandardAction::Undo, m_doc, SLOT(undo()));
478         a->setWhatsThis(i18n("Revert the most recent editing actions"));
479 
480         a = m_editRedo = ac->addAction(KStandardAction::Redo, m_doc, SLOT(redo()));
481         a->setWhatsThis(i18n("Revert the most recent undo operation"));
482 
483         // Tools > Scripts
484         // stored inside scoped pointer to ensure we destruct it early enough as it does internal cleanups of other child objects
485         m_scriptActionMenu.reset(new KateScriptActionMenu(this, i18n("&Scripts")));
486         ac->addAction(QStringLiteral("tools_scripts"), m_scriptActionMenu.data());
487 
488         a = ac->addAction(QStringLiteral("tools_apply_wordwrap"));
489         a->setText(i18n("Apply &Word Wrap"));
490         a->setWhatsThis(
491             i18n("Use this to wrap the current line, or to reformat the selected lines as paragraph, "
492                  "to fit the 'Wrap words at' setting in the configuration dialog.<br /><br />"
493                  "This is a static word wrap, meaning the document is changed."));
494         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::applyWordWrap);
495 
496         a = ac->addAction(QStringLiteral("tools_cleanIndent"));
497         a->setText(i18n("&Clean Indentation"));
498         a->setWhatsThis(
499             i18n("Use this to clean the indentation of a selected block of text (only tabs/only spaces).<br /><br />"
500                  "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
501         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cleanIndent);
502 
503         a = ac->addAction(QStringLiteral("tools_align"));
504         a->setText(i18n("&Align"));
505         a->setWhatsThis(i18n("Use this to align the current line or block of text to its proper indent level."));
506         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::align);
507 
508         a = ac->addAction(QStringLiteral("tools_comment"));
509         a->setText(i18n("C&omment"));
510         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_D));
511         a->setWhatsThis(
512             i18n("This command comments out the current line or a selected block of text.<br /><br />"
513                  "The characters for single/multiple line comments are defined within the language's highlighting."));
514         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::comment);
515 
516         a = ac->addAction(QStringLiteral("Previous Editing Line"));
517         a->setText(i18n("Go to previous editing line"));
518         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_E));
519         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::goToPreviousEditingPosition);
520 
521         a = ac->addAction(QStringLiteral("Next Editing Line"));
522         a->setText(i18n("Go to next editing line"));
523         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_E));
524         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::goToNextEditingPosition);
525 
526         a = ac->addAction(QStringLiteral("tools_uncomment"));
527         a->setText(i18n("Unco&mment"));
528         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_D));
529         a->setWhatsThis(
530             i18n("This command removes comments from the current line or a selected block of text.<br /><br />"
531                  "The characters for single/multiple line comments are defined within the language's highlighting."));
532         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::uncomment);
533 
534         a = ac->addAction(QStringLiteral("tools_toggle_comment"));
535         a->setText(i18n("Toggle Comment"));
536         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_Slash));
537         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleComment);
538 
539         a = m_toggleWriteLock = new KToggleAction(i18n("&Read Only Mode"), this);
540         a->setWhatsThis(i18n("Lock/unlock the document for writing"));
541         a->setChecked(!doc()->isReadWrite());
542         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleWriteLock);
543         ac->addAction(QStringLiteral("tools_toggle_write_lock"), a);
544 
545         a = ac->addAction(QStringLiteral("tools_uppercase"));
546         a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-uppercase")));
547         a->setText(i18n("Uppercase"));
548         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_U));
549         a->setWhatsThis(
550             i18n("Convert the selection to uppercase, or the character to the "
551                  "right of the cursor if no text is selected."));
552         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::uppercase);
553 
554         a = ac->addAction(QStringLiteral("tools_lowercase"));
555         a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-lowercase")));
556         a->setText(i18n("Lowercase"));
557         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_U));
558         a->setWhatsThis(
559             i18n("Convert the selection to lowercase, or the character to the "
560                  "right of the cursor if no text is selected."));
561         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::lowercase);
562 
563         a = ac->addAction(QStringLiteral("tools_capitalize"));
564         a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-capitalize")));
565         a->setText(i18n("Capitalize"));
566         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_U));
567         a->setWhatsThis(
568             i18n("Capitalize the selection, or the word under the "
569                  "cursor if no text is selected."));
570         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::capitalize);
571 
572         a = ac->addAction(QStringLiteral("tools_join_lines"));
573         a->setText(i18n("Join Lines"));
574         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_J));
575         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::joinLines);
576 
577         a = ac->addAction(QStringLiteral("tools_invoke_code_completion"));
578         a->setText(i18n("Invoke Code Completion"));
579         a->setWhatsThis(i18n("Manually invoke command completion, usually by using a shortcut bound to this action."));
580         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_Space));
581         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::userInvokedCompletion);
582     } else {
583         for (auto *action : {m_cut, m_paste, m_pasteMenu, m_swapWithClipboard}) {
584             action->setEnabled(false);
585         }
586 
587         if (m_pasteSelection) {
588             m_pasteSelection->setEnabled(false);
589         }
590 
591         m_editUndo = nullptr;
592         m_editRedo = nullptr;
593     }
594 
595     a = ac->addAction(KStandardAction::Print, this, SLOT(print()));
596     a->setWhatsThis(i18n("Print the current document."));
597 
598     a = ac->addAction(KStandardAction::PrintPreview, this, SLOT(printPreview()));
599     a->setWhatsThis(i18n("Show print preview of current document"));
600 
601     a = ac->addAction(QStringLiteral("file_reload"));
602     a->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
603     a->setText(i18n("Reloa&d"));
604     ac->setDefaultShortcuts(a, KStandardShortcut::reload());
605     a->setWhatsThis(i18n("Reload the current document from disk."));
606     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::reloadFile);
607 
608     a = ac->addAction(KStandardAction::SaveAs, m_doc, SLOT(documentSaveAs()));
609     a->setWhatsThis(i18n("Save the current document to disk, with a name of your choice."));
610 
611     a = new KateViewEncodingAction(m_doc, this, i18n("Save As with Encoding..."), this, true /* special mode for save as */);
612     a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
613     ac->addAction(QStringLiteral("file_save_as_with_encoding"), a);
614 
615     a = ac->addAction(QStringLiteral("file_save_copy_as"));
616     a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
617     a->setText(i18n("Save &Copy As..."));
618     a->setWhatsThis(i18n("Save a copy of the current document to disk."));
619     connect(a, &QAction::triggered, m_doc, &KTextEditor::DocumentPrivate::documentSaveCopyAs);
620 
621     a = ac->addAction(KStandardAction::GotoLine, this, SLOT(gotoLine()));
622     a->setWhatsThis(i18n("This command opens a dialog and lets you choose a line that you want the cursor to move to."));
623 
624     a = ac->addAction(QStringLiteral("modified_line_up"));
625     a->setText(i18n("Move to Previous Modified Line"));
626     a->setWhatsThis(i18n("Move upwards to the previous modified line."));
627     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toPrevModifiedLine);
628 
629     a = ac->addAction(QStringLiteral("modified_line_down"));
630     a->setText(i18n("Move to Next Modified Line"));
631     a->setWhatsThis(i18n("Move downwards to the next modified line."));
632     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toNextModifiedLine);
633 
634     a = ac->addAction(QStringLiteral("set_confdlg"));
635     a->setText(i18n("&Configure Editor..."));
636     a->setIcon(QIcon::fromTheme(QStringLiteral("preferences-other")));
637     a->setWhatsThis(i18n("Configure various aspects of this editor."));
638     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotConfigDialog);
639 
640     m_modeAction = new KateModeMenu(i18n("&Mode"), this);
641     ac->addAction(QStringLiteral("tools_mode"), m_modeAction);
642     m_modeAction->setWhatsThis(i18n(
643         "Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example."));
644     m_modeAction->updateMenu(m_doc);
645 
646     KateHighlightingMenu *menu = new KateHighlightingMenu(i18n("&Highlighting"), this);
647     ac->addAction(QStringLiteral("tools_highlighting"), menu);
648     menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted."));
649     menu->updateMenu(m_doc);
650 
651     KateViewSchemaAction *schemaMenu = new KateViewSchemaAction(i18n("&Color Theme"), this);
652     ac->addAction(QStringLiteral("view_schemas"), schemaMenu);
653     schemaMenu->updateMenu(this);
654 
655     // indentation menu
656     KateViewIndentationAction *indentMenu = new KateViewIndentationAction(m_doc, i18n("&Indentation"), this);
657     ac->addAction(QStringLiteral("tools_indentation"), indentMenu);
658 
659     m_selectAll = a = ac->addAction(KStandardAction::SelectAll, this, SLOT(selectAll()));
660     a->setWhatsThis(i18n("Select the entire text of the current document."));
661 
662     m_deSelect = a = ac->addAction(KStandardAction::Deselect, this, SLOT(clearSelection()));
663     a->setWhatsThis(i18n("If you have selected something within the current document, this will no longer be selected."));
664 
665     a = ac->addAction(QStringLiteral("view_inc_font_sizes"));
666     a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
667     a->setText(i18n("Enlarge Font"));
668     ac->setDefaultShortcuts(a, KStandardShortcut::zoomIn());
669     a->setWhatsThis(i18n("This increases the display font size."));
670     connect(a, &QAction::triggered, m_viewInternal, [this]() {
671         m_viewInternal->slotIncFontSizes();
672     });
673 
674     a = ac->addAction(QStringLiteral("view_dec_font_sizes"));
675     a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
676     a->setText(i18n("Shrink Font"));
677     ac->setDefaultShortcuts(a, KStandardShortcut::zoomOut());
678     a->setWhatsThis(i18n("This decreases the display font size."));
679     connect(a, &QAction::triggered, m_viewInternal, [this]() {
680         m_viewInternal->slotDecFontSizes();
681     });
682 
683     a = ac->addAction(QStringLiteral("view_reset_font_sizes"));
684     a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
685     a->setText(i18n("Reset Font Size"));
686     ac->setDefaultShortcuts(a, KStandardShortcut::shortcut(KStandardShortcut::ActualSize));
687     a->setWhatsThis(i18n("This resets the display font size."));
688     connect(a, &QAction::triggered, m_viewInternal, &KateViewInternal::slotResetFontSizes);
689 
690     a = m_toggleBlockSelection = new KToggleAction(i18n("Bl&ock Selection Mode"), this);
691     ac->addAction(QStringLiteral("set_verticalSelect"), a);
692     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B));
693     a->setWhatsThis(i18n("This command allows switching between the normal (line based) selection mode and the block selection mode."));
694     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleBlockSelection);
695 
696     a = ac->addAction(QStringLiteral("switch_next_input_mode"));
697     a->setText(i18n("Switch to next Input Mode"));
698     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_V));
699     a->setWhatsThis(i18n("Switch to the next input mode."));
700     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cycleInputMode);
701 
702     a = m_toggleInsert = new KToggleAction(i18n("Overwr&ite Mode"), this);
703     ac->addAction(QStringLiteral("set_insert"), a);
704     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Insert));
705     a->setWhatsThis(i18n("Choose whether you want the text you type to be inserted or to overwrite existing text."));
706     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleInsert);
707 
708     KToggleAction *toggleAction;
709     a = m_toggleDynWrap = toggleAction = new KToggleAction(i18n("&Dynamic Word Wrap"), this);
710     a->setIcon(QIcon::fromTheme(QStringLiteral("text-wrap")));
711     ac->addAction(QStringLiteral("view_dynamic_word_wrap"), a);
712     a->setWhatsThis(
713         i18n("If this option is checked, the text lines will be wrapped at the view border on the screen.<br /><br />"
714              "This is only a view option, meaning the document will not changed."));
715     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleDynWordWrap);
716 
717     a = m_setDynWrapIndicators = new KSelectAction(i18n("Dynamic Word Wrap Indicators"), this);
718     ac->addAction(QStringLiteral("dynamic_word_wrap_indicators"), a);
719     a->setWhatsThis(i18n("Choose when the Dynamic Word Wrap Indicators should be displayed"));
720     connect(m_setDynWrapIndicators, &KSelectAction::indexTriggered, this, &KTextEditor::ViewPrivate::setDynWrapIndicators);
721     const QStringList list2{i18n("&Off"), i18n("Follow &Line Numbers"), i18n("&Always On")};
722     m_setDynWrapIndicators->setItems(list2);
723     m_setDynWrapIndicators->setEnabled(m_toggleDynWrap->isChecked()); // only synced on real change, later
724 
725     a = toggleAction = new KToggleAction(i18n("Static Word Wrap"), this);
726     ac->addAction(QStringLiteral("view_static_word_wrap"), a);
727     a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the column defined in the editing properties."));
728     connect(a, &KToggleAction::triggered, [=] {
729         if (m_doc) {
730             m_doc->setWordWrap(!m_doc->wordWrap());
731         }
732     });
733 
734     a = toggleAction = m_toggleWWMarker = new KToggleAction(i18n("Show Static &Word Wrap Marker"), this);
735     ac->addAction(QStringLiteral("view_word_wrap_marker"), a);
736     a->setWhatsThis(
737         i18n("Show/hide the Word Wrap Marker, a vertical line drawn at the word "
738              "wrap column as defined in the editing properties"));
739     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleWWMarker);
740 
741     a = toggleAction = m_toggleFoldingMarkers = new KToggleAction(i18n("Show Folding &Markers"), this);
742     ac->addAction(QStringLiteral("view_folding_markers"), a);
743     a->setWhatsThis(i18n("You can choose if the codefolding marks should be shown, if codefolding is possible."));
744     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleFoldingMarkers);
745 
746     a = m_toggleIconBar = toggleAction = new KToggleAction(i18n("Show &Icon Border"), this);
747     ac->addAction(QStringLiteral("view_border"), a);
748     a->setWhatsThis(i18n("Show/hide the icon border.<br /><br />The icon border shows bookmark symbols, for instance."));
749     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleIconBorder);
750 
751     a = toggleAction = m_toggleLineNumbers = new KToggleAction(i18n("Show &Line Numbers"), this);
752     ac->addAction(QStringLiteral("view_line_numbers"), a);
753     a->setWhatsThis(i18n("Show/hide the line numbers on the left hand side of the view."));
754     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleLineNumbersOn);
755 
756     a = m_toggleScrollBarMarks = toggleAction = new KToggleAction(i18n("Show Scroll&bar Marks"), this);
757     ac->addAction(QStringLiteral("view_scrollbar_marks"), a);
758     a->setWhatsThis(i18n("Show/hide the marks on the vertical scrollbar.<br /><br />The marks show bookmarks, for instance."));
759     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleScrollBarMarks);
760 
761     a = m_toggleScrollBarMiniMap = toggleAction = new KToggleAction(i18n("Show Scrollbar Mini-Map"), this);
762     ac->addAction(QStringLiteral("view_scrollbar_minimap"), a);
763     a->setWhatsThis(i18n("Show/hide the mini-map on the vertical scrollbar.<br /><br />The mini-map shows an overview of the whole document."));
764     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleScrollBarMiniMap);
765 
766     a = m_doc->autoReloadToggleAction();
767     ac->addAction(QStringLiteral("view_auto_reload"), a);
768 
769     //   a = m_toggleScrollBarMiniMapAll = toggleAction = new KToggleAction(i18n("Show the whole document in the Mini-Map"), this);
770     //   ac->addAction(QLatin1String("view_scrollbar_minimap_all"), a);
771     //   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
772     //   mini-map.")); connect(a, SIGNAL(triggered(bool)), SLOT(toggleScrollBarMiniMapAll())); connect(m_toggleScrollBarMiniMap, SIGNAL(triggered(bool)),
773     //   m_toggleScrollBarMiniMapAll, SLOT(setEnabled(bool)));
774 
775     a = m_toggleNPSpaces = new KToggleAction(i18n("Show Non-Printable Spaces"), this);
776     ac->addAction(QStringLiteral("view_non_printable_spaces"), a);
777     a->setWhatsThis(i18n("Show/hide bounding box around non-printable spaces"));
778     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleNPSpaces);
779 
780     a = m_switchCmdLine = ac->addAction(QStringLiteral("switch_to_cmd_line"));
781     a->setText(i18n("Switch to Command Line"));
782     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_F7));
783     a->setWhatsThis(i18n("Show/hide the command line on the bottom of the view."));
784     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::switchToCmdLine);
785 
786     KActionMenu *am = new KActionMenu(i18n("Input Modes"), this);
787     m_inputModeActions = new QActionGroup(am);
788     ac->addAction(QStringLiteral("view_input_modes"), am);
789     auto switchInputModeAction = ac->action(QStringLiteral("switch_next_input_mode"));
790     am->addAction(switchInputModeAction);
791     am->addSeparator();
792     for (const auto &mode : m_viewInternal->m_inputModes) {
793         a = new QAction(mode->viewInputModeHuman(), m_inputModeActions);
794         am->addAction(a);
795         a->setWhatsThis(i18n("Activate/deactivate %1", mode->viewInputModeHuman()));
796         const InputMode im = mode->viewInputMode();
797         a->setData(static_cast<int>(im));
798         a->setCheckable(true);
799         if (im == m_config->inputMode()) {
800             a->setChecked(true);
801         }
802         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleInputMode);
803     }
804 
805     a = m_setEndOfLine = new KSelectAction(i18n("&End of Line"), this);
806     ac->addAction(QStringLiteral("set_eol"), a);
807     a->setWhatsThis(i18n("Choose which line endings should be used, when you save the document"));
808     const QStringList list{i18nc("@item:inmenu End of Line", "&UNIX"),
809                            i18nc("@item:inmenu End of Line", "&Windows/DOS"),
810                            i18nc("@item:inmenu End of Line", "&Macintosh")};
811     m_setEndOfLine->setItems(list);
812     m_setEndOfLine->setCurrentItem(doc()->config()->eol());
813     connect(m_setEndOfLine, &KSelectAction::indexTriggered, this, &KTextEditor::ViewPrivate::setEol);
814 
815     a = m_addBom = new KToggleAction(i18n("Add &Byte Order Mark (BOM)"), this);
816     m_addBom->setChecked(doc()->config()->bom());
817     ac->addAction(QStringLiteral("add_bom"), a);
818     a->setWhatsThis(i18n("Enable/disable adding of byte order marks for UTF-8/UTF-16 encoded files while saving"));
819     connect(m_addBom, &KToggleAction::triggered, this, &KTextEditor::ViewPrivate::setAddBom);
820 
821     // encoding menu
822     m_encodingAction = new KateViewEncodingAction(m_doc, this, i18n("E&ncoding"), this);
823     ac->addAction(QStringLiteral("set_encoding"), m_encodingAction);
824 
825     a = ac->addAction(KStandardAction::Find, this, SLOT(find()));
826     a->setWhatsThis(i18n("Look up the first occurrence of a piece of text or regular expression."));
827     addAction(a);
828 
829     a = ac->addAction(QStringLiteral("edit_find_selected"));
830     a->setText(i18n("Find Selected"));
831     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_H));
832     a->setWhatsThis(i18n("Finds next occurrence of selected text."));
833     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findSelectedForwards);
834 
835     a = ac->addAction(QStringLiteral("edit_find_selected_backwards"));
836     a->setText(i18n("Find Selected Backwards"));
837     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_H));
838     a->setWhatsThis(i18n("Finds previous occurrence of selected text."));
839     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findSelectedBackwards);
840 
841     a = ac->addAction(KStandardAction::FindNext, this, SLOT(findNext()));
842     a->setWhatsThis(i18n("Look up the next occurrence of the search phrase."));
843     addAction(a);
844 
845     a = ac->addAction(KStandardAction::FindPrev, QStringLiteral("edit_find_prev"), this, SLOT(findPrevious()));
846     a->setWhatsThis(i18n("Look up the previous occurrence of the search phrase."));
847     addAction(a);
848 
849     a = ac->addAction(KStandardAction::Replace, this, SLOT(replace()));
850     a->setWhatsThis(i18n("Look up a piece of text or regular expression and replace the result with some given text."));
851 
852     m_spell->createActions(ac);
853     m_toggleOnTheFlySpellCheck = new KToggleAction(i18n("Automatic Spell Checking"), this);
854     m_toggleOnTheFlySpellCheck->setWhatsThis(i18n("Enable/disable automatic spell checking"));
855     connect(m_toggleOnTheFlySpellCheck, &KToggleAction::triggered, this, &KTextEditor::ViewPrivate::toggleOnTheFlySpellCheck);
856     ac->addAction(QStringLiteral("tools_toggle_automatic_spell_checking"), m_toggleOnTheFlySpellCheck);
857     ac->setDefaultShortcut(m_toggleOnTheFlySpellCheck, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
858 
859     a = ac->addAction(QStringLiteral("tools_change_dictionary"));
860     a->setText(i18n("Change Dictionary..."));
861     a->setWhatsThis(i18n("Change the dictionary that is used for spell checking."));
862     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::changeDictionary);
863 
864     a = ac->addAction(QStringLiteral("tools_clear_dictionary_ranges"));
865     a->setText(i18n("Clear Dictionary Ranges"));
866     a->setEnabled(false);
867     a->setWhatsThis(i18n("Remove all the separate dictionary ranges that were set for spell checking."));
868     connect(a, &QAction::triggered, m_doc, &KTextEditor::DocumentPrivate::clearDictionaryRanges);
869     connect(m_doc, &KTextEditor::DocumentPrivate::dictionaryRangesPresent, a, &QAction::setEnabled);
870 
871     m_copyHtmlAction = ac->addAction(QStringLiteral("edit_copy_html"), this, SLOT(exportHtmlToClipboard()));
872     m_copyHtmlAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
873     m_copyHtmlAction->setText(i18n("Copy as &HTML"));
874     m_copyHtmlAction->setWhatsThis(i18n("Use this command to copy the currently selected text as HTML to the system clipboard."));
875 
876     a = ac->addAction(QStringLiteral("file_export_html"), this, SLOT(exportHtmlToFile()));
877     a->setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
878     a->setText(i18n("E&xport as HTML..."));
879     a->setWhatsThis(
880         i18n("This command allows you to export the current document"
881              " with all highlighting information into a HTML document."));
882 
883     m_spellingMenu->createActions(ac);
884 
885     m_bookmarks->createActions(ac);
886 
887     slotSelectionChanged();
888 
889     // Now setup the editing actions before adding the associated
890     // widget and setting the shortcut context
891     setupEditActions();
892     setupCodeFolding();
893     slotClipboardHistoryChanged();
894 
895     ac->addAssociatedWidget(m_viewInternal);
896 
897     const auto actions = ac->actions();
898     for (QAction *action : actions) {
899         action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
900     }
901 
902     connect(this, &KTextEditor::ViewPrivate::selectionChanged, this, &KTextEditor::ViewPrivate::slotSelectionChanged);
903 }
904 
slotConfigDialog()905 void KTextEditor::ViewPrivate::slotConfigDialog()
906 {
907     // invoke config dialog, will auto-save configuration to katepartrc
908     KTextEditor::EditorPrivate::self()->configDialog(this);
909 }
910 
setupEditActions()911 void KTextEditor::ViewPrivate::setupEditActions()
912 {
913     // If you add an editing action to this
914     // function make sure to include the line
915     // m_editActions << a after creating the action
916     KActionCollection *ac = actionCollection();
917 
918     QAction *a = ac->addAction(QStringLiteral("word_left"));
919     a->setText(i18n("Move Word Left"));
920     ac->setDefaultShortcuts(a, KStandardShortcut::backwardWord());
921     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::wordLeft);
922     m_editActions.push_back(a);
923 
924     a = ac->addAction(QStringLiteral("select_char_left"));
925     a->setText(i18n("Select Character Left"));
926     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_Left));
927     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftCursorLeft);
928     m_editActions.push_back(a);
929 
930     a = ac->addAction(QStringLiteral("select_word_left"));
931     a->setText(i18n("Select Word Left"));
932     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_Left));
933     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftWordLeft);
934     m_editActions.push_back(a);
935 
936     a = ac->addAction(QStringLiteral("word_right"));
937     a->setText(i18n("Move Word Right"));
938     ac->setDefaultShortcuts(a, KStandardShortcut::forwardWord());
939     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::wordRight);
940     m_editActions.push_back(a);
941 
942     a = ac->addAction(QStringLiteral("select_char_right"));
943     a->setText(i18n("Select Character Right"));
944     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_Right));
945     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftCursorRight);
946     m_editActions.push_back(a);
947 
948     a = ac->addAction(QStringLiteral("select_word_right"));
949     a->setText(i18n("Select Word Right"));
950     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_Right));
951     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftWordRight);
952     m_editActions.push_back(a);
953 
954     a = ac->addAction(QStringLiteral("beginning_of_line"));
955     a->setText(i18n("Move to Beginning of Line"));
956     ac->setDefaultShortcuts(a, KStandardShortcut::beginningOfLine());
957     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::home);
958     m_editActions.push_back(a);
959 
960     a = ac->addAction(QStringLiteral("beginning_of_document"));
961     a->setText(i18n("Move to Beginning of Document"));
962     ac->setDefaultShortcuts(a, KStandardShortcut::begin());
963     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::top);
964     m_editActions.push_back(a);
965 
966     a = ac->addAction(QStringLiteral("select_beginning_of_line"));
967     a->setText(i18n("Select to Beginning of Line"));
968     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_Home));
969     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftHome);
970     m_editActions.push_back(a);
971 
972     a = ac->addAction(QStringLiteral("select_beginning_of_document"));
973     a->setText(i18n("Select to Beginning of Document"));
974     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_Home));
975     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftTop);
976     m_editActions.push_back(a);
977 
978     a = ac->addAction(QStringLiteral("end_of_line"));
979     a->setText(i18n("Move to End of Line"));
980     ac->setDefaultShortcuts(a, KStandardShortcut::endOfLine());
981     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::end);
982     m_editActions.push_back(a);
983 
984     a = ac->addAction(QStringLiteral("end_of_document"));
985     a->setText(i18n("Move to End of Document"));
986     ac->setDefaultShortcuts(a, KStandardShortcut::end());
987     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::bottom);
988     m_editActions.push_back(a);
989 
990     a = ac->addAction(QStringLiteral("select_end_of_line"));
991     a->setText(i18n("Select to End of Line"));
992     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_End));
993     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftEnd);
994     m_editActions.push_back(a);
995 
996     a = ac->addAction(QStringLiteral("select_end_of_document"));
997     a->setText(i18n("Select to End of Document"));
998     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_End));
999     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftBottom);
1000     m_editActions.push_back(a);
1001 
1002     a = ac->addAction(QStringLiteral("select_line_up"));
1003     a->setText(i18n("Select to Previous Line"));
1004     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_Up));
1005     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftUp);
1006     m_editActions.push_back(a);
1007 
1008     a = ac->addAction(QStringLiteral("scroll_line_up"));
1009     a->setText(i18n("Scroll Line Up"));
1010     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_Up));
1011     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::scrollUp);
1012     m_editActions.push_back(a);
1013 
1014     a = ac->addAction(QStringLiteral("move_line_down"));
1015     a->setText(i18n("Move to Next Line"));
1016     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Down));
1017     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::down);
1018     m_editActions.push_back(a);
1019 
1020     a = ac->addAction(QStringLiteral("move_line_up"));
1021     a->setText(i18n("Move to Previous Line"));
1022     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Up));
1023     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::up);
1024     m_editActions.push_back(a);
1025 
1026     a = ac->addAction(QStringLiteral("move_cursor_right"));
1027     a->setText(i18n("Move Cursor Right"));
1028     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Right));
1029     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cursorRight);
1030     m_editActions.push_back(a);
1031 
1032     a = ac->addAction(QStringLiteral("move_cursor_left"));
1033     a->setText(i18n("Move Cursor Left"));
1034     ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Left));
1035     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cursorLeft);
1036     m_editActions.push_back(a);
1037 
1038     a = ac->addAction(QStringLiteral("select_line_down"));
1039     a->setText(i18n("Select to Next Line"));
1040     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_Down));
1041     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftDown);
1042     m_editActions.push_back(a);
1043 
1044     a = ac->addAction(QStringLiteral("scroll_line_down"));
1045     a->setText(i18n("Scroll Line Down"));
1046     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_Down));
1047     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::scrollDown);
1048     m_editActions.push_back(a);
1049 
1050     a = ac->addAction(QStringLiteral("scroll_page_up"));
1051     a->setText(i18n("Scroll Page Up"));
1052     ac->setDefaultShortcuts(a, KStandardShortcut::prior());
1053     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::pageUp);
1054     m_editActions.push_back(a);
1055 
1056     a = ac->addAction(QStringLiteral("select_page_up"));
1057     a->setText(i18n("Select Page Up"));
1058     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_PageUp));
1059     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftPageUp);
1060     m_editActions.push_back(a);
1061 
1062     a = ac->addAction(QStringLiteral("move_top_of_view"));
1063     a->setText(i18n("Move to Top of View"));
1064     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT + Qt::Key_Home));
1065     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::topOfView);
1066     m_editActions.push_back(a);
1067 
1068     a = ac->addAction(QStringLiteral("select_top_of_view"));
1069     a->setText(i18n("Select to Top of View"));
1070     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_Home));
1071     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftTopOfView);
1072     m_editActions.push_back(a);
1073 
1074     a = ac->addAction(QStringLiteral("scroll_page_down"));
1075     a->setText(i18n("Scroll Page Down"));
1076     ac->setDefaultShortcuts(a, KStandardShortcut::next());
1077     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::pageDown);
1078     m_editActions.push_back(a);
1079 
1080     a = ac->addAction(QStringLiteral("select_page_down"));
1081     a->setText(i18n("Select Page Down"));
1082     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::Key_PageDown));
1083     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftPageDown);
1084     m_editActions.push_back(a);
1085 
1086     a = ac->addAction(QStringLiteral("move_bottom_of_view"));
1087     a->setText(i18n("Move to Bottom of View"));
1088     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT + Qt::Key_End));
1089     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::bottomOfView);
1090     m_editActions.push_back(a);
1091 
1092     a = ac->addAction(QStringLiteral("select_bottom_of_view"));
1093     a->setText(i18n("Select to Bottom of View"));
1094     ac->setDefaultShortcut(a, QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_End));
1095     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftBottomOfView);
1096     m_editActions.push_back(a);
1097 
1098     a = ac->addAction(QStringLiteral("to_matching_bracket"));
1099     a->setText(i18n("Move to Matching Bracket"));
1100     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_6));
1101     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toMatchingBracket);
1102     // m_editActions << a;
1103 
1104     a = ac->addAction(QStringLiteral("select_matching_bracket"));
1105     a->setText(i18n("Select to Matching Bracket"));
1106     ac->setDefaultShortcut(a, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_6));
1107     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftToMatchingBracket);
1108     // m_editActions << a;
1109 
1110     // anders: shortcuts doing any changes should not be created in read-only mode
1111     if (!doc()->readOnly()) {
1112         a = ac->addAction(QStringLiteral("transpose_char"));
1113         a->setText(i18n("Transpose Characters"));
1114         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_T));
1115         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::transpose);
1116         m_editActions.push_back(a);
1117 
1118         a = ac->addAction(QStringLiteral("transpose_word"));
1119         a->setText(i18n("Transpose Words"));
1120         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::transposeWord);
1121         m_editActions.push_back(a);
1122 
1123         a = ac->addAction(QStringLiteral("delete_line"));
1124         a->setText(i18n("Delete Line"));
1125         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_K));
1126         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::killLine);
1127         m_editActions.push_back(a);
1128 
1129         a = ac->addAction(QStringLiteral("delete_word_left"));
1130         a->setText(i18n("Delete Word Left"));
1131         ac->setDefaultShortcuts(a, KStandardShortcut::deleteWordBack());
1132         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::deleteWordLeft);
1133         m_editActions.push_back(a);
1134 
1135         a = ac->addAction(QStringLiteral("delete_word_right"));
1136         a->setText(i18n("Delete Word Right"));
1137         ac->setDefaultShortcuts(a, KStandardShortcut::deleteWordForward());
1138         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::deleteWordRight);
1139         m_editActions.push_back(a);
1140 
1141         a = ac->addAction(QStringLiteral("delete_next_character"));
1142         a->setText(i18n("Delete Next Character"));
1143         ac->setDefaultShortcut(a, QKeySequence(Qt::Key_Delete));
1144         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::keyDelete);
1145         m_editActions.push_back(a);
1146 
1147         a = ac->addAction(QStringLiteral("backspace"));
1148         a->setText(i18n("Backspace"));
1149         QList<QKeySequence> scuts;
1150         scuts << QKeySequence(Qt::Key_Backspace) << QKeySequence(Qt::SHIFT + Qt::Key_Backspace);
1151         ac->setDefaultShortcuts(a, scuts);
1152         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::backspace);
1153         m_editActions.push_back(a);
1154 
1155         a = ac->addAction(QStringLiteral("insert_tabulator"));
1156         a->setText(i18n("Insert Tab"));
1157         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::insertTab);
1158         m_editActions.push_back(a);
1159 
1160         a = ac->addAction(QStringLiteral("smart_newline"));
1161         a->setText(i18n("Insert Smart Newline"));
1162         a->setWhatsThis(i18n("Insert newline including leading characters of the current line which are not letters or numbers."));
1163         scuts.clear();
1164         scuts << QKeySequence(Qt::SHIFT + Qt::Key_Return) << QKeySequence(Qt::SHIFT + Qt::Key_Enter);
1165         ac->setDefaultShortcuts(a, scuts);
1166         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::smartNewline);
1167         m_editActions.push_back(a);
1168 
1169         a = ac->addAction(QStringLiteral("no_indent_newline"));
1170         a->setText(i18n("Insert a non-indented Newline"));
1171         a->setWhatsThis(i18n("Insert a new line without indentation, regardless of indentation settings."));
1172         scuts.clear();
1173         scuts << QKeySequence(Qt::CTRL + Qt::Key_Return) << QKeySequence(Qt::CTRL + Qt::Key_Enter);
1174         ac->setDefaultShortcuts(a, scuts);
1175         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::noIndentNewline);
1176         m_editActions.push_back(a);
1177 
1178         a = ac->addAction(QStringLiteral("tools_indent"));
1179         a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-more")));
1180         a->setText(i18n("&Indent"));
1181         a->setWhatsThis(
1182             i18n("Use this to indent a selected block of text.<br /><br />"
1183                  "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
1184         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::Key_I));
1185         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::indent);
1186 
1187         a = ac->addAction(QStringLiteral("tools_unindent"));
1188         a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-less")));
1189         a->setText(i18n("&Unindent"));
1190         a->setWhatsThis(i18n("Use this to unindent a selected block of text."));
1191         ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I));
1192         connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::unIndent);
1193     }
1194 
1195     if (hasFocus()) {
1196         slotGotFocus();
1197     } else {
1198         slotLostFocus();
1199     }
1200 }
1201 
setupCodeFolding()1202 void KTextEditor::ViewPrivate::setupCodeFolding()
1203 {
1204     KActionCollection *ac = this->actionCollection();
1205     QAction *a;
1206 
1207     a = ac->addAction(QStringLiteral("folding_toplevel"));
1208     a->setText(i18n("Fold Toplevel Nodes"));
1209     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Minus));
1210     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotFoldToplevelNodes);
1211 
1212     a = ac->addAction(QStringLiteral("folding_expandtoplevel"));
1213     a->setText(i18n("Unfold Toplevel Nodes"));
1214     ac->setDefaultShortcut(a, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Plus));
1215     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotExpandToplevelNodes);
1216 
1217     /*a = ac->addAction(QLatin1String("folding_expandall"));
1218     a->setText(i18n("Unfold All Nodes"));
1219     connect(a, SIGNAL(triggered(bool)), doc()->foldingTree(), SLOT(expandAll()));
1220 
1221     a = ac->addAction(QLatin1String("folding_collapse_dsComment"));
1222     a->setText(i18n("Fold Multiline Comments"));
1223     connect(a, SIGNAL(triggered(bool)), doc()->foldingTree(), SLOT(collapseAll_dsComments()));
1224     */
1225     a = ac->addAction(QStringLiteral("folding_toggle_current"));
1226     a->setText(i18n("Toggle Current Node"));
1227     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotToggleFolding);
1228 
1229     a = ac->addAction(QStringLiteral("folding_toggle_in_current"));
1230     a->setText(i18n("Toggle Contained Nodes"));
1231     connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotToggleFoldingsInRange);
1232 }
1233 
slotFoldToplevelNodes()1234 void KTextEditor::ViewPrivate::slotFoldToplevelNodes()
1235 {
1236     for (int line = 0; line < doc()->lines(); ++line) {
1237         if (textFolding().isLineVisible(line)) {
1238             foldLine(line);
1239         }
1240     }
1241 }
1242 
slotExpandToplevelNodes()1243 void KTextEditor::ViewPrivate::slotExpandToplevelNodes()
1244 {
1245     const auto topLevelRanges(textFolding().foldingRangesForParentRange());
1246     for (const auto &range : topLevelRanges) {
1247         textFolding().unfoldRange(range.first);
1248     }
1249 }
1250 
slotToggleFolding()1251 void KTextEditor::ViewPrivate::slotToggleFolding()
1252 {
1253     int line = cursorPosition().line();
1254     bool actionDone = false;
1255     while (!actionDone && (line > -1)) {
1256         actionDone = unfoldLine(line);
1257         if (!actionDone) {
1258             actionDone = foldLine(line--).isValid();
1259         }
1260     }
1261 }
1262 
slotToggleFoldingsInRange()1263 void KTextEditor::ViewPrivate::slotToggleFoldingsInRange()
1264 {
1265     int line = cursorPosition().line();
1266     while (!toggleFoldingsInRange(line) && (line > -1)) {
1267         --line;
1268     }
1269 }
1270 
foldLine(int line)1271 KTextEditor::Range KTextEditor::ViewPrivate::foldLine(int line)
1272 {
1273     KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(line);
1274     if (!foldingRange.isValid()) {
1275         return foldingRange;
1276     }
1277 
1278     // Ensure not to fold the end marker to avoid a deceptive look, but only on token based folding
1279     // ensure we don't compute an invalid line by moving outside of the foldingRange range by checking onSingleLine(), see bug 417890
1280     Kate::TextLine startTextLine = doc()->buffer().plainLine(line);
1281     if (!startTextLine->markedAsFoldingStartIndentation() && !foldingRange.onSingleLine()) {
1282         const int adjustedLine = foldingRange.end().line() - 1;
1283         foldingRange.setEnd(KTextEditor::Cursor(adjustedLine, doc()->buffer().plainLine(adjustedLine)->length()));
1284     }
1285 
1286     // Don't try to fold a single line, which can happens due to adjustment above
1287     // FIXME Avoid to offer such a folding marker
1288     if (!foldingRange.onSingleLine()) {
1289         textFolding().newFoldingRange(foldingRange, Kate::TextFolding::Folded);
1290     }
1291 
1292     return foldingRange;
1293 }
1294 
unfoldLine(int line)1295 bool KTextEditor::ViewPrivate::unfoldLine(int line)
1296 {
1297     bool actionDone = false;
1298     const KTextEditor::Cursor currentCursor = cursorPosition();
1299 
1300     // ask the folding info for this line, if any folds are around!
1301     // auto = QVector<QPair<qint64, Kate::TextFolding::FoldingRangeFlags>>
1302     auto startingRanges = textFolding().foldingRangesStartingOnLine(line);
1303     for (int i = 0; i < startingRanges.size() && !actionDone; ++i) {
1304         // Avoid jumping view in case of a big unfold and ensure nice highlight of folding marker
1305         setCursorPosition(textFolding().foldingRange(startingRanges[i].first).start());
1306 
1307         actionDone |= textFolding().unfoldRange(startingRanges[i].first);
1308     }
1309 
1310     if (!actionDone) {
1311         // Nothing unfolded? Restore old cursor position!
1312         setCursorPosition(currentCursor);
1313     }
1314 
1315     return actionDone;
1316 }
1317 
toggleFoldingOfLine(int line)1318 bool KTextEditor::ViewPrivate::toggleFoldingOfLine(int line)
1319 {
1320     bool actionDone = unfoldLine(line);
1321     if (!actionDone) {
1322         actionDone = foldLine(line).isValid();
1323     }
1324 
1325     return actionDone;
1326 }
1327 
toggleFoldingsInRange(int line)1328 bool KTextEditor::ViewPrivate::toggleFoldingsInRange(int line)
1329 {
1330     KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(line);
1331     if (!foldingRange.isValid()) {
1332         // Either line is not valid or there is no start range
1333         return false;
1334     }
1335 
1336     bool actionDone = false; // Track success
1337     const KTextEditor::Cursor currentCursor = cursorPosition();
1338 
1339     // Don't be too eager but obliging! Only toggle containing ranges which are
1340     // visible -> Be done when the range is folded
1341     actionDone |= unfoldLine(line);
1342 
1343     if (!actionDone) {
1344         // Unfold all in range, but not the range itself
1345         for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1346             actionDone |= unfoldLine(ln);
1347         }
1348 
1349         if (actionDone) {
1350             // In most cases we want now a not moved cursor
1351             setCursorPosition(currentCursor);
1352         }
1353     }
1354 
1355     if (!actionDone) {
1356         // Fold all in range, but not the range itself
1357         for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1358             KTextEditor::Range fr = foldLine(ln);
1359             if (fr.isValid()) {
1360                 // qMax to avoid infinite loop in case of range without content
1361                 ln = qMax(ln, fr.end().line() - 1);
1362                 actionDone = true;
1363             }
1364         }
1365     }
1366 
1367     if (!actionDone) {
1368         // At this point was an unfolded range clicked which contains no "childs"
1369         // We assume the user want to fold it by the wrong button, be obliging!
1370         actionDone |= foldLine(line).isValid();
1371     }
1372 
1373     // At this point we should be always true
1374     return actionDone;
1375 }
1376 
viewMode() const1377 KTextEditor::View::ViewMode KTextEditor::ViewPrivate::viewMode() const
1378 {
1379     return currentInputMode()->viewMode();
1380 }
1381 
viewModeHuman() const1382 QString KTextEditor::ViewPrivate::viewModeHuman() const
1383 {
1384     QString currentMode = currentInputMode()->viewModeHuman();
1385 
1386     // append read-only if needed
1387     if (!doc()->isReadWrite()) {
1388         currentMode = i18n("(R/O) %1", currentMode);
1389     }
1390 
1391     // return full mode
1392     return currentMode;
1393 }
1394 
viewInputMode() const1395 KTextEditor::View::InputMode KTextEditor::ViewPrivate::viewInputMode() const
1396 {
1397     return currentInputMode()->viewInputMode();
1398 }
1399 
viewInputModeHuman() const1400 QString KTextEditor::ViewPrivate::viewInputModeHuman() const
1401 {
1402     return currentInputMode()->viewInputModeHuman();
1403 }
1404 
setInputMode(KTextEditor::View::InputMode mode)1405 void KTextEditor::ViewPrivate::setInputMode(KTextEditor::View::InputMode mode)
1406 {
1407     if (currentInputMode()->viewInputMode() == mode) {
1408         return;
1409     }
1410 
1411     m_viewInternal->m_currentInputMode->deactivate();
1412     m_viewInternal->m_currentInputMode = m_viewInternal->m_inputModes[mode].get();
1413     m_viewInternal->m_currentInputMode->activate();
1414 
1415     config()->setValue(KateViewConfig::InputMode,
1416                        mode); // TODO: this could be called from read config procedure, so it's not a good idea to set a specific view mode here
1417 
1418     /* small duplication, but need to do this if not toggled by action */
1419     const auto inputModeActions = m_inputModeActions->actions();
1420     for (QAction *action : inputModeActions) {
1421         if (static_cast<InputMode>(action->data().toInt()) == mode) {
1422             action->setChecked(true);
1423             break;
1424         }
1425     }
1426 
1427     /* inform the rest of the system about the change */
1428     Q_EMIT viewInputModeChanged(this, mode);
1429     Q_EMIT viewModeChanged(this, viewMode());
1430 }
1431 
slotDocumentAboutToReload()1432 void KTextEditor::ViewPrivate::slotDocumentAboutToReload()
1433 {
1434     if (doc()->isAutoReload()) {
1435         const int lastVisibleLine = m_viewInternal->endLine();
1436         const int currentLine = cursorPosition().line();
1437         m_gotoBottomAfterReload = (lastVisibleLine == currentLine) && (currentLine == doc()->lastLine());
1438         if (!m_gotoBottomAfterReload) {
1439             // Ensure the view jumps not back when user scrolls around
1440             const int firstVisibleLine = 1 + lastVisibleLine - m_viewInternal->linesDisplayed();
1441             const int newLine = qBound(firstVisibleLine, currentLine, lastVisibleLine);
1442             setCursorPositionVisual(KTextEditor::Cursor(newLine, cursorPosition().column()));
1443         }
1444     } else {
1445         m_gotoBottomAfterReload = false;
1446     }
1447 }
1448 
slotDocumentReloaded()1449 void KTextEditor::ViewPrivate::slotDocumentReloaded()
1450 {
1451     if (m_gotoBottomAfterReload) {
1452         bottom();
1453     }
1454 }
1455 
slotGotFocus()1456 void KTextEditor::ViewPrivate::slotGotFocus()
1457 {
1458     // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotGotFocus";
1459     currentInputMode()->gotFocus();
1460 
1461     //  update current view and scrollbars
1462     //  it is needed for styles that implement different frame and scrollbar
1463     // rendering when focused
1464     update();
1465     if (m_viewInternal->m_lineScroll->isVisible()) {
1466         m_viewInternal->m_lineScroll->update();
1467     }
1468 
1469     if (m_viewInternal->m_columnScroll->isVisible()) {
1470         m_viewInternal->m_columnScroll->update();
1471     }
1472 
1473     Q_EMIT focusIn(this);
1474 }
1475 
slotLostFocus()1476 void KTextEditor::ViewPrivate::slotLostFocus()
1477 {
1478     // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotLostFocus";
1479     currentInputMode()->lostFocus();
1480 
1481     //  update current view and scrollbars
1482     //  it is needed for styles that implement different frame and scrollbar
1483     // rendering when focused
1484     update();
1485     if (m_viewInternal->m_lineScroll->isVisible()) {
1486         m_viewInternal->m_lineScroll->update();
1487     }
1488 
1489     if (m_viewInternal->m_columnScroll->isVisible()) {
1490         m_viewInternal->m_columnScroll->update();
1491     }
1492 
1493     Q_EMIT focusOut(this);
1494 }
1495 
setDynWrapIndicators(int mode)1496 void KTextEditor::ViewPrivate::setDynWrapIndicators(int mode)
1497 {
1498     config()->setValue(KateViewConfig::DynWordWrapIndicators, mode);
1499 }
1500 
isOverwriteMode() const1501 bool KTextEditor::ViewPrivate::isOverwriteMode() const
1502 {
1503     return doc()->config()->ovr();
1504 }
1505 
reloadFile()1506 void KTextEditor::ViewPrivate::reloadFile()
1507 {
1508     // bookmarks and cursor positions are temporarily saved by the document
1509     doc()->documentReload();
1510 }
1511 
slotReadWriteChanged()1512 void KTextEditor::ViewPrivate::slotReadWriteChanged()
1513 {
1514     if (m_toggleWriteLock) {
1515         m_toggleWriteLock->setChecked(!doc()->isReadWrite());
1516     }
1517 
1518     m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
1519     m_paste->setEnabled(doc()->isReadWrite());
1520     m_pasteMenu->setEnabled(doc()->isReadWrite() && !KTextEditor::EditorPrivate::self()->clipboardHistory().isEmpty());
1521     if (m_pasteSelection) {
1522         m_pasteSelection->setEnabled(doc()->isReadWrite());
1523     }
1524     m_swapWithClipboard->setEnabled(doc()->isReadWrite());
1525     m_setEndOfLine->setEnabled(doc()->isReadWrite());
1526 
1527     static const auto l = {QStringLiteral("edit_replace"),
1528                            QStringLiteral("tools_spelling"),
1529                            QStringLiteral("tools_indent"),
1530                            QStringLiteral("tools_unindent"),
1531                            QStringLiteral("tools_cleanIndent"),
1532                            QStringLiteral("tools_align"),
1533                            QStringLiteral("tools_comment"),
1534                            QStringLiteral("tools_uncomment"),
1535                            QStringLiteral("tools_toggle_comment"),
1536                            QStringLiteral("tools_uppercase"),
1537                            QStringLiteral("tools_lowercase"),
1538                            QStringLiteral("tools_capitalize"),
1539                            QStringLiteral("tools_join_lines"),
1540                            QStringLiteral("tools_apply_wordwrap"),
1541                            QStringLiteral("tools_spelling_from_cursor"),
1542                            QStringLiteral("tools_spelling_selection")};
1543 
1544     for (const auto &action : l) {
1545         QAction *a = actionCollection()->action(action);
1546         if (a) {
1547             a->setEnabled(doc()->isReadWrite());
1548         }
1549     }
1550     slotUpdateUndo();
1551 
1552     currentInputMode()->readWriteChanged(doc()->isReadWrite());
1553 
1554     // => view mode changed
1555     Q_EMIT viewModeChanged(this, viewMode());
1556     Q_EMIT viewInputModeChanged(this, viewInputMode());
1557 }
1558 
slotClipboardHistoryChanged()1559 void KTextEditor::ViewPrivate::slotClipboardHistoryChanged()
1560 {
1561     m_pasteMenu->setEnabled(doc()->isReadWrite() && !KTextEditor::EditorPrivate::self()->clipboardHistory().isEmpty());
1562 }
1563 
slotUpdateUndo()1564 void KTextEditor::ViewPrivate::slotUpdateUndo()
1565 {
1566     if (doc()->readOnly()) {
1567         return;
1568     }
1569 
1570     m_editUndo->setEnabled(doc()->isReadWrite() && doc()->undoCount() > 0);
1571     m_editRedo->setEnabled(doc()->isReadWrite() && doc()->redoCount() > 0);
1572 }
1573 
setCursorPositionInternal(const KTextEditor::Cursor position,uint tabwidth,bool calledExternally)1574 bool KTextEditor::ViewPrivate::setCursorPositionInternal(const KTextEditor::Cursor position, uint tabwidth, bool calledExternally)
1575 {
1576     Kate::TextLine l = doc()->kateTextLine(position.line());
1577 
1578     if (!l) {
1579         return false;
1580     }
1581 
1582     const QString line_str = l->text();
1583 
1584     int x = 0;
1585     int z = 0;
1586     for (; z < line_str.length() && z < position.column(); z++) {
1587         if (line_str[z] == QLatin1Char('\t')) {
1588             x += tabwidth - (x % tabwidth);
1589         } else {
1590             x++;
1591         }
1592     }
1593 
1594     if (blockSelection()) {
1595         if (z < position.column()) {
1596             x += position.column() - z;
1597         }
1598     }
1599 
1600     m_viewInternal->updateCursor(KTextEditor::Cursor(position.line(), x),
1601                                  false,
1602                                  calledExternally /* force center for external calls, see bug 408418 */,
1603                                  calledExternally);
1604 
1605     return true;
1606 }
1607 
toggleInsert()1608 void KTextEditor::ViewPrivate::toggleInsert()
1609 {
1610     doc()->config()->setOvr(!doc()->config()->ovr());
1611     m_toggleInsert->setChecked(isOverwriteMode());
1612 
1613     Q_EMIT viewModeChanged(this, viewMode());
1614     Q_EMIT viewInputModeChanged(this, viewInputMode());
1615 }
1616 
slotSaveCanceled(const QString & error)1617 void KTextEditor::ViewPrivate::slotSaveCanceled(const QString &error)
1618 {
1619     if (!error.isEmpty()) { // happens when canceling a job
1620         KMessageBox::error(this, error);
1621     }
1622 }
1623 
gotoLine()1624 void KTextEditor::ViewPrivate::gotoLine()
1625 {
1626     gotoBar()->updateData();
1627     bottomViewBar()->showBarWidget(gotoBar());
1628 }
1629 
changeDictionary()1630 void KTextEditor::ViewPrivate::changeDictionary()
1631 {
1632     dictionaryBar()->updateData();
1633     bottomViewBar()->showBarWidget(dictionaryBar());
1634 }
1635 
joinLines()1636 void KTextEditor::ViewPrivate::joinLines()
1637 {
1638     int first = selectionRange().start().line();
1639     int last = selectionRange().end().line();
1640     // int left = doc()->line( last ).length() - doc()->selEndCol();
1641     if (first == last) {
1642         first = cursorPosition().line();
1643         last = first + 1;
1644     }
1645     doc()->joinLines(first, last);
1646 }
1647 
readSessionConfig(const KConfigGroup & config,const QSet<QString> & flags)1648 void KTextEditor::ViewPrivate::readSessionConfig(const KConfigGroup &config, const QSet<QString> &flags)
1649 {
1650     Q_UNUSED(flags)
1651 
1652     // cursor position
1653     setCursorPositionInternal(KTextEditor::Cursor(config.readEntry("CursorLine", 0), config.readEntry("CursorColumn", 0)));
1654 
1655     m_config->setDynWordWrap(config.readEntry("Dynamic Word Wrap", false));
1656 
1657     // restore text folding
1658     m_savedFoldingState = QJsonDocument::fromJson(config.readEntry("TextFolding", QByteArray()));
1659     applyFoldingState();
1660 
1661     for (const auto &mode : m_viewInternal->m_inputModes) {
1662         mode->readSessionConfig(config);
1663     }
1664 }
1665 
writeSessionConfig(KConfigGroup & config,const QSet<QString> & flags)1666 void KTextEditor::ViewPrivate::writeSessionConfig(KConfigGroup &config, const QSet<QString> &flags)
1667 {
1668     Q_UNUSED(flags)
1669 
1670     // cursor position
1671     config.writeEntry("CursorLine", cursorPosition().line());
1672     config.writeEntry("CursorColumn", cursorPosition().column());
1673 
1674     config.writeEntry("Dynamic Word Wrap", m_config->dynWordWrap());
1675 
1676     // save text folding state
1677     saveFoldingState();
1678     config.writeEntry("TextFolding", m_savedFoldingState.toJson(QJsonDocument::Compact));
1679     m_savedFoldingState = QJsonDocument();
1680 
1681     for (const auto &mode : m_viewInternal->m_inputModes) {
1682         mode->writeSessionConfig(config);
1683     }
1684 }
1685 
getEol() const1686 int KTextEditor::ViewPrivate::getEol() const
1687 {
1688     return doc()->config()->eol();
1689 }
1690 
setEol(int eol)1691 void KTextEditor::ViewPrivate::setEol(int eol)
1692 {
1693     if (!doc()->isReadWrite()) {
1694         return;
1695     }
1696 
1697     if (m_updatingDocumentConfig) {
1698         return;
1699     }
1700 
1701     if (eol != doc()->config()->eol()) {
1702         doc()->setModified(true); // mark modified (bug #143120)
1703         doc()->config()->setEol(eol);
1704     }
1705 }
1706 
setAddBom(bool enabled)1707 void KTextEditor::ViewPrivate::setAddBom(bool enabled)
1708 {
1709     if (!doc()->isReadWrite()) {
1710         return;
1711     }
1712 
1713     if (m_updatingDocumentConfig) {
1714         return;
1715     }
1716 
1717     doc()->config()->setBom(enabled);
1718     doc()->bomSetByUser();
1719 }
1720 
setIconBorder(bool enable)1721 void KTextEditor::ViewPrivate::setIconBorder(bool enable)
1722 {
1723     config()->setValue(KateViewConfig::ShowIconBar, enable);
1724 }
1725 
toggleIconBorder()1726 void KTextEditor::ViewPrivate::toggleIconBorder()
1727 {
1728     config()->setValue(KateViewConfig::ShowIconBar, !config()->iconBar());
1729 }
1730 
setLineNumbersOn(bool enable)1731 void KTextEditor::ViewPrivate::setLineNumbersOn(bool enable)
1732 {
1733     config()->setValue(KateViewConfig::ShowLineNumbers, enable);
1734 }
1735 
toggleLineNumbersOn()1736 void KTextEditor::ViewPrivate::toggleLineNumbersOn()
1737 {
1738     config()->setValue(KateViewConfig::ShowLineNumbers, !config()->lineNumbers());
1739 }
1740 
setScrollBarMarks(bool enable)1741 void KTextEditor::ViewPrivate::setScrollBarMarks(bool enable)
1742 {
1743     config()->setValue(KateViewConfig::ShowScrollBarMarks, enable);
1744 }
1745 
toggleScrollBarMarks()1746 void KTextEditor::ViewPrivate::toggleScrollBarMarks()
1747 {
1748     config()->setValue(KateViewConfig::ShowScrollBarMarks, !config()->scrollBarMarks());
1749 }
1750 
setScrollBarMiniMap(bool enable)1751 void KTextEditor::ViewPrivate::setScrollBarMiniMap(bool enable)
1752 {
1753     config()->setValue(KateViewConfig::ShowScrollBarMiniMap, enable);
1754 }
1755 
toggleScrollBarMiniMap()1756 void KTextEditor::ViewPrivate::toggleScrollBarMiniMap()
1757 {
1758     config()->setValue(KateViewConfig::ShowScrollBarMiniMap, !config()->scrollBarMiniMap());
1759 }
1760 
setScrollBarMiniMapAll(bool enable)1761 void KTextEditor::ViewPrivate::setScrollBarMiniMapAll(bool enable)
1762 {
1763     config()->setValue(KateViewConfig::ShowScrollBarMiniMapAll, enable);
1764 }
1765 
toggleScrollBarMiniMapAll()1766 void KTextEditor::ViewPrivate::toggleScrollBarMiniMapAll()
1767 {
1768     config()->setValue(KateViewConfig::ShowScrollBarMiniMapAll, !config()->scrollBarMiniMapAll());
1769 }
1770 
setScrollBarMiniMapWidth(int width)1771 void KTextEditor::ViewPrivate::setScrollBarMiniMapWidth(int width)
1772 {
1773     config()->setValue(KateViewConfig::ScrollBarMiniMapWidth, width);
1774 }
1775 
toggleDynWordWrap()1776 void KTextEditor::ViewPrivate::toggleDynWordWrap()
1777 {
1778     config()->setDynWordWrap(!config()->dynWordWrap());
1779 }
1780 
toggleWWMarker()1781 void KTextEditor::ViewPrivate::toggleWWMarker()
1782 {
1783     m_renderer->config()->setWordWrapMarker(!m_renderer->config()->wordWrapMarker());
1784 }
1785 
toggleNPSpaces()1786 void KTextEditor::ViewPrivate::toggleNPSpaces()
1787 {
1788     m_renderer->setShowNonPrintableSpaces(!m_renderer->showNonPrintableSpaces());
1789     m_viewInternal->update(); // force redraw
1790 }
1791 
toggleWordCount(bool on)1792 void KTextEditor::ViewPrivate::toggleWordCount(bool on)
1793 {
1794     config()->setShowWordCount(on);
1795 }
1796 
setFoldingMarkersOn(bool enable)1797 void KTextEditor::ViewPrivate::setFoldingMarkersOn(bool enable)
1798 {
1799     config()->setValue(KateViewConfig::ShowFoldingBar, enable);
1800 }
1801 
toggleFoldingMarkers()1802 void KTextEditor::ViewPrivate::toggleFoldingMarkers()
1803 {
1804     config()->setValue(KateViewConfig::ShowFoldingBar, !config()->foldingBar());
1805 }
1806 
iconBorder()1807 bool KTextEditor::ViewPrivate::iconBorder()
1808 {
1809     return m_viewInternal->m_leftBorder->iconBorderOn();
1810 }
1811 
lineNumbersOn()1812 bool KTextEditor::ViewPrivate::lineNumbersOn()
1813 {
1814     return m_viewInternal->m_leftBorder->lineNumbersOn();
1815 }
1816 
scrollBarMarks()1817 bool KTextEditor::ViewPrivate::scrollBarMarks()
1818 {
1819     return m_viewInternal->m_lineScroll->showMarks();
1820 }
1821 
scrollBarMiniMap()1822 bool KTextEditor::ViewPrivate::scrollBarMiniMap()
1823 {
1824     return m_viewInternal->m_lineScroll->showMiniMap();
1825 }
1826 
dynWrapIndicators()1827 int KTextEditor::ViewPrivate::dynWrapIndicators()
1828 {
1829     return m_viewInternal->m_leftBorder->dynWrapIndicators();
1830 }
1831 
foldingMarkersOn()1832 bool KTextEditor::ViewPrivate::foldingMarkersOn()
1833 {
1834     return m_viewInternal->m_leftBorder->foldingMarkersOn();
1835 }
1836 
toggleWriteLock()1837 void KTextEditor::ViewPrivate::toggleWriteLock()
1838 {
1839     doc()->setReadWrite(!doc()->isReadWrite());
1840 }
1841 
registerTextHintProvider(KTextEditor::TextHintProvider * provider)1842 void KTextEditor::ViewPrivate::registerTextHintProvider(KTextEditor::TextHintProvider *provider)
1843 {
1844     m_viewInternal->registerTextHintProvider(provider);
1845 }
1846 
unregisterTextHintProvider(KTextEditor::TextHintProvider * provider)1847 void KTextEditor::ViewPrivate::unregisterTextHintProvider(KTextEditor::TextHintProvider *provider)
1848 {
1849     m_viewInternal->unregisterTextHintProvider(provider);
1850 }
1851 
setTextHintDelay(int delay)1852 void KTextEditor::ViewPrivate::setTextHintDelay(int delay)
1853 {
1854     m_viewInternal->setTextHintDelay(delay);
1855 }
1856 
textHintDelay() const1857 int KTextEditor::ViewPrivate::textHintDelay() const
1858 {
1859     return m_viewInternal->textHintDelay();
1860 }
1861 
find()1862 void KTextEditor::ViewPrivate::find()
1863 {
1864     currentInputMode()->find();
1865 }
1866 
findSelectedForwards()1867 void KTextEditor::ViewPrivate::findSelectedForwards()
1868 {
1869     currentInputMode()->findSelectedForwards();
1870 }
1871 
findSelectedBackwards()1872 void KTextEditor::ViewPrivate::findSelectedBackwards()
1873 {
1874     currentInputMode()->findSelectedBackwards();
1875 }
1876 
replace()1877 void KTextEditor::ViewPrivate::replace()
1878 {
1879     currentInputMode()->findReplace();
1880 }
1881 
findNext()1882 void KTextEditor::ViewPrivate::findNext()
1883 {
1884     currentInputMode()->findNext();
1885 }
1886 
findPrevious()1887 void KTextEditor::ViewPrivate::findPrevious()
1888 {
1889     currentInputMode()->findPrevious();
1890 }
1891 
showSearchWrappedHint(bool isReverseSearch)1892 void KTextEditor::ViewPrivate::showSearchWrappedHint(bool isReverseSearch)
1893 {
1894     // show message widget when wrapping
1895     const QIcon icon = isReverseSearch ? QIcon::fromTheme(QStringLiteral("go-up-search")) : QIcon::fromTheme(QStringLiteral("go-down-search"));
1896 
1897     if (!m_wrappedMessage || m_isLastSearchReversed != isReverseSearch) {
1898         m_isLastSearchReversed = isReverseSearch;
1899         m_wrappedMessage = new KTextEditor::Message(i18n("Search wrapped"), KTextEditor::Message::Information);
1900         m_wrappedMessage->setIcon(icon);
1901         m_wrappedMessage->setPosition(KTextEditor::Message::BottomInView);
1902         m_wrappedMessage->setAutoHide(2000);
1903         m_wrappedMessage->setAutoHideMode(KTextEditor::Message::Immediate);
1904         m_wrappedMessage->setView(this);
1905         this->doc()->postMessage(m_wrappedMessage);
1906     }
1907 }
1908 
slotSelectionChanged()1909 void KTextEditor::ViewPrivate::slotSelectionChanged()
1910 {
1911     m_copy->setEnabled(selection() || m_config->smartCopyCut());
1912     m_deSelect->setEnabled(selection());
1913     m_copyHtmlAction->setEnabled(selection());
1914 
1915     // update highlighting of current selected word
1916     selectionChangedForHighlights();
1917 
1918     if (doc()->readOnly()) {
1919         return;
1920     }
1921 
1922     m_cut->setEnabled(selection() || m_config->smartCopyCut());
1923 }
1924 
switchToCmdLine()1925 void KTextEditor::ViewPrivate::switchToCmdLine()
1926 {
1927     currentInputMode()->activateCommandLine();
1928 }
1929 
renderer()1930 KateRenderer *KTextEditor::ViewPrivate::renderer()
1931 {
1932     return m_renderer;
1933 }
1934 
updateConfig()1935 void KTextEditor::ViewPrivate::updateConfig()
1936 {
1937     if (m_startingUp) {
1938         return;
1939     }
1940 
1941     // dyn. word wrap & markers
1942     if (m_hasWrap != config()->dynWordWrap()) {
1943         m_viewInternal->prepareForDynWrapChange();
1944 
1945         m_hasWrap = config()->dynWordWrap();
1946 
1947         m_viewInternal->dynWrapChanged();
1948 
1949         m_setDynWrapIndicators->setEnabled(config()->dynWordWrap());
1950         m_toggleDynWrap->setChecked(config()->dynWordWrap());
1951     }
1952 
1953     m_viewInternal->m_leftBorder->setDynWrapIndicators(config()->dynWordWrapIndicators());
1954     m_setDynWrapIndicators->setCurrentItem(config()->dynWordWrapIndicators());
1955 
1956     // line numbers
1957     m_viewInternal->m_leftBorder->setLineNumbersOn(config()->lineNumbers());
1958     m_toggleLineNumbers->setChecked(config()->lineNumbers());
1959 
1960     // icon bar
1961     m_viewInternal->m_leftBorder->setIconBorderOn(config()->iconBar());
1962     m_toggleIconBar->setChecked(config()->iconBar());
1963 
1964     // scrollbar marks
1965     m_viewInternal->m_lineScroll->setShowMarks(config()->scrollBarMarks());
1966     m_toggleScrollBarMarks->setChecked(config()->scrollBarMarks());
1967 
1968     // scrollbar mini-map
1969     m_viewInternal->m_lineScroll->setShowMiniMap(config()->scrollBarMiniMap());
1970     m_toggleScrollBarMiniMap->setChecked(config()->scrollBarMiniMap());
1971 
1972     // scrollbar mini-map - (whole document)
1973     m_viewInternal->m_lineScroll->setMiniMapAll(config()->scrollBarMiniMapAll());
1974     // m_toggleScrollBarMiniMapAll->setChecked( config()->scrollBarMiniMapAll() );
1975 
1976     // scrollbar mini-map.width
1977     m_viewInternal->m_lineScroll->setMiniMapWidth(config()->scrollBarMiniMapWidth());
1978 
1979     // misc edit
1980     m_toggleBlockSelection->setChecked(blockSelection());
1981     m_toggleInsert->setChecked(isOverwriteMode());
1982 
1983     updateFoldingConfig();
1984 
1985     // bookmark
1986     m_bookmarks->setSorting((KateBookmarks::Sorting)config()->bookmarkSort());
1987 
1988     m_viewInternal->setAutoCenterLines(config()->autoCenterLines());
1989 
1990     for (const auto &input : m_viewInternal->m_inputModes) {
1991         input->updateConfig();
1992     }
1993 
1994     setInputMode(config()->inputMode());
1995 
1996     reflectOnTheFlySpellCheckStatus(doc()->isOnTheFlySpellCheckingEnabled());
1997 
1998     // register/unregister word completion...
1999     bool wc = config()->wordCompletion();
2000     if (wc != isCompletionModelRegistered(KTextEditor::EditorPrivate::self()->wordCompletionModel())) {
2001         if (wc) {
2002             registerCompletionModel(KTextEditor::EditorPrivate::self()->wordCompletionModel());
2003         } else {
2004             unregisterCompletionModel(KTextEditor::EditorPrivate::self()->wordCompletionModel());
2005         }
2006     }
2007 
2008     bool kc = config()->keywordCompletion();
2009     if (kc != isCompletionModelRegistered(KTextEditor::EditorPrivate::self()->keywordCompletionModel())) {
2010         if (kc) {
2011             registerCompletionModel(KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2012         } else {
2013             unregisterCompletionModel(KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2014         }
2015     }
2016 
2017     m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
2018     m_copy->setEnabled(selection() || m_config->smartCopyCut());
2019 
2020     // if not disabled, update status bar
2021     if (m_statusBar) {
2022         m_statusBar->updateStatus();
2023     }
2024 
2025     // now redraw...
2026     m_viewInternal->cache()->clear();
2027     tagAll();
2028     updateView(true);
2029 
2030     Q_EMIT configChanged(this);
2031 }
2032 
updateDocumentConfig()2033 void KTextEditor::ViewPrivate::updateDocumentConfig()
2034 {
2035     if (m_startingUp) {
2036         return;
2037     }
2038 
2039     m_updatingDocumentConfig = true;
2040 
2041     m_setEndOfLine->setCurrentItem(doc()->config()->eol());
2042 
2043     m_addBom->setChecked(doc()->config()->bom());
2044 
2045     m_updatingDocumentConfig = false;
2046 
2047     // maybe block selection or wrap-cursor mode changed
2048     ensureCursorColumnValid();
2049 
2050     // first change this
2051     m_renderer->setTabWidth(doc()->config()->tabWidth());
2052     m_renderer->setIndentWidth(doc()->config()->indentationWidth());
2053 
2054     // now redraw...
2055     m_viewInternal->cache()->clear();
2056     tagAll();
2057     updateView(true);
2058 }
2059 
updateRendererConfig()2060 void KTextEditor::ViewPrivate::updateRendererConfig()
2061 {
2062     if (m_startingUp) {
2063         return;
2064     }
2065 
2066     m_toggleWWMarker->setChecked(m_renderer->config()->wordWrapMarker());
2067 
2068     m_viewInternal->updateBracketMarkAttributes();
2069     m_viewInternal->updateBracketMarks();
2070 
2071     // now redraw...
2072     m_viewInternal->cache()->clear();
2073     tagAll();
2074     m_viewInternal->updateView(true);
2075 
2076     // update the left border right, for example linenumbers
2077     m_viewInternal->m_leftBorder->updateFont();
2078     m_viewInternal->m_leftBorder->repaint();
2079 
2080     m_viewInternal->m_lineScroll->queuePixmapUpdate();
2081 
2082     currentInputMode()->updateRendererConfig();
2083 
2084     // @@ showIndentLines is not cached anymore.
2085     //  m_renderer->setShowIndentLines (m_renderer->config()->showIndentationLines());
2086     Q_EMIT configChanged(this);
2087 }
2088 
updateFoldingConfig()2089 void KTextEditor::ViewPrivate::updateFoldingConfig()
2090 {
2091     // folding bar
2092     m_viewInternal->m_leftBorder->setFoldingMarkersOn(config()->foldingBar());
2093     m_toggleFoldingMarkers->setChecked(config()->foldingBar());
2094 
2095     if (hasCommentInFirstLine(m_doc)) {
2096         if (config()->foldFirstLine() && !m_autoFoldedFirstLine) {
2097             foldLine(0);
2098             m_autoFoldedFirstLine = true;
2099         } else if (!config()->foldFirstLine() && m_autoFoldedFirstLine) {
2100             unfoldLine(0);
2101             m_autoFoldedFirstLine = false;
2102         }
2103     } else {
2104         m_autoFoldedFirstLine = false;
2105     }
2106 
2107 #if 0
2108     // FIXME: FOLDING
2109     const QStringList l = {
2110           QStringLiteral("folding_toplevel")
2111         , QStringLiteral("folding_expandtoplevel")
2112         , QStringLiteral("folding_toggle_current")
2113         , QStringLiteral("folding_toggle_in_current")
2114     };
2115 
2116     QAction *a = 0;
2117     for (int z = 0; z < l.size(); z++)
2118         if ((a = actionCollection()->action(l[z].toAscii().constData()))) {
2119             a->setEnabled(doc()->highlight() && doc()->highlight()->allowsFolding());
2120         }
2121 #endif
2122 }
2123 
ensureCursorColumnValid()2124 void KTextEditor::ViewPrivate::ensureCursorColumnValid()
2125 {
2126     KTextEditor::Cursor c = m_viewInternal->cursorPosition();
2127 
2128     // make sure the cursor is valid:
2129     // - in block selection mode or if wrap cursor is off, the column is arbitrary
2130     // - otherwise: it's bounded by the line length
2131     if (!blockSelection() && wrapCursor() && (!c.isValid() || c.column() > doc()->lineLength(c.line()))) {
2132         c.setColumn(doc()->lineLength(cursorPosition().line()));
2133         setCursorPosition(c);
2134     }
2135 }
2136 
2137 // BEGIN EDIT STUFF
editStart()2138 void KTextEditor::ViewPrivate::editStart()
2139 {
2140     m_viewInternal->editStart();
2141 }
2142 
editEnd(int editTagLineStart,int editTagLineEnd,bool tagFrom)2143 void KTextEditor::ViewPrivate::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
2144 {
2145     m_viewInternal->editEnd(editTagLineStart, editTagLineEnd, tagFrom);
2146 }
2147 
editSetCursor(const KTextEditor::Cursor cursor)2148 void KTextEditor::ViewPrivate::editSetCursor(const KTextEditor::Cursor cursor)
2149 {
2150     m_viewInternal->editSetCursor(cursor);
2151 }
2152 // END
2153 
2154 // BEGIN TAG & CLEAR
tagLine(const KTextEditor::Cursor virtualCursor)2155 bool KTextEditor::ViewPrivate::tagLine(const KTextEditor::Cursor virtualCursor)
2156 {
2157     return m_viewInternal->tagLine(virtualCursor);
2158 }
2159 
tagRange(const KTextEditor::Range & range,bool realLines)2160 bool KTextEditor::ViewPrivate::tagRange(const KTextEditor::Range &range, bool realLines)
2161 {
2162     return m_viewInternal->tagRange(range, realLines);
2163 }
2164 
tagLines(KTextEditor::LineRange lineRange,bool realLines)2165 bool KTextEditor::ViewPrivate::tagLines(KTextEditor::LineRange lineRange, bool realLines)
2166 {
2167     return m_viewInternal->tagLines(lineRange.start(), lineRange.end(), realLines);
2168 }
2169 
tagLines(KTextEditor::Cursor start,KTextEditor::Cursor end,bool realCursors)2170 bool KTextEditor::ViewPrivate::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors)
2171 {
2172     return m_viewInternal->tagLines(start, end, realCursors);
2173 }
2174 
tagAll()2175 void KTextEditor::ViewPrivate::tagAll()
2176 {
2177     m_viewInternal->tagAll();
2178 }
2179 
clear()2180 void KTextEditor::ViewPrivate::clear()
2181 {
2182     m_viewInternal->clear();
2183 }
2184 
repaintText(bool paintOnlyDirty)2185 void KTextEditor::ViewPrivate::repaintText(bool paintOnlyDirty)
2186 {
2187     if (paintOnlyDirty) {
2188         m_viewInternal->updateDirty();
2189     } else {
2190         m_viewInternal->update();
2191     }
2192 }
2193 
updateView(bool changed)2194 void KTextEditor::ViewPrivate::updateView(bool changed)
2195 {
2196     // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::updateView";
2197 
2198     m_viewInternal->updateView(changed);
2199     m_viewInternal->m_leftBorder->update();
2200 }
2201 
2202 // END
2203 
slotHlChanged()2204 void KTextEditor::ViewPrivate::slotHlChanged()
2205 {
2206     KateHighlighting *hl = doc()->highlight();
2207     bool ok(!hl->getCommentStart(0).isEmpty() || !hl->getCommentSingleLineStart(0).isEmpty());
2208 
2209     if (actionCollection()->action(QStringLiteral("tools_comment"))) {
2210         actionCollection()->action(QStringLiteral("tools_comment"))->setEnabled(ok);
2211     }
2212 
2213     if (actionCollection()->action(QStringLiteral("tools_uncomment"))) {
2214         actionCollection()->action(QStringLiteral("tools_uncomment"))->setEnabled(ok);
2215     }
2216 
2217     if (actionCollection()->action(QStringLiteral("tools_toggle_comment"))) {
2218         actionCollection()->action(QStringLiteral("tools_toggle_comment"))->setEnabled(ok);
2219     }
2220 
2221     // show folding bar if "view defaults" says so, otherwise enable/disable only the menu entry
2222     updateFoldingConfig();
2223 }
2224 
virtualCursorColumn() const2225 int KTextEditor::ViewPrivate::virtualCursorColumn() const
2226 {
2227     return doc()->toVirtualColumn(m_viewInternal->cursorPosition());
2228 }
2229 
notifyMousePositionChanged(const KTextEditor::Cursor newPosition)2230 void KTextEditor::ViewPrivate::notifyMousePositionChanged(const KTextEditor::Cursor newPosition)
2231 {
2232     Q_EMIT mousePositionChanged(this, newPosition);
2233 }
2234 
2235 // BEGIN KTextEditor::SelectionInterface stuff
2236 
setSelection(const KTextEditor::Range & selection)2237 bool KTextEditor::ViewPrivate::setSelection(const KTextEditor::Range &selection)
2238 {
2239     // anything to do?
2240     if (selection == m_selection) {
2241         return true;
2242     }
2243 
2244     // backup old range
2245     KTextEditor::Range oldSelection = m_selection;
2246 
2247     // set new range
2248     m_selection.setRange(selection.isEmpty() ? KTextEditor::Range::invalid() : selection);
2249 
2250     // trigger update of correct area
2251     tagSelection(oldSelection);
2252     repaintText(true);
2253 
2254     // emit holy signal
2255     Q_EMIT selectionChanged(this);
2256 
2257     // be done
2258     return true;
2259 }
2260 
clearSelection()2261 bool KTextEditor::ViewPrivate::clearSelection()
2262 {
2263     return clearSelection(true);
2264 }
2265 
clearSelection(bool redraw,bool finishedChangingSelection)2266 bool KTextEditor::ViewPrivate::clearSelection(bool redraw, bool finishedChangingSelection)
2267 {
2268     // no selection, nothing to do...
2269     if (!selection()) {
2270         return false;
2271     }
2272 
2273     // backup old range
2274     KTextEditor::Range oldSelection = m_selection;
2275 
2276     // invalidate current selection
2277     m_selection.setRange(KTextEditor::Range::invalid());
2278 
2279     // trigger update of correct area
2280     tagSelection(oldSelection);
2281     if (redraw) {
2282         repaintText(true);
2283     }
2284 
2285     // emit holy signal
2286     if (finishedChangingSelection) {
2287         Q_EMIT selectionChanged(this);
2288     }
2289 
2290     m_viewInternal->m_selChangedByUser = false;
2291     // be done
2292     return true;
2293 }
2294 
selection() const2295 bool KTextEditor::ViewPrivate::selection() const
2296 {
2297     if (!wrapCursor()) {
2298         return m_selection != KTextEditor::Range::invalid();
2299     } else {
2300         return m_selection.toRange().isValid();
2301     }
2302 }
2303 
selectionText() const2304 QString KTextEditor::ViewPrivate::selectionText() const
2305 {
2306     return doc()->text(m_selection, blockSelect);
2307 }
2308 
removeSelectedText()2309 bool KTextEditor::ViewPrivate::removeSelectedText()
2310 {
2311     if (!selection()) {
2312         return false;
2313     }
2314 
2315     doc()->editStart();
2316 
2317     // Optimization: clear selection before removing text
2318     KTextEditor::Range selection = m_selection;
2319 
2320     doc()->removeText(selection, blockSelect);
2321 
2322     // don't redraw the cleared selection - that's done in editEnd().
2323     if (blockSelect) {
2324         int selectionColumn = qMin(doc()->toVirtualColumn(selection.start()), doc()->toVirtualColumn(selection.end()));
2325         KTextEditor::Range newSelection = selection;
2326         newSelection.setStart(KTextEditor::Cursor(newSelection.start().line(), doc()->fromVirtualColumn(newSelection.start().line(), selectionColumn)));
2327         newSelection.setEnd(KTextEditor::Cursor(newSelection.end().line(), doc()->fromVirtualColumn(newSelection.end().line(), selectionColumn)));
2328         setSelection(newSelection);
2329         setCursorPositionInternal(newSelection.start());
2330     } else {
2331         clearSelection(false);
2332     }
2333 
2334     doc()->editEnd();
2335 
2336     return true;
2337 }
2338 
selectAll()2339 bool KTextEditor::ViewPrivate::selectAll()
2340 {
2341     setBlockSelection(false);
2342     top();
2343     shiftBottom();
2344     return true;
2345 }
2346 
cursorSelected(const KTextEditor::Cursor cursor)2347 bool KTextEditor::ViewPrivate::cursorSelected(const KTextEditor::Cursor cursor)
2348 {
2349     KTextEditor::Cursor ret = cursor;
2350     if ((!blockSelect) && (ret.column() < 0)) {
2351         ret.setColumn(0);
2352     }
2353 
2354     if (blockSelect) {
2355         return cursor.line() >= m_selection.start().line() && ret.line() <= m_selection.end().line() && ret.column() >= m_selection.start().column()
2356             && ret.column() <= m_selection.end().column();
2357     } else {
2358         return m_selection.toRange().contains(cursor) || m_selection.end() == cursor;
2359     }
2360 }
2361 
lineSelected(int line)2362 bool KTextEditor::ViewPrivate::lineSelected(int line)
2363 {
2364     return !blockSelect && m_selection.toRange().containsLine(line);
2365 }
2366 
lineEndSelected(const KTextEditor::Cursor lineEndPos)2367 bool KTextEditor::ViewPrivate::lineEndSelected(const KTextEditor::Cursor lineEndPos)
2368 {
2369     return (!blockSelect)
2370         && (lineEndPos.line() > m_selection.start().line()
2371             || (lineEndPos.line() == m_selection.start().line() && (m_selection.start().column() < lineEndPos.column() || lineEndPos.column() == -1)))
2372         && (lineEndPos.line() < m_selection.end().line()
2373             || (lineEndPos.line() == m_selection.end().line() && (lineEndPos.column() <= m_selection.end().column() && lineEndPos.column() != -1)));
2374 }
2375 
lineHasSelected(int line)2376 bool KTextEditor::ViewPrivate::lineHasSelected(int line)
2377 {
2378     return selection() && m_selection.toRange().containsLine(line);
2379 }
2380 
lineIsSelection(int line)2381 bool KTextEditor::ViewPrivate::lineIsSelection(int line)
2382 {
2383     return (line == m_selection.start().line() && line == m_selection.end().line());
2384 }
2385 
tagSelection(const KTextEditor::Range & oldSelection)2386 void KTextEditor::ViewPrivate::tagSelection(const KTextEditor::Range &oldSelection)
2387 {
2388     if (selection()) {
2389         if (oldSelection.start().line() == -1) {
2390             // We have to tag the whole lot if
2391             // 1) we have a selection, and:
2392             //  a) it's new; or
2393             tagLines(m_selection, true);
2394 
2395         } else if (blockSelection()
2396                    && (oldSelection.start().column() != m_selection.start().column() || oldSelection.end().column() != m_selection.end().column())) {
2397             //  b) we're in block selection mode and the columns have changed
2398             tagLines(m_selection, true);
2399             tagLines(oldSelection, true);
2400 
2401         } else {
2402             if (oldSelection.start() != m_selection.start()) {
2403                 tagLines(KTextEditor::LineRange(oldSelection.start().line(), m_selection.start().line()), true);
2404             }
2405 
2406             if (oldSelection.end() != m_selection.end()) {
2407                 tagLines(KTextEditor::LineRange(oldSelection.end().line(), m_selection.end().line()), true);
2408             }
2409         }
2410 
2411     } else {
2412         // No more selection, clean up
2413         tagLines(oldSelection, true);
2414     }
2415 }
2416 
selectWord(const KTextEditor::Cursor cursor)2417 void KTextEditor::ViewPrivate::selectWord(const KTextEditor::Cursor cursor)
2418 {
2419     setSelection(doc()->wordRangeAt(cursor));
2420 }
2421 
selectLine(const KTextEditor::Cursor cursor)2422 void KTextEditor::ViewPrivate::selectLine(const KTextEditor::Cursor cursor)
2423 {
2424     int line = cursor.line();
2425     if (line + 1 >= doc()->lines()) {
2426         setSelection(KTextEditor::Range(line, 0, line, doc()->lineLength(line)));
2427     } else {
2428         setSelection(KTextEditor::Range(line, 0, line + 1, 0));
2429     }
2430 }
2431 
cut()2432 void KTextEditor::ViewPrivate::cut()
2433 {
2434     if (!selection() && !m_config->smartCopyCut()) {
2435         return;
2436     }
2437 
2438     copy();
2439     if (!selection()) {
2440         selectLine(cursorPosition());
2441     }
2442     removeSelectedText();
2443 }
2444 
copy() const2445 void KTextEditor::ViewPrivate::copy() const
2446 {
2447     QString text;
2448 
2449     if (!selection()) {
2450         if (!m_config->smartCopyCut()) {
2451             return;
2452         }
2453         text = doc()->line(cursorPosition().line()) + QLatin1Char('\n');
2454         m_viewInternal->moveEdge(KateViewInternal::left, false);
2455     } else {
2456         text = selectionText();
2457     }
2458 
2459     // copy to clipboard and our history!
2460     KTextEditor::EditorPrivate::self()->copyToClipboard(text);
2461 }
2462 
pasteSelection()2463 void KTextEditor::ViewPrivate::pasteSelection()
2464 {
2465     m_temporaryAutomaticInvocationDisabled = true;
2466     doc()->paste(this, QApplication::clipboard()->text(QClipboard::Selection));
2467     m_temporaryAutomaticInvocationDisabled = false;
2468 }
2469 
swapWithClipboard()2470 void KTextEditor::ViewPrivate::swapWithClipboard()
2471 {
2472     m_temporaryAutomaticInvocationDisabled = true;
2473 
2474     // get text to paste
2475     const auto text = QApplication::clipboard()->text(QClipboard::Clipboard);
2476 
2477     // do copy
2478     copy();
2479 
2480     // do paste of "previous" clipboard content we saved
2481     doc()->paste(this, text);
2482 
2483     m_temporaryAutomaticInvocationDisabled = false;
2484 }
2485 
applyWordWrap()2486 void KTextEditor::ViewPrivate::applyWordWrap()
2487 {
2488     int first = selectionRange().start().line();
2489     int last = selectionRange().end().line();
2490 
2491     if (first == last) {
2492         // Either no selection or only one line selected, wrap only the current line
2493         first = cursorPosition().line();
2494         last = first;
2495     }
2496 
2497     doc()->wrapParagraph(first, last);
2498 }
2499 
2500 // END
2501 
2502 // BEGIN KTextEditor::BlockSelectionInterface stuff
2503 
blockSelection() const2504 bool KTextEditor::ViewPrivate::blockSelection() const
2505 {
2506     return blockSelect;
2507 }
2508 
setBlockSelection(bool on)2509 bool KTextEditor::ViewPrivate::setBlockSelection(bool on)
2510 {
2511     if (on != blockSelect) {
2512         blockSelect = on;
2513 
2514         KTextEditor::Range oldSelection = m_selection;
2515 
2516         const bool hadSelection = clearSelection(false, false);
2517 
2518         setSelection(oldSelection);
2519 
2520         m_toggleBlockSelection->setChecked(blockSelection());
2521 
2522         // when leaving block selection mode, if cursor is at an invalid position or past the end of the
2523         // line, move the cursor to the last column of the current line unless cursor wrapping is off
2524         ensureCursorColumnValid();
2525 
2526         if (!hadSelection) {
2527             // emit selectionChanged() according to the KTextEditor::View api
2528             // documentation also if there is no selection around. This is needed,
2529             // as e.g. the Kate App status bar uses this signal to update the state
2530             // of the selection mode (block selection, line based selection)
2531             Q_EMIT selectionChanged(this);
2532         }
2533     }
2534 
2535     return true;
2536 }
2537 
toggleBlockSelection()2538 bool KTextEditor::ViewPrivate::toggleBlockSelection()
2539 {
2540     m_toggleBlockSelection->setChecked(!blockSelect);
2541     return setBlockSelection(!blockSelect);
2542 }
2543 
wrapCursor() const2544 bool KTextEditor::ViewPrivate::wrapCursor() const
2545 {
2546     return !blockSelection();
2547 }
2548 
2549 // END
2550 
slotTextInserted(KTextEditor::View * view,const KTextEditor::Cursor position,const QString & text)2551 void KTextEditor::ViewPrivate::slotTextInserted(KTextEditor::View *view, const KTextEditor::Cursor position, const QString &text)
2552 {
2553     Q_EMIT textInserted(view, position, text);
2554 }
2555 
insertTemplateInternal(const KTextEditor::Cursor c,const QString & templateString,const QString & script)2556 bool KTextEditor::ViewPrivate::insertTemplateInternal(const KTextEditor::Cursor c, const QString &templateString, const QString &script)
2557 {
2558     // no empty templates
2559     if (templateString.isEmpty()) {
2560         return false;
2561     }
2562 
2563     // not for read-only docs
2564     if (!doc()->isReadWrite()) {
2565         return false;
2566     }
2567 
2568     // only one handler maybe active at a time; store it in the document.
2569     // Clear it first to make sure at no time two handlers are active at once
2570     doc()->setActiveTemplateHandler(nullptr);
2571     doc()->setActiveTemplateHandler(new KateTemplateHandler(this, c, templateString, script, doc()->undoManager()));
2572     return true;
2573 }
2574 
tagLines(KTextEditor::Range range,bool realRange)2575 bool KTextEditor::ViewPrivate::tagLines(KTextEditor::Range range, bool realRange)
2576 {
2577     return tagLines(range.start(), range.end(), realRange);
2578 }
2579 
deactivateEditActions()2580 void KTextEditor::ViewPrivate::deactivateEditActions()
2581 {
2582     for (QAction *action : std::as_const(m_editActions)) {
2583         action->setEnabled(false);
2584     }
2585 }
2586 
activateEditActions()2587 void KTextEditor::ViewPrivate::activateEditActions()
2588 {
2589     for (QAction *action : std::as_const(m_editActions)) {
2590         action->setEnabled(true);
2591     }
2592 }
2593 
mouseTrackingEnabled() const2594 bool KTextEditor::ViewPrivate::mouseTrackingEnabled() const
2595 {
2596     // FIXME support
2597     return true;
2598 }
2599 
setMouseTrackingEnabled(bool)2600 bool KTextEditor::ViewPrivate::setMouseTrackingEnabled(bool)
2601 {
2602     // FIXME support
2603     return true;
2604 }
2605 
isCompletionActive() const2606 bool KTextEditor::ViewPrivate::isCompletionActive() const
2607 {
2608     return completionWidget()->isCompletionActive();
2609 }
2610 
completionWidget() const2611 KateCompletionWidget *KTextEditor::ViewPrivate::completionWidget() const
2612 {
2613     if (!m_completionWidget) {
2614         m_completionWidget = new KateCompletionWidget(const_cast<KTextEditor::ViewPrivate *>(this));
2615     }
2616 
2617     return m_completionWidget;
2618 }
2619 
startCompletion(const KTextEditor::Range & word,KTextEditor::CodeCompletionModel * model)2620 void KTextEditor::ViewPrivate::startCompletion(const KTextEditor::Range &word, KTextEditor::CodeCompletionModel *model)
2621 {
2622     completionWidget()->startCompletion(word, model);
2623 }
2624 
startCompletion(const Range & word,const QList<KTextEditor::CodeCompletionModel * > & models,KTextEditor::CodeCompletionModel::InvocationType invocationType)2625 void KTextEditor::ViewPrivate::startCompletion(const Range &word,
2626                                                const QList<KTextEditor::CodeCompletionModel *> &models,
2627                                                KTextEditor::CodeCompletionModel::InvocationType invocationType)
2628 {
2629     completionWidget()->startCompletion(word, models, invocationType);
2630 }
2631 
abortCompletion()2632 void KTextEditor::ViewPrivate::abortCompletion()
2633 {
2634     completionWidget()->abortCompletion();
2635 }
2636 
forceCompletion()2637 void KTextEditor::ViewPrivate::forceCompletion()
2638 {
2639     completionWidget()->execute();
2640 }
2641 
registerCompletionModel(KTextEditor::CodeCompletionModel * model)2642 void KTextEditor::ViewPrivate::registerCompletionModel(KTextEditor::CodeCompletionModel *model)
2643 {
2644     completionWidget()->registerCompletionModel(model);
2645 }
2646 
unregisterCompletionModel(KTextEditor::CodeCompletionModel * model)2647 void KTextEditor::ViewPrivate::unregisterCompletionModel(KTextEditor::CodeCompletionModel *model)
2648 {
2649     completionWidget()->unregisterCompletionModel(model);
2650 }
2651 
isCompletionModelRegistered(KTextEditor::CodeCompletionModel * model) const2652 bool KTextEditor::ViewPrivate::isCompletionModelRegistered(KTextEditor::CodeCompletionModel *model) const
2653 {
2654     return completionWidget()->isCompletionModelRegistered(model);
2655 }
2656 
codeCompletionModels() const2657 QList<KTextEditor::CodeCompletionModel *> KTextEditor::ViewPrivate::codeCompletionModels() const
2658 {
2659     return completionWidget()->codeCompletionModels();
2660 }
2661 
isAutomaticInvocationEnabled() const2662 bool KTextEditor::ViewPrivate::isAutomaticInvocationEnabled() const
2663 {
2664     return !m_temporaryAutomaticInvocationDisabled && m_config->automaticCompletionInvocation();
2665 }
2666 
setAutomaticInvocationEnabled(bool enabled)2667 void KTextEditor::ViewPrivate::setAutomaticInvocationEnabled(bool enabled)
2668 {
2669     config()->setValue(KateViewConfig::AutomaticCompletionInvocation, enabled);
2670 }
2671 
sendCompletionExecuted(const KTextEditor::Cursor position,KTextEditor::CodeCompletionModel * model,const QModelIndex & index)2672 void KTextEditor::ViewPrivate::sendCompletionExecuted(const KTextEditor::Cursor position, KTextEditor::CodeCompletionModel *model, const QModelIndex &index)
2673 {
2674     Q_EMIT completionExecuted(this, position, model, index);
2675 }
2676 
sendCompletionAborted()2677 void KTextEditor::ViewPrivate::sendCompletionAborted()
2678 {
2679     Q_EMIT completionAborted(this);
2680 }
2681 
paste(const QString * textToPaste)2682 void KTextEditor::ViewPrivate::paste(const QString *textToPaste)
2683 {
2684     m_temporaryAutomaticInvocationDisabled = true;
2685     doc()->paste(this, textToPaste ? *textToPaste : QApplication::clipboard()->text(QClipboard::Clipboard));
2686     m_temporaryAutomaticInvocationDisabled = false;
2687 }
2688 
setCursorPosition(KTextEditor::Cursor position)2689 bool KTextEditor::ViewPrivate::setCursorPosition(KTextEditor::Cursor position)
2690 {
2691     return setCursorPositionInternal(position, 1, true);
2692 }
2693 
cursorPosition() const2694 KTextEditor::Cursor KTextEditor::ViewPrivate::cursorPosition() const
2695 {
2696     return m_viewInternal->cursorPosition();
2697 }
2698 
cursorPositionVirtual() const2699 KTextEditor::Cursor KTextEditor::ViewPrivate::cursorPositionVirtual() const
2700 {
2701     return KTextEditor::Cursor(m_viewInternal->cursorPosition().line(), virtualCursorColumn());
2702 }
2703 
cursorToCoordinate(const KTextEditor::Cursor & cursor) const2704 QPoint KTextEditor::ViewPrivate::cursorToCoordinate(const KTextEditor::Cursor &cursor) const
2705 {
2706     // map from ViewInternal to View coordinates
2707     const QPoint pt = m_viewInternal->cursorToCoordinate(cursor, true, false);
2708     return pt == QPoint(-1, -1) ? pt : m_viewInternal->mapToParent(pt);
2709 }
2710 
coordinatesToCursor(const QPoint & coords) const2711 KTextEditor::Cursor KTextEditor::ViewPrivate::coordinatesToCursor(const QPoint &coords) const
2712 {
2713     // map from View to ViewInternal coordinates
2714     return m_viewInternal->coordinatesToCursor(m_viewInternal->mapFromParent(coords), false);
2715 }
2716 
cursorPositionCoordinates() const2717 QPoint KTextEditor::ViewPrivate::cursorPositionCoordinates() const
2718 {
2719     // map from ViewInternal to View coordinates
2720     const QPoint pt = m_viewInternal->cursorCoordinates(false);
2721     return pt == QPoint(-1, -1) ? pt : m_viewInternal->mapToParent(pt);
2722 }
2723 
setScrollPositionInternal(KTextEditor::Cursor cursor)2724 void KTextEditor::ViewPrivate::setScrollPositionInternal(KTextEditor::Cursor cursor)
2725 {
2726     m_viewInternal->scrollPos(cursor, false, true, false);
2727 }
2728 
setHorizontalScrollPositionInternal(int x)2729 void KTextEditor::ViewPrivate::setHorizontalScrollPositionInternal(int x)
2730 {
2731     m_viewInternal->scrollColumns(x);
2732 }
2733 
maxScrollPositionInternal() const2734 KTextEditor::Cursor KTextEditor::ViewPrivate::maxScrollPositionInternal() const
2735 {
2736     return m_viewInternal->maxStartPos(true);
2737 }
2738 
firstDisplayedLineInternal(LineType lineType) const2739 int KTextEditor::ViewPrivate::firstDisplayedLineInternal(LineType lineType) const
2740 {
2741     if (lineType == RealLine) {
2742         return m_textFolding.visibleLineToLine(m_viewInternal->startLine());
2743     } else {
2744         return m_viewInternal->startLine();
2745     }
2746 }
2747 
lastDisplayedLineInternal(LineType lineType) const2748 int KTextEditor::ViewPrivate::lastDisplayedLineInternal(LineType lineType) const
2749 {
2750     if (lineType == RealLine) {
2751         return m_textFolding.visibleLineToLine(m_viewInternal->endLine());
2752     } else {
2753         return m_viewInternal->endLine();
2754     }
2755 }
2756 
textAreaRectInternal() const2757 QRect KTextEditor::ViewPrivate::textAreaRectInternal() const
2758 {
2759     const auto sourceRect = m_viewInternal->rect();
2760     const auto topLeft = m_viewInternal->mapTo(this, sourceRect.topLeft());
2761     const auto bottomRight = m_viewInternal->mapTo(this, sourceRect.bottomRight());
2762     return {topLeft, bottomRight};
2763 }
2764 
setCursorPositionVisual(const KTextEditor::Cursor position)2765 bool KTextEditor::ViewPrivate::setCursorPositionVisual(const KTextEditor::Cursor position)
2766 {
2767     return setCursorPositionInternal(position, doc()->config()->tabWidth(), true);
2768 }
2769 
currentTextLine()2770 QString KTextEditor::ViewPrivate::currentTextLine()
2771 {
2772     return doc()->line(cursorPosition().line());
2773 }
2774 
textLayout(int line) const2775 QTextLayout *KTextEditor::ViewPrivate::textLayout(int line) const
2776 {
2777     KateLineLayoutPtr thisLine = m_viewInternal->cache()->line(line);
2778 
2779     return thisLine->isValid() ? thisLine->layout() : nullptr;
2780 }
2781 
textLayout(const KTextEditor::Cursor pos) const2782 QTextLayout *KTextEditor::ViewPrivate::textLayout(const KTextEditor::Cursor pos) const
2783 {
2784     KateLineLayoutPtr thisLine = m_viewInternal->cache()->line(pos);
2785 
2786     return thisLine->isValid() ? thisLine->layout() : nullptr;
2787 }
2788 
indent()2789 void KTextEditor::ViewPrivate::indent()
2790 {
2791     KTextEditor::Cursor c(cursorPosition().line(), 0);
2792     KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
2793     doc()->indent(r, 1);
2794 }
2795 
unIndent()2796 void KTextEditor::ViewPrivate::unIndent()
2797 {
2798     KTextEditor::Cursor c(cursorPosition().line(), 0);
2799     KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
2800     doc()->indent(r, -1);
2801 }
2802 
cleanIndent()2803 void KTextEditor::ViewPrivate::cleanIndent()
2804 {
2805     KTextEditor::Cursor c(cursorPosition().line(), 0);
2806     KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
2807     doc()->indent(r, 0);
2808 }
2809 
align()2810 void KTextEditor::ViewPrivate::align()
2811 {
2812     // no selection: align current line; selection: use selection range
2813     const int line = cursorPosition().line();
2814     KTextEditor::Range alignRange(KTextEditor::Cursor(line, 0), KTextEditor::Cursor(line, 0));
2815     if (selection()) {
2816         alignRange = selectionRange();
2817     }
2818 
2819     doc()->align(this, alignRange);
2820 }
2821 
comment()2822 void KTextEditor::ViewPrivate::comment()
2823 {
2824     m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight);
2825     doc()->comment(this, cursorPosition().line(), cursorPosition().column(), 1);
2826     m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight);
2827 }
2828 
uncomment()2829 void KTextEditor::ViewPrivate::uncomment()
2830 {
2831     doc()->comment(this, cursorPosition().line(), cursorPosition().column(), -1);
2832 }
2833 
toggleComment()2834 void KTextEditor::ViewPrivate::toggleComment()
2835 {
2836     m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight);
2837     doc()->comment(this, cursorPosition().line(), cursorPosition().column(), 0);
2838     m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight);
2839 }
2840 
uppercase()2841 void KTextEditor::ViewPrivate::uppercase()
2842 {
2843     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Uppercase);
2844 }
2845 
killLine()2846 void KTextEditor::ViewPrivate::killLine()
2847 {
2848     if (m_selection.isEmpty()) {
2849         doc()->removeLine(cursorPosition().line());
2850     } else {
2851         doc()->editStart();
2852         // cache endline, else that moves and we might delete complete document if last line is selected!
2853         for (int line = m_selection.end().line(), endLine = m_selection.start().line(); line >= endLine; line--) {
2854             doc()->removeLine(line);
2855         }
2856         doc()->editEnd();
2857     }
2858 }
2859 
lowercase()2860 void KTextEditor::ViewPrivate::lowercase()
2861 {
2862     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Lowercase);
2863 }
2864 
capitalize()2865 void KTextEditor::ViewPrivate::capitalize()
2866 {
2867     doc()->editStart();
2868     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Lowercase);
2869     doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Capitalize);
2870     doc()->editEnd();
2871 }
2872 
keyReturn()2873 void KTextEditor::ViewPrivate::keyReturn()
2874 {
2875     doc()->newLine(this);
2876     m_viewInternal->iconBorder()->updateForCursorLineChange();
2877     m_viewInternal->updateView();
2878 }
2879 
smartNewline()2880 void KTextEditor::ViewPrivate::smartNewline()
2881 {
2882     const KTextEditor::Cursor cursor = cursorPosition();
2883     const int ln = cursor.line();
2884     Kate::TextLine line = doc()->kateTextLine(ln);
2885     int col = qMin(cursor.column(), line->firstChar());
2886     if (col != -1) {
2887         while (line->length() > col && !(line->at(col).isLetterOrNumber() || line->at(col) == QLatin1Char('_')) && col < cursor.column()) {
2888             ++col;
2889         }
2890     } else {
2891         col = line->length(); // stay indented
2892     }
2893     doc()->editStart();
2894     doc()->editWrapLine(ln, cursor.column());
2895     doc()->insertText(KTextEditor::Cursor(ln + 1, 0), line->string(0, col));
2896     doc()->editEnd();
2897 
2898     m_viewInternal->updateView();
2899 }
2900 
noIndentNewline()2901 void KTextEditor::ViewPrivate::noIndentNewline()
2902 {
2903     doc()->newLine(this, KTextEditor::DocumentPrivate::NoIndent);
2904     m_viewInternal->iconBorder()->updateForCursorLineChange();
2905     m_viewInternal->updateView();
2906 }
2907 
backspace()2908 void KTextEditor::ViewPrivate::backspace()
2909 {
2910     doc()->backspace(this, cursorPosition());
2911 }
2912 
insertTab()2913 void KTextEditor::ViewPrivate::insertTab()
2914 {
2915     doc()->insertTab(this, cursorPosition());
2916 }
2917 
deleteWordLeft()2918 void KTextEditor::ViewPrivate::deleteWordLeft()
2919 {
2920     doc()->editStart();
2921     m_viewInternal->wordPrev(true);
2922     KTextEditor::Range selection = selectionRange();
2923     removeSelectedText();
2924     doc()->editEnd();
2925     m_viewInternal->tagRange(selection, true);
2926     m_viewInternal->updateDirty();
2927 }
2928 
keyDelete()2929 void KTextEditor::ViewPrivate::keyDelete()
2930 {
2931     doc()->del(this, cursorPosition());
2932 }
2933 
deleteWordRight()2934 void KTextEditor::ViewPrivate::deleteWordRight()
2935 {
2936     doc()->editStart();
2937     m_viewInternal->wordNext(true);
2938     KTextEditor::Range selection = selectionRange();
2939     removeSelectedText();
2940     doc()->editEnd();
2941     m_viewInternal->tagRange(selection, true);
2942     m_viewInternal->updateDirty();
2943 }
2944 
transpose()2945 void KTextEditor::ViewPrivate::transpose()
2946 {
2947     doc()->transpose(cursorPosition());
2948 }
2949 
transposeWord()2950 void KTextEditor::ViewPrivate::transposeWord()
2951 {
2952     const KTextEditor::Cursor originalCurPos = cursorPosition();
2953     const KTextEditor::Range firstWord = doc()->wordRangeAt(originalCurPos);
2954     if (!firstWord.isValid()) {
2955         return;
2956     }
2957 
2958     auto wordIsInvalid = [](QStringView word) {
2959         for (const QChar &character : word) {
2960             if (character.isLetterOrNumber()) {
2961                 return false;
2962             }
2963         }
2964         return true;
2965     };
2966 
2967     if (wordIsInvalid(doc()->text(firstWord))) {
2968         return;
2969     }
2970 
2971     setCursorPosition(firstWord.end());
2972     wordRight();
2973     KTextEditor::Cursor curPos = cursorPosition();
2974     // swap with the word to the right if it exists, otherwise try to swap with word to the left
2975     if (curPos.line() != firstWord.end().line() || curPos.column() == firstWord.end().column()) {
2976         setCursorPosition(firstWord.start());
2977         wordLeft();
2978         curPos = cursorPosition();
2979         // if there is still no next word in this line, no swapping will be done
2980         if (curPos.line() != firstWord.start().line() || curPos.column() == firstWord.start().column() || wordIsInvalid(doc()->wordAt(curPos))) {
2981             setCursorPosition(originalCurPos);
2982             return;
2983         }
2984     }
2985 
2986     if (wordIsInvalid(doc()->wordAt(curPos))) {
2987         setCursorPosition(originalCurPos);
2988         return;
2989     }
2990 
2991     const KTextEditor::Range secondWord = doc()->wordRangeAt(curPos);
2992     doc()->swapTextRanges(firstWord, secondWord);
2993 
2994     // return cursor to its original position inside the word before swap
2995     // 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
2996     const int offsetFromWordEnd = firstWord.end().column() - originalCurPos.column();
2997     setCursorPosition(cursorPosition() - KTextEditor::Cursor(0, offsetFromWordEnd));
2998 }
2999 
cursorLeft()3000 void KTextEditor::ViewPrivate::cursorLeft()
3001 {
3002     if (selection() && !config()->persistentSelection()) {
3003         if (currentTextLine().isRightToLeft()) {
3004             m_viewInternal->updateCursor(selectionRange().end());
3005             setSelection(KTextEditor::Range::invalid());
3006         } else {
3007             m_viewInternal->updateCursor(selectionRange().start());
3008             setSelection(KTextEditor::Range::invalid());
3009         }
3010 
3011     } else {
3012         if (currentTextLine().isRightToLeft()) {
3013             m_viewInternal->cursorNextChar();
3014         } else {
3015             m_viewInternal->cursorPrevChar();
3016         }
3017     }
3018 }
3019 
shiftCursorLeft()3020 void KTextEditor::ViewPrivate::shiftCursorLeft()
3021 {
3022     if (currentTextLine().isRightToLeft()) {
3023         m_viewInternal->cursorNextChar(true);
3024     } else {
3025         m_viewInternal->cursorPrevChar(true);
3026     }
3027 }
3028 
cursorRight()3029 void KTextEditor::ViewPrivate::cursorRight()
3030 {
3031     if (selection() && !config()->persistentSelection()) {
3032         if (currentTextLine().isRightToLeft()) {
3033             m_viewInternal->updateCursor(selectionRange().start());
3034             setSelection(KTextEditor::Range::invalid());
3035         } else {
3036             m_viewInternal->updateCursor(selectionRange().end());
3037             setSelection(KTextEditor::Range::invalid());
3038         }
3039 
3040     } else {
3041         if (currentTextLine().isRightToLeft()) {
3042             m_viewInternal->cursorPrevChar();
3043         } else {
3044             m_viewInternal->cursorNextChar();
3045         }
3046     }
3047 }
3048 
shiftCursorRight()3049 void KTextEditor::ViewPrivate::shiftCursorRight()
3050 {
3051     if (currentTextLine().isRightToLeft()) {
3052         m_viewInternal->cursorPrevChar(true);
3053     } else {
3054         m_viewInternal->cursorNextChar(true);
3055     }
3056 }
3057 
wordLeft()3058 void KTextEditor::ViewPrivate::wordLeft()
3059 {
3060     if (currentTextLine().isRightToLeft()) {
3061         m_viewInternal->wordNext(m_viewInternal->isUserSelecting());
3062     } else {
3063         m_viewInternal->wordPrev(m_viewInternal->isUserSelecting());
3064     }
3065 }
3066 
shiftWordLeft()3067 void KTextEditor::ViewPrivate::shiftWordLeft()
3068 {
3069     if (currentTextLine().isRightToLeft()) {
3070         m_viewInternal->wordNext(true);
3071     } else {
3072         m_viewInternal->wordPrev(true);
3073     }
3074 }
3075 
wordRight()3076 void KTextEditor::ViewPrivate::wordRight()
3077 {
3078     if (currentTextLine().isRightToLeft()) {
3079         m_viewInternal->wordPrev(m_viewInternal->isUserSelecting());
3080     } else {
3081         m_viewInternal->wordNext(m_viewInternal->isUserSelecting());
3082     }
3083 }
3084 
shiftWordRight()3085 void KTextEditor::ViewPrivate::shiftWordRight()
3086 {
3087     if (currentTextLine().isRightToLeft()) {
3088         m_viewInternal->wordPrev(true);
3089     } else {
3090         m_viewInternal->wordNext(true);
3091     }
3092 }
3093 
home()3094 void KTextEditor::ViewPrivate::home()
3095 {
3096     m_viewInternal->home(m_viewInternal->isUserSelecting());
3097 }
3098 
shiftHome()3099 void KTextEditor::ViewPrivate::shiftHome()
3100 {
3101     m_viewInternal->home(true);
3102 }
3103 
end()3104 void KTextEditor::ViewPrivate::end()
3105 {
3106     m_viewInternal->end(m_viewInternal->isUserSelecting());
3107 }
3108 
shiftEnd()3109 void KTextEditor::ViewPrivate::shiftEnd()
3110 {
3111     m_viewInternal->end(true);
3112 }
3113 
up()3114 void KTextEditor::ViewPrivate::up()
3115 {
3116     m_viewInternal->cursorUp(m_viewInternal->isUserSelecting());
3117 }
3118 
shiftUp()3119 void KTextEditor::ViewPrivate::shiftUp()
3120 {
3121     m_viewInternal->cursorUp(true);
3122 }
3123 
down()3124 void KTextEditor::ViewPrivate::down()
3125 {
3126     m_viewInternal->cursorDown(m_viewInternal->isUserSelecting());
3127 }
3128 
shiftDown()3129 void KTextEditor::ViewPrivate::shiftDown()
3130 {
3131     m_viewInternal->cursorDown(true);
3132 }
3133 
scrollUp()3134 void KTextEditor::ViewPrivate::scrollUp()
3135 {
3136     m_viewInternal->scrollUp();
3137 }
3138 
scrollDown()3139 void KTextEditor::ViewPrivate::scrollDown()
3140 {
3141     m_viewInternal->scrollDown();
3142 }
3143 
topOfView()3144 void KTextEditor::ViewPrivate::topOfView()
3145 {
3146     m_viewInternal->topOfView();
3147 }
3148 
shiftTopOfView()3149 void KTextEditor::ViewPrivate::shiftTopOfView()
3150 {
3151     m_viewInternal->topOfView(true);
3152 }
3153 
bottomOfView()3154 void KTextEditor::ViewPrivate::bottomOfView()
3155 {
3156     m_viewInternal->bottomOfView();
3157 }
3158 
shiftBottomOfView()3159 void KTextEditor::ViewPrivate::shiftBottomOfView()
3160 {
3161     m_viewInternal->bottomOfView(true);
3162 }
3163 
pageUp()3164 void KTextEditor::ViewPrivate::pageUp()
3165 {
3166     m_viewInternal->pageUp(m_viewInternal->isUserSelecting());
3167 }
3168 
shiftPageUp()3169 void KTextEditor::ViewPrivate::shiftPageUp()
3170 {
3171     m_viewInternal->pageUp(true);
3172 }
3173 
pageDown()3174 void KTextEditor::ViewPrivate::pageDown()
3175 {
3176     m_viewInternal->pageDown(m_viewInternal->isUserSelecting());
3177 }
3178 
shiftPageDown()3179 void KTextEditor::ViewPrivate::shiftPageDown()
3180 {
3181     m_viewInternal->pageDown(true);
3182 }
3183 
top()3184 void KTextEditor::ViewPrivate::top()
3185 {
3186     m_viewInternal->top_home();
3187 }
3188 
shiftTop()3189 void KTextEditor::ViewPrivate::shiftTop()
3190 {
3191     m_viewInternal->top_home(true);
3192 }
3193 
bottom()3194 void KTextEditor::ViewPrivate::bottom()
3195 {
3196     m_viewInternal->bottom_end();
3197 }
3198 
shiftBottom()3199 void KTextEditor::ViewPrivate::shiftBottom()
3200 {
3201     m_viewInternal->bottom_end(true);
3202 }
3203 
toMatchingBracket()3204 void KTextEditor::ViewPrivate::toMatchingBracket()
3205 {
3206     m_viewInternal->cursorToMatchingBracket();
3207 }
3208 
shiftToMatchingBracket()3209 void KTextEditor::ViewPrivate::shiftToMatchingBracket()
3210 {
3211     m_viewInternal->cursorToMatchingBracket(true);
3212 }
3213 
toPrevModifiedLine()3214 void KTextEditor::ViewPrivate::toPrevModifiedLine()
3215 {
3216     const int startLine = cursorPosition().line() - 1;
3217     const int line = doc()->findTouchedLine(startLine, false);
3218     if (line >= 0) {
3219         KTextEditor::Cursor c(line, 0);
3220         m_viewInternal->updateSelection(c, false);
3221         m_viewInternal->updateCursor(c);
3222     }
3223 }
3224 
toNextModifiedLine()3225 void KTextEditor::ViewPrivate::toNextModifiedLine()
3226 {
3227     const int startLine = cursorPosition().line() + 1;
3228     const int line = doc()->findTouchedLine(startLine, true);
3229     if (line >= 0) {
3230         KTextEditor::Cursor c(line, 0);
3231         m_viewInternal->updateSelection(c, false);
3232         m_viewInternal->updateCursor(c);
3233     }
3234 }
3235 
selectionRange() const3236 KTextEditor::Range KTextEditor::ViewPrivate::selectionRange() const
3237 {
3238     return m_selection;
3239 }
3240 
document() const3241 KTextEditor::Document *KTextEditor::ViewPrivate::document() const
3242 {
3243     return m_doc;
3244 }
3245 
setContextMenu(QMenu * menu)3246 void KTextEditor::ViewPrivate::setContextMenu(QMenu *menu)
3247 {
3248     if (m_contextMenu) {
3249         disconnect(m_contextMenu.data(), &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
3250         disconnect(m_contextMenu.data(), &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
3251     }
3252     m_contextMenu = menu;
3253     m_userContextMenuSet = true;
3254 
3255     if (m_contextMenu) {
3256         connect(m_contextMenu.data(), &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
3257         connect(m_contextMenu.data(), &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
3258     }
3259 }
3260 
contextMenu() const3261 QMenu *KTextEditor::ViewPrivate::contextMenu() const
3262 {
3263     if (m_userContextMenuSet) {
3264         return m_contextMenu;
3265     } else {
3266         KXMLGUIClient *client = const_cast<KTextEditor::ViewPrivate *>(this);
3267         while (client->parentClient()) {
3268             client = client->parentClient();
3269         }
3270 
3271         // qCDebug(LOG_KTE) << "looking up all menu containers";
3272         if (client->factory()) {
3273             const QList<QWidget *> menuContainers = client->factory()->containers(QStringLiteral("menu"));
3274             for (QWidget *w : menuContainers) {
3275                 if (w->objectName() == QLatin1String("ktexteditor_popup")) {
3276                     // perhaps optimize this block
3277                     QMenu *menu = (QMenu *)w;
3278                     // menu is a reusable instance shared among all views. Therefore,
3279                     // disconnect the current receiver(s) from the menu show/hide signals
3280                     // before connecting `this` view. This ensures that only the current
3281                     // view gets a signal when the menu is about to be shown or hidden,
3282                     // and not also the view(s) that previously had the menu open.
3283                     disconnect(menu, &QMenu::aboutToShow, nullptr, nullptr);
3284                     disconnect(menu, &QMenu::aboutToHide, nullptr, nullptr);
3285                     connect(menu, &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
3286                     connect(menu, &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
3287                     return menu;
3288                 }
3289             }
3290         }
3291     }
3292     return nullptr;
3293 }
3294 
defaultContextMenu(QMenu * menu) const3295 QMenu *KTextEditor::ViewPrivate::defaultContextMenu(QMenu *menu) const
3296 {
3297     if (!menu) {
3298         menu = new QMenu(const_cast<KTextEditor::ViewPrivate *>(this));
3299     }
3300 
3301     if (m_editUndo) {
3302         menu->addAction(m_editUndo);
3303     }
3304     if (m_editRedo) {
3305         menu->addAction(m_editRedo);
3306     }
3307 
3308     menu->addSeparator();
3309     menu->addAction(m_cut);
3310     menu->addAction(m_copy);
3311     menu->addAction(m_paste);
3312     if (m_pasteSelection) {
3313         menu->addAction(m_pasteSelection);
3314     }
3315     menu->addAction(m_swapWithClipboard);
3316     menu->addSeparator();
3317     menu->addAction(m_selectAll);
3318     menu->addAction(m_deSelect);
3319     QAction *editing = actionCollection()->action(QStringLiteral("tools_scripts_Editing"));
3320     if (editing) {
3321         menu->addAction(editing);
3322     }
3323     if (QAction *spellingSuggestions = actionCollection()->action(QStringLiteral("spelling_suggestions"))) {
3324         menu->addSeparator();
3325         menu->addAction(spellingSuggestions);
3326     }
3327     if (QAction *bookmark = actionCollection()->action(QStringLiteral("bookmarks"))) {
3328         menu->addSeparator();
3329         menu->addAction(bookmark);
3330     }
3331 
3332     return menu;
3333 }
3334 
aboutToShowContextMenu()3335 void KTextEditor::ViewPrivate::aboutToShowContextMenu()
3336 {
3337     QMenu *menu = qobject_cast<QMenu *>(sender());
3338 
3339     if (menu) {
3340         Q_EMIT contextMenuAboutToShow(this, menu);
3341     }
3342 }
3343 
aboutToHideContextMenu()3344 void KTextEditor::ViewPrivate::aboutToHideContextMenu()
3345 {
3346     m_spellingMenu->setUseMouseForMisspelledRange(false);
3347 }
3348 
3349 // BEGIN ConfigInterface stff
configKeys() const3350 QStringList KTextEditor::ViewPrivate::configKeys() const
3351 {
3352     static const QStringList keys = {QStringLiteral("icon-bar"),
3353                                      QStringLiteral("line-numbers"),
3354                                      QStringLiteral("dynamic-word-wrap"),
3355                                      QStringLiteral("background-color"),
3356                                      QStringLiteral("selection-color"),
3357                                      QStringLiteral("search-highlight-color"),
3358                                      QStringLiteral("replace-highlight-color"),
3359                                      QStringLiteral("default-mark-type"),
3360                                      QStringLiteral("allow-mark-menu"),
3361                                      QStringLiteral("folding-bar"),
3362                                      QStringLiteral("folding-preview"),
3363                                      QStringLiteral("icon-border-color"),
3364                                      QStringLiteral("folding-marker-color"),
3365                                      QStringLiteral("line-number-color"),
3366                                      QStringLiteral("current-line-number-color"),
3367                                      QStringLiteral("modification-markers"),
3368                                      QStringLiteral("keyword-completion"),
3369                                      QStringLiteral("word-count"),
3370                                      QStringLiteral("line-count"),
3371                                      QStringLiteral("scrollbar-minimap"),
3372                                      QStringLiteral("scrollbar-preview"),
3373                                      QStringLiteral("font"),
3374                                      QStringLiteral("theme")};
3375     return keys;
3376 }
3377 
configValue(const QString & key)3378 QVariant KTextEditor::ViewPrivate::configValue(const QString &key)
3379 {
3380     if (key == QLatin1String("icon-bar")) {
3381         return config()->iconBar();
3382     } else if (key == QLatin1String("line-numbers")) {
3383         return config()->lineNumbers();
3384     } else if (key == QLatin1String("dynamic-word-wrap")) {
3385         return config()->dynWordWrap();
3386     } else if (key == QLatin1String("background-color")) {
3387         return renderer()->config()->backgroundColor();
3388     } else if (key == QLatin1String("selection-color")) {
3389         return renderer()->config()->selectionColor();
3390     } else if (key == QLatin1String("search-highlight-color")) {
3391         return renderer()->config()->searchHighlightColor();
3392     } else if (key == QLatin1String("replace-highlight-color")) {
3393         return renderer()->config()->replaceHighlightColor();
3394     } else if (key == QLatin1String("default-mark-type")) {
3395         return config()->defaultMarkType();
3396     } else if (key == QLatin1String("allow-mark-menu")) {
3397         return config()->allowMarkMenu();
3398     } else if (key == QLatin1String("folding-bar")) {
3399         return config()->foldingBar();
3400     } else if (key == QLatin1String("folding-preview")) {
3401         return config()->foldingPreview();
3402     } else if (key == QLatin1String("icon-border-color")) {
3403         return renderer()->config()->iconBarColor();
3404     } else if (key == QLatin1String("folding-marker-color")) {
3405         return renderer()->config()->foldingColor();
3406     } else if (key == QLatin1String("line-number-color")) {
3407         return renderer()->config()->lineNumberColor();
3408     } else if (key == QLatin1String("current-line-number-color")) {
3409         return renderer()->config()->currentLineNumberColor();
3410     } else if (key == QLatin1String("modification-markers")) {
3411         return config()->lineModification();
3412     } else if (key == QLatin1String("keyword-completion")) {
3413         return config()->keywordCompletion();
3414     } else if (key == QLatin1String("word-count")) {
3415         return config()->showWordCount();
3416     } else if (key == QLatin1String("line-count")) {
3417         return config()->showLineCount();
3418     } else if (key == QLatin1String("scrollbar-minimap")) {
3419         return config()->scrollBarMiniMap();
3420     } else if (key == QLatin1String("scrollbar-preview")) {
3421         return config()->scrollBarPreview();
3422     } else if (key == QLatin1String("font")) {
3423         return renderer()->config()->baseFont();
3424     } else if (key == QLatin1String("theme")) {
3425         return renderer()->config()->schema();
3426     }
3427 
3428     // return invalid variant
3429     return QVariant();
3430 }
3431 
setConfigValue(const QString & key,const QVariant & value)3432 void KTextEditor::ViewPrivate::setConfigValue(const QString &key, const QVariant &value)
3433 {
3434     // First, try the new config interface
3435     if (config()->setValue(key, value)) {
3436         return;
3437 
3438     } else if (renderer()->config()->setValue(key, value)) {
3439         return;
3440     }
3441 
3442     // No success? Go the old way
3443     if (value.canConvert(QVariant::Color)) {
3444         if (key == QLatin1String("background-color")) {
3445             renderer()->config()->setBackgroundColor(value.value<QColor>());
3446         } else if (key == QLatin1String("selection-color")) {
3447             renderer()->config()->setSelectionColor(value.value<QColor>());
3448         } else if (key == QLatin1String("search-highlight-color")) {
3449             renderer()->config()->setSearchHighlightColor(value.value<QColor>());
3450         } else if (key == QLatin1String("replace-highlight-color")) {
3451             renderer()->config()->setReplaceHighlightColor(value.value<QColor>());
3452         } else if (key == QLatin1String("icon-border-color")) {
3453             renderer()->config()->setIconBarColor(value.value<QColor>());
3454         } else if (key == QLatin1String("folding-marker-color")) {
3455             renderer()->config()->setFoldingColor(value.value<QColor>());
3456         } else if (key == QLatin1String("line-number-color")) {
3457             renderer()->config()->setLineNumberColor(value.value<QColor>());
3458         } else if (key == QLatin1String("current-line-number-color")) {
3459             renderer()->config()->setCurrentLineNumberColor(value.value<QColor>());
3460         }
3461     } else if (value.type() == QVariant::Bool) {
3462         // Note explicit type check above. If we used canConvert, then
3463         // values of type UInt will be trapped here.
3464         if (key == QLatin1String("dynamic-word-wrap")) {
3465             config()->setDynWordWrap(value.toBool());
3466         } else if (key == QLatin1String("word-count")) {
3467             config()->setShowWordCount(value.toBool());
3468         } else if (key == QLatin1String("line-count")) {
3469             config()->setShowLineCount(value.toBool());
3470         }
3471     } else if (key == QLatin1String("font") && value.canConvert(QVariant::Font)) {
3472         renderer()->config()->setFont(value.value<QFont>());
3473     } else if (key == QLatin1String("theme") && value.type() == QVariant::String) {
3474         renderer()->config()->setSchema(value.value<QString>());
3475     }
3476 }
3477 
3478 // END ConfigInterface
3479 
userInvokedCompletion()3480 void KTextEditor::ViewPrivate::userInvokedCompletion()
3481 {
3482     completionWidget()->userInvokedCompletion();
3483 }
3484 
bottomViewBar() const3485 KateViewBar *KTextEditor::ViewPrivate::bottomViewBar() const
3486 {
3487     return m_bottomViewBar;
3488 }
3489 
gotoBar()3490 KateGotoBar *KTextEditor::ViewPrivate::gotoBar()
3491 {
3492     if (!m_gotoBar) {
3493         m_gotoBar = new KateGotoBar(this);
3494         bottomViewBar()->addBarWidget(m_gotoBar);
3495     }
3496 
3497     return m_gotoBar;
3498 }
3499 
dictionaryBar()3500 KateDictionaryBar *KTextEditor::ViewPrivate::dictionaryBar()
3501 {
3502     if (!m_dictionaryBar) {
3503         m_dictionaryBar = new KateDictionaryBar(this);
3504         bottomViewBar()->addBarWidget(m_dictionaryBar);
3505     }
3506 
3507     return m_dictionaryBar;
3508 }
3509 
setAnnotationModel(KTextEditor::AnnotationModel * model)3510 void KTextEditor::ViewPrivate::setAnnotationModel(KTextEditor::AnnotationModel *model)
3511 {
3512     KTextEditor::AnnotationModel *oldmodel = m_annotationModel;
3513     m_annotationModel = model;
3514     m_viewInternal->m_leftBorder->annotationModelChanged(oldmodel, m_annotationModel);
3515 }
3516 
annotationModel() const3517 KTextEditor::AnnotationModel *KTextEditor::ViewPrivate::annotationModel() const
3518 {
3519     return m_annotationModel;
3520 }
3521 
setAnnotationBorderVisible(bool visible)3522 void KTextEditor::ViewPrivate::setAnnotationBorderVisible(bool visible)
3523 {
3524     m_viewInternal->m_leftBorder->setAnnotationBorderOn(visible);
3525 }
3526 
isAnnotationBorderVisible() const3527 bool KTextEditor::ViewPrivate::isAnnotationBorderVisible() const
3528 {
3529     return m_viewInternal->m_leftBorder->annotationBorderOn();
3530 }
3531 
annotationItemDelegate() const3532 KTextEditor::AbstractAnnotationItemDelegate *KTextEditor::ViewPrivate::annotationItemDelegate() const
3533 {
3534     return m_viewInternal->m_leftBorder->annotationItemDelegate();
3535 }
3536 
setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate * delegate)3537 void KTextEditor::ViewPrivate::setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate)
3538 {
3539     m_viewInternal->m_leftBorder->setAnnotationItemDelegate(delegate);
3540 }
3541 
uniformAnnotationItemSizes() const3542 bool KTextEditor::ViewPrivate::uniformAnnotationItemSizes() const
3543 {
3544     return m_viewInternal->m_leftBorder->uniformAnnotationItemSizes();
3545 }
3546 
setAnnotationUniformItemSizes(bool enable)3547 void KTextEditor::ViewPrivate::setAnnotationUniformItemSizes(bool enable)
3548 {
3549     m_viewInternal->m_leftBorder->setAnnotationUniformItemSizes(enable);
3550 }
3551 
visibleRange()3552 KTextEditor::Range KTextEditor::ViewPrivate::visibleRange()
3553 {
3554     // ensure that the view is up-to-date, otherwise 'endPos()' might fail!
3555     if (!m_viewInternal->endPos().isValid()) {
3556         m_viewInternal->updateView();
3557     }
3558     return KTextEditor::Range(m_viewInternal->toRealCursor(m_viewInternal->startPos()), m_viewInternal->toRealCursor(m_viewInternal->endPos()));
3559 }
3560 
event(QEvent * e)3561 bool KTextEditor::ViewPrivate::event(QEvent *e)
3562 {
3563     switch (e->type()) {
3564     case QEvent::StyleChange:
3565         setupLayout();
3566         return true;
3567     default:
3568         return KTextEditor::View::event(e);
3569     }
3570 }
3571 
paintEvent(QPaintEvent * e)3572 void KTextEditor::ViewPrivate::paintEvent(QPaintEvent *e)
3573 {
3574     // base class
3575     KTextEditor::View::paintEvent(e);
3576 
3577     const QRect contentsRect = m_topSpacer->geometry() | m_bottomSpacer->geometry() | m_leftSpacer->geometry() | m_rightSpacer->geometry();
3578 
3579     if (contentsRect.isValid()) {
3580         QStyleOptionFrame opt;
3581         opt.initFrom(this);
3582         opt.frameShape = QFrame::StyledPanel;
3583         opt.state |= QStyle::State_Sunken;
3584 
3585         // clear mouseOver and focus state
3586         // update from relevant widgets
3587         opt.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
3588         const QList<QWidget *> widgets = QList<QWidget *>()
3589             << m_viewInternal << m_viewInternal->m_leftBorder << m_viewInternal->m_lineScroll << m_viewInternal->m_columnScroll;
3590         for (const QWidget *w : widgets) {
3591             if (w->hasFocus()) {
3592                 opt.state |= QStyle::State_HasFocus;
3593             }
3594             if (w->underMouse()) {
3595                 opt.state |= QStyle::State_MouseOver;
3596             }
3597         }
3598 
3599         // update rect
3600         opt.rect = contentsRect;
3601 
3602         // render
3603         QPainter paint(this);
3604         paint.setClipRegion(e->region());
3605         paint.setRenderHints(QPainter::Antialiasing);
3606         style()->drawControl(QStyle::CE_ShapedFrame, &opt, &paint, this);
3607     }
3608 }
3609 
toggleOnTheFlySpellCheck(bool b)3610 void KTextEditor::ViewPrivate::toggleOnTheFlySpellCheck(bool b)
3611 {
3612     doc()->onTheFlySpellCheckingEnabled(b);
3613 }
3614 
reflectOnTheFlySpellCheckStatus(bool enabled)3615 void KTextEditor::ViewPrivate::reflectOnTheFlySpellCheckStatus(bool enabled)
3616 {
3617     m_spellingMenu->setVisible(enabled);
3618     m_toggleOnTheFlySpellCheck->setChecked(enabled);
3619 }
3620 
spellingMenu()3621 KateSpellingMenu *KTextEditor::ViewPrivate::spellingMenu()
3622 {
3623     return m_spellingMenu;
3624 }
3625 
notifyAboutRangeChange(KTextEditor::LineRange lineRange,bool needsRepaint)3626 void KTextEditor::ViewPrivate::notifyAboutRangeChange(KTextEditor::LineRange lineRange, bool needsRepaint)
3627 {
3628 #ifdef VIEW_RANGE_DEBUG
3629     // output args
3630     qCDebug(LOG_KTE) << "trigger attribute changed in line range " << lineRange << "needsRepaint" << needsRepaint;
3631 #endif
3632 
3633     // if we need repaint, we will need to collect the line ranges we will update
3634     if (needsRepaint && lineRange.isValid()) {
3635         if (m_lineToUpdateRange.isValid()) {
3636             m_lineToUpdateRange.expandToRange(lineRange);
3637         } else {
3638             m_lineToUpdateRange = lineRange;
3639         }
3640     }
3641 
3642     // first call => trigger later update of view via delayed signal to group updates
3643     if (!m_delayedUpdateTimer.isActive()) {
3644         m_delayedUpdateTimer.start();
3645     }
3646 }
3647 
slotDelayedUpdateOfView()3648 void KTextEditor::ViewPrivate::slotDelayedUpdateOfView()
3649 {
3650 #ifdef VIEW_RANGE_DEBUG
3651     // output args
3652     qCDebug(LOG_KTE) << "delayed attribute changed in line range" << m_lineToUpdateRange;
3653 #endif
3654     // update ranges in
3655     updateRangesIn(KTextEditor::Attribute::ActivateMouseIn);
3656     updateRangesIn(KTextEditor::Attribute::ActivateCaretIn);
3657 
3658     // update view, if valid line range, else only feedback update wanted anyway
3659     if (m_lineToUpdateRange.isValid()) {
3660         tagLines(m_lineToUpdateRange, true);
3661         updateView(true);
3662     }
3663 
3664     // reset flags
3665     m_lineToUpdateRange = KTextEditor::LineRange::invalid();
3666 }
3667 
updateRangesIn(KTextEditor::Attribute::ActivationType activationType)3668 void KTextEditor::ViewPrivate::updateRangesIn(KTextEditor::Attribute::ActivationType activationType)
3669 {
3670     // new ranges with cursor in, default none
3671     QSet<Kate::TextRange *> newRangesIn;
3672 
3673     // on which range set we work?
3674     QSet<Kate::TextRange *> &oldSet = (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_rangesMouseIn : m_rangesCaretIn;
3675 
3676     // which cursor position to honor?
3677     KTextEditor::Cursor currentCursor =
3678         (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_viewInternal->mousePosition() : m_viewInternal->cursorPosition();
3679 
3680     // first: validate the remembered ranges
3681     QSet<Kate::TextRange *> validRanges;
3682     for (Kate::TextRange *range : std::as_const(oldSet)) {
3683         if (doc()->buffer().rangePointerValid(range)) {
3684             validRanges.insert(range);
3685         }
3686     }
3687 
3688     // cursor valid? else no new ranges can be found
3689     if (currentCursor.isValid() && currentCursor.line() < doc()->buffer().lines()) {
3690         // now: get current ranges for the line of cursor with an attribute
3691         const QVector<Kate::TextRange *> rangesForCurrentCursor = doc()->buffer().rangesForLine(currentCursor.line(), this, false);
3692 
3693         // match which ranges really fit the given cursor
3694         for (Kate::TextRange *range : rangesForCurrentCursor) {
3695             // range has no dynamic attribute of right type and no feedback object
3696             auto attribute = range->attribute();
3697             if ((!attribute || !attribute->dynamicAttribute(activationType)) && !range->feedback()) {
3698                 continue;
3699             }
3700 
3701             // range doesn't contain cursor, not interesting
3702             if ((range->startInternal().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) ? (currentCursor < range->toRange().start())
3703                                                                                                      : (currentCursor <= range->toRange().start())) {
3704                 continue;
3705             }
3706 
3707             if ((range->endInternal().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) ? (range->toRange().end() <= currentCursor)
3708                                                                                                    : (range->toRange().end() < currentCursor)) {
3709                 continue;
3710             }
3711 
3712             // range contains cursor, was it already in old set?
3713             auto it = validRanges.find(range);
3714             if (it != validRanges.end()) {
3715                 // insert in new, remove from old, be done with it
3716                 newRangesIn.insert(range);
3717                 validRanges.erase(it);
3718                 continue;
3719             }
3720 
3721             // oh, new range, trigger update and insert into new set
3722             newRangesIn.insert(range);
3723 
3724             if (attribute && attribute->dynamicAttribute(activationType)) {
3725                 notifyAboutRangeChange(range->toLineRange(), true);
3726             }
3727 
3728             // feedback
3729             if (range->feedback()) {
3730                 if (activationType == KTextEditor::Attribute::ActivateMouseIn) {
3731                     range->feedback()->mouseEnteredRange(range, this);
3732                 } else {
3733                     range->feedback()->caretEnteredRange(range, this);
3734                     Q_EMIT caretChangedRange(this);
3735                 }
3736             }
3737 
3738 #ifdef VIEW_RANGE_DEBUG
3739             // found new range for activation
3740             qCDebug(LOG_KTE) << "activated new range" << range << "by" << activationType;
3741 #endif
3742         }
3743     }
3744 
3745     // now: notify for left ranges!
3746     for (Kate::TextRange *range : std::as_const(validRanges)) {
3747         // range valid + right dynamic attribute, trigger update
3748         if (range->toRange().isValid() && range->attribute() && range->attribute()->dynamicAttribute(activationType)) {
3749             notifyAboutRangeChange(range->toLineRange(), true);
3750         }
3751 
3752         // feedback
3753         if (range->feedback()) {
3754             if (activationType == KTextEditor::Attribute::ActivateMouseIn) {
3755                 range->feedback()->mouseExitedRange(range, this);
3756             } else {
3757                 range->feedback()->caretExitedRange(range, this);
3758                 Q_EMIT caretChangedRange(this);
3759             }
3760         }
3761     }
3762 
3763     // set new ranges
3764     oldSet = newRangesIn;
3765 }
3766 
postMessage(KTextEditor::Message * message,QList<QSharedPointer<QAction>> actions)3767 void KTextEditor::ViewPrivate::postMessage(KTextEditor::Message *message, QList<QSharedPointer<QAction>> actions)
3768 {
3769     // just forward to KateMessageWidget :-)
3770     auto messageWidget = m_messageWidgets[message->position()];
3771     if (!messageWidget) {
3772         // this branch is used for: TopInView, CenterInView, and BottomInView
3773         messageWidget = new KateMessageWidget(m_viewInternal, true);
3774         m_messageWidgets[message->position()] = messageWidget;
3775         m_notificationLayout->addWidget(messageWidget, message->position());
3776         connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
3777         connect(this, &KTextEditor::ViewPrivate::cursorPositionChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
3778     }
3779     messageWidget->postMessage(message, std::move(actions));
3780 }
3781 
messageWidget()3782 KateMessageWidget *KTextEditor::ViewPrivate::messageWidget()
3783 {
3784     return m_messageWidgets[KTextEditor::Message::TopInView];
3785 }
3786 
saveFoldingState()3787 void KTextEditor::ViewPrivate::saveFoldingState()
3788 {
3789     m_savedFoldingState = m_textFolding.exportFoldingRanges();
3790 }
3791 
applyFoldingState()3792 void KTextEditor::ViewPrivate::applyFoldingState()
3793 {
3794     m_textFolding.importFoldingRanges(m_savedFoldingState);
3795     m_savedFoldingState = QJsonDocument();
3796 }
3797 
exportHtmlToFile(const QString & file)3798 void KTextEditor::ViewPrivate::exportHtmlToFile(const QString &file)
3799 {
3800     KateExporter(this).exportToFile(file);
3801 }
3802 
exportHtmlToClipboard()3803 void KTextEditor::ViewPrivate::exportHtmlToClipboard()
3804 {
3805     KateExporter(this).exportToClipboard();
3806 }
3807 
exportHtmlToFile()3808 void KTextEditor::ViewPrivate::exportHtmlToFile()
3809 {
3810     const QString file = QFileDialog::getSaveFileName(this, i18n("Export File as HTML"), doc()->documentName());
3811     if (!file.isEmpty()) {
3812         KateExporter(this).exportToFile(file);
3813     }
3814 }
3815 
clearHighlights()3816 void KTextEditor::ViewPrivate::clearHighlights()
3817 {
3818     m_rangesForHighlights.clear();
3819     m_currentTextForHighlights.clear();
3820 }
3821 
selectionChangedForHighlights()3822 void KTextEditor::ViewPrivate::selectionChangedForHighlights()
3823 {
3824     QString text;
3825     // if text of selection is still the same, abort
3826     if (selection() && selectionRange().onSingleLine()) {
3827         text = selectionText();
3828         if (text == m_currentTextForHighlights) {
3829             return;
3830         }
3831     }
3832 
3833     // text changed: remove all highlights + create new ones
3834     // (do not call clearHighlights(), since this also resets the m_currentTextForHighlights
3835     m_rangesForHighlights.clear();
3836 
3837     // do not highlight strings with leading and trailing spaces
3838     if (!text.isEmpty() && (text.at(0).isSpace() || text.at(text.length() - 1).isSpace())) {
3839         return;
3840     }
3841 
3842     // trigger creation of ranges for current view range
3843     m_currentTextForHighlights = text;
3844     createHighlights();
3845 }
3846 
createHighlights()3847 void KTextEditor::ViewPrivate::createHighlights()
3848 {
3849     // do nothing if no text to highlight
3850     if (m_currentTextForHighlights.isEmpty()) {
3851         return;
3852     }
3853 
3854     // clear existing highlighting ranges, otherwise we stack over and over the same ones eventually
3855     m_rangesForHighlights.clear();
3856 
3857     KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute());
3858     attr->setBackground(Qt::yellow);
3859 
3860     // set correct highlight color from Kate's color schema
3861     QColor fgColor = defaultStyleAttribute(KTextEditor::dsNormal)->foreground().color();
3862     QColor bgColor = renderer()->config()->searchHighlightColor();
3863     attr->setForeground(fgColor);
3864     attr->setBackground(bgColor);
3865 
3866     KTextEditor::Cursor start(visibleRange().start());
3867     KTextEditor::Range searchRange;
3868 
3869     // only add word boundary if we can find the text then
3870     // fixes $lala hl
3871     QString pattern = QRegularExpression::escape(m_currentTextForHighlights);
3872     if (m_currentTextForHighlights.contains(QRegularExpression(QLatin1String("\\b") + pattern, QRegularExpression::UseUnicodePropertiesOption))) {
3873         pattern.prepend(QLatin1String("\\b"));
3874     }
3875 
3876     if (m_currentTextForHighlights.contains(QRegularExpression(pattern + QLatin1String("\\b"), QRegularExpression::UseUnicodePropertiesOption))) {
3877         pattern += QLatin1String("\\b");
3878     }
3879 
3880     QVector<KTextEditor::Range> matches;
3881     do {
3882         searchRange.setRange(start, visibleRange().end());
3883 
3884         matches = doc()->searchText(searchRange, pattern, KTextEditor::Regex);
3885 
3886         if (matches.first().isValid()) {
3887             std::unique_ptr<KTextEditor::MovingRange> mr(doc()->newMovingRange(matches.first()));
3888             mr->setZDepth(-90000.0); // Set the z-depth to slightly worse than the selection
3889             mr->setAttribute(attr);
3890             mr->setView(this);
3891             mr->setAttributeOnlyForViews(true);
3892             m_rangesForHighlights.push_back(std::move(mr));
3893             start = matches.first().end();
3894         }
3895     } while (matches.first().isValid());
3896 }
3897 
currentInputMode() const3898 KateAbstractInputMode *KTextEditor::ViewPrivate::currentInputMode() const
3899 {
3900     return m_viewInternal->m_currentInputMode;
3901 }
3902 
toggleInputMode()3903 void KTextEditor::ViewPrivate::toggleInputMode()
3904 {
3905     if (QAction *a = dynamic_cast<QAction *>(sender())) {
3906         setInputMode(static_cast<KTextEditor::View::InputMode>(a->data().toInt()));
3907     }
3908 }
3909 
cycleInputMode()3910 void KTextEditor::ViewPrivate::cycleInputMode()
3911 {
3912     InputMode current = currentInputMode()->viewInputMode();
3913     InputMode to = (current == KTextEditor::View::NormalInputMode) ? KTextEditor::View::ViInputMode : KTextEditor::View::NormalInputMode;
3914     setInputMode(to);
3915 }
3916 
3917 // BEGIN KTextEditor::PrintInterface stuff
print()3918 bool KTextEditor::ViewPrivate::print()
3919 {
3920     return KatePrinter::print(this);
3921 }
3922 
printPreview()3923 void KTextEditor::ViewPrivate::printPreview()
3924 {
3925     KatePrinter::printPreview(this);
3926 }
3927 
3928 // END
3929 
3930 // BEGIN KTextEditor::InlineNoteInterface
registerInlineNoteProvider(KTextEditor::InlineNoteProvider * provider)3931 void KTextEditor::ViewPrivate::registerInlineNoteProvider(KTextEditor::InlineNoteProvider *provider)
3932 {
3933     if (std::find(m_inlineNoteProviders.cbegin(), m_inlineNoteProviders.cend(), provider) == m_inlineNoteProviders.cend()) {
3934         m_inlineNoteProviders.push_back(provider);
3935 
3936         connect(provider, &KTextEditor::InlineNoteProvider::inlineNotesReset, this, &KTextEditor::ViewPrivate::inlineNotesReset);
3937         connect(provider, &KTextEditor::InlineNoteProvider::inlineNotesChanged, this, &KTextEditor::ViewPrivate::inlineNotesLineChanged);
3938 
3939         inlineNotesReset();
3940     }
3941 }
3942 
unregisterInlineNoteProvider(KTextEditor::InlineNoteProvider * provider)3943 void KTextEditor::ViewPrivate::unregisterInlineNoteProvider(KTextEditor::InlineNoteProvider *provider)
3944 {
3945     auto it = std::find(m_inlineNoteProviders.cbegin(), m_inlineNoteProviders.cend(), provider);
3946     if (it != m_inlineNoteProviders.cend()) {
3947         m_inlineNoteProviders.erase(it);
3948         provider->disconnect(this);
3949 
3950         inlineNotesReset();
3951     }
3952 }
3953 
inlineNotes(int line) const3954 QVarLengthArray<KateInlineNoteData, 8> KTextEditor::ViewPrivate::inlineNotes(int line) const
3955 {
3956     QVarLengthArray<KateInlineNoteData, 8> allInlineNotes;
3957     for (KTextEditor::InlineNoteProvider *provider : m_inlineNoteProviders) {
3958         int index = 0;
3959         for (auto column : provider->inlineNotes(line)) {
3960             const bool underMouse = Cursor(line, column) == m_viewInternal->m_activeInlineNote.m_position;
3961             KateInlineNoteData note =
3962                 {provider, this, {line, column}, index, underMouse, m_viewInternal->renderer()->currentFont(), m_viewInternal->renderer()->lineHeight()};
3963             allInlineNotes.append(note);
3964             index++;
3965         }
3966     }
3967     return allInlineNotes;
3968 }
3969 
inlineNoteRect(const KateInlineNoteData & note) const3970 QRect KTextEditor::ViewPrivate::inlineNoteRect(const KateInlineNoteData &note) const
3971 {
3972     return m_viewInternal->inlineNoteRect(note);
3973 }
3974 
inlineNotesReset()3975 void KTextEditor::ViewPrivate::inlineNotesReset()
3976 {
3977     m_viewInternal->m_activeInlineNote = {};
3978     tagLines(KTextEditor::LineRange(0, doc()->lastLine()), true);
3979 }
3980 
inlineNotesLineChanged(int line)3981 void KTextEditor::ViewPrivate::inlineNotesLineChanged(int line)
3982 {
3983     if (line == m_viewInternal->m_activeInlineNote.m_position.line()) {
3984         m_viewInternal->m_activeInlineNote = {};
3985     }
3986     tagLines({line, line}, true);
3987 }
3988 
3989 // END KTextEditor::InlineNoteInterface
3990 
defaultStyleAttribute(KTextEditor::DefaultStyle defaultStyle) const3991 KTextEditor::Attribute::Ptr KTextEditor::ViewPrivate::defaultStyleAttribute(KTextEditor::DefaultStyle defaultStyle) const
3992 {
3993     KateRendererConfig *renderConfig = const_cast<KTextEditor::ViewPrivate *>(this)->renderer()->config();
3994 
3995     KTextEditor::Attribute::Ptr style = doc()->highlight()->attributes(renderConfig->schema()).at(defaultStyle);
3996     if (!style->hasProperty(QTextFormat::BackgroundBrush)) {
3997         // make sure the returned style has the default background color set
3998         style = new KTextEditor::Attribute(*style);
3999         style->setBackground(QBrush(renderConfig->backgroundColor()));
4000     }
4001     return style;
4002 }
4003 
lineAttributes(int line)4004 QList<KTextEditor::AttributeBlock> KTextEditor::ViewPrivate::lineAttributes(int line)
4005 {
4006     QList<KTextEditor::AttributeBlock> attribs;
4007 
4008     if (line < 0 || line >= doc()->lines()) {
4009         return attribs;
4010     }
4011 
4012     Kate::TextLine kateLine = doc()->kateTextLine(line);
4013     if (!kateLine) {
4014         return attribs;
4015     }
4016 
4017     const QVector<Kate::TextLineData::Attribute> &intAttrs = kateLine->attributesList();
4018     for (int i = 0; i < intAttrs.size(); ++i) {
4019         if (intAttrs[i].length > 0 && intAttrs[i].attributeValue > 0) {
4020             attribs << KTextEditor::AttributeBlock(intAttrs.at(i).offset, intAttrs.at(i).length, renderer()->attribute(intAttrs.at(i).attributeValue));
4021         }
4022     }
4023 
4024     return attribs;
4025 }
4026