KTextEditor

kateview.cpp
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 "clipboardhistorydialog.h"
18#include "export/exporter.h"
19#include "inlinenotedata.h"
20#include "kateabstractinputmode.h"
21#include "kateautoindent.h"
22#include "katebookmarks.h"
23#include "katebuffer.h"
24#include "katecompletionwidget.h"
25#include "kateconfig.h"
26#include "katedialogs.h"
27#include "katedocument.h"
28#include "kateglobal.h"
29#include "katehighlight.h"
30#include "katehighlightmenu.h"
31#include "katekeywordcompletion.h"
32#include "katelayoutcache.h"
33#include "katemessagewidget.h"
34#include "katemodemenu.h"
35#include "katepartdebug.h"
36#include "katerenderer.h"
37#include "katestatusbar.h"
38#include "katetemplatehandler.h"
39#include "katetextline.h"
40#include "kateundomanager.h"
41#include "kateviewhelpers.h"
42#include "kateviewinternal.h"
43#include "katewordcompletion.h"
44#include "printing/kateprinter.h"
45#include "screenshotdialog.h"
46#include "script/katescriptaction.h"
47#include "script/katescriptmanager.h"
48#include "spellcheck/spellcheck.h"
49#include "spellcheck/spellcheckdialog.h"
50#include "spellcheck/spellingmenu.h"
51
52#include <KTextEditor/Message>
53#include <ktexteditor/annotationinterface.h>
54#include <ktexteditor/inlinenoteprovider.h>
55#include <ktexteditor/texthintinterface.h>
56
57#include <KActionCollection>
58#include <KConfig>
59#include <KConfigGroup>
60#include <KCursor>
61#include <KMessageBox>
62#include <KSelectAction>
63#include <KStandardActions>
64#include <KStandardShortcut>
65#include <KToggleAction>
66#include <KXMLGUIFactory>
67
68#include <QActionGroup>
69#include <QApplication>
70#include <QClipboard>
71#include <QFileDialog>
72#include <QFileInfo>
73#include <QFont>
74#include <QInputDialog>
75#include <QKeyEvent>
76#include <QLayout>
77#include <QMimeData>
78#include <QPainter>
79#include <QRegularExpression>
80#include <QTextToSpeech>
81#include <QToolTip>
82
83// #define VIEW_RANGE_DEBUG
84
85// END includes
86
87namespace
88{
89bool hasCommentInFirstLine(KTextEditor::DocumentPrivate *doc)
90{
91 const Kate::TextLine line = doc->kateTextLine(0);
92 return doc->isComment(0, line.firstChar());
93}
94
95}
96
97void KTextEditor::ViewPrivate::blockFix(KTextEditor::Range &range)
98{
99 if (range.start().column() > range.end().column()) {
100 int tmp = range.start().column();
101 range.setStart(KTextEditor::Cursor(range.start().line(), range.end().column()));
102 range.setEnd(KTextEditor::Cursor(range.end().line(), tmp));
103 }
104}
105
106KTextEditor::ViewPrivate::ViewPrivate(KTextEditor::DocumentPrivate *doc, QWidget *parent, KTextEditor::MainWindow *mainWindow)
107 : KTextEditor::View(this, parent)
108 , m_completionWidget(nullptr)
109 , m_annotationModel(nullptr)
110 , m_markedSelection(false)
111 , m_hasWrap(false)
112 , m_doc(doc)
113 , m_textFolding(doc->buffer())
114 , m_config(new KateViewConfig(this))
115 , m_renderer(new KateRenderer(doc, m_textFolding, this))
116 , m_viewInternal(new KateViewInternal(this))
117 , m_spell(new KateSpellCheckDialog(this))
118 , m_bookmarks(new KateBookmarks(this))
119 , m_topSpacer(new QSpacerItem(0, 0))
120 , m_leftSpacer(new QSpacerItem(0, 0))
121 , m_rightSpacer(new QSpacerItem(0, 0))
122 , m_bottomSpacer(new QSpacerItem(0, 0))
123 , m_startingUp(true)
124 , m_updatingDocumentConfig(false)
125 , m_selection(m_doc->buffer(), KTextEditor::Range::invalid(), Kate::TextRange::ExpandLeft, Kate::TextRange::AllowEmpty)
126 , blockSelect(false)
127 , m_bottomViewBar(nullptr)
128 , m_gotoBar(nullptr)
129 , m_dictionaryBar(nullptr)
130 , m_spellingMenu(new KateSpellingMenu(this))
131 , m_userContextMenuSet(false)
132 , m_lineToUpdateRange(KTextEditor::LineRange::invalid())
133 , m_mainWindow(mainWindow ? mainWindow : KTextEditor::EditorPrivate::self()->dummyMainWindow()) // use dummy window if no window there!
134 , m_statusBar(nullptr)
135 , m_temporaryAutomaticInvocationDisabled(false)
136 , m_autoFoldedFirstLine(false)
137{
138 // queued connect to collapse view updates for range changes, INIT THIS EARLY ENOUGH!
139 connect(this, &KTextEditor::ViewPrivate::delayedUpdateOfView, this, &KTextEditor::ViewPrivate::slotDelayedUpdateOfView, Qt::QueuedConnection);
140
141 m_delayedUpdateTimer.setSingleShot(true);
142 m_delayedUpdateTimer.setInterval(0);
143 connect(&m_delayedUpdateTimer, &QTimer::timeout, this, &KTextEditor::ViewPrivate::delayedUpdateOfView);
144
147
148 // selection if for this view only and will invalidate if becoming empty
149 m_selection.setView(this);
150
151 // use z depth defined in moving ranges interface
152 m_selection.setZDepth(-100000.0);
153
155
156 // try to let the main window, if any, create a view bar for this view
157 QWidget *bottomBarParent = m_mainWindow->createViewBar(this);
158
159 m_bottomViewBar = new KateViewBar(bottomBarParent != nullptr, bottomBarParent ? bottomBarParent : this, this);
160
161 // ugly workaround:
162 // Force the layout to be left-to-right even on RTL desktop, as discussed
163 // on the mailing list. This will cause the lines and icons panel to be on
164 // the left, even for Arabic/Hebrew/Farsi/whatever users.
165 setLayoutDirection(Qt::LeftToRight);
166
167 m_bottomViewBar->installEventFilter(m_viewInternal);
168
169 // add KateMessageWidget for KTE::MessageInterface immediately above view
170 m_messageWidgets[KTextEditor::Message::AboveView] = new KateMessageWidget(this);
172 m_messageWidgets[KTextEditor::Message::AboveView]->hide();
173
174 // add KateMessageWidget for KTE::MessageInterface immediately above view
175 m_messageWidgets[KTextEditor::Message::BelowView] = new KateMessageWidget(this);
177 m_messageWidgets[KTextEditor::Message::BelowView]->hide();
178
179 // add bottom viewbar...
180 if (bottomBarParent) {
181 m_mainWindow->addWidgetToViewBar(this, m_bottomViewBar);
182 }
183
184 // add layout for floating message widgets to KateViewInternal
185 m_notificationLayout = new KateMessageLayout(m_viewInternal);
186 m_notificationLayout->setContentsMargins(20, 20, 20, 20);
187 m_viewInternal->setLayout(m_notificationLayout);
188
189 // this really is needed :)
190 m_viewInternal->updateView();
191
192 doc->addView(this);
193
194 setFocusProxy(m_viewInternal);
195 setFocusPolicy(Qt::StrongFocus);
196
197 setXMLFile(QStringLiteral("katepart5ui.rc"));
198
199 setupConnections();
200 setupActions();
201
202 // auto word completion
203 new KateWordCompletionView(this, actionCollection());
204
205 // update the enabled state of the undo/redo actions...
206 slotUpdateUndo();
207
208 // create the status bar of this view
209 // do this after action creation, we use some of them!
210 toggleStatusBar();
211
212 m_startingUp = false;
213 updateConfig();
214
215 slotHlChanged();
216 KCursor::setAutoHideCursor(m_viewInternal, true);
217
218 for (auto messageWidget : m_messageWidgets) {
219 if (messageWidget) {
220 // user interaction (scrolling) starts notification auto-hide timer
221 connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
222
223 // user interaction (cursor navigation) starts notification auto-hide timer
225 }
226 }
227
228 // folding restoration on reload
229 connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::saveFoldingState);
230 connect(m_doc, &KTextEditor::DocumentPrivate::reloaded, this, &KTextEditor::ViewPrivate::applyFoldingState);
231
232 connect(m_doc, &KTextEditor::DocumentPrivate::reloaded, this, &KTextEditor::ViewPrivate::slotDocumentReloaded);
233 connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::slotDocumentAboutToReload);
234
235 // update highlights on scrolling and co
236 connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, this, &KTextEditor::ViewPrivate::createHighlights);
237
238 // clear highlights on reload
239 connect(m_doc, &KTextEditor::DocumentPrivate::aboutToReload, this, &KTextEditor::ViewPrivate::clearHighlights);
240
241 // setup layout
242 setupLayout();
243}
244
245KTextEditor::ViewPrivate::~ViewPrivate()
246{
247 // de-register views early from global collections
248 // otherwise we might "use" them again during destruction in a half-valid state
249 // see e.g. bug 422546
250 // Kate::TextBuffer::notifyAboutRangeChange will access views() in a chain during
251 // deletion of m_viewInternal
252 doc()->removeView(this);
254
255 delete m_completionWidget;
256
257 // remove from xmlgui factory, to be safe
258 if (factory()) {
259 factory()->removeClient(this);
260 }
261
262 // delete internal view before view bar!
263 delete m_viewInternal;
264
265 // remove view bar again, if needed
266 m_mainWindow->deleteViewBar(this);
267 m_bottomViewBar = nullptr;
268
269 delete m_renderer;
270
271 delete m_config;
272}
273
274void KTextEditor::ViewPrivate::toggleStatusBar()
275{
276 // if there, delete it
277 if (m_statusBar) {
278 bottomViewBar()->removePermanentBarWidget(m_statusBar);
279 delete m_statusBar;
280 m_statusBar = nullptr;
281 Q_EMIT statusBarEnabledChanged(this, false);
282 return;
283 }
284
285 // else: create it
286 m_statusBar = new KateStatusBar(this);
287 bottomViewBar()->addPermanentBarWidget(m_statusBar);
288 Q_EMIT statusBarEnabledChanged(this, true);
289}
290
291void KTextEditor::ViewPrivate::setupLayout()
292{
293 // delete old layout if any
294 if (layout()) {
295 delete layout();
296
297 // need to recreate spacers because they are deleted with
298 // their belonging layout
299 m_topSpacer = new QSpacerItem(0, 0);
300 m_leftSpacer = new QSpacerItem(0, 0);
301 m_rightSpacer = new QSpacerItem(0, 0);
302 m_bottomSpacer = new QSpacerItem(0, 0);
303 }
304
305 // set margins
307 opt.initFrom(this);
308 opt.frameShape = QFrame::StyledPanel;
309 opt.state |= QStyle::State_Sunken;
310 const int margin = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this);
311 m_topSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
312 m_leftSpacer->changeSize(margin, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
313 m_rightSpacer->changeSize(margin, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
314 m_bottomSpacer->changeSize(0, margin, QSizePolicy::Minimum, QSizePolicy::Fixed);
315
316 // define layout
317 QGridLayout *layout = new QGridLayout(this);
318 layout->setContentsMargins(0, 0, 0, 0);
319 layout->setSpacing(0);
320
321 const bool frameAroundContents = style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, this);
322 if (frameAroundContents) {
323 // top message widget
324 layout->addWidget(m_messageWidgets[KTextEditor::Message::AboveView], 0, 0, 1, 5);
325
326 // top spacer
327 layout->addItem(m_topSpacer, 1, 0, 1, 4);
328
329 // left spacer
330 layout->addItem(m_leftSpacer, 2, 0, 1, 1);
331
332 // left border
333 layout->addWidget(m_viewInternal->m_leftBorder, 2, 1, 1, 1);
334
335 // view
336 layout->addWidget(m_viewInternal, 2, 2, 1, 1);
337
338 // right spacer
339 layout->addItem(m_rightSpacer, 2, 3, 1, 1);
340
341 // bottom spacer
342 layout->addItem(m_bottomSpacer, 3, 0, 1, 4);
343
344 // vertical scrollbar
345 layout->addWidget(m_viewInternal->m_lineScroll, 1, 4, 3, 1);
346
347 // horizontal scrollbar
348 layout->addWidget(m_viewInternal->m_columnScroll, 4, 0, 1, 4);
349
350 // dummy
351 layout->addWidget(m_viewInternal->m_dummy, 4, 4, 1, 1);
352
353 // bottom message
354 layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], 5, 0, 1, 5);
355
356 // bottom viewbar
357 if (m_bottomViewBar->parentWidget() == this) {
358 layout->addWidget(m_bottomViewBar, 6, 0, 1, 5);
359 }
360
361 // stretch
362 layout->setColumnStretch(2, 1);
363 layout->setRowStretch(2, 1);
364
365 // adjust scrollbar background
366 m_viewInternal->m_lineScroll->setBackgroundRole(QPalette::Window);
367 m_viewInternal->m_lineScroll->setAutoFillBackground(false);
368
369 m_viewInternal->m_columnScroll->setBackgroundRole(QPalette::Window);
370 m_viewInternal->m_columnScroll->setAutoFillBackground(false);
371
372 } else {
373 // top message widget
374 layout->addWidget(m_messageWidgets[KTextEditor::Message::AboveView], 0, 0, 1, 5);
375
376 // top spacer
377 layout->addItem(m_topSpacer, 1, 0, 1, 5);
378
379 // left spacer
380 layout->addItem(m_leftSpacer, 2, 0, 1, 1);
381
382 // left border
383 layout->addWidget(m_viewInternal->m_leftBorder, 2, 1, 1, 1);
384
385 // view
386 layout->addWidget(m_viewInternal, 2, 2, 1, 1);
387
388 // vertical scrollbar
389 layout->addWidget(m_viewInternal->m_lineScroll, 2, 3, 1, 1);
390
391 // right spacer
392 layout->addItem(m_rightSpacer, 2, 4, 1, 1);
393
394 // horizontal scrollbar
395 layout->addWidget(m_viewInternal->m_columnScroll, 3, 1, 1, 2);
396
397 // dummy
398 layout->addWidget(m_viewInternal->m_dummy, 3, 3, 1, 1);
399
400 // bottom spacer
401 layout->addItem(m_bottomSpacer, 4, 0, 1, 5);
402
403 // bottom message
404 layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], 5, 0, 1, 5);
405
406 // bottom viewbar
407 if (m_bottomViewBar->parentWidget() == this) {
408 layout->addWidget(m_bottomViewBar, 6, 0, 1, 5);
409 }
410
411 // stretch
412 layout->setColumnStretch(2, 1);
413 layout->setRowStretch(2, 1);
414
415 // adjust scrollbar background
416 m_viewInternal->m_lineScroll->setBackgroundRole(QPalette::Base);
417 m_viewInternal->m_lineScroll->setAutoFillBackground(true);
418
419 m_viewInternal->m_columnScroll->setBackgroundRole(QPalette::Base);
420 m_viewInternal->m_columnScroll->setAutoFillBackground(true);
421 }
422}
423
424void KTextEditor::ViewPrivate::setupConnections()
425{
426 connect(m_doc, &KTextEditor::DocumentPrivate::undoChanged, this, &KTextEditor::ViewPrivate::slotUpdateUndo);
427 connect(m_doc, &KTextEditor::DocumentPrivate::highlightingModeChanged, this, &KTextEditor::ViewPrivate::slotHlChanged);
428 connect(m_doc, &KTextEditor::DocumentPrivate::canceled, this, &KTextEditor::ViewPrivate::slotSaveCanceled);
429 connect(m_viewInternal, &KateViewInternal::dropEventPass, this, &KTextEditor::ViewPrivate::dropEventPass);
430
431 connect(m_doc, &KTextEditor::DocumentPrivate::annotationModelChanged, m_viewInternal->m_leftBorder, &KateIconBorder::annotationModelChanged);
432}
433
434void KTextEditor::ViewPrivate::goToPreviousEditingPosition()
435{
436 auto c = doc()->lastEditingPosition(KTextEditor::DocumentPrivate::Previous, cursorPosition());
437 if (c.isValid()) {
438 setCursorPosition(c);
439 }
440}
441
442void KTextEditor::ViewPrivate::goToNextEditingPosition()
443{
444 auto c = doc()->lastEditingPosition(KTextEditor::DocumentPrivate::Next, cursorPosition());
445 if (c.isValid()) {
446 setCursorPosition(c);
447 }
448}
449void KTextEditor::ViewPrivate::setupActions()
450{
451 KActionCollection *ac = actionCollection();
452 QAction *a;
453
454 m_toggleWriteLock = nullptr;
455
456 m_cut = a = ac->addAction(KStandardActions::Cut, this, &KTextEditor::ViewPrivate::cut);
457 a->setWhatsThis(i18n("Cut the selected text and move it to the clipboard"));
458
459 m_paste = a = ac->addAction(KStandardActions::Paste, this, [this] {
460 paste();
461 });
462 a->setWhatsThis(i18n("Paste previously copied or cut clipboard contents"));
463
464 m_copy = a = ac->addAction(KStandardActions::Copy, this, &KTextEditor::ViewPrivate::copy);
465 a->setWhatsThis(i18n("Use this command to copy the currently selected text to the system clipboard."));
466
467 m_clipboardHistory = a = ac->addAction(QStringLiteral("clipboard_history_paste"), this, [this] {
468 ClipboardHistoryDialog chd(mainWindow()->window(), this);
469 chd.openDialog(KTextEditor::EditorPrivate::self()->clipboardHistory());
470 });
471 a->setText(i18n("Clipboard &History Paste"));
473
475 m_pasteSelection = a = ac->addAction(QStringLiteral("edit_paste_selection"), this, &KTextEditor::ViewPrivate::pasteSelection);
476 a->setText(i18n("Paste Selection"));
478 a->setWhatsThis(i18n("Paste previously mouse selection contents"));
479 }
480
481 a = ac->addAction(QStringLiteral("edit_copy_file_location"), this, &KTextEditor::ViewPrivate::copyFileLocation);
482 a->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
483 a->setText(i18n("Copy File Location"));
484 a->setWhatsThis(i18n("Copy the current file name and line number"));
485
486 m_swapWithClipboard = a = ac->addAction(QStringLiteral("edit_swap_with_clipboard"), this, &KTextEditor::ViewPrivate::swapWithClipboard);
487 a->setText(i18n("Swap with Clipboard Contents"));
488 a->setWhatsThis(i18n("Swap the selected text with the clipboard contents"));
489
490 m_screenshotSelection = a = ac->addAction(QStringLiteral("text_screenshot_selection"), this, &KTextEditor::ViewPrivate::screenshot);
491 a->setText(i18n("Take Screenshot of Selection"));
492
493 if (!doc()->readOnly()) {
494 a = ac->addAction(KStandardActions::Save, m_doc, &KTextEditor::DocumentPrivate::documentSave);
495 a->setWhatsThis(i18n("Save the current document"));
496
497 a = m_editUndo = ac->addAction(KStandardActions::Undo, m_doc, &KTextEditor::DocumentPrivate::undo);
498 a->setWhatsThis(i18n("Revert the most recent editing actions"));
499
500 a = m_editRedo = ac->addAction(KStandardActions::Redo, m_doc, &KTextEditor::DocumentPrivate::redo);
501 a->setWhatsThis(i18n("Revert the most recent undo operation"));
502
503 // Tools > Scripts
504 // stored inside scoped pointer to ensure we destruct it early enough as it does internal cleanups of other child objects
505 m_scriptActionMenu.reset(new KateScriptActionMenu(this, i18n("&Scripts")));
506 ac->addAction(QStringLiteral("tools_scripts"), m_scriptActionMenu.get());
507
508 a = ac->addAction(QStringLiteral("tools_apply_wordwrap"));
509 a->setText(i18n("Apply &Word Wrap"));
510 a->setWhatsThis(
511 i18n("Use this to wrap the current line, or to reformat the selected lines as paragraph, "
512 "to fit the 'Wrap words at' setting in the configuration dialog.<br /><br />"
513 "This is a static word wrap, meaning the document is changed."));
514 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::applyWordWrap);
515
516 a = ac->addAction(QStringLiteral("tools_cleanIndent"));
517 a->setText(i18n("&Clean Indentation"));
518 a->setWhatsThis(
519 i18n("Use this to clean the indentation of a selected block of text (only tabs/only spaces).<br /><br />"
520 "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
521 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cleanIndent);
522
523 a = ac->addAction(QStringLiteral("tools_formatIndent"));
524 a->setText(i18n("&Format Indentation"));
525 a->setWhatsThis(i18n("Use this to auto indent the current line or block of text to its proper indent level."));
526 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::formatIndent);
527
528 a = ac->addAction(QStringLiteral("tools_alignOn"));
529 a->setText(i18nc("@action", "&Align On…"));
530 a->setWhatsThis(
531 i18n("This command aligns lines in the selected block or whole document on the column given by a regular expression "
532 "that you will be prompted for.<br /><br />"
533 "If you give an empty pattern it will align on the first non-blank character by default.<br />"
534 "If the pattern has a capture it will indent on the captured match.<br /><br />"
535 "<i>Examples</i>:<br />"
536 "With '-' it will insert spaces before the first '-' of each lines to align them all on the same column.<br />"
537 "With ':\\s+(.)' it will insert spaces before the first non-blank character that occurs after a colon to align "
538 "them all on the same column."));
539 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::alignOn);
540
541 a = ac->addAction(QStringLiteral("tools_comment"));
542 a->setText(i18n("C&omment"));
544 a->setWhatsThis(
545 i18n("This command comments out the current line or a selected block of text.<br /><br />"
546 "The characters for single/multiple line comments are defined within the language's highlighting."));
547 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::comment);
548
549 a = ac->addAction(QStringLiteral("Previous Editing Line"));
550 a->setText(i18n("Go to Previous Editing Location"));
551 a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
553 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::goToPreviousEditingPosition);
554
555 a = ac->addAction(QStringLiteral("Next Editing Line"));
556 a->setText(i18n("Go to Next Editing Location"));
557 a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
559 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::goToNextEditingPosition);
560
561 a = ac->addAction(QStringLiteral("tools_uncomment"));
562 a->setText(i18n("Unco&mment"));
564 a->setWhatsThis(
565 i18n("This command removes comments from the current line or a selected block of text.<br /><br />"
566 "The characters for single/multiple line comments are defined within the language's highlighting."));
567 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::uncomment);
568
569 a = ac->addAction(QStringLiteral("tools_toggle_comment"));
570 a->setText(i18n("Toggle Comment"));
572 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleComment);
573
574 a = m_toggleWriteLock = new KToggleAction(i18n("&Read Only Mode"), this);
575 a->setWhatsThis(i18n("Lock/unlock the document for writing"));
576 a->setChecked(!doc()->isReadWrite());
577 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleWriteLock);
578 ac->addAction(QStringLiteral("tools_toggle_write_lock"), a);
579
580 a = m_forceRTLDirection = new KToggleAction(i18n("&Force RTL Direction"), this);
581 a->setWhatsThis(i18n("Force RTL Text Direction"));
582 connect(a, &QAction::triggered, this, [this](bool checked) {
583 m_forceRTL = checked;
584 tagAll();
585 updateView(true);
586 });
587 ac->addAction(QStringLiteral("force_rtl_direction"), a);
588
589 a = ac->addAction(QStringLiteral("tools_uppercase"));
590 a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-uppercase")));
591 a->setText(i18n("Uppercase"));
593 a->setWhatsThis(
594 i18n("Convert the selection to uppercase, or the character to the "
595 "right of the cursor if no text is selected."));
596 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::uppercase);
597
598 a = ac->addAction(QStringLiteral("tools_lowercase"));
599 a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-lowercase")));
600 a->setText(i18n("Lowercase"));
602 a->setWhatsThis(
603 i18n("Convert the selection to lowercase, or the character to the "
604 "right of the cursor if no text is selected."));
605 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::lowercase);
606
607 a = ac->addAction(QStringLiteral("tools_capitalize"));
608 a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-capitalize")));
609 a->setText(i18n("Capitalize"));
611 a->setWhatsThis(
612 i18n("Capitalize the selection, or the word under the "
613 "cursor if no text is selected."));
614 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::capitalize);
615
616 a = ac->addAction(QStringLiteral("tools_join_lines"));
617 a->setText(i18n("Join Lines"));
619 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::joinLines);
620
621 a = ac->addAction(QStringLiteral("tools_invoke_code_completion"));
622 a->setText(i18n("Invoke Code Completion"));
623 a->setWhatsThis(i18n("Manually invoke command completion, usually by using a shortcut bound to this action."));
625 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::userInvokedCompletion);
626
627 a = ac->addAction(QStringLiteral("remove_trailing_spaces"));
628 a->setText(i18n("Remove Trailing Spaces"));
629 connect(a, &QAction::triggered, this, [this] {
631 });
632 } else {
633 for (auto *action : {m_cut, m_paste, m_clipboardHistory, m_swapWithClipboard}) {
634 action->setEnabled(false);
635 }
636
637 if (m_pasteSelection) {
638 m_pasteSelection->setEnabled(false);
639 }
640
641 m_editUndo = nullptr;
642 m_editRedo = nullptr;
643 }
644
645 a = ac->addAction(KStandardActions::Print, this, &KTextEditor::ViewPrivate::print);
646 a->setWhatsThis(i18n("Print the current document."));
647
648 a = ac->addAction(KStandardActions::PrintPreview, this, &KTextEditor::ViewPrivate::printPreview);
649 a->setWhatsThis(i18n("Show print preview of current document"));
650
651 a = ac->addAction(QStringLiteral("file_reload"));
652 a->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
653 a->setText(i18n("Reloa&d"));
655 a->setWhatsThis(i18n("Reload the current document from disk."));
656 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::reloadFile);
657
658 a = ac->addAction(KStandardActions::SaveAs, m_doc, &KTextEditor::DocumentPrivate::documentSaveAs);
659 a->setWhatsThis(i18n("Save the current document to disk, with a name of your choice."));
660
661 a = new KateViewEncodingAction(m_doc, this, i18nc("@action", "Save As with Encoding…"), this, true /* special mode for save as */);
662 a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
663 ac->addAction(QStringLiteral("file_save_as_with_encoding"), a);
664
665 a = ac->addAction(QStringLiteral("file_save_copy_as"));
666 a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
667 a->setText(i18nc("@action", "Save &Copy As…"));
668 a->setWhatsThis(i18n("Save a copy of the current document to disk."));
669 connect(a, &QAction::triggered, m_doc, &KTextEditor::DocumentPrivate::documentSaveCopyAs);
670
671 a = ac->addAction(KStandardActions::GotoLine, this, &KTextEditor::ViewPrivate::gotoLine);
672 a->setWhatsThis(i18n("This command opens a dialog and lets you choose a line that you want the cursor to move to."));
673
674 a = ac->addAction(QStringLiteral("modified_line_up"));
675 a->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
676 a->setText(i18n("Go to Previous Modified Line"));
677 a->setWhatsThis(i18n("Move upwards to the previous modified line."));
678 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toPrevModifiedLine);
679
680 a = ac->addAction(QStringLiteral("modified_line_down"));
681 a->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
682 a->setText(i18n("Go to Next Modified Line"));
683 a->setWhatsThis(i18n("Move downwards to the next modified line."));
684 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toNextModifiedLine);
685
686 a = ac->addAction(QStringLiteral("set_confdlg"));
687 a->setText(i18nc("@action", "&Configure Editor…"));
688 a->setIcon(QIcon::fromTheme(QStringLiteral("preferences-other")));
689 a->setWhatsThis(i18n("Configure various aspects of this editor."));
690 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotConfigDialog);
691
692 m_modeAction = new KateModeMenu(i18n("&Mode"), this);
693 ac->addAction(QStringLiteral("tools_mode"), m_modeAction);
694 m_modeAction->setWhatsThis(i18n(
695 "Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example."));
696 m_modeAction->updateMenu(m_doc);
697
698 KateHighlightingMenu *menu = new KateHighlightingMenu(i18n("&Highlighting"), this);
699 ac->addAction(QStringLiteral("tools_highlighting"), menu);
700 menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted."));
701 menu->updateMenu(m_doc);
702
703 KateViewSchemaAction *schemaMenu = new KateViewSchemaAction(i18n("&Editor Color Theme"), this);
704 schemaMenu->setIcon(QIcon::fromTheme(QStringLiteral("kcolorchooser")));
705 ac->addAction(QStringLiteral("view_schemas"), schemaMenu);
706 schemaMenu->updateMenu(this);
707
708 // indentation menu
709 KateViewIndentationAction *indentMenu = new KateViewIndentationAction(m_doc, i18n("&Indentation"), this);
710 ac->addAction(QStringLiteral("tools_indentation"), indentMenu);
711
712 m_selectAll = ac->addAction(KStandardActions::SelectAll, this, [this] {
713 selectAll();
714 qApp->clipboard()->setText(selectionText(), QClipboard::Selection);
715 });
716 a->setWhatsThis(i18n("Select the entire text of the current document."));
717
718 m_deSelect = a = ac->addAction(KStandardActions::Deselect, this, qOverload<>(&KTextEditor::ViewPrivate::clearSelection));
719 a->setWhatsThis(i18n("If you have selected something within the current document, this will no longer be selected."));
720
721 a = ac->addAction(QStringLiteral("view_inc_font_sizes"));
722 a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
723 a->setText(i18n("Enlarge Font"));
725 a->setWhatsThis(i18n("This increases the display font size."));
726 connect(a, &QAction::triggered, m_viewInternal, [this]() {
727 m_viewInternal->slotIncFontSizes();
728 });
729
730 a = ac->addAction(QStringLiteral("view_dec_font_sizes"));
731 a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
732 a->setText(i18n("Shrink Font"));
734 a->setWhatsThis(i18n("This decreases the display font size."));
735 connect(a, &QAction::triggered, m_viewInternal, [this]() {
736 m_viewInternal->slotDecFontSizes();
737 });
738
739 a = ac->addAction(QStringLiteral("view_reset_font_sizes"));
740 a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
741 a->setText(i18n("Reset Font Size"));
743 a->setWhatsThis(i18n("This resets the display font size."));
744 connect(a, &QAction::triggered, m_viewInternal, &KateViewInternal::slotResetFontSizes);
745
746 a = m_toggleBlockSelection = new KToggleAction(i18n("Bl&ock Selection Mode"), this);
747 ac->addAction(QStringLiteral("set_verticalSelect"), a);
749 a->setWhatsThis(i18n("This command allows switching between the normal (line based) selection mode and the block selection mode."));
750 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleBlockSelection);
751
752 a = ac->addAction(QStringLiteral("switch_next_input_mode"));
753 a->setText(i18n("Switch to Next Input Mode"));
755 a->setWhatsThis(i18n("Switch to the next input mode."));
756 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cycleInputMode);
757
758 a = m_toggleInsert = new KToggleAction(i18n("Overwr&ite Mode"), this);
759 ac->addAction(QStringLiteral("set_insert"), a);
761 a->setWhatsThis(i18n("Choose whether you want the text you type to be inserted or to overwrite existing text."));
762 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleInsert);
763
764 KToggleAction *toggleAction;
765 a = m_toggleShowSpace = toggleAction = new KToggleAction(i18n("Show Whitespace"), this);
766 ac->addAction(QStringLiteral("view_show_whitespaces"), a);
767 a->setWhatsThis(
768 i18n("If this option is checked, whitespaces in this document will be visible.<br /><br />"
769 "This is only a view option, meaning the document will not be changed."));
770 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleShowSpaces);
771
772 a = m_toggleDynWrap = toggleAction = new KToggleAction(i18n("&Dynamic Word Wrap"), this);
773 a->setIcon(QIcon::fromTheme(QStringLiteral("text-wrap")));
774 ac->addAction(QStringLiteral("view_dynamic_word_wrap"), a);
775 a->setWhatsThis(
776 i18n("If this option is checked, the text lines will be wrapped at the view border on the screen.<br /><br />"
777 "This is only a view option, meaning the document will not be changed."));
778 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleDynWordWrap);
779
780 a = m_setDynWrapIndicators = new KSelectAction(i18n("Dynamic Word Wrap Indicators"), this);
781 ac->addAction(QStringLiteral("dynamic_word_wrap_indicators"), a);
782 a->setWhatsThis(i18n("Choose when the Dynamic Word Wrap Indicators should be displayed"));
783 connect(m_setDynWrapIndicators, &KSelectAction::indexTriggered, this, &KTextEditor::ViewPrivate::setDynWrapIndicators);
784 const QStringList list2{i18n("&Off"), i18n("Follow &Line Numbers"), i18n("&Always On")};
785 m_setDynWrapIndicators->setItems(list2);
786 m_setDynWrapIndicators->setEnabled(m_toggleDynWrap->isChecked()); // only synced on real change, later
787
788 a = toggleAction = new KToggleAction(i18n("Static Word Wrap"), this);
789 ac->addAction(QStringLiteral("view_static_word_wrap"), a);
790 a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the column defined in the editing properties."));
792 if (m_doc) {
793 m_doc->setWordWrap(!m_doc->wordWrap());
794 }
795 });
796
797 a = toggleAction = m_toggleWWMarker = new KToggleAction(i18n("Show Static &Word Wrap Marker"), this);
798 ac->addAction(QStringLiteral("view_word_wrap_marker"), a);
799 a->setWhatsThis(
800 i18n("Show/hide the Word Wrap Marker, a vertical line drawn at the word "
801 "wrap column as defined in the editing properties"));
802 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleWWMarker);
803
804 a = toggleAction = m_toggleFoldingMarkers = new KToggleAction(i18n("Show Folding &Markers"), this);
805 ac->addAction(QStringLiteral("view_folding_markers"), a);
806 a->setWhatsThis(i18n("You can choose if the codefolding marks should be shown, if codefolding is possible."));
807 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleFoldingMarkers);
808
809 a = m_toggleIconBar = toggleAction = new KToggleAction(i18n("Show &Icon Border"), this);
810 ac->addAction(QStringLiteral("view_border"), a);
811 a->setWhatsThis(i18n("Show/hide the icon border.<br /><br />The icon border shows bookmark symbols, for instance."));
812 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleIconBorder);
813
814 a = toggleAction = m_toggleLineNumbers = new KToggleAction(i18n("Show &Line Numbers"), this);
815 ac->addAction(QStringLiteral("view_line_numbers"), a);
816 a->setWhatsThis(i18n("Show/hide the line numbers on the left hand side of the view."));
817 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleLineNumbersOn);
818
819 a = m_toggleScrollBarMarks = toggleAction = new KToggleAction(i18n("Show Scroll&bar Marks"), this);
820 ac->addAction(QStringLiteral("view_scrollbar_marks"), a);
821 a->setWhatsThis(i18n("Show/hide the marks on the vertical scrollbar.<br /><br />The marks show bookmarks, for instance."));
822 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleScrollBarMarks);
823
824 a = m_toggleScrollBarMiniMap = toggleAction = new KToggleAction(i18n("Show Scrollbar Mini-Map"), this);
825 ac->addAction(QStringLiteral("view_scrollbar_minimap"), a);
826 a->setWhatsThis(i18n("Show/hide the mini-map on the vertical scrollbar.<br /><br />The mini-map shows an overview of the whole document."));
827 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleScrollBarMiniMap);
828
829 a = m_doc->autoReloadToggleAction();
830 ac->addAction(QStringLiteral("view_auto_reload"), a);
831
832 // a = m_toggleScrollBarMiniMapAll = toggleAction = new KToggleAction(i18n("Show the whole document in the Mini-Map"), this);
833 // ac->addAction(QLatin1String("view_scrollbar_minimap_all"), a);
834 // 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
835 // mini-map.")); connect(a, SIGNAL(triggered(bool)), SlOT(toggleScrollBarMiniMapAll())); connect(m_toggleScrollBarMiniMap, SIGNAL(triggered(bool)),
836 // m_toggleScrollBarMiniMapAll, SLOT(setEnabled(bool)));
837
838 a = m_toggleNPSpaces = new KToggleAction(i18n("Show Non-Printable Spaces"), this);
839 ac->addAction(QStringLiteral("view_non_printable_spaces"), a);
840 a->setWhatsThis(i18n("Show/hide bounding box around non-printable spaces"));
841 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleNPSpaces);
842
843 a = m_switchCmdLine = ac->addAction(QStringLiteral("switch_to_cmd_line"));
844 a->setText(i18n("Switch to Command Line"));
846 a->setWhatsThis(i18n("Show/hide the command line on the bottom of the view."));
847 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::switchToCmdLine);
848
849 KActionMenu *am = new KActionMenu(i18n("Input Modes"), this);
850 m_inputModeActions = new QActionGroup(am);
851 ac->addAction(QStringLiteral("view_input_modes"), am);
852 auto switchInputModeAction = ac->action(QStringLiteral("switch_next_input_mode"));
853 am->addAction(switchInputModeAction);
854 am->addSeparator();
855 for (const auto &mode : m_viewInternal->m_inputModes) {
856 a = new QAction(mode->viewInputModeHuman(), m_inputModeActions);
857 am->addAction(a);
858 a->setWhatsThis(i18n("Activate/deactivate %1", mode->viewInputModeHuman()));
859 const InputMode im = mode->viewInputMode();
860 a->setData(static_cast<int>(im));
861 a->setCheckable(true);
862 if (im == m_config->inputMode()) {
863 a->setChecked(true);
864 }
865 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleInputMode);
866 }
867
868 a = m_setEndOfLine = new KSelectAction(i18n("&End of Line"), this);
869 ac->addAction(QStringLiteral("set_eol"), a);
870 a->setWhatsThis(i18n("Choose which line endings should be used, when you save the document"));
871 const QStringList list{i18nc("@item:inmenu End of Line", "&UNIX"),
872 i18nc("@item:inmenu End of Line", "&Windows/DOS"),
873 i18nc("@item:inmenu End of Line", "&Macintosh")};
874 m_setEndOfLine->setItems(list);
875 m_setEndOfLine->setCurrentItem(doc()->config()->eol());
876 connect(m_setEndOfLine, &KSelectAction::indexTriggered, this, &KTextEditor::ViewPrivate::setEol);
877
878 a = m_addBom = new KToggleAction(i18n("Add &Byte Order Mark (BOM)"), this);
879 m_addBom->setChecked(doc()->config()->bom());
880 ac->addAction(QStringLiteral("add_bom"), a);
881 a->setWhatsThis(i18n("Enable/disable adding of byte order marks for UTF-8/UTF-16 encoded files while saving"));
882 connect(m_addBom, &KToggleAction::triggered, this, &KTextEditor::ViewPrivate::setAddBom);
883
884 // encoding menu
885 m_encodingAction = new KateViewEncodingAction(m_doc, this, i18n("E&ncoding"), this);
886 ac->addAction(QStringLiteral("set_encoding"), m_encodingAction);
887
888 a = ac->addAction(KStandardActions::Find, this, &KTextEditor::ViewPrivate::find);
889 a->setWhatsThis(i18n("Look up the first occurrence of a piece of text or regular expression."));
890 addAction(a);
891
892 a = ac->addAction(QStringLiteral("edit_find_selected"));
893 a->setText(i18n("Find Selected"));
895 a->setWhatsThis(i18n("Finds next occurrence of selected text."));
896 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findSelectedForwards);
897
898 a = ac->addAction(QStringLiteral("edit_find_selected_backwards"));
899 a->setText(i18n("Find Selected Backwards"));
901 a->setWhatsThis(i18n("Finds previous occurrence of selected text."));
902 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findSelectedBackwards);
903
904 a = ac->addAction(QStringLiteral("edit_find_multicursor_next_occurrence"));
905 a->setText(i18n("Find and Select Next Occurrence"));
907 a->setWhatsThis(i18n("Finds next occurrence of the word under cursor and add it to selection."));
908 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findNextOccurunceAndSelect);
909
910 a = ac->addAction(QStringLiteral("edit_skip_multicursor_current_occurrence"));
911 a->setText(i18n("Mark Currently Selected Occurrence as Skipped"));
913 a->setWhatsThis(i18n("Marks the currently selected word as skipped."));
914 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::skipCurrentOccurunceSelection);
915
916 a = ac->addAction(QStringLiteral("edit_find_multicursor_all_occurrences"));
917 a->setText(i18n("Find and Select All Occurrences"));
919 a->setWhatsThis(i18n("Finds all occurrences of the word under cursor and selects them."));
920 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::findAllOccuruncesAndSelect);
921
922 a = ac->addAction(KStandardActions::FindNext, this, &KTextEditor::ViewPrivate::findNext);
923 a->setWhatsThis(i18n("Look up the next occurrence of the search phrase."));
924 addAction(a);
925
926 a = ac->addAction(KStandardActions::FindPrev, QStringLiteral("edit_find_prev"), this, &KTextEditor::ViewPrivate::findPrevious);
927 a->setWhatsThis(i18n("Look up the previous occurrence of the search phrase."));
928 addAction(a);
929
930 a = ac->addAction(KStandardActions::Replace, this, &KTextEditor::ViewPrivate::replace);
931 a->setWhatsThis(i18n("Look up a piece of text or regular expression and replace the result with some given text."));
932
933 a = ac->addAction(QStringLiteral("edit_create_multi_cursor_from_sel"));
934 a->setText(i18n("Add Cursors to Line Ends"));
936 a->setWhatsThis(i18n("Creates a cursor at the end of every line in selection."));
937 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::createMultiCursorsFromSelection);
938
939 a = ac->addAction(QStringLiteral("edit_create_multi_cursor_down"));
940 a->setText(i18n("Add Caret below Cursor"));
942 a->setWhatsThis(i18n("Adds a caret in the line below the current caret."));
943 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::addSecondaryCursorDown);
944
945 a = ac->addAction(QStringLiteral("edit_create_multi_cursor_up"));
946 a->setText(i18n("Add Caret above Cursor"));
948 a->setWhatsThis(i18n("Adds a caret in the line above the current caret."));
949 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::addSecondaryCursorUp);
950
951 a = ac->addAction(QStringLiteral("edit_toggle_camel_case_cursor"));
952 a->setText(i18n("Toggle Camel Case Cursor Movement"));
953 a->setWhatsThis(i18n("Toggle between normal word movement and camel case cursor movement."));
954 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toggleCamelCaseCursor);
955
956 a = ac->addAction(QStringLiteral("edit_remove_cursors_from_empty_lines"));
957 a->setText(i18n("Remove Cursors from Empty Lines"));
958 a->setWhatsThis(i18n("Remove cursors from empty lines"));
959 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::removeCursorsFromEmptyLines);
960
961 m_spell->createActions(ac);
962 m_toggleOnTheFlySpellCheck = new KToggleAction(i18n("Automatic Spell Checking"), this);
963 m_toggleOnTheFlySpellCheck->setWhatsThis(i18n("Enable/disable automatic spell checking"));
964 connect(m_toggleOnTheFlySpellCheck, &KToggleAction::triggered, this, &KTextEditor::ViewPrivate::toggleOnTheFlySpellCheck);
965 ac->addAction(QStringLiteral("tools_toggle_automatic_spell_checking"), m_toggleOnTheFlySpellCheck);
966 ac->setDefaultShortcut(m_toggleOnTheFlySpellCheck, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_O));
967
968 a = ac->addAction(QStringLiteral("tools_change_dictionary"));
969 a->setText(i18nc("@action", "Change Dictionary…"));
970 a->setWhatsThis(i18n("Change the dictionary that is used for spell checking."));
971 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::changeDictionary);
972
973 a = ac->addAction(QStringLiteral("tools_clear_dictionary_ranges"));
974 a->setText(i18n("Clear Dictionary Ranges"));
975 a->setEnabled(false);
976 a->setWhatsThis(i18n("Remove all the separate dictionary ranges that were set for spell checking."));
977 connect(a, &QAction::triggered, m_doc, &KTextEditor::DocumentPrivate::clearDictionaryRanges);
978 connect(m_doc, &KTextEditor::DocumentPrivate::dictionaryRangesPresent, a, &QAction::setEnabled);
979
980 m_copyHtmlAction = ac->addAction(QStringLiteral("edit_copy_html"), this, SLOT(exportHtmlToClipboard()));
981 m_copyHtmlAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
982 m_copyHtmlAction->setText(i18n("Copy as &HTML"));
983 m_copyHtmlAction->setWhatsThis(i18n("Use this command to copy the currently selected text as HTML to the system clipboard."));
984
985 a = ac->addAction(QStringLiteral("file_export_html"), this, SLOT(exportHtmlToFile()));
986 a->setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
987 a->setText(i18nc("@action", "E&xport as HTML…"));
988 a->setWhatsThis(
989 i18n("This command allows you to export the current document"
990 " with all highlighting information into a HTML document."));
991
992 m_spellingMenu->createActions(ac);
993
994 m_bookmarks->createActions(ac);
995
996 slotSelectionChanged();
997
998 // Now setup the editing actions before adding the associated
999 // widget and setting the shortcut context
1000 setupEditActions();
1001 setupCodeFolding();
1002 setupSpeechActions();
1003
1004 ac->addAssociatedWidget(m_viewInternal);
1005
1006 const auto actions = ac->actions();
1007 for (QAction *action : actions) {
1008 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
1009 }
1010
1011 connect(this, &KTextEditor::ViewPrivate::selectionChanged, this, &KTextEditor::ViewPrivate::slotSelectionChanged);
1012}
1013
1014void KTextEditor::ViewPrivate::slotConfigDialog()
1015{
1016 // invoke config dialog, will auto-save configuration to katepartrc
1018}
1019
1020void KTextEditor::ViewPrivate::setupEditActions()
1021{
1022 // If you add an editing action to this
1023 // function make sure to include the line
1024 // m_editActions << a after creating the action
1025 KActionCollection *ac = actionCollection();
1026
1027 QAction *a = ac->addAction(QStringLiteral("word_left"));
1028 a->setText(i18n("Move Word Left"));
1030 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::wordLeft);
1031 m_editActions.push_back(a);
1032
1033 a = ac->addAction(QStringLiteral("select_char_left"));
1034 a->setText(i18n("Select Character Left"));
1036 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftCursorLeft);
1037 m_editActions.push_back(a);
1038
1039 a = ac->addAction(QStringLiteral("select_word_left"));
1040 a->setText(i18n("Select Word Left"));
1042 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftWordLeft);
1043 m_editActions.push_back(a);
1044
1045 a = ac->addAction(QStringLiteral("word_right"));
1046 a->setText(i18n("Move Word Right"));
1048 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::wordRight);
1049 m_editActions.push_back(a);
1050
1051 a = ac->addAction(QStringLiteral("select_char_right"));
1052 a->setText(i18n("Select Character Right"));
1054 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftCursorRight);
1055 m_editActions.push_back(a);
1056
1057 a = ac->addAction(QStringLiteral("select_word_right"));
1058 a->setText(i18n("Select Word Right"));
1060 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftWordRight);
1061 m_editActions.push_back(a);
1062
1063 a = ac->addAction(QStringLiteral("mark_selection"));
1064 a->setText(i18n("Start the Marked Selection"));
1065 a->setWhatsThis(i18n("Emulate the Emacs-like selection mode, where the beginning is marked and then the selection is continuously updated."));
1066 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::markSelection);
1067 m_editActions.push_back(a);
1068
1069 a = ac->addAction(QStringLiteral("beginning_of_line"));
1070 a->setText(i18n("Move to Beginning of Line"));
1072 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::home);
1073 m_editActions.push_back(a);
1074
1075 a = ac->addAction(QStringLiteral("beginning_of_document"));
1076 a->setText(i18n("Move to Beginning of Document"));
1078 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::top);
1079 m_editActions.push_back(a);
1080
1081 a = ac->addAction(QStringLiteral("select_beginning_of_line"));
1082 a->setText(i18n("Select to Beginning of Line"));
1084 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftHome);
1085 m_editActions.push_back(a);
1086
1087 a = ac->addAction(QStringLiteral("select_beginning_of_document"));
1088 a->setText(i18n("Select to Beginning of Document"));
1090 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftTop);
1091 m_editActions.push_back(a);
1092
1093 a = ac->addAction(QStringLiteral("end_of_line"));
1094 a->setText(i18n("Move to End of Line"));
1096 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::end);
1097 m_editActions.push_back(a);
1098
1099 a = ac->addAction(QStringLiteral("end_of_document"));
1100 a->setText(i18n("Move to End of Document"));
1102 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::bottom);
1103 m_editActions.push_back(a);
1104
1105 a = ac->addAction(QStringLiteral("select_end_of_line"));
1106 a->setText(i18n("Select to End of Line"));
1108 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftEnd);
1109 m_editActions.push_back(a);
1110
1111 a = ac->addAction(QStringLiteral("select_end_of_document"));
1112 a->setText(i18n("Select to End of Document"));
1114 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftBottom);
1115 m_editActions.push_back(a);
1116
1117 a = ac->addAction(QStringLiteral("select_line_up"));
1118 a->setText(i18n("Select to Previous Line"));
1120 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftUp);
1121 m_editActions.push_back(a);
1122
1123 a = ac->addAction(QStringLiteral("scroll_line_up"));
1124 a->setText(i18n("Scroll Line Up"));
1126 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::scrollUp);
1127 m_editActions.push_back(a);
1128
1129 a = ac->addAction(QStringLiteral("move_line_down"));
1130 a->setText(i18n("Move to Next Line"));
1132 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::down);
1133 m_editActions.push_back(a);
1134
1135 a = ac->addAction(QStringLiteral("move_line_up"));
1136 a->setText(i18n("Move to Previous Line"));
1138 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::up);
1139 m_editActions.push_back(a);
1140
1141 a = ac->addAction(QStringLiteral("move_cursor_right"));
1142 a->setText(i18n("Move Cursor Right"));
1144 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cursorRight);
1145 m_editActions.push_back(a);
1146
1147 a = ac->addAction(QStringLiteral("move_cursor_left"));
1148 a->setText(i18n("Move Cursor Left"));
1150 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::cursorLeft);
1151 m_editActions.push_back(a);
1152
1153 a = ac->addAction(QStringLiteral("select_line_down"));
1154 a->setText(i18n("Select to Next Line"));
1156 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftDown);
1157 m_editActions.push_back(a);
1158
1159 a = ac->addAction(QStringLiteral("scroll_line_down"));
1160 a->setText(i18n("Scroll Line Down"));
1162 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::scrollDown);
1163 m_editActions.push_back(a);
1164
1165 a = ac->addAction(QStringLiteral("scroll_page_up"));
1166 a->setText(i18n("Scroll Page Up"));
1168 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::pageUp);
1169 m_editActions.push_back(a);
1170
1171 a = ac->addAction(QStringLiteral("select_page_up"));
1172 a->setText(i18n("Select Page Up"));
1174 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftPageUp);
1175 m_editActions.push_back(a);
1176
1177 a = ac->addAction(QStringLiteral("move_top_of_view"));
1178 a->setText(i18n("Move to Top of View"));
1180 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::topOfView);
1181 m_editActions.push_back(a);
1182
1183 a = ac->addAction(QStringLiteral("select_top_of_view"));
1184 a->setText(i18n("Select to Top of View"));
1186 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftTopOfView);
1187 m_editActions.push_back(a);
1188
1189 a = ac->addAction(QStringLiteral("scroll_page_down"));
1190 a->setText(i18n("Scroll Page Down"));
1192 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::pageDown);
1193 m_editActions.push_back(a);
1194
1195 a = ac->addAction(QStringLiteral("select_page_down"));
1196 a->setText(i18n("Select Page Down"));
1198 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftPageDown);
1199 m_editActions.push_back(a);
1200
1201 a = ac->addAction(QStringLiteral("move_bottom_of_view"));
1202 a->setText(i18n("Move to Bottom of View"));
1204 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::bottomOfView);
1205 m_editActions.push_back(a);
1206
1207 a = ac->addAction(QStringLiteral("select_bottom_of_view"));
1208 a->setText(i18n("Select to Bottom of View"));
1210 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftBottomOfView);
1211 m_editActions.push_back(a);
1212
1213 a = ac->addAction(QStringLiteral("to_matching_bracket"));
1214 a->setText(i18n("Go to Matching Bracket"));
1216 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::toMatchingBracket);
1217 // m_editActions << a;
1218
1219 a = ac->addAction(QStringLiteral("select_matching_bracket"));
1220 a->setText(i18n("Select to Matching Bracket"));
1222 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::shiftToMatchingBracket);
1223 // m_editActions << a;
1224
1225 // anders: shortcuts doing any changes should not be created in read-only mode
1226 if (!doc()->readOnly()) {
1227 a = ac->addAction(QStringLiteral("transpose_char"));
1228 a->setText(i18n("Transpose Characters"));
1229 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::transpose);
1230 m_editActions.push_back(a);
1231
1232 a = ac->addAction(QStringLiteral("transpose_word"));
1233 a->setText(i18n("Transpose Words"));
1234 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::transposeWord);
1235 m_editActions.push_back(a);
1236
1237 a = ac->addAction(QStringLiteral("delete_line"));
1238 a->setText(i18n("Delete Line"));
1240 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::killLine);
1241 m_editActions.push_back(a);
1242
1243 a = ac->addAction(QStringLiteral("delete_word_left"));
1244 a->setText(i18n("Delete Word Left"));
1246 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::deleteWordLeft);
1247 m_editActions.push_back(a);
1248
1249 a = ac->addAction(QStringLiteral("delete_word_right"));
1250 a->setText(i18n("Delete Word Right"));
1252 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::deleteWordRight);
1253 m_editActions.push_back(a);
1254
1255 a = ac->addAction(QStringLiteral("delete_next_character"));
1256 a->setText(i18n("Delete Next Character"));
1258 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::keyDelete);
1259 m_editActions.push_back(a);
1260
1261 a = ac->addAction(QStringLiteral("backspace"));
1262 a->setText(i18n("Backspace"));
1263 QList<QKeySequence> scuts;
1265 ac->setDefaultShortcuts(a, scuts);
1266 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::backspace);
1267 m_editActions.push_back(a);
1268
1269 a = ac->addAction(QStringLiteral("insert_tabulator"));
1270 a->setText(i18n("Insert Tab Character"));
1271 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::insertTab);
1272 m_editActions.push_back(a);
1273
1274 a = ac->addAction(QStringLiteral("smart_newline"));
1275 a->setText(i18n("Insert Smart Newline"));
1276 a->setWhatsThis(i18n("Insert newline including leading characters of the current line which are not letters or numbers."));
1277 scuts.clear();
1279 ac->setDefaultShortcuts(a, scuts);
1280 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::smartNewline);
1281 m_editActions.push_back(a);
1282
1283 a = ac->addAction(QStringLiteral("no_indent_newline"));
1284 a->setText(i18n("Insert a Non-Indented Newline"));
1285 a->setWhatsThis(i18n("Insert a new line without indentation, regardless of indentation settings."));
1286 scuts.clear();
1288 ac->setDefaultShortcuts(a, scuts);
1289 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::noIndentNewline);
1290 m_editActions.push_back(a);
1291
1292 a = ac->addAction(QStringLiteral("newline_above"));
1293 a->setText(i18n("Insert a Newline Above Current Line"));
1294 a->setWhatsThis(i18n("Insert a new line above current line without modifying the current line."));
1295 scuts.clear();
1297 ac->setDefaultShortcuts(a, scuts);
1298 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::newLineAbove);
1299 m_editActions.push_back(a);
1300
1301 a = ac->addAction(QStringLiteral("newline_below"));
1302 a->setText(i18n("Insert a Newline Below Current Line"));
1303 a->setWhatsThis(i18n("Insert a new line below current line without modifying the current line."));
1304 scuts.clear();
1306 ac->setDefaultShortcuts(a, scuts);
1307 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::newLineBelow);
1308 m_editActions.push_back(a);
1309
1310 a = ac->addAction(QStringLiteral("tools_indent"));
1311 a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-more")));
1312 a->setText(i18n("&Indent"));
1313 a->setWhatsThis(
1314 i18n("Use this to indent a selected block of text.<br /><br />"
1315 "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
1317 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::indent);
1318
1319 a = ac->addAction(QStringLiteral("tools_unindent"));
1320 a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-less")));
1321 a->setText(i18n("&Unindent"));
1322 a->setWhatsThis(i18n("Use this to unindent a selected block of text."));
1324 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::unIndent);
1325 }
1326
1327 if (hasFocus()) {
1328 slotGotFocus();
1329 } else {
1330 slotLostFocus();
1331 }
1332}
1333
1334void KTextEditor::ViewPrivate::setupCodeFolding()
1335{
1336 KActionCollection *ac = this->actionCollection();
1337 QAction *a;
1338
1339 a = ac->addAction(QStringLiteral("folding_toplevel"));
1340 a->setText(i18n("Fold Toplevel Nodes"));
1341 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotFoldToplevelNodes);
1342
1343 a = ac->addAction(QStringLiteral("folding_expandtoplevel"));
1344 a->setText(i18n("Unfold Toplevel Nodes"));
1345 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotExpandToplevelNodes);
1346
1347 a = ac->addAction(QStringLiteral("folding_toggle_current"));
1348 a->setText(i18n("Toggle Current Node"));
1349 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotToggleFolding);
1350
1351 a = ac->addAction(QStringLiteral("folding_toggle_in_current"));
1352 a->setText(i18n("Toggle Contained Nodes"));
1353 connect(a, &QAction::triggered, this, &KTextEditor::ViewPrivate::slotToggleFoldingsInRange);
1354}
1355
1356void KTextEditor::ViewPrivate::setupSpeechActions()
1357{
1358 KActionCollection *ac = actionCollection();
1359
1360 QAction *a = ac->addAction(QStringLiteral("tools_speech_say"));
1361 a->setText(i18n("Say current selection or document"));
1362 connect(a, &QAction::triggered, this, [this]() {
1363 if (selection()) {
1364 KTextEditor::EditorPrivate::self()->speechEngine(this)->say(selectionText());
1365 } else {
1366 KTextEditor::EditorPrivate::self()->speechEngine(this)->say(document()->text());
1367 }
1368 });
1369
1370 a = ac->addAction(QStringLiteral("tools_speech_stop"));
1371 a->setText(i18n("Stop current output"));
1372 connect(a, &QAction::triggered, this, [this]() {
1374 });
1375
1376 a = ac->addAction(QStringLiteral("tools_speech_pause"));
1377 a->setText(i18n("Pause current output"));
1378 connect(a, &QAction::triggered, this, [this]() {
1380 });
1381
1382 a = ac->addAction(QStringLiteral("tools_speech_resume"));
1383 a->setText(i18n("Resume current output"));
1384 connect(a, &QAction::triggered, this, [this]() {
1386 });
1387}
1388
1389void KTextEditor::ViewPrivate::slotFoldToplevelNodes()
1390{
1391 for (int line = 0; line < doc()->lines(); ++line) {
1392 if (textFolding().isLineVisible(line)) {
1393 foldLine(line);
1394 }
1395 }
1396}
1397
1398void KTextEditor::ViewPrivate::slotExpandToplevelNodes()
1399{
1400 const auto topLevelRanges(textFolding().foldingRangesForParentRange());
1401 for (const auto &range : topLevelRanges) {
1402 textFolding().unfoldRange(range.first);
1403 }
1404}
1405
1406void KTextEditor::ViewPrivate::slotToggleFolding()
1407{
1408 int line = cursorPosition().line();
1409 bool actionDone = false;
1410 while (!actionDone && (line > -1)) {
1411 actionDone = unfoldLine(line);
1412 if (!actionDone) {
1413 actionDone = foldLine(line--).isValid();
1414 }
1415 }
1416}
1417
1418void KTextEditor::ViewPrivate::slotToggleFoldingsInRange()
1419{
1420 int line = cursorPosition().line();
1421 while (!toggleFoldingsInRange(line) && (line > -1)) {
1422 --line;
1423 }
1424}
1425
1426KTextEditor::Range KTextEditor::ViewPrivate::foldLine(int line)
1427{
1428 KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(line);
1429 if (!foldingRange.isValid()) {
1430 return foldingRange;
1431 }
1432
1433 // Ensure not to fold the end marker to avoid a deceptive look, but only on token based folding
1434 // ensure we don't compute an invalid line by moving outside of the foldingRange range by checking onSingleLine(), see bug 417890
1435 if (!m_doc->buffer().isFoldingStartingOnLine(line).second && !foldingRange.onSingleLine()) {
1436 const int adjustedLine = foldingRange.end().line() - 1;
1437 foldingRange.setEnd(KTextEditor::Cursor(adjustedLine, doc()->buffer().plainLine(adjustedLine).length()));
1438 }
1439
1440 // Check if the discovered fold is already folded up.
1441 // If so, we should not fold the same range again!
1442 // This can lead to issues where we need to open the fold multiple times
1443 // in order to actually open it.
1444 auto folds = textFolding().foldingRangesStartingOnLine(line);
1445 for (int i = 0; i < folds.size(); ++i) {
1446 KTextEditor::Range fold = textFolding().foldingRange(folds[i].first);
1447 if (fold == foldingRange) {
1448 return foldingRange;
1449 }
1450 }
1451
1452 // Don't try to fold a single line, which can happens due to adjustment above
1453 // FIXME Avoid to offer such a folding marker
1454 if (!foldingRange.onSingleLine()) {
1455 textFolding().newFoldingRange(foldingRange, Kate::TextFolding::Folded);
1456 }
1457
1458 return foldingRange;
1459}
1460
1461bool KTextEditor::ViewPrivate::unfoldLine(int line)
1462{
1463 bool actionDone = false;
1464 const KTextEditor::Cursor currentCursor = cursorPosition();
1465
1466 // ask the folding info for this line, if any folds are around!
1467 // auto = QList<QPair<qint64, Kate::TextFolding::FoldingRangeFlags>>
1468 auto startingRanges = textFolding().foldingRangesStartingOnLine(line);
1469 for (int i = 0; i < startingRanges.size() && !actionDone; ++i) {
1470 // Avoid jumping view in case of a big unfold and ensure nice highlight of folding marker
1471 setCursorPosition(textFolding().foldingRange(startingRanges[i].first).start());
1472
1473 actionDone |= textFolding().unfoldRange(startingRanges[i].first);
1474 }
1475
1476 if (!actionDone) {
1477 // Nothing unfolded? Restore old cursor position!
1478 setCursorPosition(currentCursor);
1479 }
1480
1481 return actionDone;
1482}
1483
1484bool KTextEditor::ViewPrivate::toggleFoldingOfLine(int line)
1485{
1486 bool actionDone = unfoldLine(line);
1487 if (!actionDone) {
1488 actionDone = foldLine(line).isValid();
1489 }
1490
1491 return actionDone;
1492}
1493
1494bool KTextEditor::ViewPrivate::toggleFoldingsInRange(int line)
1495{
1496 KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(line);
1497 if (!foldingRange.isValid()) {
1498 // Either line is not valid or there is no start range
1499 return false;
1500 }
1501
1502 bool actionDone = false; // Track success
1503 const KTextEditor::Cursor currentCursor = cursorPosition();
1504
1505 // Don't be too eager but obliging! Only toggle containing ranges which are
1506 // visible -> Be done when the range is folded
1507 actionDone |= unfoldLine(line);
1508
1509 if (!actionDone) {
1510 // Unfold all in range, but not the range itself
1511 for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1512 actionDone |= unfoldLine(ln);
1513 }
1514
1515 if (actionDone) {
1516 // In most cases we want now a not moved cursor
1517 setCursorPosition(currentCursor);
1518 }
1519 }
1520
1521 if (!actionDone) {
1522 // Fold all in range, but not the range itself
1523 for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1524 KTextEditor::Range fr = foldLine(ln);
1525 if (fr.isValid()) {
1526 // qMax to avoid infinite loop in case of range without content
1527 ln = qMax(ln, fr.end().line() - 1);
1528 actionDone = true;
1529 }
1530 }
1531 }
1532
1533 if (!actionDone) {
1534 // At this point was an unfolded range clicked which contains no "childs"
1535 // We assume the user want to fold it by the wrong button, be obliging!
1536 actionDone |= foldLine(line).isValid();
1537 }
1538
1539 // At this point we should be always true
1540 return actionDone;
1541}
1542
1543KTextEditor::View::ViewMode KTextEditor::ViewPrivate::viewMode() const
1544{
1545 return currentInputMode()->viewMode();
1546}
1547
1548QString KTextEditor::ViewPrivate::viewModeHuman() const
1549{
1550 QString currentMode = currentInputMode()->viewModeHuman();
1551
1552 // append read-only if needed
1553 if (!doc()->isReadWrite()) {
1554 currentMode = i18n("(R/O) %1", currentMode);
1555 }
1556
1557 // return full mode
1558 return currentMode;
1559}
1560
1561KTextEditor::View::InputMode KTextEditor::ViewPrivate::viewInputMode() const
1562{
1563 return currentInputMode()->viewInputMode();
1564}
1565
1566QString KTextEditor::ViewPrivate::viewInputModeHuman() const
1567{
1568 return currentInputMode()->viewInputModeHuman();
1569}
1570
1571void KTextEditor::ViewPrivate::setInputMode(KTextEditor::View::InputMode mode, const bool rememberInConfig)
1572{
1573 if (currentInputMode()->viewInputMode() == mode) {
1574 return;
1575 }
1576
1577 // No multi cursors for vi
1579 clearSecondaryCursors();
1580 }
1581
1582 m_viewInternal->m_currentInputMode->deactivate();
1583 m_viewInternal->m_currentInputMode = m_viewInternal->m_inputModes[mode].get();
1584 m_viewInternal->m_currentInputMode->activate();
1585
1586 // remember in local config if requested, we skip this for the calls in updateConfig
1587 if (rememberInConfig) {
1588 config()->setValue(KateViewConfig::InputMode, mode);
1589 }
1590
1591 /* small duplication, but need to do this if not toggled by action */
1592 const auto inputModeActions = m_inputModeActions->actions();
1593 for (QAction *action : inputModeActions) {
1594 if (static_cast<InputMode>(action->data().toInt()) == mode) {
1595 action->setChecked(true);
1596 break;
1597 }
1598 }
1599
1600 /* inform the rest of the system about the change */
1601 Q_EMIT viewInputModeChanged(this, mode);
1602 Q_EMIT viewModeChanged(this, viewMode());
1603}
1604
1605void KTextEditor::ViewPrivate::slotDocumentAboutToReload()
1606{
1607 if (doc()->isAutoReload()) {
1608 const int lastVisibleLine = m_viewInternal->endLine();
1609 const int currentLine = cursorPosition().line();
1610 m_gotoBottomAfterReload = (lastVisibleLine == currentLine) && (currentLine == doc()->lastLine());
1611 if (!m_gotoBottomAfterReload) {
1612 // Ensure the view jumps not back when user scrolls around
1613 const int firstVisibleLine = 1 + lastVisibleLine - m_viewInternal->linesDisplayed();
1614 const int newLine = qBound(firstVisibleLine, currentLine, lastVisibleLine);
1615 setCursorPositionVisual(KTextEditor::Cursor(newLine, cursorPosition().column()));
1616 }
1617 } else {
1618 m_gotoBottomAfterReload = false;
1619 }
1620}
1621
1622void KTextEditor::ViewPrivate::slotDocumentReloaded()
1623{
1624 if (m_gotoBottomAfterReload) {
1625 bottom();
1626 }
1627}
1628
1629void KTextEditor::ViewPrivate::slotGotFocus()
1630{
1631 // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotGotFocus";
1632 currentInputMode()->gotFocus();
1633
1634 // update current view and scrollbars
1635 // it is needed for styles that implement different frame and scrollbar
1636 // rendering when focused
1637 update();
1638 if (m_viewInternal->m_lineScroll->isVisible()) {
1639 m_viewInternal->m_lineScroll->update();
1640 }
1641
1642 if (m_viewInternal->m_columnScroll->isVisible()) {
1643 m_viewInternal->m_columnScroll->update();
1644 }
1645
1646 Q_EMIT focusIn(this);
1647}
1648
1649void KTextEditor::ViewPrivate::slotLostFocus()
1650{
1651 // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotLostFocus";
1652 currentInputMode()->lostFocus();
1653
1654 // update current view and scrollbars
1655 // it is needed for styles that implement different frame and scrollbar
1656 // rendering when focused
1657 update();
1658 if (m_viewInternal->m_lineScroll->isVisible()) {
1659 m_viewInternal->m_lineScroll->update();
1660 }
1661
1662 if (m_viewInternal->m_columnScroll->isVisible()) {
1663 m_viewInternal->m_columnScroll->update();
1664 }
1665
1666 if (doc()->config()->autoSave() && doc()->config()->autoSaveOnFocusOut() && doc()->isModified() && doc()->url().isLocalFile()) {
1667 doc()->documentSave();
1668 }
1669
1670 Q_EMIT focusOut(this);
1671}
1672
1673void KTextEditor::ViewPrivate::setDynWrapIndicators(int mode)
1674{
1675 config()->setValue(KateViewConfig::DynWordWrapIndicators, mode);
1676}
1677
1678bool KTextEditor::ViewPrivate::isOverwriteMode() const
1679{
1680 return doc()->config()->ovr();
1681}
1682
1683void KTextEditor::ViewPrivate::reloadFile()
1684{
1685 // bookmarks and cursor positions are temporarily saved by the document
1686 doc()->documentReload();
1687}
1688
1689void KTextEditor::ViewPrivate::slotReadWriteChanged()
1690{
1691 if (m_toggleWriteLock) {
1692 m_toggleWriteLock->setChecked(!doc()->isReadWrite());
1693 }
1694
1695 m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
1696 m_paste->setEnabled(doc()->isReadWrite());
1697 if (m_pasteSelection) {
1698 m_pasteSelection->setEnabled(doc()->isReadWrite());
1699 }
1700 m_swapWithClipboard->setEnabled(doc()->isReadWrite());
1701 m_setEndOfLine->setEnabled(doc()->isReadWrite());
1702
1703 static const auto l = {QStringLiteral("edit_replace"),
1704 QStringLiteral("tools_spelling"),
1705 QStringLiteral("tools_indent"),
1706 QStringLiteral("tools_unindent"),
1707 QStringLiteral("tools_cleanIndent"),
1708 QStringLiteral("tools_formatIndet"),
1709 QStringLiteral("tools_alignOn"),
1710 QStringLiteral("tools_comment"),
1711 QStringLiteral("tools_uncomment"),
1712 QStringLiteral("tools_toggle_comment"),
1713 QStringLiteral("tools_uppercase"),
1714 QStringLiteral("tools_lowercase"),
1715 QStringLiteral("tools_capitalize"),
1716 QStringLiteral("tools_join_lines"),
1717 QStringLiteral("tools_apply_wordwrap"),
1718 QStringLiteral("tools_spelling_from_cursor"),
1719 QStringLiteral("tools_spelling_selection")};
1720
1721 for (const auto &action : l) {
1722 QAction *a = actionCollection()->action(action);
1723 if (a) {
1724 a->setEnabled(doc()->isReadWrite());
1725 }
1726 }
1727 slotUpdateUndo();
1728
1729 currentInputMode()->readWriteChanged(doc()->isReadWrite());
1730
1731 // => view mode changed
1732 Q_EMIT viewModeChanged(this, viewMode());
1733 Q_EMIT viewInputModeChanged(this, viewInputMode());
1734}
1735
1736void KTextEditor::ViewPrivate::toggleCamelCaseCursor()
1737{
1738 const auto enabled = doc()->config()->camelCursor();
1739 doc()->config()->setCamelCursor(!enabled);
1741 if (enabled) {
1742 m = new KTextEditor::Message(i18n("Camel case movement disabled"));
1743 } else {
1744 m = new KTextEditor::Message(i18n("Camel case movement enabled"));
1745 }
1747 m->setAutoHide(1000);
1749 doc()->postMessage(m);
1750}
1751
1752void KTextEditor::ViewPrivate::slotUpdateUndo()
1753{
1754 if (doc()->readOnly()) {
1755 return;
1756 }
1757
1758 m_editUndo->setEnabled(doc()->isReadWrite() && doc()->undoCount() > 0);
1759 m_editRedo->setEnabled(doc()->isReadWrite() && doc()->redoCount() > 0);
1760}
1761
1762bool KTextEditor::ViewPrivate::setCursorPositionInternal(const KTextEditor::Cursor position, uint tabwidth, bool calledExternally)
1763{
1764 if (position.line() < 0 || position.line() >= doc()->lines()) {
1765 return false;
1766 }
1767
1768 Kate::TextLine l = doc()->kateTextLine(position.line());
1769 const QString line_str = l.text();
1770
1771 int x = 0;
1772 int z = 0;
1773 for (; z < line_str.length() && z < position.column(); z++) {
1774 if (line_str[z] == QLatin1Char('\t')) {
1775 x += tabwidth - (x % tabwidth);
1776 } else {
1777 x++;
1778 }
1779 }
1780
1781 if (blockSelection()) {
1782 if (z < position.column()) {
1783 x += position.column() - z;
1784 }
1785 }
1786
1787 m_viewInternal->updateCursor(KTextEditor::Cursor(position.line(), x),
1788 false,
1789 calledExternally /* force center for external calls, see bug 408418 */,
1790 calledExternally);
1791
1792 return true;
1793}
1794
1795void KTextEditor::ViewPrivate::toggleInsert()
1796{
1797 doc()->config()->setOvr(!doc()->config()->ovr());
1798 m_toggleInsert->setChecked(isOverwriteMode());
1799
1800 // No multi cursors for overwrite mode
1801 if (isOverwriteMode()) {
1802 clearSecondaryCursors();
1803 }
1804
1805 Q_EMIT viewModeChanged(this, viewMode());
1806 Q_EMIT viewInputModeChanged(this, viewInputMode());
1807}
1808
1809void KTextEditor::ViewPrivate::slotSaveCanceled(const QString &error)
1810{
1811 if (!error.isEmpty()) { // happens when canceling a job
1812 KMessageBox::error(this, error);
1813 }
1814}
1815
1816void KTextEditor::ViewPrivate::gotoLine()
1817{
1818 gotoBar()->updateData();
1819 bottomViewBar()->showBarWidget(gotoBar());
1820}
1821
1822void KTextEditor::ViewPrivate::changeDictionary()
1823{
1824 dictionaryBar()->updateData();
1825 bottomViewBar()->showBarWidget(dictionaryBar());
1826}
1827
1828void KTextEditor::ViewPrivate::joinLines()
1829{
1830 int first = selectionRange().start().line();
1831 int last = selectionRange().end().line();
1832 // int left = doc()->line( last ).length() - doc()->selEndCol();
1833 if (first == last) {
1834 first = cursorPosition().line();
1835 last = first + 1;
1836 }
1837 doc()->joinLines(first, last);
1838}
1839
1840void KTextEditor::ViewPrivate::readSessionConfig(const KConfigGroup &config, const QSet<QString> &flags)
1841{
1842 Q_UNUSED(flags)
1843
1844 // cursor position
1845 KTextEditor::Cursor savedPosition(config.readEntry("CursorLine", 0), config.readEntry("CursorColumn", 0));
1846 setCursorPositionInternal(savedPosition);
1847
1848 // scroll position
1849 const int scroll = config.readEntry("ScrollLine", -1);
1850 if (scroll >= 0 && scroll < doc()->lines() && savedPosition.line() < doc()->lines()) {
1851 setScrollPositionInternal(KTextEditor::Cursor(scroll, 0));
1852 }
1853
1854 // only touch that if we did write it out in writeSessionConfig, bug 487216
1855 if (config.hasKey("Dynamic Word Wrap")) {
1856 // in any case, use the current global setting as default
1857 m_config->setDynWordWrap(config.readEntry("Dynamic Word Wrap", m_config->global()->dynWordWrap()));
1858 }
1859
1860 // restore text folding
1861 m_savedFoldingState = QJsonDocument::fromJson(config.readEntry("TextFolding", QByteArray()));
1862 applyFoldingState();
1863
1864 m_forceRTL = config.readEntry("Force RTL Direction", false);
1865 m_forceRTLDirection->setChecked(m_forceRTL);
1866
1867 for (const auto &mode : m_viewInternal->m_inputModes) {
1868 mode->readSessionConfig(config);
1869 }
1870}
1871
1872void KTextEditor::ViewPrivate::writeSessionConfig(KConfigGroup &config, const QSet<QString> &)
1873{
1874 // ensure we don't amass stuff
1875 config.deleteGroup();
1876
1877 // cursor position
1878 const auto cursor = cursorPosition();
1879 if (cursor.isValid() && cursor != KTextEditor::Cursor(0, 0)) {
1880 config.writeEntry("CursorLine", cursor.line());
1881 config.writeEntry("CursorColumn", cursor.column());
1882 }
1883
1884 // save scroll position if its different from cursorPosition
1885 const int scrollLine = firstDisplayedLineInternal(LineType::RealLine);
1886 if (scrollLine > 0 && scrollLine != cursor.line()) {
1887 config.writeEntry("ScrollLine", scrollLine);
1888 }
1889
1890 // only write if set in this view
1891 if (m_config->isSet(KateViewConfig::DynamicWordWrap)) {
1892 config.writeEntry("Dynamic Word Wrap", m_config->dynWordWrap());
1893 }
1894
1895 // save text folding state
1896 saveFoldingState();
1897 if (!m_savedFoldingState.object().value(QLatin1String("ranges")).toArray().isEmpty()) {
1898 config.writeEntry("TextFolding", m_savedFoldingState.toJson(QJsonDocument::Compact));
1899 m_savedFoldingState = QJsonDocument();
1900 }
1901
1902 if (m_forceRTL) {
1903 config.writeEntry("Force RTL Direction", m_forceRTL);
1904 }
1905
1906 for (const auto &mode : m_viewInternal->m_inputModes) {
1907 mode->writeSessionConfig(config);
1908 }
1909}
1910
1911int KTextEditor::ViewPrivate::getEol() const
1912{
1913 return doc()->config()->eol();
1914}
1915
1916QMenu *KTextEditor::ViewPrivate::getEolMenu()
1917{
1918 return m_setEndOfLine->menu();
1919}
1920
1921void KTextEditor::ViewPrivate::setEol(int eol)
1922{
1923 if (!doc()->isReadWrite()) {
1924 return;
1925 }
1926
1927 if (m_updatingDocumentConfig) {
1928 return;
1929 }
1930
1931 if (eol != doc()->config()->eol()) {
1932 doc()->setModified(true); // mark modified (bug #143120)
1933 doc()->config()->setEol(eol);
1934 }
1935}
1936
1937void KTextEditor::ViewPrivate::setAddBom(bool enabled)
1938{
1939 if (!doc()->isReadWrite()) {
1940 return;
1941 }
1942
1943 if (m_updatingDocumentConfig) {
1944 return;
1945 }
1946
1947 doc()->config()->setBom(enabled);
1948 doc()->bomSetByUser();
1949}
1950
1951void KTextEditor::ViewPrivate::setIconBorder(bool enable)
1952{
1953 config()->setValue(KateViewConfig::ShowIconBar, enable);
1954}
1955
1956void KTextEditor::ViewPrivate::toggleIconBorder()
1957{
1958 config()->setValue(KateViewConfig::ShowIconBar, !config()->iconBar());
1959}
1960
1961void KTextEditor::ViewPrivate::setLineNumbersOn(bool enable)
1962{
1963 config()->setValue(KateViewConfig::ShowLineNumbers, enable);
1964}
1965
1966void KTextEditor::ViewPrivate::toggleLineNumbersOn()
1967{
1968 config()->setValue(KateViewConfig::ShowLineNumbers, !config()->lineNumbers());
1969}
1970
1971void KTextEditor::ViewPrivate::setScrollBarMarks(bool enable)
1972{
1973 config()->setValue(KateViewConfig::ShowScrollBarMarks, enable);
1974}
1975
1976void KTextEditor::ViewPrivate::toggleScrollBarMarks()
1977{
1978 config()->setValue(KateViewConfig::ShowScrollBarMarks, !config()->scrollBarMarks());
1979}
1980
1981void KTextEditor::ViewPrivate::setScrollBarMiniMap(bool enable)
1982{
1983 config()->setValue(KateViewConfig::ShowScrollBarMiniMap, enable);
1984}
1985
1986void KTextEditor::ViewPrivate::toggleScrollBarMiniMap()
1987{
1988 config()->setValue(KateViewConfig::ShowScrollBarMiniMap, !config()->scrollBarMiniMap());
1989}
1990
1991void KTextEditor::ViewPrivate::setScrollBarMiniMapAll(bool enable)
1992{
1993 config()->setValue(KateViewConfig::ShowScrollBarMiniMapAll, enable);
1994}
1995
1996void KTextEditor::ViewPrivate::toggleScrollBarMiniMapAll()
1997{
1998 config()->setValue(KateViewConfig::ShowScrollBarMiniMapAll, !config()->scrollBarMiniMapAll());
1999}
2000
2001void KTextEditor::ViewPrivate::setScrollBarMiniMapWidth(int width)
2002{
2003 config()->setValue(KateViewConfig::ScrollBarMiniMapWidth, width);
2004}
2005
2006void KTextEditor::ViewPrivate::toggleShowSpaces()
2007{
2008 if (m_updatingDocumentConfig) {
2009 return;
2010 }
2011
2012 using WhitespaceRendering = KateDocumentConfig::WhitespaceRendering;
2013 doc()->config()->setShowSpaces(doc()->config()->showSpaces() != WhitespaceRendering::None ? WhitespaceRendering::None : WhitespaceRendering::All);
2014}
2015
2016void KTextEditor::ViewPrivate::toggleDynWordWrap()
2017{
2018 config()->setDynWordWrap(!config()->dynWordWrap());
2019}
2020
2021void KTextEditor::ViewPrivate::toggleWWMarker()
2022{
2023 m_renderer->config()->setWordWrapMarker(!m_renderer->config()->wordWrapMarker());
2024}
2025
2026void KTextEditor::ViewPrivate::toggleNPSpaces()
2027{
2028 m_renderer->setShowNonPrintableSpaces(!m_renderer->showNonPrintableSpaces());
2029 m_viewInternal->update(); // force redraw
2030}
2031
2032void KTextEditor::ViewPrivate::toggleWordCount(bool on)
2033{
2034 config()->setShowWordCount(on);
2035}
2036
2037void KTextEditor::ViewPrivate::setFoldingMarkersOn(bool enable)
2038{
2039 config()->setValue(KateViewConfig::ShowFoldingBar, enable);
2040}
2041
2042void KTextEditor::ViewPrivate::toggleFoldingMarkers()
2043{
2044 config()->setValue(KateViewConfig::ShowFoldingBar, !config()->foldingBar());
2045}
2046
2047bool KTextEditor::ViewPrivate::iconBorder()
2048{
2049 return m_viewInternal->m_leftBorder->iconBorderOn();
2050}
2051
2052bool KTextEditor::ViewPrivate::lineNumbersOn()
2053{
2054 return m_viewInternal->m_leftBorder->lineNumbersOn();
2055}
2056
2057bool KTextEditor::ViewPrivate::scrollBarMarks()
2058{
2059 return m_viewInternal->m_lineScroll->showMarks();
2060}
2061
2062bool KTextEditor::ViewPrivate::scrollBarMiniMap()
2063{
2064 return m_viewInternal->m_lineScroll->showMiniMap();
2065}
2066
2067int KTextEditor::ViewPrivate::dynWrapIndicators()
2068{
2069 return m_viewInternal->m_leftBorder->dynWrapIndicators();
2070}
2071
2072bool KTextEditor::ViewPrivate::foldingMarkersOn()
2073{
2074 return m_viewInternal->m_leftBorder->foldingMarkersOn();
2075}
2076
2077bool KTextEditor::ViewPrivate::forceRTLDirection() const
2078{
2079 return m_forceRTL;
2080}
2081
2082void KTextEditor::ViewPrivate::toggleWriteLock()
2083{
2084 doc()->setReadWrite(!doc()->isReadWrite());
2085}
2086
2087void KTextEditor::ViewPrivate::registerTextHintProvider(KTextEditor::TextHintProvider *provider)
2088{
2089 m_viewInternal->registerTextHintProvider(provider);
2090}
2091
2092void KTextEditor::ViewPrivate::unregisterTextHintProvider(KTextEditor::TextHintProvider *provider)
2093{
2094 m_viewInternal->unregisterTextHintProvider(provider);
2095}
2096
2097void KTextEditor::ViewPrivate::setTextHintDelay(int delay)
2098{
2099 m_viewInternal->setTextHintDelay(delay);
2100}
2101
2102int KTextEditor::ViewPrivate::textHintDelay() const
2103{
2104 return m_viewInternal->textHintDelay();
2105}
2106
2107// NOLINTNEXTLINE(readability-make-member-function-const)
2108void KTextEditor::ViewPrivate::find()
2109{
2110 currentInputMode()->find();
2111}
2112
2113// NOLINTNEXTLINE(readability-make-member-function-const)
2114void KTextEditor::ViewPrivate::findSelectedForwards()
2115{
2116 currentInputMode()->findSelectedForwards();
2117}
2118
2119// NOLINTNEXTLINE(readability-make-member-function-const)
2120void KTextEditor::ViewPrivate::findSelectedBackwards()
2121{
2122 currentInputMode()->findSelectedBackwards();
2123}
2124
2125void KTextEditor::ViewPrivate::skipCurrentOccurunceSelection()
2126{
2127 if (isMulticursorNotAllowed()) {
2128 return;
2129 }
2130 m_skipCurrentSelection = true;
2131}
2132
2133void KTextEditor::ViewPrivate::findNextOccurunceAndSelect()
2134{
2135 if (isMulticursorNotAllowed()) {
2136 return;
2137 }
2138
2139 const auto text = selection() ? doc()->text(selectionRange()) : QString();
2140 if (text.isEmpty()) {
2141 const auto selection = doc()->wordRangeAt(cursorPosition());
2142 // We don't want matching word highlights
2143 setSelection(selection);
2144 setCursorPosition(selection.end());
2145 clearHighlights();
2146
2147 for (auto &c : m_secondaryCursors) {
2148 const auto range = doc()->wordRangeAt(c.cursor());
2149 if (!c.range && !c.anchor.isValid()) {
2150 c.anchor = range.start();
2151 c.range.reset(newSecondarySelectionRange(range));
2152 c.pos->setPosition(range.end());
2153 }
2154 tagLines(range);
2155 }
2156 return;
2157 } else if (!m_rangesForHighlights.empty()) {
2158 clearHighlights();
2159 }
2160
2161 // Use selection range end as starting point
2162 const auto lastSelectionRange = selectionRange();
2163
2164 KTextEditor::Range searchRange(lastSelectionRange.end(), doc()->documentRange().end());
2165 QList<KTextEditor::Range> matches = doc()->searchText(searchRange, text, KTextEditor::Default);
2166 if (!matches.isEmpty() && !matches.constFirst().isValid()) {
2167 searchRange.setRange(doc()->documentRange().start(), lastSelectionRange.end());
2168 matches = doc()->searchText(searchRange, text, KTextEditor::Default);
2169 }
2170
2171 // No match found or only one possible match
2172 if (matches.empty() || !matches.constFirst().isValid() || matches.constFirst() == selectionRange()) {
2173 return;
2174 }
2175
2176 auto it = std::find_if(m_secondaryCursors.begin(), m_secondaryCursors.end(), [&](const SecondaryCursor &c) {
2177 return c.range && c.range->toRange() == matches.constFirst();
2178 });
2179
2180 if (it != m_secondaryCursors.end()) {
2181 m_secondaryCursors.erase(it);
2182 }
2183
2184 // Move our primary to cursor to this match and select it
2185 // Ensure we don't create occurence highlights
2186 setSelection(matches.constFirst());
2187 setCursorPosition(matches.constFirst().end());
2188 clearHighlights();
2189
2190 // If we are skipping this selection, then we don't have to do anything
2191 if (!m_skipCurrentSelection) {
2192 PlainSecondaryCursor c;
2193 c.pos = lastSelectionRange.end();
2194 c.range = lastSelectionRange;
2195 // make our previous primary selection a secondary
2196 addSecondaryCursorsWithSelection({c});
2197 }
2198 // reset value
2199 m_skipCurrentSelection = false;
2200}
2201
2202void KTextEditor::ViewPrivate::findAllOccuruncesAndSelect()
2203{
2204 if (isMulticursorNotAllowed()) {
2205 return;
2206 }
2207
2208 QString text = selection() ? doc()->text(selectionRange()) : QString();
2209 if (text.isEmpty()) {
2210 const auto selection = doc()->wordRangeAt(cursorPosition());
2211 setSelection(selection);
2212 setCursorPosition(selection.end());
2213 clearHighlights();
2214 text = doc()->text(selection);
2215
2216 for (auto &c : m_secondaryCursors) {
2217 const auto range = doc()->wordRangeAt(c.cursor());
2218 if (!c.range && !c.anchor.isValid()) {
2219 c.anchor = range.start();
2220 c.range.reset(newSecondarySelectionRange(range));
2221 c.pos->setPosition(range.end());
2222 }
2223 tagLines(range);
2224 }
2225 }
2226
2227 KTextEditor::Range searchRange(doc()->documentRange());
2229 QList<PlainSecondaryCursor> resultRanges;
2230 do {
2231 matches = doc()->searchText(searchRange, text, KTextEditor::Default);
2232
2233 if (matches.constFirst().isValid()) {
2234 // Dont add if matches primary selection
2235 if (matches.constFirst() != selectionRange()) {
2236 PlainSecondaryCursor c;
2237 c.pos = matches.constFirst().end();
2238 c.range = matches.constFirst();
2239 resultRanges.push_back(c);
2240 }
2241 searchRange.setStart(matches.constFirst().end());
2242 }
2243 } while (matches.first().isValid());
2244
2245 // ensure to clear occurence highlights
2246 if (!resultRanges.empty()) {
2247 clearHighlights();
2248 }
2249
2250 clearSecondaryCursors();
2251 addSecondaryCursorsWithSelection(resultRanges);
2252}
2253
2254// NOLINTNEXTLINE(readability-make-member-function-const)
2255void KTextEditor::ViewPrivate::replace()
2256{
2257 currentInputMode()->findReplace();
2258}
2259
2260// NOLINTNEXTLINE(readability-make-member-function-const)
2261void KTextEditor::ViewPrivate::findNext()
2262{
2263 currentInputMode()->findNext();
2264}
2265
2266// NOLINTNEXTLINE(readability-make-member-function-const)
2267void KTextEditor::ViewPrivate::findPrevious()
2268{
2269 currentInputMode()->findPrevious();
2270}
2271
2272void KTextEditor::ViewPrivate::showSearchWrappedHint(bool isReverseSearch)
2273{
2274 // show message widget when wrapping
2275 const QIcon icon = isReverseSearch ? QIcon::fromTheme(QStringLiteral("go-up-search")) : QIcon::fromTheme(QStringLiteral("go-down-search"));
2276
2277 if (!m_wrappedMessage || m_isLastSearchReversed != isReverseSearch) {
2278 m_isLastSearchReversed = isReverseSearch;
2279 m_wrappedMessage = new KTextEditor::Message(i18n("Search wrapped"), KTextEditor::Message::Information);
2280 m_wrappedMessage->setIcon(icon);
2281 m_wrappedMessage->setPosition(KTextEditor::Message::BottomInView);
2282 m_wrappedMessage->setAutoHide(2000);
2283 m_wrappedMessage->setAutoHideMode(KTextEditor::Message::Immediate);
2284 m_wrappedMessage->setView(this);
2285 this->doc()->postMessage(m_wrappedMessage);
2286 }
2287}
2288
2289void KTextEditor::ViewPrivate::createMultiCursorsFromSelection()
2290{
2291 if (!selection() || selectionRange().isEmpty()) {
2292 return;
2293 }
2294 // Is this really needed?
2295 // Lets just clear them now for simplicity
2296 clearSecondaryCursors();
2297
2298 const auto range = selectionRange();
2299 QList<KTextEditor::Cursor> cursorsToAdd;
2300 const auto start = range.start().line() < 0 ? 0 : range.start().line();
2301 const auto end = range.end().line() > doc()->lines() ? doc()->lines() : range.end().line();
2302 const auto currentLine = cursorPosition().line();
2303 setCursorPosition({currentLine, doc()->lineLength(currentLine)});
2304 for (int line = start; line <= end; ++line) {
2305 if (line != currentLine) {
2306 cursorsToAdd.push_back({line, doc()->lineLength(line)});
2307 }
2308 }
2309 // clear selection
2310 setSelection({});
2311 setSecondaryCursors(cursorsToAdd);
2312}
2313
2314void KTextEditor::ViewPrivate::removeCursorsFromEmptyLines()
2315{
2316 if (!m_secondaryCursors.empty()) {
2317 std::vector<KTextEditor::Cursor> cursorsToRemove;
2318 for (const auto &c : m_secondaryCursors) {
2319 auto cursor = c.cursor();
2320 if (doc()->lineLength(cursor.line()) == 0) {
2321 cursorsToRemove.push_back(cursor);
2322 }
2323 }
2324 removeSecondaryCursors(cursorsToRemove);
2325 }
2326}
2327
2328void KTextEditor::ViewPrivate::slotSelectionChanged()
2329{
2330 m_copy->setEnabled(selection() || m_config->smartCopyCut());
2331 m_deSelect->setEnabled(selection());
2332 m_copyHtmlAction->setEnabled(selection());
2333
2334 // update highlighting of current selected word
2335 selectionChangedForHighlights();
2336
2337 if (doc()->readOnly()) {
2338 return;
2339 }
2340
2341 m_cut->setEnabled(selection() || m_config->smartCopyCut());
2342 m_screenshotSelection->setVisible(selection());
2343 m_screenshotSelection->setEnabled(selection());
2344}
2345
2346// NOLINTNEXTLINE(readability-make-member-function-const)
2347void KTextEditor::ViewPrivate::switchToCmdLine()
2348{
2349 currentInputMode()->activateCommandLine();
2350}
2351
2352KateRenderer *KTextEditor::ViewPrivate::renderer()
2353{
2354 return m_renderer;
2355}
2356
2357KateRendererConfig *KTextEditor::ViewPrivate::rendererConfig()
2358{
2359 return m_renderer->config();
2360}
2361
2362void KTextEditor::ViewPrivate::updateConfig()
2363{
2364 if (m_startingUp) {
2365 return;
2366 }
2367
2368 m_toggleShowSpace->setChecked(doc()->config()->showSpaces() != KateDocumentConfig::WhitespaceRendering::None);
2369
2370 // dyn. word wrap & markers
2371 if (m_hasWrap != config()->dynWordWrap()) {
2372 m_hasWrap = config()->dynWordWrap();
2373
2374 m_viewInternal->dynWrapChanged();
2375
2376 m_setDynWrapIndicators->setEnabled(config()->dynWordWrap());
2377 m_toggleDynWrap->setChecked(config()->dynWordWrap());
2378 }
2379
2380 m_viewInternal->m_leftBorder->setDynWrapIndicators(config()->dynWordWrapIndicators());
2381 m_setDynWrapIndicators->setCurrentItem(config()->dynWordWrapIndicators());
2382
2383 // line numbers
2384 m_viewInternal->m_leftBorder->setLineNumbersOn(config()->lineNumbers());
2385 m_toggleLineNumbers->setChecked(config()->lineNumbers());
2386
2387 // icon bar
2388 m_viewInternal->m_leftBorder->setIconBorderOn(config()->iconBar());
2389 m_toggleIconBar->setChecked(config()->iconBar());
2390
2391 // scrollbar marks
2392 m_viewInternal->m_lineScroll->setShowMarks(config()->scrollBarMarks());
2393 m_toggleScrollBarMarks->setChecked(config()->scrollBarMarks());
2394
2395 // scrollbar mini-map
2396 m_viewInternal->m_lineScroll->setShowMiniMap(config()->scrollBarMiniMap());
2397 m_toggleScrollBarMiniMap->setChecked(config()->scrollBarMiniMap());
2398
2399 // scrollbar mini-map - (whole document)
2400 m_viewInternal->m_lineScroll->setMiniMapAll(config()->scrollBarMiniMapAll());
2401 // m_toggleScrollBarMiniMapAll->setChecked( config()->scrollBarMiniMapAll() );
2402
2403 // scrollbar mini-map.width
2404 m_viewInternal->m_lineScroll->setMiniMapWidth(config()->scrollBarMiniMapWidth());
2405
2406 // misc edit
2407 m_toggleBlockSelection->setChecked(blockSelection());
2408 m_toggleInsert->setChecked(isOverwriteMode());
2409
2410 updateFoldingConfig();
2411
2412 // bookmark
2413 m_bookmarks->setSorting((KateBookmarks::Sorting)config()->bookmarkSort());
2414
2415 m_viewInternal->setAutoCenterLines(config()->autoCenterLines());
2416
2417 for (const auto &input : m_viewInternal->m_inputModes) {
2418 input->updateConfig();
2419 }
2420
2421 setInputMode(config()->inputMode(), false /* don't remember in config for these calls */);
2422
2423 reflectOnTheFlySpellCheckStatus(doc()->isOnTheFlySpellCheckingEnabled());
2424
2425 // register/unregister word completion...
2426 bool wc = config()->wordCompletion();
2427 if (wc != isCompletionModelRegistered(KTextEditor::EditorPrivate::self()->wordCompletionModel())) {
2428 if (wc) {
2429 registerCompletionModel(KTextEditor::EditorPrivate::self()->wordCompletionModel());
2430 } else {
2431 unregisterCompletionModel(KTextEditor::EditorPrivate::self()->wordCompletionModel());
2432 }
2433 }
2434
2435 bool kc = config()->keywordCompletion();
2436 if (kc != isCompletionModelRegistered(KTextEditor::EditorPrivate::self()->keywordCompletionModel())) {
2437 if (kc) {
2438 registerCompletionModel(KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2439 } else {
2440 unregisterCompletionModel(KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2441 }
2442 }
2443
2444 m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
2445 m_copy->setEnabled(selection() || m_config->smartCopyCut());
2446
2447 m_accessibilityEnabled = m_config->value(KateViewConfig::EnableAccessibility).toBool();
2448
2449 // if not disabled, update status bar
2450 if (m_statusBar) {
2451 m_statusBar->updateStatus();
2452 }
2453
2454 // now redraw...
2455 m_viewInternal->cache()->clear();
2456 tagAll();
2457 updateView(true);
2458
2459 Q_EMIT configChanged(this);
2460}
2461
2462void KTextEditor::ViewPrivate::updateDocumentConfig()
2463{
2464 if (m_startingUp) {
2465 return;
2466 }
2467
2468 m_updatingDocumentConfig = true;
2469
2470 m_setEndOfLine->setCurrentItem(doc()->config()->eol());
2471
2472 m_addBom->setChecked(doc()->config()->bom());
2473
2474 m_updatingDocumentConfig = false;
2475
2476 // maybe block selection or wrap-cursor mode changed
2477 ensureCursorColumnValid();
2478
2479 // first change this
2480 m_renderer->setTabWidth(doc()->config()->tabWidth());
2481 m_renderer->setIndentWidth(doc()->config()->indentationWidth());
2482
2483 // now redraw...
2484 m_viewInternal->cache()->clear();
2485 tagAll();
2486 updateView(true);
2487}
2488
2489void KTextEditor::ViewPrivate::updateRendererConfig()
2490{
2491 if (m_startingUp) {
2492 return;
2493 }
2494
2495 m_toggleWWMarker->setChecked(m_renderer->config()->wordWrapMarker());
2496
2497 m_viewInternal->updateBracketMarkAttributes();
2498 m_viewInternal->updateBracketMarks();
2499
2500 // now redraw...
2501 m_viewInternal->cache()->clear();
2502 tagAll();
2503 m_viewInternal->updateView(true);
2504
2505 // update the left border right, for example linenumbers
2506 m_viewInternal->m_leftBorder->updateFont();
2507 m_viewInternal->m_leftBorder->repaint();
2508
2509 m_viewInternal->m_lineScroll->queuePixmapUpdate();
2510
2511 currentInputMode()->updateRendererConfig();
2512
2513 // @@ showIndentLines is not cached anymore.
2514 // m_renderer->setShowIndentLines (m_renderer->config()->showIndentationLines());
2515 Q_EMIT configChanged(this);
2516}
2517
2518void KTextEditor::ViewPrivate::updateFoldingConfig()
2519{
2520 // folding bar
2521 m_viewInternal->m_leftBorder->setFoldingMarkersOn(config()->foldingBar());
2522 m_toggleFoldingMarkers->setChecked(config()->foldingBar());
2523
2524 if (hasCommentInFirstLine(m_doc)) {
2525 if (config()->foldFirstLine() && !m_autoFoldedFirstLine) {
2526 foldLine(0);
2527 m_autoFoldedFirstLine = true;
2528 } else if (!config()->foldFirstLine() && m_autoFoldedFirstLine) {
2529 unfoldLine(0);
2530 m_autoFoldedFirstLine = false;
2531 }
2532 } else {
2533 m_autoFoldedFirstLine = false;
2534 }
2535
2536#if 0
2537 // FIXME: FOLDING
2538 const QStringList l = {
2539 QStringLiteral("folding_toplevel")
2540 , QStringLiteral("folding_expandtoplevel")
2541 , QStringLiteral("folding_toggle_current")
2542 , QStringLiteral("folding_toggle_in_current")
2543 };
2544
2545 QAction *a = 0;
2546 for (int z = 0; z < l.size(); z++)
2547 if ((a = actionCollection()->action(l[z].toAscii().constData()))) {
2548 a->setEnabled(doc()->highlight() && doc()->highlight()->allowsFolding());
2549 }
2550#endif
2551}
2552
2553void KTextEditor::ViewPrivate::ensureCursorColumnValid()
2554{
2555 KTextEditor::Cursor c = m_viewInternal->cursorPosition();
2556
2557 // make sure the cursor is valid:
2558 // - in block selection mode or if wrap cursor is off, the column is arbitrary
2559 // - otherwise: it's bounded by the line length
2560 if (!blockSelection() && wrapCursor() && (!c.isValid() || c.column() > doc()->lineLength(c.line()))) {
2561 c.setColumn(doc()->lineLength(cursorPosition().line()));
2562 setCursorPosition(c);
2563 }
2564}
2565
2566// BEGIN EDIT STUFF
2567void KTextEditor::ViewPrivate::editStart()
2568{
2569 m_viewInternal->editStart();
2570}
2571
2572void KTextEditor::ViewPrivate::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
2573{
2574 m_viewInternal->editEnd(editTagLineStart, editTagLineEnd, tagFrom);
2575 textFolding().editEnd(editTagLineStart, editTagLineEnd, [this](int line) {
2576 return m_doc->buffer().isFoldingStartingOnLine(line).first;
2577 });
2578}
2579
2580void KTextEditor::ViewPrivate::editSetCursor(const KTextEditor::Cursor cursor)
2581{
2582 m_viewInternal->editSetCursor(cursor);
2583}
2584// END
2585
2586// BEGIN TAG & CLEAR
2587bool KTextEditor::ViewPrivate::tagLine(const KTextEditor::Cursor virtualCursor)
2588{
2589 return m_viewInternal->tagLine(virtualCursor);
2590}
2591
2592bool KTextEditor::ViewPrivate::tagRange(KTextEditor::Range range, bool realLines)
2593{
2594 return m_viewInternal->tagRange(range, realLines);
2595}
2596
2597bool KTextEditor::ViewPrivate::tagLines(KTextEditor::LineRange lineRange, bool realLines)
2598{
2599 return m_viewInternal->tagLines(lineRange.start(), lineRange.end(), realLines);
2600}
2601
2602bool KTextEditor::ViewPrivate::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors)
2603{
2604 return m_viewInternal->tagLines(start, end, realCursors);
2605}
2606
2607void KTextEditor::ViewPrivate::tagAll()
2608{
2609 m_viewInternal->tagAll();
2610}
2611
2612void KTextEditor::ViewPrivate::clear()
2613{
2614 m_viewInternal->clear();
2615}
2616
2617void KTextEditor::ViewPrivate::repaintText(bool paintOnlyDirty)
2618{
2619 if (paintOnlyDirty) {
2620 m_viewInternal->updateDirty();
2621 } else {
2622 m_viewInternal->update();
2623 }
2624}
2625
2626void KTextEditor::ViewPrivate::updateView(bool changed)
2627{
2628 // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::updateView";
2629 m_viewInternal->updateView(changed);
2630 m_viewInternal->m_leftBorder->update();
2631}
2632
2633// END
2634
2635void KTextEditor::ViewPrivate::slotHlChanged()
2636{
2637 KateHighlighting *hl = doc()->highlight();
2638 bool ok(!hl->getCommentStart(0).isEmpty() || !hl->getCommentSingleLineStart(0).isEmpty());
2639
2640 if (actionCollection()->action(QStringLiteral("tools_comment"))) {
2641 actionCollection()->action(QStringLiteral("tools_comment"))->setEnabled(ok);
2642 }
2643
2644 if (actionCollection()->action(QStringLiteral("tools_uncomment"))) {
2645 actionCollection()->action(QStringLiteral("tools_uncomment"))->setEnabled(ok);
2646 }
2647
2648 if (actionCollection()->action(QStringLiteral("tools_toggle_comment"))) {
2649 actionCollection()->action(QStringLiteral("tools_toggle_comment"))->setEnabled(ok);
2650 }
2651
2652 // show folding bar if "view defaults" says so, otherwise enable/disable only the menu entry
2653 updateFoldingConfig();
2654}
2655
2656int KTextEditor::ViewPrivate::virtualCursorColumn() const
2657{
2658 return doc()->toVirtualColumn(m_viewInternal->cursorPosition());
2659}
2660
2661void KTextEditor::ViewPrivate::notifyMousePositionChanged(const KTextEditor::Cursor newPosition)
2662{
2663 Q_EMIT mousePositionChanged(this, newPosition);
2664}
2665
2666// BEGIN KTextEditor::SelectionInterface stuff
2667
2668bool KTextEditor::ViewPrivate::setSelection(KTextEditor::Range selection)
2669{
2670 // anything to do?
2671 if (selection == m_selection) {
2672 return true;
2673 }
2674
2675 // backup old range
2676 KTextEditor::Range oldSelection = m_selection;
2677
2678 // set new range
2679 m_selection.setRange(selection.isEmpty() ? KTextEditor::Range::invalid() : selection);
2680
2681 // trigger update of correct area
2682 tagSelection(oldSelection);
2683 repaintText(true);
2684
2685 // emit holy signal
2686 Q_EMIT selectionChanged(this);
2687
2688 // be done
2689 return true;
2690}
2691
2692bool KTextEditor::ViewPrivate::clearSelection()
2693{
2694 return clearSelection(true);
2695}
2696
2697bool KTextEditor::ViewPrivate::clearSelection(bool redraw, bool finishedChangingSelection)
2698{
2699 // no selection, nothing to do...
2700 if (!selection()) {
2701 return false;
2702 }
2703
2704 // backup old range
2705 KTextEditor::Range oldSelection = m_selection;
2706
2707 // invalidate current selection
2709
2710 // trigger update of correct area
2711 tagSelection(oldSelection);
2712 if (redraw) {
2713 repaintText(true);
2714 }
2715
2716 // emit holy signal
2717 if (finishedChangingSelection) {
2718 Q_EMIT selectionChanged(this);
2719 }
2720
2721 m_viewInternal->m_selChangedByUser = false;
2722 // be done
2723 return true;
2724}
2725
2726bool KTextEditor::ViewPrivate::selection() const
2727{
2728 if (!wrapCursor()) {
2729 return m_selection != KTextEditor::Range::invalid();
2730 } else {
2731 return m_selection.toRange().isValid();
2732 }
2733}
2734
2735QString KTextEditor::ViewPrivate::selectionText() const
2736{
2737 if (blockSelect) {
2738 return doc()->text(m_selection, blockSelect);
2739 }
2740
2742 for (const auto &c : m_secondaryCursors) {
2743 if (c.range) {
2744 ranges.push_back(c.range->toRange());
2745 }
2746 }
2747 ranges.push_back(m_selection.toRange());
2748 std::sort(ranges.begin(), ranges.end());
2749
2750 QString text;
2751 text.reserve(ranges.size() * m_selection.toRange().columnWidth());
2752 for (int i = 0; i < ranges.size() - 1; ++i) {
2753 text += doc()->text(ranges[i]) + QStringLiteral("\n");
2754 }
2755 text += doc()->text(ranges.last());
2756
2757 return text;
2758}
2759
2760bool KTextEditor::ViewPrivate::removeSelectedText()
2761{
2762 if (!hasSelections()) {
2763 return false;
2764 }
2765
2767
2768 bool removed = false;
2769 // Handle multicursors selection removal
2770 if (!blockSelect) {
2771 completionWidget()->setIgnoreBufferSignals(true);
2772 for (auto &c : m_secondaryCursors) {
2773 if (c.range) {
2774 removed = true;
2775 doc()->removeText(c.range->toRange());
2776 c.clearSelection();
2777 }
2778 }
2779 completionWidget()->setIgnoreBufferSignals(false);
2780 }
2781
2782 // Optimization: clear selection before removing text
2783 KTextEditor::Range selection = m_selection;
2784 if (!selection.isValid()) {
2785 return removed;
2786 }
2787 doc()->removeText(selection, blockSelect);
2788 removed = true;
2789
2790 // don't redraw the cleared selection - that's done in editEnd().
2791 if (blockSelect) {
2792 int selectionColumn = qMin(doc()->toVirtualColumn(selection.start()), doc()->toVirtualColumn(selection.end()));
2793 KTextEditor::Range newSelection = selection;
2794 newSelection.setStart(KTextEditor::Cursor(newSelection.start().line(), doc()->fromVirtualColumn(newSelection.start().line(), selectionColumn)));
2795 newSelection.setEnd(KTextEditor::Cursor(newSelection.end().line(), doc()->fromVirtualColumn(newSelection.end().line(), selectionColumn)));
2796 setSelection(newSelection);
2797 setCursorPositionInternal(newSelection.start());
2798 } else {
2799 clearSecondarySelections();
2800 clearSelection(false);
2801 }
2802
2803 return removed;
2804}
2805
2806bool KTextEditor::ViewPrivate::selectAll()
2807{
2808 clearSecondaryCursors();
2809 setBlockSelection(false);
2810 // We use setSelection here to ensure we don't scroll anywhere
2811 // The cursor stays in place i.e., it doesn't move to end of selection
2812 // that is okay and expected.
2813 // The idea here is to maintain scroll position in case select all was
2814 // mistakenly triggered, and also to if you just want to copy text,
2815 // there is no need to scroll anywhere.
2816 setSelection(doc()->documentRange());
2817 m_viewInternal->moveCursorToSelectionEdge(/*scroll=*/false);
2818 m_viewInternal->updateMicroFocus();
2819 return true;
2820}
2821
2822bool KTextEditor::ViewPrivate::cursorSelected(const KTextEditor::Cursor cursor)
2823{
2824 KTextEditor::Cursor ret = cursor;
2825 if ((!blockSelect) && (ret.column() < 0)) {
2826 ret.setColumn(0);
2827 }
2828
2829 if (blockSelect) {
2830 return cursor.line() >= m_selection.start().line() && ret.line() <= m_selection.end().line() && ret.column() >= m_selection.start().column()
2831 && ret.column() <= m_selection.end().column();
2832 } else {
2833 return m_selection.toRange().contains(cursor) || m_selection.end() == cursor;
2834 }
2835}
2836
2837bool KTextEditor::ViewPrivate::lineSelected(int line)
2838{
2839 return !blockSelect && m_selection.toRange().containsLine(line);
2840}
2841
2842bool KTextEditor::ViewPrivate::lineEndSelected(const KTextEditor::Cursor lineEndPos)
2843{
2844 return (!blockSelect)
2845 && (lineEndPos.line() > m_selection.start().line()
2846 || (lineEndPos.line() == m_selection.start().line() && (m_selection.start().column() < lineEndPos.column() || lineEndPos.column() == -1)))
2847 && (lineEndPos.line() < m_selection.end().line()
2848 || (lineEndPos.line() == m_selection.end().line() && (lineEndPos.column() <= m_selection.end().column() && lineEndPos.column() != -1)));
2849}
2850
2851bool KTextEditor::ViewPrivate::lineHasSelected(int line)
2852{
2853 return selection() && m_selection.toRange().containsLine(line);
2854}
2855
2856bool KTextEditor::ViewPrivate::lineIsSelection(int line)
2857{
2858 return (line == m_selection.start().line() && line == m_selection.end().line());
2859}
2860
2861void KTextEditor::ViewPrivate::tagSelection(KTextEditor::Range oldSelection)
2862{
2863 if (selection()) {
2864 if (oldSelection.start().line() == -1) {
2865 // We have to tag the whole lot if
2866 // 1) we have a selection, and:
2867 // a) it's new; or
2868 tagLines(m_selection, true);
2869
2870 } else if (blockSelection()
2871 && (oldSelection.start().column() != m_selection.start().column() || oldSelection.end().column() != m_selection.end().column())) {
2872 // b) we're in block selection mode and the columns have changed
2873 tagLines(m_selection, true);
2874 tagLines(oldSelection, true);
2875
2876 } else {
2877 if (oldSelection.start() != m_selection.start()) {
2878 tagLines(KTextEditor::LineRange(oldSelection.start().line(), m_selection.start().line()), true);
2879 }
2880
2881 if (oldSelection.end() != m_selection.end()) {
2882 tagLines(KTextEditor::LineRange(oldSelection.end().line(), m_selection.end().line()), true);
2883 }
2884 }
2885
2886 } else {
2887 // No more selection, clean up
2888 tagLines(oldSelection, true);
2889 }
2890}
2891
2892void KTextEditor::ViewPrivate::selectWord(const KTextEditor::Cursor cursor)
2893{
2894 setSelection(doc()->wordRangeAt(cursor));
2895}
2896
2897void KTextEditor::ViewPrivate::selectLine(const KTextEditor::Cursor cursor)
2898{
2899 int line = cursor.line();
2900 if (line + 1 >= doc()->lines()) {
2901 setSelection(KTextEditor::Range(line, 0, line, doc()->lineLength(line)));
2902 } else {
2903 setSelection(KTextEditor::Range(line, 0, line + 1, 0));
2904 }
2905}
2906
2907void KTextEditor::ViewPrivate::cut()
2908{
2909 if (!selection() && !m_config->smartCopyCut()) {
2910 return;
2911 }
2912
2913 // If markedSelection is true, copy() invalidates the selection,
2914 // which would obviate the removeSelectedText() here below.
2915 m_markedSelection = false;
2916
2917 copy();
2918 if (!selection()) {
2919 selectLine(cursorPosition());
2920 }
2921 removeSelectedText();
2922}
2923
2924void KTextEditor::ViewPrivate::copy()
2925{
2926 QString text;
2927 KTextEditor::EditorPrivate::self()->copyToMulticursorClipboard({});
2928
2929 if (!selection()) {
2930 if (!m_config->smartCopyCut()) {
2931 return;
2932 }
2933 text = doc()->line(cursorPosition().line()) + QLatin1Char('\n');
2934 m_viewInternal->moveEdge(KateViewInternal::left, false);
2935 } else {
2936 text = selectionText();
2937
2938 // Multicursor copy
2939 if (!m_secondaryCursors.empty()) {
2941 for (const auto &c : m_secondaryCursors) {
2942 if (c.range) {
2943 ranges.push_back(c.range->toRange());
2944 }
2945 }
2946 ranges.push_back(m_selection.toRange());
2947 std::sort(ranges.begin(), ranges.end());
2948 QStringList texts;
2949 for (auto range : ranges) {
2950 texts.append(doc()->text(range));
2951 }
2952 KTextEditor::EditorPrivate::self()->copyToMulticursorClipboard(texts);
2953 }
2954
2955 if (m_markedSelection) {
2956 setSelection(KTextEditor::Range::invalid());
2957 m_markedSelection = false;
2958 }
2959 }
2960
2961 // copy to clipboard and our history!
2962 KTextEditor::EditorPrivate::self()->copyToClipboard(text, m_doc->url().fileName());
2963}
2964
2965void KTextEditor::ViewPrivate::screenshot()
2966{
2967 if (!selection()) {
2968 return;
2969 }
2970
2971 ScreenshotDialog d(selectionRange(), this);
2972 d.renderScreenshot(m_renderer);
2973 d.exec();
2974}
2975
2976void KTextEditor::ViewPrivate::pasteSelection()
2977{
2978 m_temporaryAutomaticInvocationDisabled = true;
2979 doc()->paste(this, QApplication::clipboard()->text(QClipboard::Selection));
2980 m_temporaryAutomaticInvocationDisabled = false;
2981}
2982
2983void KTextEditor::ViewPrivate::swapWithClipboard()
2984{
2985 m_temporaryAutomaticInvocationDisabled = true;
2986
2987 // get text to paste
2989
2990 // do copy
2991 copy();
2992
2993 // do paste of "previous" clipboard content we saved
2994 doc()->paste(this, text);
2995
2996 m_temporaryAutomaticInvocationDisabled = false;
2997}
2998
2999void KTextEditor::ViewPrivate::applyWordWrap()
3000{
3001 int first = selectionRange().start().line();
3002 int last = selectionRange().end().line();
3003
3004 if (first == last) {
3005 // Either no selection or only one line selected, wrap only the current line
3006 first = cursorPosition().line();
3007 last = first;
3008 }
3009
3010 doc()->wrapParagraph(first, last);
3011}
3012
3013// END
3014
3015// BEGIN KTextEditor::BlockSelectionInterface stuff
3016
3017bool KTextEditor::ViewPrivate::blockSelection() const
3018{
3019 return blockSelect;
3020}
3021
3022bool KTextEditor::ViewPrivate::setBlockSelection(bool on)
3023{
3024 if (on != blockSelect) {
3025 blockSelect = on;
3026
3027 KTextEditor::Range oldSelection = m_selection;
3028
3029 const bool hadSelection = clearSelection(false, false);
3030
3031 setSelection(oldSelection);
3032
3033 m_toggleBlockSelection->setChecked(blockSelection());
3034
3035 // when leaving block selection mode, if cursor is at an invalid position or past the end of the
3036 // line, move the cursor to the last column of the current line unless cursor wrapping is off
3037 ensureCursorColumnValid();
3038
3039 if (!hadSelection) {
3040 // emit selectionChanged() according to the KTextEditor::View api
3041 // documentation also if there is no selection around. This is needed,
3042 // as e.g. the Kate App status bar uses this signal to update the state
3043 // of the selection mode (block selection, line based selection)
3044 Q_EMIT selectionChanged(this);
3045 }
3046 }
3047
3048 return true;
3049}
3050
3051bool KTextEditor::ViewPrivate::toggleBlockSelection()
3052{
3053 // no multicursors for blockselect
3054 clearSecondaryCursors();
3055
3056 m_toggleBlockSelection->setChecked(!blockSelect);
3057 return setBlockSelection(!blockSelect);
3058}
3059
3060bool KTextEditor::ViewPrivate::wrapCursor() const
3061{
3062 return !blockSelection();
3063}
3064
3065// END
3066
3067void KTextEditor::ViewPrivate::slotTextInserted(KTextEditor::View *view, const KTextEditor::Cursor position, const QString &text)
3068{
3069 Q_EMIT textInserted(view, position, text);
3070}
3071
3072bool KTextEditor::ViewPrivate::insertTemplateInternal(const KTextEditor::Cursor c, const QString &templateString, const QString &script)
3073{
3074 // no empty templates
3075 if (templateString.isEmpty()) {
3076 return false;
3077 }
3078
3079 // not for read-only docs
3080 if (!doc()->isReadWrite()) {
3081 return false;
3082 }
3083
3084 // only one handler maybe active at a time; store it in the document.
3085 // Clear it first to make sure at no time two handlers are active at once
3086 doc()->setActiveTemplateHandler(nullptr);
3087 doc()->setActiveTemplateHandler(new KateTemplateHandler(this, c, templateString, script, doc()->undoManager()));
3088 return true;
3089}
3090
3091bool KTextEditor::ViewPrivate::tagLines(KTextEditor::Range range, bool realRange)
3092{
3093 return tagLines(range.start(), range.end(), realRange);
3094}
3095
3096void KTextEditor::ViewPrivate::deactivateEditActions()
3097{
3098 for (QAction *action : std::as_const(m_editActions)) {
3099 action->setEnabled(false);
3100 }
3101}
3102
3103void KTextEditor::ViewPrivate::activateEditActions()
3104{
3105 for (QAction *action : std::as_const(m_editActions)) {
3106 action->setEnabled(true);
3107 }
3108}
3109
3110bool KTextEditor::ViewPrivate::mouseTrackingEnabled() const
3111{
3112 // FIXME support
3113 return true;
3114}
3115
3116bool KTextEditor::ViewPrivate::setMouseTrackingEnabled(bool)
3117{
3118 // FIXME support
3119 return true;
3120}
3121
3122bool KTextEditor::ViewPrivate::isMulticursorNotAllowed() const
3123{
3124 return blockSelection() || isOverwriteMode() || currentInputMode()->viewInputMode() == KTextEditor::View::InputMode::ViInputMode;
3125}
3126
3127void KTextEditor::ViewPrivate::addSecondaryCursor(KTextEditor::Cursor pos)
3128{
3129 auto primaryCursor = cursorPosition();
3130 const bool overlapsOrOnPrimary = pos == primaryCursor || (selection() && selectionRange().contains(pos));
3131 if (overlapsOrOnPrimary && m_secondaryCursors.empty()) {
3132 // Clicking on primary cursor while it is the only cursor,
3133 // we do nothing
3134 return;
3135 } else if (overlapsOrOnPrimary) {
3136 // Clicking on primary cursor, we have secondaries
3137 // so just make the last secondary cursor primary
3138 // and remove caret at current primary cursor position
3139 auto &last = m_secondaryCursors.back();
3140 setCursorPosition(last.cursor());
3141 if (last.range) {
3142 setSelection(last.range->toRange());
3143 Q_ASSERT(last.anchor.isValid());
3144 m_viewInternal->m_selectAnchor = last.anchor;
3145 }
3146 m_secondaryCursors.pop_back();
3147 return;
3148 }
3149
3150 // If there are any existing cursors at this position
3151 // remove them and be done i.e., if you click on an
3152 // existing cursor it is removed.
3153 if (removeSecondaryCursors({pos}, /*removeIfSelectionOverlap=*/true)) {
3154 return;
3155 }
3156
3157 // We are adding a new cursor!
3158 // - Move primary cursor to the position where the click happened
3159 // - Old primary cursor becomes a secondary cursor
3160 // Doing it like this makes multi mouse selections very easy
3161 setCursorPosition(pos);
3162 KTextEditor::ViewPrivate::PlainSecondaryCursor p;
3163 p.pos = primaryCursor;
3164 p.range = selection() ? selectionRange() : KTextEditor::Range::invalid();
3165 clearSelection();
3166 addSecondaryCursorsWithSelection({p});
3167}
3168
3169void KTextEditor::ViewPrivate::setSecondaryCursors(const QList<KTextEditor::Cursor> &positions)
3170{
3171 clearSecondaryCursors();
3172
3173 if (positions.isEmpty() || isMulticursorNotAllowed()) {
3174 return;
3175 }
3176
3177 const auto totalLines = doc()->lines();
3178 for (auto p : positions) {
3179 if (p != cursorPosition() && p.line() < totalLines) {
3180 SecondaryCursor c;
3181 c.pos.reset(static_cast<Kate::TextCursor *>(doc()->newMovingCursor(p)));
3182 m_secondaryCursors.push_back(std::move(c));
3183 tagLine(p);
3184 }
3185 }
3186 sortCursors();
3187 paintCursors();
3188}
3189
3190void KTextEditor::ViewPrivate::clearSecondarySelections()
3191{
3192 for (auto &c : m_secondaryCursors) {
3193 c.range.reset();
3194 c.anchor = KTextEditor::Cursor::invalid();
3195 }
3196}
3197
3198void KTextEditor::ViewPrivate::clearSecondaryCursors()
3199{
3200 if (m_secondaryCursors.empty()) {
3201 return;
3202 }
3203 for (const auto &c : m_secondaryCursors) {
3204 tagLine(c.cursor());
3205 }
3206 m_secondaryCursors.clear();
3207 m_viewInternal->updateDirty();
3208}
3209
3210const std::vector<KTextEditor::ViewPrivate::SecondaryCursor> &KTextEditor::ViewPrivate::secondaryCursors() const
3211{
3212 return m_secondaryCursors;
3213}
3214
3215QList<KTextEditor::ViewPrivate::PlainSecondaryCursor> KTextEditor::ViewPrivate::plainSecondaryCursors() const
3216{
3218 cursors.reserve(m_secondaryCursors.size());
3219 std::transform(m_secondaryCursors.begin(), m_secondaryCursors.end(), std::back_inserter(cursors), [](const SecondaryCursor &c) {
3220 if (c.range) {
3221 return PlainSecondaryCursor{.pos=c.cursor(), .range=c.range->toRange()};
3222 }
3223 return PlainSecondaryCursor{.pos=c.cursor(), .range=KTextEditor::Range::invalid()};
3224 });
3225 return cursors;
3226}
3227
3228bool KTextEditor::ViewPrivate::removeSecondaryCursors(const std::vector<KTextEditor::Cursor> &cursorsToRemove, bool removeIfOverlapsSelection)
3229{
3230 Q_ASSERT(std::is_sorted(cursorsToRemove.begin(), cursorsToRemove.end()));
3231
3233
3234 if (removeIfOverlapsSelection) {
3235 m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3236 m_secondaryCursors.end(),
3237 [&](const SecondaryCursor &c) {
3238 auto it = std::find_if(cursorsToRemove.begin(), cursorsToRemove.end(), [&c](KTextEditor::Cursor pos) {
3239 return c.cursor() == pos || (c.range && c.range->contains(pos));
3240 });
3241 const bool match = it != cursorsToRemove.end();
3242 if (match) {
3243 linesToTag.push_back(c.cursor());
3244 }
3245 return match;
3246 }),
3247 m_secondaryCursors.end());
3248 } else {
3249 m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3250 m_secondaryCursors.end(),
3251 [&](const SecondaryCursor &c) {
3252 auto it = std::find_if(cursorsToRemove.begin(), cursorsToRemove.end(), [&c](KTextEditor::Cursor pos) {
3253 return c.cursor() == pos;
3254 });
3255 const bool match = it != cursorsToRemove.end();
3256 if (match) {
3257 linesToTag.push_back(c.cursor());
3258 }
3259 return match;
3260 }),
3261 m_secondaryCursors.end());
3262 }
3263
3264 for (const auto &c : linesToTag) {
3265 tagLine(m_viewInternal->toVirtualCursor(c));
3266 }
3267 return !linesToTag.empty();
3268
3269 for (auto cur : cursorsToRemove) {
3270 auto &sec = m_secondaryCursors;
3271 auto it = std::find_if(sec.begin(), sec.end(), [cur](const SecondaryCursor &c) {
3272 return c.cursor() == cur;
3273 });
3274 if (it != sec.end()) {
3275 // removedAny = true;
3276 m_secondaryCursors.erase(it);
3277 tagLine(m_viewInternal->toVirtualCursor(cur));
3278 }
3279 }
3280
3281 // if (removedAny) {
3282 m_viewInternal->updateDirty();
3283 if (cursorPosition() == KTextEditor::Cursor(0, 0)) {
3284 m_viewInternal->paintCursor();
3285 }
3286 return !linesToTag.empty();
3287 // }
3288 // return removedAny;
3289}
3290
3291void KTextEditor::ViewPrivate::ensureUniqueCursors(bool matchLine)
3292{
3293 if (m_secondaryCursors.empty()) {
3294 return;
3295 }
3296
3297 std::vector<SecondaryCursor>::iterator it;
3298 if (matchLine) {
3299 auto matchLine = [](const SecondaryCursor &l, const SecondaryCursor &r) {
3300 return l.cursor().line() == r.cursor().line();
3301 };
3302 it = std::unique(m_secondaryCursors.begin(), m_secondaryCursors.end(), matchLine);
3303 } else {
3304 it = std::unique(m_secondaryCursors.begin(), m_secondaryCursors.end());
3305 }
3306 if (it != m_secondaryCursors.end()) {
3307 m_secondaryCursors.erase(it, m_secondaryCursors.end());
3308 }
3309
3310 if (matchLine) {
3311 const int ln = cursorPosition().line();
3312 m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3313 m_secondaryCursors.end(),
3314 [ln](const SecondaryCursor &c) {
3315 return c.cursor().line() == ln;
3316 }),
3317 m_secondaryCursors.end());
3318 } else {
3319 const auto cp = cursorPosition();
3320 m_secondaryCursors.erase(std::remove_if(m_secondaryCursors.begin(),
3321 m_secondaryCursors.end(),
3322 [cp](const SecondaryCursor &c) {
3323 return c.cursor() == cp;
3324 }),
3325 m_secondaryCursors.end());
3326 }
3327}
3328
3329void KTextEditor::ViewPrivate::addSecondaryCursorsWithSelection(const QList<PlainSecondaryCursor> &cursorsWithSelection)
3330{
3331 if (isMulticursorNotAllowed() || cursorsWithSelection.isEmpty()) {
3332 return;
3333 }
3334
3335 for (const auto &c : cursorsWithSelection) {
3336 // We don't want to add on top of primary cursor
3337 if (c.pos == cursorPosition()) {
3338 continue;
3339 }
3340 SecondaryCursor n;
3341 n.pos.reset(static_cast<Kate::TextCursor *>(doc()->newMovingCursor(c.pos)));
3342 if (c.range.isValid()) {
3343 n.range.reset(newSecondarySelectionRange(c.range));
3344 n.anchor = c.range.start() == c.pos ? c.range.end() : c.range.start();
3345 }
3346 m_secondaryCursors.push_back(std::move(n));
3347 }
3348 sortCursors();
3349 paintCursors();
3350}
3351
3352Kate::TextRange *KTextEditor::ViewPrivate::newSecondarySelectionRange(KTextEditor::Range selRange)
3353{
3355 auto range = new Kate::TextRange(doc()->buffer(), selRange, expandBehaviour);
3356 static KTextEditor::Attribute::Ptr selAttr;
3357 if (!selAttr) {
3358 selAttr = new KTextEditor::Attribute;
3359 auto color = QColor::fromRgba(theme().editorColor(KSyntaxHighlighting::Theme::TextSelection));
3360 selAttr->setBackground(color);
3361 }
3362 range->setZDepth(-999999.);
3363 range->setAttribute(selAttr);
3364 return range;
3365}
3366
3367bool KTextEditor::ViewPrivate::hasSelections() const
3368{
3369 if (selection())
3370 return true;
3371 return std::any_of(m_secondaryCursors.cbegin(), m_secondaryCursors.cend(), [](const SecondaryCursor &c) {
3372 return c.range && !c.range->isEmpty();
3373 });
3374}
3375
3376void KTextEditor::ViewPrivate::addSecondaryCursorDown()
3377{
3378 KTextEditor::Cursor last = cursorPosition();
3379 const auto &secondary = secondaryCursors();
3380 if (!secondary.empty()) {
3381 last = secondary.back().cursor();
3382 last = std::max(cursorPosition(), last);
3383 }
3384 if (last.line() >= doc()->lastLine()) {
3385 return;
3386 }
3387
3388 auto nextRange = m_viewInternal->nextLayout(last);
3389 if (!nextRange.isValid()) {
3390 return;
3391 }
3392 auto primaryCursorLineLayout = m_viewInternal->currentLayout(cursorPosition());
3393 if (!primaryCursorLineLayout.isValid()) {
3394 return;
3395 }
3396
3397 int x = renderer()->cursorToX(primaryCursorLineLayout, cursorPosition().column(), !wrapCursor());
3398 auto next = renderer()->xToCursor(nextRange, x, !wrapCursor());
3399 addSecondaryCursor(next);
3400}
3401
3402void KTextEditor::ViewPrivate::addSecondaryCursorUp()
3403{
3404 KTextEditor::Cursor last = cursorPosition();
3405 const auto &secondary = secondaryCursors();
3406 if (!secondary.empty()) {
3407 last = secondary.front().cursor();
3408 last = std::min(cursorPosition(), last);
3409 }
3410 if (last.line() == 0) {
3411 return;
3412 }
3413 auto nextRange = m_viewInternal->previousLayout(last);
3414 if (!nextRange.isValid()) {
3415 return;
3416 }
3417
3418 auto primaryCursorLineLayout = m_viewInternal->currentLayout(cursorPosition());
3419 if (!primaryCursorLineLayout.isValid()) {
3420 return;
3421 }
3422
3423 int x = renderer()->cursorToX(primaryCursorLineLayout, cursorPosition().column(), !wrapCursor());
3424 auto next = renderer()->xToCursor(nextRange, x, !wrapCursor());
3425 addSecondaryCursor(next);
3426}
3427
3428QList<KTextEditor::Cursor> KTextEditor::ViewPrivate::cursors() const
3429{
3431 ret.reserve(m_secondaryCursors.size() + 1);
3432 ret << cursorPosition();
3433 std::transform(m_secondaryCursors.begin(), m_secondaryCursors.end(), std::back_inserter(ret), [](const SecondaryCursor &c) {
3434 return c.cursor();
3435 });
3436 return ret;
3437}
3438
3439QList<KTextEditor::Range> KTextEditor::ViewPrivate::selectionRanges() const
3440{
3441 if (!selection()) {
3442 return {};
3443 }
3444
3446 ret.reserve(m_secondaryCursors.size() + 1);
3447 ret << selectionRange();
3448 std::transform(m_secondaryCursors.begin(), m_secondaryCursors.end(), std::back_inserter(ret), [](const SecondaryCursor &c) {
3449 if (!c.range) {
3450 qWarning() << "selectionRanges(): Unexpected null selection range, please fix";
3451 return KTextEditor::Range::invalid();
3452 }
3453 return c.range->toRange();
3454 });
3455 return ret;
3456}
3457
3458void KTextEditor::ViewPrivate::setCursors(const QList<KTextEditor::Cursor> &cursorPositions)
3459{
3460 if (isMulticursorNotAllowed()) {
3461 qWarning() << "setCursors failed: Multicursors not allowed because one of the following is true"
3462 << ", blockSelection: " << blockSelection() << ", overwriteMode: " << isOverwriteMode()
3463 << ", viMode: " << (currentInputMode()->viewInputMode() == KTextEditor::View::InputMode::ViInputMode);
3464 return;
3465 }
3466
3467 clearSecondaryCursors();
3468 if (cursorPositions.empty()) {
3469 return;
3470 }
3471
3472 const auto primary = cursorPositions.front();
3473 // We clear primary selection because primary and secondary
3474 // cursors should always have same selection state
3475 setSelection({});
3476 setCursorPosition(primary);
3477 // First will be auto ignored because it equals cursorPosition()
3478 setSecondaryCursors(cursorPositions);
3479}
3480
3481void KTextEditor::ViewPrivate::setSelections(const QList<KTextEditor::Range> &selectionRanges)
3482{
3483 if (isMulticursorNotAllowed()) {
3484 qWarning() << "setSelections failed: Multicursors not allowed because one of the following is true"
3485 << ", blockSelection: " << blockSelection() << ", overwriteMode: " << isOverwriteMode()
3486 << ", viMode: " << (currentInputMode()->viewInputMode() == KTextEditor::View::InputMode::ViInputMode);
3487 return;
3488 }
3489
3490 clearSecondaryCursors();
3491 setSelection({});
3492 if (selectionRanges.isEmpty()) {
3493 return;
3494 }
3495
3496 auto first = selectionRanges.front();
3497 setCursorPosition(first.end());
3498 setSelection(first);
3499
3500 if (selectionRanges.size() == 1) {
3501 return;
3502 }
3503
3504 const auto docRange = doc()->documentRange();
3505 for (auto it = selectionRanges.begin() + 1; it != selectionRanges.end(); ++it) {
3506 KTextEditor::Range r = *it;
3507 KTextEditor::Cursor c = r.end();
3508 if (c == cursorPosition() || !r.isValid() || r.isEmpty() || !docRange.contains(r)) {
3509 continue;
3510 }
3511
3512 SecondaryCursor n;
3513 n.pos.reset(static_cast<Kate::TextCursor *>(doc()->newMovingCursor(c)));
3514 n.range.reset(newSecondarySelectionRange(r));
3515 n.anchor = r.start();
3516 m_secondaryCursors.push_back(std::move(n));
3517 }
3518 m_viewInternal->mergeSelections();
3519
3520 sortCursors();
3521 paintCursors();
3522}
3523
3524void KTextEditor::ViewPrivate::sortCursors()
3525{
3526 std::sort(m_secondaryCursors.begin(), m_secondaryCursors.end());
3527 ensureUniqueCursors();
3528}
3529
3530void KTextEditor::ViewPrivate::paintCursors()
3531{
3532 if (m_viewInternal->m_cursorTimer.isActive()) {
3534 m_viewInternal->m_cursorTimer.start(QApplication::cursorFlashTime() / 2);
3535 }
3536 renderer()->setDrawCaret(true);
3537 }
3538 m_viewInternal->paintCursor();
3539}
3540
3541bool KTextEditor::ViewPrivate::isCompletionActive() const
3542{
3543 return completionWidget()->isCompletionActive();
3544}
3545
3546KateCompletionWidget *KTextEditor::ViewPrivate::completionWidget() const
3547{
3548 if (!m_completionWidget) {
3549 m_completionWidget = new KateCompletionWidget(const_cast<KTextEditor::ViewPrivate *>(this));
3550 }
3551
3552 return m_completionWidget;
3553}
3554
3555void KTextEditor::ViewPrivate::startCompletion(KTextEditor::Range word, KTextEditor::CodeCompletionModel *model)
3556{
3557 completionWidget()->startCompletion(word, model);
3558}
3559
3560void KTextEditor::ViewPrivate::startCompletion(const Range &word,
3562 KTextEditor::CodeCompletionModel::InvocationType invocationType)
3563{
3564 completionWidget()->startCompletion(word, models, invocationType);
3565}
3566
3567void KTextEditor::ViewPrivate::abortCompletion()
3568{
3569 completionWidget()->abortCompletion();
3570}
3571
3572void KTextEditor::ViewPrivate::forceCompletion()
3573{
3574 completionWidget()->execute();
3575}
3576
3577void KTextEditor::ViewPrivate::registerCompletionModel(KTextEditor::CodeCompletionModel *model)
3578{
3579 completionWidget()->registerCompletionModel(model);
3580}
3581
3582void KTextEditor::ViewPrivate::unregisterCompletionModel(KTextEditor::CodeCompletionModel *model)
3583{
3584 completionWidget()->unregisterCompletionModel(model);
3585}
3586
3587bool KTextEditor::ViewPrivate::isCompletionModelRegistered(KTextEditor::CodeCompletionModel *model) const
3588{
3589 return completionWidget()->isCompletionModelRegistered(model);
3590}
3591
3592QList<KTextEditor::CodeCompletionModel *> KTextEditor::ViewPrivate::codeCompletionModels() const
3593{
3594 return completionWidget()->codeCompletionModels();
3595}
3596
3597bool KTextEditor::ViewPrivate::isAutomaticInvocationEnabled() const
3598{
3599 return !m_temporaryAutomaticInvocationDisabled && m_config->automaticCompletionInvocation();
3600}
3601
3602void KTextEditor::ViewPrivate::setAutomaticInvocationEnabled(bool enabled)
3603{
3604 config()->setValue(KateViewConfig::AutomaticCompletionInvocation, enabled);
3605}
3606
3607void KTextEditor::ViewPrivate::sendCompletionExecuted(const KTextEditor::Cursor position, KTextEditor::CodeCompletionModel *model, const QModelIndex &index)
3608{
3609 Q_EMIT completionExecuted(this, position, model, index);
3610}
3611
3612void KTextEditor::ViewPrivate::sendCompletionAborted()
3613{
3614 Q_EMIT completionAborted(this);
3615}
3616
3617void KTextEditor::ViewPrivate::paste(const QString *textToPaste)
3618{
3619 const int cursorCount = m_secondaryCursors.size() + 1; // 1 primary cursor
3620 const auto multicursorClipboard = KTextEditor::EditorPrivate::self()->multicursorClipboard();
3621 if (cursorCount == multicursorClipboard.size() && !textToPaste) {
3622 if (doc()->multiPaste(this, multicursorClipboard)) {
3623 return;
3624 }
3625 } else if (!textToPaste && cursorCount > 1) {
3626 // We still have multiple cursors, but the amount
3627 // of multicursors doesn't match the entry count in clipboard
3628 QStringList texts;
3629 texts.reserve(cursorCount);
3631 for (int i = 0; i < cursorCount; ++i) {
3632 texts << clipboard;
3633 }
3634 // It might still fail for e.g., if we are in block mode,
3635 // in that case we will fallback to normal pasting below
3636 if (doc()->multiPaste(this, texts)) {
3637 return;
3638 }
3639 }
3640
3641 m_temporaryAutomaticInvocationDisabled = true;
3642 doc()->paste(this, textToPaste ? *textToPaste : QApplication::clipboard()->text(QClipboard::Clipboard));
3643 m_temporaryAutomaticInvocationDisabled = false;
3644}
3645
3646bool KTextEditor::ViewPrivate::setCursorPosition(KTextEditor::Cursor position)
3647{
3648 return setCursorPositionInternal(position, 1, true);
3649}
3650
3651KTextEditor::Cursor KTextEditor::ViewPrivate::cursorPosition() const
3652{
3653 return m_viewInternal->cursorPosition();
3654}
3655
3656KTextEditor::Cursor KTextEditor::ViewPrivate::cursorPositionVirtual() const
3657{
3658 return KTextEditor::Cursor(m_viewInternal->cursorPosition().line(), virtualCursorColumn());
3659}
3660
3661QPoint KTextEditor::ViewPrivate::cursorToCoordinate(KTextEditor::Cursor cursor) const
3662{
3663 // map from ViewInternal to View coordinates
3664 const QPoint pt = m_viewInternal->cursorToCoordinate(cursor, true, false);
3665 return pt == QPoint(-1, -1) ? pt : m_viewInternal->mapToParent(pt);
3666}
3667
3668KTextEditor::Cursor KTextEditor::ViewPrivate::coordinatesToCursor(const QPoint &coords) const
3669{
3670 // map from View to ViewInternal coordinates
3671 return m_viewInternal->coordinatesToCursor(m_viewInternal->mapFromParent(coords), false);
3672}
3673
3674QPoint KTextEditor::ViewPrivate::cursorPositionCoordinates() const
3675{
3676 // map from ViewInternal to View coordinates
3677 const QPoint pt = m_viewInternal->cursorCoordinates(false);
3678 return pt == QPoint(-1, -1) ? pt : m_viewInternal->mapToParent(pt);
3679}
3680
3681void KTextEditor::ViewPrivate::setScrollPositionInternal(KTextEditor::Cursor cursor)
3682{
3683 m_viewInternal->scrollPos(cursor, false, true, false);
3684}
3685
3686void KTextEditor::ViewPrivate::setHorizontalScrollPositionInternal(int x)
3687{
3688 m_viewInternal->scrollColumns(x);
3689}
3690
3691KTextEditor::Cursor KTextEditor::ViewPrivate::maxScrollPositionInternal() const
3692{
3693 return m_viewInternal->maxStartPos(true);
3694}
3695
3696int KTextEditor::ViewPrivate::firstDisplayedLineInternal(LineType lineType) const
3697{
3698 if (lineType == RealLine) {
3699 return m_textFolding.visibleLineToLine(m_viewInternal->startLine());
3700 } else {
3701 return m_viewInternal->startLine();
3702 }
3703}
3704
3705int KTextEditor::ViewPrivate::lastDisplayedLineInternal(LineType lineType) const
3706{
3707 if (lineType == RealLine) {
3708 return m_textFolding.visibleLineToLine(m_viewInternal->endLine());
3709 } else {
3710 return m_viewInternal->endLine();
3711 }
3712}
3713
3714QRect KTextEditor::ViewPrivate::textAreaRectInternal() const
3715{
3716 const auto sourceRect = m_viewInternal->rect();
3717 const auto topLeft = m_viewInternal->mapTo(this, sourceRect.topLeft());
3718 const auto bottomRight = m_viewInternal->mapTo(this, sourceRect.bottomRight());
3719 return {topLeft, bottomRight};
3720}
3721
3722bool KTextEditor::ViewPrivate::setCursorPositionVisual(const KTextEditor::Cursor position)
3723{
3724 return setCursorPositionInternal(position, doc()->config()->tabWidth(), true);
3725}
3726
3727QScrollBar *KTextEditor::ViewPrivate::verticalScrollBar() const
3728{
3729 return m_viewInternal->m_lineScroll;
3730}
3731
3732QScrollBar *KTextEditor::ViewPrivate::horizontalScrollBar() const
3733{
3734 return m_viewInternal->m_columnScroll;
3735}
3736
3737bool KTextEditor::ViewPrivate::isLineRTL(int line) const
3738{
3739 const QString s = doc()->line(line);
3740 if (s.isEmpty()) {
3741 int line = cursorPosition().line();
3742 if (line == 0) {
3743 const int count = doc()->lines();
3744 for (int i = 1; i < count; ++i) {
3745 const QString ln = doc()->line(i);
3746 if (ln.isEmpty()) {
3747 continue;
3748 }
3749 return ln.isRightToLeft();
3750 }
3751 } else {
3752 int line = cursorPosition().line();
3753 for (; line >= 0; --line) {
3754 const QString s = doc()->line(line);
3755 if (s.isEmpty()) {
3756 continue;
3757 }
3758 return s.isRightToLeft();
3759 }
3760 }
3761 return false;
3762 } else {
3763 return s.isRightToLeft();
3764 }
3765}
3766
3767QTextLayout *KTextEditor::ViewPrivate::textLayout(const KTextEditor::Cursor pos) const
3768{
3769 KateLineLayout *thisLine = m_viewInternal->cache()->line(pos.line());
3770 return thisLine && thisLine->isValid() ? thisLine->layout() : nullptr;
3771}
3772
3773void KTextEditor::ViewPrivate::indent()
3774{
3775 KTextEditor::Cursor c(cursorPosition().line(), 0);
3776 KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
3777 doc()->indent(r, 1);
3778}
3779
3780void KTextEditor::ViewPrivate::unIndent()
3781{
3782 KTextEditor::Cursor c(cursorPosition().line(), 0);
3783 KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
3784 doc()->indent(r, -1);
3785}
3786
3787void KTextEditor::ViewPrivate::cleanIndent()
3788{
3789 KTextEditor::Cursor c(cursorPosition().line(), 0);
3790 KTextEditor::Range r = selection() ? selectionRange() : KTextEditor::Range(c, c);
3791 doc()->indent(r, 0);
3792}
3793
3794void KTextEditor::ViewPrivate::formatIndent()
3795{
3796 // no selection: align current line; selection: use selection range
3797 const int line = cursorPosition().line();
3798 KTextEditor::Range formatRange(KTextEditor::Cursor(line, 0), KTextEditor::Cursor(line, 0));
3799 if (selection()) {
3800 formatRange = selectionRange();
3801 }
3802
3803 doc()->align(this, formatRange);
3804}
3805
3806// alias of formatIndent, for backward compatibility
3807void KTextEditor::ViewPrivate::align()
3808{
3809 formatIndent();
3810}
3811
3812void KTextEditor::ViewPrivate::alignOn()
3813{
3814 static QString pattern;
3815 KTextEditor::Range range;
3816 if (!selection()) {
3817 range = doc()->documentRange();
3818 } else {
3819 range = selectionRange();
3820 }
3821 bool ok;
3822 pattern = QInputDialog::getText(window(), i18n("Align On"), i18n("Alignment pattern:"), QLineEdit::Normal, pattern, &ok);
3823 if (!ok) {
3824 return;
3825 }
3826 doc()->alignOn(range, pattern, this->blockSelection());
3827}
3828
3829void KTextEditor::ViewPrivate::comment()
3830{
3831 m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight);
3832 doc()->comment(this, cursorPosition().line(), cursorPosition().column(), DocumentPrivate::Comment);
3833 m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight);
3834}
3835
3836void KTextEditor::ViewPrivate::uncomment()
3837{
3838 doc()->comment(this, cursorPosition().line(), cursorPosition().column(), DocumentPrivate::UnComment);
3839}
3840
3841void KTextEditor::ViewPrivate::toggleComment()
3842{
3843 m_selection.setInsertBehaviors(Kate::TextRange::ExpandLeft | Kate::TextRange::ExpandRight);
3844 doc()->comment(this, cursorPosition().line(), cursorPosition().column(), DocumentPrivate::ToggleComment);
3845 m_selection.setInsertBehaviors(Kate::TextRange::ExpandRight);
3846}
3847
3848void KTextEditor::ViewPrivate::uppercase()
3849{
3850 doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Uppercase);
3851}
3852
3853void KTextEditor::ViewPrivate::killLine()
3854{
3855 std::vector<int> linesToRemove;
3856 if (m_selection.isEmpty()) {
3857 // collect lines of all cursors
3858 linesToRemove.reserve(m_secondaryCursors.size() + 1);
3859 for (const auto &c : m_secondaryCursors) {
3860 linesToRemove.push_back(c.pos->line());
3861 }
3862 // add primary cursor line
3863 linesToRemove.push_back(cursorPosition().line());
3864 } else {
3865 linesToRemove.reserve(m_secondaryCursors.size() + 1);
3866 for (const auto &c : m_secondaryCursors) {
3867 const auto &range = c.range;
3868 if (!range) {
3869 continue;
3870 }
3871 for (int line = range->end().line(); line >= range->start().line(); line--) {
3872 linesToRemove.push_back(line);
3873 }
3874 }
3875
3876 // cache endline, else that moves and we might delete complete document if last line is selected!
3877 for (int line = m_selection.end().line(), endLine = m_selection.start().line(); line >= endLine; line--) {
3878 linesToRemove.push_back(line);
3879 }
3880 }
3881
3882 std::sort(linesToRemove.begin(), linesToRemove.end(), std::greater{});
3883 linesToRemove.erase(std::unique(linesToRemove.begin(), linesToRemove.end()), linesToRemove.end());
3884
3885 doc()->editStart();
3886 std::for_each(linesToRemove.begin(), linesToRemove.end(), [this](int line) {
3887 doc()->removeLine(line);
3888 });
3889 doc()->editEnd();
3890
3891 ensureUniqueCursors();
3892}
3893
3894void KTextEditor::ViewPrivate::lowercase()
3895{
3896 doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Lowercase);
3897}
3898
3899void KTextEditor::ViewPrivate::capitalize()
3900{
3901 doc()->editStart();
3902 doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Lowercase);
3903 doc()->transform(this, cursorPosition(), KTextEditor::DocumentPrivate::Capitalize);
3904 doc()->editEnd();
3905}
3906
3907void KTextEditor::ViewPrivate::keyReturn()
3908{
3909 doc()->newLine(this);
3910 m_viewInternal->iconBorder()->updateForCursorLineChange();
3911 m_viewInternal->updateView();
3912}
3913
3914void KTextEditor::ViewPrivate::smartNewline()
3915{
3916 const KTextEditor::Cursor cursor = cursorPosition();
3917 const int ln = cursor.line();
3918 Kate::TextLine line = doc()->kateTextLine(ln);
3919 int col = qMin(cursor.column(), line.firstChar());
3920 if (col != -1) {
3921 while (line.length() > col && !(line.at(col).isLetterOrNumber() || line.at(col) == QLatin1Char('_')) && col < cursor.column()) {
3922 ++col;
3923 }
3924 } else {
3925 col = line.length(); // stay indented
3926 }
3927 doc()->editStart();
3928 doc()->editWrapLine(ln, cursor.column());
3929 doc()->insertText(KTextEditor::Cursor(ln + 1, 0), line.string(0, col));
3930 doc()->editEnd();
3931
3932 m_viewInternal->updateView();
3933}
3934
3935void KTextEditor::ViewPrivate::noIndentNewline()
3936{
3937 doc()->newLine(this, KTextEditor::DocumentPrivate::NoIndent);
3938 m_viewInternal->iconBorder()->updateForCursorLineChange();
3939 m_viewInternal->updateView();
3940}
3941
3942void KTextEditor::ViewPrivate::newLineAbove()
3943{
3944 doc()->newLine(this, KTextEditor::DocumentPrivate::Indent, KTextEditor::DocumentPrivate::Above);
3945 m_viewInternal->iconBorder()->updateForCursorLineChange();
3946 m_viewInternal->updateView();
3947}
3948
3949void KTextEditor::ViewPrivate::newLineBelow()
3950{
3951 doc()->newLine(this, KTextEditor::DocumentPrivate::Indent, KTextEditor::DocumentPrivate::Below);
3952 m_viewInternal->iconBorder()->updateForCursorLineChange();
3953 m_viewInternal->updateView();
3954}
3955
3956void KTextEditor::ViewPrivate::backspace()
3957{
3958 // Will take care of both multi and primary cursors
3959 doc()->backspace(this);
3960}
3961
3962void KTextEditor::ViewPrivate::insertTab()
3963{
3964 doc()->insertTab(this, cursorPosition());
3965}
3966
3967void KTextEditor::ViewPrivate::deleteWordLeft()
3968{
3969 doc()->editStart();
3970 m_viewInternal->wordPrev(true);
3971 KTextEditor::Range selection = selectionRange();
3972 removeSelectedText();
3973 doc()->editEnd();
3974
3975 ensureUniqueCursors();
3976
3977 m_viewInternal->tagRange(selection, true);
3978 m_viewInternal->updateDirty();
3979}
3980
3981void KTextEditor::ViewPrivate::keyDelete()
3982{
3984
3985 if (blockSelect) {
3986 KTextEditor::Range selection = m_selection;
3987 if (selection.isValid() && selection.start().column() == selection.end().column()) {
3988 KTextEditor::Cursor end = {selection.end().line(), selection.end().column() + 1};
3989 selection = {selection.start(), end};
3990 doc()->removeText(selection, blockSelect);
3991 return;
3992 }
3993 }
3994
3995 // multi cursor
3996
3997 if (removeSelectedText()) {
3998 return;
3999 }
4000
4001 for (const auto &c : m_secondaryCursors) {
4002 if (c.range) {
4003 doc()->removeText(c.range->toRange());
4004 } else {
4005 doc()->del(this, c.cursor());
4006 }
4007 }
4008
4009 // primary cursor
4010 doc()->del(this, cursorPosition());
4011
4012 ensureUniqueCursors();
4013}
4014
4015void KTextEditor::ViewPrivate::deleteWordRight()
4016{
4017 doc()->editStart();
4018 m_viewInternal->wordNext(true);
4019 KTextEditor::Range selection = selectionRange();
4020 removeSelectedText();
4021 doc()->editEnd();
4022
4023 ensureUniqueCursors();
4024
4025 m_viewInternal->tagRange(selection, true);
4026 m_viewInternal->updateDirty();
4027}
4028
4029void KTextEditor::ViewPrivate::transpose()
4030{
4031 doc()->editStart();
4032 for (const auto &c : m_secondaryCursors) {
4033 doc()->transpose(c.cursor());
4034 }
4035 doc()->transpose(cursorPosition());
4036 doc()->editEnd();
4037}
4038
4039void KTextEditor::ViewPrivate::transposeWord()
4040{
4041 const KTextEditor::Cursor originalCurPos = cursorPosition();
4042 const KTextEditor::Range firstWord = doc()->wordRangeAt(originalCurPos);
4043 if (!firstWord.isValid()) {
4044 return;
4045 }
4046
4047 auto wordIsInvalid = [](QStringView word) {
4048 for (const QChar &character : word) {
4049 if (character.isLetterOrNumber()) {
4050 return false;
4051 }
4052 }
4053 return true;
4054 };
4055
4056 if (wordIsInvalid(doc()->text(firstWord))) {
4057 return;
4058 }
4059
4060 setCursorPosition(firstWord.end());
4061 wordRight();
4062 KTextEditor::Cursor curPos = cursorPosition();
4063 // swap with the word to the right if it exists, otherwise try to swap with word to the left
4064 if (curPos.line() != firstWord.end().line() || curPos.column() == firstWord.end().column()) {
4065 setCursorPosition(firstWord.start());
4066 wordLeft();
4067 curPos = cursorPosition();
4068 // if there is still no next word in this line, no swapping will be done
4069 if (curPos.line() != firstWord.start().line() || curPos.column() == firstWord.start().column() || wordIsInvalid(doc()->wordAt(curPos))) {
4070 setCursorPosition(originalCurPos);
4071 return;
4072 }
4073 }
4074
4075 if (wordIsInvalid(doc()->wordAt(curPos))) {
4076 setCursorPosition(originalCurPos);
4077 return;
4078 }
4079
4080 const KTextEditor::Range secondWord = doc()->wordRangeAt(curPos);
4081 doc()->swapTextRanges(firstWord, secondWord);
4082
4083 // return cursor to its original position inside the word before swap
4084 // 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
4085 const int offsetFromWordEnd = firstWord.end().column() - originalCurPos.column();
4086 setCursorPosition(cursorPosition() - KTextEditor::Cursor(0, offsetFromWordEnd));
4087}
4088
4089void KTextEditor::ViewPrivate::cursorLeft()
4090{
4091 if (selection() && !config()->persistentSelection() && !m_markedSelection) {
4092 if (isLineRTL(cursorPosition().line())) {
4093 m_viewInternal->updateCursor(selectionRange().end());
4094 setSelection(KTextEditor::Range::invalid());
4095 } else {
4096 m_viewInternal->updateCursor(selectionRange().start());
4097 setSelection(KTextEditor::Range::invalid());
4098 }
4099
4100 for (const auto &c : m_secondaryCursors) {
4101 if (!c.range) {
4102 continue;
4103 }
4104 const bool rtl = isLineRTL(c.cursor().line());
4105 c.pos->setPosition(rtl ? c.range->end() : c.range->start());
4106 }
4107 clearSecondarySelections();
4108 } else {
4109 if (isLineRTL(cursorPosition().line())) {
4110 m_viewInternal->cursorNextChar(m_markedSelection);
4111 } else {
4112 m_viewInternal->cursorPrevChar(m_markedSelection);
4113 }
4114 }
4115}
4116
4117void KTextEditor::ViewPrivate::shiftCursorLeft()
4118{
4119 if (isLineRTL(cursorPosition().line())) {
4120 m_viewInternal->cursorNextChar(true);
4121 } else {
4122 m_viewInternal->cursorPrevChar(true);
4123 }
4124}
4125
4126void KTextEditor::ViewPrivate::cursorRight()
4127{
4128 if (selection() && !config()->persistentSelection() && !m_markedSelection) {
4129 if (isLineRTL(cursorPosition().line())) {
4130 m_viewInternal->updateCursor(selectionRange().start());
4131 setSelection(KTextEditor::Range::invalid());
4132 } else {
4133 m_viewInternal->updateCursor(selectionRange().end());
4134 setSelection(KTextEditor::Range::invalid());
4135 }
4136
4137 for (const auto &c : m_secondaryCursors) {
4138 if (!c.range) {
4139 continue;
4140 }
4141 const bool rtl = doc()->line(c.cursor().line()).isRightToLeft();
4142 c.pos->setPosition(rtl ? c.range->start() : c.range->end());
4143 }
4144 clearSecondarySelections();
4145 } else {
4146 if (isLineRTL(cursorPosition().line())) {
4147 m_viewInternal->cursorPrevChar(m_markedSelection);
4148 } else {
4149 m_viewInternal->cursorNextChar(m_markedSelection);
4150 }
4151 }
4152}
4153
4154void KTextEditor::ViewPrivate::shiftCursorRight()
4155{
4156 if (isLineRTL(cursorPosition().line())) {
4157 m_viewInternal->cursorPrevChar(true);
4158 } else {
4159 m_viewInternal->cursorNextChar(true);
4160 }
4161}
4162
4163void KTextEditor::ViewPrivate::wordLeft()
4164{
4165 if (isLineRTL(cursorPosition().line())) {
4166 m_viewInternal->wordNext(m_markedSelection);
4167 } else {
4168 m_viewInternal->wordPrev(m_markedSelection);
4169 }
4170}
4171
4172void KTextEditor::ViewPrivate::shiftWordLeft()
4173{
4174 if (isLineRTL(cursorPosition().line())) {
4175 m_viewInternal->wordNext(true);
4176 } else {
4177 m_viewInternal->wordPrev(true);
4178 }
4179}
4180
4181void KTextEditor::ViewPrivate::wordRight()
4182{
4183 if (isLineRTL(cursorPosition().line())) {
4184 m_viewInternal->wordPrev(m_markedSelection);
4185 } else {
4186 m_viewInternal->wordNext(m_markedSelection);
4187 }
4188}
4189
4190void KTextEditor::ViewPrivate::shiftWordRight()
4191{
4192 if (isLineRTL(cursorPosition().line())) {
4193 m_viewInternal->wordPrev(true);
4194 } else {
4195 m_viewInternal->wordNext(true);
4196 }
4197}
4198
4199void KTextEditor::ViewPrivate::markSelection()
4200{
4201 if (m_markedSelection && selection()) {
4202 setSelection(KTextEditor::Range::invalid());
4203 clearSecondarySelections();
4204 } else {
4205 m_markedSelection = !m_markedSelection;
4206 }
4207}
4208
4209void KTextEditor::ViewPrivate::home()
4210{
4211 m_viewInternal->home(m_markedSelection);
4212}
4213
4214void KTextEditor::ViewPrivate::shiftHome()
4215{
4216 m_viewInternal->home(true);
4217}
4218
4219void KTextEditor::ViewPrivate::end()
4220{
4221 m_viewInternal->end(m_markedSelection);
4222}
4223
4224void KTextEditor::ViewPrivate::shiftEnd()
4225{
4226 m_viewInternal->end(true);
4227}
4228
4229void KTextEditor::ViewPrivate::up()
4230{
4231 m_viewInternal->cursorUp(m_markedSelection);
4232}
4233
4234void KTextEditor::ViewPrivate::shiftUp()
4235{
4236 m_viewInternal->cursorUp(true);
4237}
4238
4239void KTextEditor::ViewPrivate::down()
4240{
4241 m_viewInternal->cursorDown(m_markedSelection);
4242}
4243
4244void KTextEditor::ViewPrivate::shiftDown()
4245{
4246 m_viewInternal->cursorDown(true);
4247}
4248
4249void KTextEditor::ViewPrivate::scrollUp()
4250{
4251 m_viewInternal->scrollUp();
4252}
4253
4254void KTextEditor::ViewPrivate::scrollDown()
4255{
4256 m_viewInternal->scrollDown();
4257}
4258
4259void KTextEditor::ViewPrivate::topOfView()
4260{
4261 m_viewInternal->topOfView();
4262}
4263
4264void KTextEditor::ViewPrivate::shiftTopOfView()
4265{
4266 m_viewInternal->topOfView(true);
4267}
4268
4269void KTextEditor::ViewPrivate::bottomOfView()
4270{
4271 m_viewInternal->bottomOfView();
4272}
4273
4274void KTextEditor::ViewPrivate::shiftBottomOfView()
4275{
4276 m_viewInternal->bottomOfView(true);
4277}
4278
4279void KTextEditor::ViewPrivate::pageUp()
4280{
4281 m_viewInternal->pageUp(m_markedSelection);
4282}
4283
4284void KTextEditor::ViewPrivate::shiftPageUp()
4285{
4286 m_viewInternal->pageUp(true);
4287}
4288
4289void KTextEditor::ViewPrivate::pageDown()
4290{
4291 m_viewInternal->pageDown(m_markedSelection);
4292}
4293
4294void KTextEditor::ViewPrivate::shiftPageDown()
4295{
4296 m_viewInternal->pageDown(true);
4297}
4298
4299void KTextEditor::ViewPrivate::top()
4300{
4301 m_viewInternal->top_home(m_markedSelection);
4302}
4303
4304void KTextEditor::ViewPrivate::shiftTop()
4305{
4306 m_viewInternal->top_home(true);
4307}
4308
4309void KTextEditor::ViewPrivate::bottom()
4310{
4311 m_viewInternal->bottom_end(m_markedSelection);
4312}
4313
4314void KTextEditor::ViewPrivate::shiftBottom()
4315{
4316 m_viewInternal->bottom_end(true);
4317}
4318
4319void KTextEditor::ViewPrivate::toMatchingBracket()
4320{
4321 m_viewInternal->cursorToMatchingBracket();
4322}
4323
4324void KTextEditor::ViewPrivate::shiftToMatchingBracket()
4325{
4326 m_viewInternal->cursorToMatchingBracket(true);
4327}
4328
4329void KTextEditor::ViewPrivate::toPrevModifiedLine()
4330{
4331 const int startLine = cursorPosition().line() - 1;
4332 const int line = doc()->findTouchedLine(startLine, false);
4333 if (line >= 0) {
4334 KTextEditor::Cursor c(line, 0);
4335 m_viewInternal->updateSelection(c, false);
4336 m_viewInternal->updateCursor(c);
4337 }
4338}
4339
4340void KTextEditor::ViewPrivate::toNextModifiedLine()
4341{
4342 const int startLine = cursorPosition().line() + 1;
4343 const int line = doc()->findTouchedLine(startLine, true);
4344 if (line >= 0) {
4345 KTextEditor::Cursor c(line, 0);
4346 m_viewInternal->updateSelection(c, false);
4347 m_viewInternal->updateCursor(c);
4348 }
4349}
4350
4351KTextEditor::Range KTextEditor::ViewPrivate::selectionRange() const
4352{
4353 return m_selection;
4354}
4355
4356KTextEditor::Document *KTextEditor::ViewPrivate::document() const
4357{
4358 return m_doc;
4359}
4360
4361void KTextEditor::ViewPrivate::setContextMenu(QMenu *menu)
4362{
4363 if (m_contextMenu) {
4364 disconnect(m_contextMenu.data(), &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
4365 disconnect(m_contextMenu.data(), &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
4366 }
4367 m_contextMenu = menu;
4368 m_userContextMenuSet = true;
4369
4370 if (m_contextMenu) {
4371 connect(m_contextMenu.data(), &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
4372 connect(m_contextMenu.data(), &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
4373 }
4374}
4375
4376QMenu *KTextEditor::ViewPrivate::contextMenu() const
4377{
4378 if (m_userContextMenuSet) {
4379 return m_contextMenu;
4380 } else {
4381 KXMLGUIClient *client = const_cast<KTextEditor::ViewPrivate *>(this);
4382 while (client->parentClient()) {
4383 client = client->parentClient();
4384 }
4385
4386 // qCDebug(LOG_KTE) << "looking up all menu containers";
4387 if (client->factory()) {
4388 const QList<QWidget *> menuContainers = client->factory()->containers(QStringLiteral("menu"));
4389 for (QWidget *w : menuContainers) {
4390 if (w->objectName() == QLatin1String("ktexteditor_popup")) {
4391 // perhaps optimize this block
4392 QMenu *menu = (QMenu *)w;
4393 // menu is a reusable instance shared among all views. Therefore,
4394 // disconnect the current receiver(s) from the menu show/hide signals
4395 // before connecting `this` view. This ensures that only the current
4396 // view gets a signal when the menu is about to be shown or hidden,
4397 // and not also the view(s) that previously had the menu open.
4398 disconnect(menu, &QMenu::aboutToShow, nullptr, nullptr);
4399 disconnect(menu, &QMenu::aboutToHide, nullptr, nullptr);
4400 connect(menu, &QMenu::aboutToShow, this, &KTextEditor::ViewPrivate::aboutToShowContextMenu);
4401 connect(menu, &QMenu::aboutToHide, this, &KTextEditor::ViewPrivate::aboutToHideContextMenu);
4402 return menu;
4403 }
4404 }
4405 }
4406 }
4407 return nullptr;
4408}
4409
4410QMenu *KTextEditor::ViewPrivate::defaultContextMenu(QMenu *menu) const
4411{
4412 if (!menu) {
4413 menu = new QMenu(const_cast<KTextEditor::ViewPrivate *>(this));
4414 }
4415
4416 if (m_editUndo) {
4417 menu->addAction(m_editUndo);
4418 }
4419 if (m_editRedo) {
4420 menu->addAction(m_editRedo);
4421 }
4422
4423 menu->addSeparator();
4424 menu->addAction(m_cut);
4425 menu->addAction(m_copy);
4426 menu->addAction(m_paste);
4427 if (m_pasteSelection) {
4428 menu->addAction(m_pasteSelection);
4429 }
4430
4431 menu->addAction(m_screenshotSelection);
4432 menu->addAction(m_swapWithClipboard);
4433 menu->addSeparator();
4434 menu->addAction(m_selectAll);
4435 menu->addAction(m_deSelect);
4436 QAction *editing = actionCollection()->action(QStringLiteral("tools_scripts_Editing"));
4437 if (editing) {
4438 menu->addAction(editing);
4439 }
4440 if (QAction *spellingSuggestions = actionCollection()->action(QStringLiteral("spelling_suggestions"))) {
4441 menu->addSeparator();
4442 menu->addAction(spellingSuggestions);
4443 }
4444 if (QAction *bookmark = actionCollection()->action(QStringLiteral("bookmarks"))) {
4445 menu->addSeparator();
4446 menu->addAction(bookmark);
4447 }
4448
4449 return menu;
4450}
4451
4452void KTextEditor::ViewPrivate::aboutToShowContextMenu()
4453{
4454 QMenu *menu = qobject_cast<QMenu *>(sender());
4455
4456 if (menu) {
4457 Q_EMIT contextMenuAboutToShow(this, menu);
4458 }
4459}
4460
4461void KTextEditor::ViewPrivate::aboutToHideContextMenu()
4462{
4463 m_spellingMenu->cleanUpAfterShown();
4464}
4465
4466// BEGIN ConfigInterface stff
4467QStringList KTextEditor::ViewPrivate::configKeys() const
4468{
4469 static const QStringList keys = {QStringLiteral("icon-bar"),
4470 QStringLiteral("line-numbers"),
4471 QStringLiteral("dynamic-word-wrap"),
4472 QStringLiteral("background-color"),
4473 QStringLiteral("selection-color"),
4474 QStringLiteral("search-highlight-color"),
4475 QStringLiteral("replace-highlight-color"),
4476 QStringLiteral("default-mark-type"),
4477 QStringLiteral("allow-mark-menu"),
4478 QStringLiteral("folding-bar"),
4479 QStringLiteral("folding-preview"),
4480 QStringLiteral("icon-border-color"),
4481 QStringLiteral("folding-marker-color"),
4482 QStringLiteral("line-number-color"),
4483 QStringLiteral("current-line-number-color"),
4484 QStringLiteral("modification-markers"),
4485 QStringLiteral("keyword-completion"),
4486 QStringLiteral("word-count"),
4487 QStringLiteral("line-count"),
4488 QStringLiteral("scrollbar-minimap"),
4489 QStringLiteral("scrollbar-preview"),
4490 QStringLiteral("font"),
4491 QStringLiteral("theme")};
4492 return keys;
4493}
4494
4495QVariant KTextEditor::ViewPrivate::configValue(const QString &key)
4496{
4497 if (key == QLatin1String("icon-bar")) {
4498 return config()->iconBar();
4499 } else if (key == QLatin1String("line-numbers")) {
4500 return config()->lineNumbers();
4501 } else if (key == QLatin1String("dynamic-word-wrap")) {
4502 return config()->dynWordWrap();
4503 } else if (key == QLatin1String("background-color")) {
4504 return rendererConfig()->backgroundColor();
4505 } else if (key == QLatin1String("selection-color")) {
4506 return rendererConfig()->selectionColor();
4507 } else if (key == QLatin1String("search-highlight-color")) {
4508 return rendererConfig()->searchHighlightColor();
4509 } else if (key == QLatin1String("replace-highlight-color")) {
4510 return rendererConfig()->replaceHighlightColor();
4511 } else if (key == QLatin1String("default-mark-type")) {
4512 return config()->defaultMarkType();
4513 } else if (key == QLatin1String("allow-mark-menu")) {
4514 return config()->allowMarkMenu();
4515 } else if (key == QLatin1String("folding-bar")) {
4516 return config()->foldingBar();
4517 } else if (key == QLatin1String("folding-preview")) {
4518 return config()->foldingPreview();
4519 } else if (key == QLatin1String("icon-border-color")) {
4520 return rendererConfig()->iconBarColor();
4521 } else if (key == QLatin1String("folding-marker-color")) {
4522 return rendererConfig()->foldingColor();
4523 } else if (key == QLatin1String("line-number-color")) {
4524 return rendererConfig()->lineNumberColor();
4525 } else if (key == QLatin1String("current-line-number-color")) {
4526 return rendererConfig()->currentLineNumberColor();
4527 } else if (key == QLatin1String("modification-markers")) {
4528 return config()->lineModification();
4529 } else if (key == QLatin1String("keyword-completion")) {
4530 return config()->keywordCompletion();
4531 } else if (key == QLatin1String("word-count")) {
4532 return config()->showWordCount();
4533 } else if (key == QLatin1String("line-count")) {
4534 return config()->showLineCount();
4535 } else if (key == QLatin1String("scrollbar-minimap")) {
4536 return config()->scrollBarMiniMap();
4537 } else if (key == QLatin1String("scrollbar-preview")) {
4538 return config()->scrollBarPreview();
4539 } else if (key == QLatin1String("font")) {
4540 return rendererConfig()->baseFont();
4541 } else if (key == QLatin1String("theme")) {
4542 return rendererConfig()->schema();
4543 }
4544
4545 // return invalid variant
4546 return QVariant();
4547}
4548
4549void KTextEditor::ViewPrivate::setConfigValue(const QString &key, const QVariant &value)
4550{
4551 // First, try the new config interface
4552 if (config()->setValue(key, value)) {
4553 return;
4554
4555 } else if (rendererConfig()->setValue(key, value)) {
4556 return;
4557 }
4558
4559 // No success? Go the old way
4560 if (value.canConvert<QColor>()) {
4561 if (key == QLatin1String("background-color")) {
4562 rendererConfig()->setBackgroundColor(value.value<QColor>());
4563 } else if (key == QLatin1String("selection-color")) {
4564 rendererConfig()->setSelectionColor(value.value<QColor>());
4565 } else if (key == QLatin1String("search-highlight-color")) {
4566 rendererConfig()->setSearchHighlightColor(value.value<QColor>());
4567 } else if (key == QLatin1String("replace-highlight-color")) {
4568 rendererConfig()->setReplaceHighlightColor(value.value<QColor>());
4569 } else if (key == QLatin1String("icon-border-color")) {
4570 rendererConfig()->setIconBarColor(value.value<QColor>());
4571 } else if (key == QLatin1String("folding-marker-color")) {
4572 rendererConfig()->setFoldingColor(value.value<QColor>());
4573 } else if (key == QLatin1String("line-number-color")) {
4574 rendererConfig()->setLineNumberColor(value.value<QColor>());
4575 } else if (key == QLatin1String("current-line-number-color")) {
4576 rendererConfig()->setCurrentLineNumberColor(value.value<QColor>());
4577 }
4578 }
4579 if (value.userType() == QMetaType::Bool) {
4580 // Note explicit type check above. If we used canConvert, then
4581 // values of type UInt will be trapped here.
4582 if (key == QLatin1String("dynamic-word-wrap")) {
4583 config()->setDynWordWrap(value.toBool());
4584 } else if (key == QLatin1String("word-count")) {
4585 config()->setShowWordCount(value.toBool());
4586 } else if (key == QLatin1String("line-count")) {
4587 config()->setShowLineCount(value.toBool());
4588 }
4589 } else if (key == QLatin1String("font") && value.canConvert<QFont>()) {
4590 rendererConfig()->setFont(value.value<QFont>());
4591 } else if (key == QLatin1String("theme") && value.userType() == QMetaType::QString) {
4592 rendererConfig()->setSchema(value.toString());
4593 }
4594}
4595
4596// END ConfigInterface
4597
4598// NOLINTNEXTLINE(readability-make-member-function-const)
4599void KTextEditor::ViewPrivate::userInvokedCompletion()
4600{
4601 completionWidget()->userInvokedCompletion();
4602}
4603
4604KateViewBar *KTextEditor::ViewPrivate::bottomViewBar() const
4605{
4606 return m_bottomViewBar;
4607}
4608
4609KateGotoBar *KTextEditor::ViewPrivate::gotoBar()
4610{
4611 if (!m_gotoBar) {
4612 m_gotoBar = new KateGotoBar(this);
4613 bottomViewBar()->addBarWidget(m_gotoBar);
4614 }
4615
4616 return m_gotoBar;
4617}
4618
4619KateDictionaryBar *KTextEditor::ViewPrivate::dictionaryBar()
4620{
4621 if (!m_dictionaryBar) {
4622 m_dictionaryBar = new KateDictionaryBar(this);
4623 bottomViewBar()->addBarWidget(m_dictionaryBar);
4624 }
4625
4626 return m_dictionaryBar;
4627}
4628
4629void KTextEditor::ViewPrivate::setAnnotationModel(KTextEditor::AnnotationModel *model)
4630{
4631 KTextEditor::AnnotationModel *oldmodel = m_annotationModel;
4632 m_annotationModel = model;
4633 m_viewInternal->m_leftBorder->annotationModelChanged(oldmodel, m_annotationModel);
4634}
4635
4636KTextEditor::AnnotationModel *KTextEditor::ViewPrivate::annotationModel() const
4637{
4638 return m_annotationModel;
4639}
4640
4641void KTextEditor::ViewPrivate::setAnnotationBorderVisible(bool visible)
4642{
4643 m_viewInternal->m_leftBorder->setAnnotationBorderOn(visible);
4644}
4645
4646bool KTextEditor::ViewPrivate::isAnnotationBorderVisible() const
4647{
4648 return m_viewInternal->m_leftBorder->annotationBorderOn();
4649}
4650
4651KTextEditor::AbstractAnnotationItemDelegate *KTextEditor::ViewPrivate::annotationItemDelegate() const
4652{
4653 return m_viewInternal->m_leftBorder->annotationItemDelegate();
4654}
4655
4656void KTextEditor::ViewPrivate::setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate)
4657{
4658 m_viewInternal->m_leftBorder->setAnnotationItemDelegate(delegate);
4659}
4660
4661bool KTextEditor::ViewPrivate::uniformAnnotationItemSizes() const
4662{
4663 return m_viewInternal->m_leftBorder->uniformAnnotationItemSizes();
4664}
4665
4666void KTextEditor::ViewPrivate::setAnnotationUniformItemSizes(bool enable)
4667{
4668 m_viewInternal->m_leftBorder->setAnnotationUniformItemSizes(enable);
4669}
4670
4671KTextEditor::Range KTextEditor::ViewPrivate::visibleRange()
4672{
4673 // ensure that the view is up-to-date, otherwise 'endPos()' might fail!
4674 if (!m_viewInternal->endPos().isValid()) {
4675 m_viewInternal->updateView();
4676 }
4677 return KTextEditor::Range(m_viewInternal->toRealCursor(m_viewInternal->startPos()), m_viewInternal->toRealCursor(m_viewInternal->endPos()));
4678}
4679
4680bool KTextEditor::ViewPrivate::event(QEvent *e)
4681{
4682 switch (e->type()) {
4684 setupLayout();
4685 return true;
4686 default:
4687 return KTextEditor::View::event(e);
4688 }
4689}
4690
4691void KTextEditor::ViewPrivate::toggleOnTheFlySpellCheck(bool b)
4692{
4693 doc()->onTheFlySpellCheckingEnabled(b);
4694}
4695
4696void KTextEditor::ViewPrivate::reflectOnTheFlySpellCheckStatus(bool enabled)
4697{
4698 m_spellingMenu->setVisible(enabled);
4699 m_toggleOnTheFlySpellCheck->setChecked(enabled);
4700}
4701
4702KateSpellingMenu *KTextEditor::ViewPrivate::spellingMenu()
4703{
4704 return m_spellingMenu;
4705}
4706
4707void KTextEditor::ViewPrivate::notifyAboutRangeChange(KTextEditor::LineRange lineRange, bool needsRepaint)
4708{
4709#ifdef VIEW_RANGE_DEBUG
4710 // output args
4711 qCDebug(LOG_KTE) << "trigger attribute changed in line range " << lineRange << "needsRepaint" << needsRepaint;
4712#endif
4713
4714 // if we need repaint, we will need to collect the line ranges we will update
4715 if (needsRepaint && lineRange.isValid()) {
4716 if (m_lineToUpdateRange.isValid()) {
4717 m_lineToUpdateRange.expandToRange(lineRange);
4718 } else {
4719 m_lineToUpdateRange = lineRange;
4720 }
4721 }
4722
4723 // first call => trigger later update of view via delayed signal to group updates
4724 if (!m_delayedUpdateTimer.isActive()) {
4725 m_delayedUpdateTimer.start();
4726 }
4727}
4728
4729void KTextEditor::ViewPrivate::slotDelayedUpdateOfView()
4730{
4731#ifdef VIEW_RANGE_DEBUG
4732 // output args
4733 qCDebug(LOG_KTE) << "delayed attribute changed in line range" << m_lineToUpdateRange;
4734#endif
4735 // update ranges in
4738
4739 // update view, if valid line range, else only feedback update wanted anyway
4740 if (m_lineToUpdateRange.isValid()) {
4741 tagLines(m_lineToUpdateRange, true);
4742 updateView(true);
4743 }
4744
4745 // reset flags
4746 m_lineToUpdateRange = KTextEditor::LineRange::invalid();
4747}
4748
4749void KTextEditor::ViewPrivate::updateRangesIn(KTextEditor::Attribute::ActivationType activationType)
4750{
4751 // new ranges with cursor in, default none
4752 QSet<Kate::TextRange *> newRangesIn;
4753
4754 // on which range set we work?
4755 QSet<Kate::TextRange *> &oldSet = (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_rangesMouseIn : m_rangesCaretIn;
4756
4757 // which cursor position to honor?
4758 KTextEditor::Cursor currentCursor =
4759 (activationType == KTextEditor::Attribute::ActivateMouseIn) ? m_viewInternal->mousePosition() : m_viewInternal->cursorPosition();
4760
4761 // first: validate the remembered ranges
4762 QSet<Kate::TextRange *> validRanges;
4763 for (Kate::TextRange *range : std::as_const(oldSet)) {
4764 if (doc()->buffer().rangePointerValid(range)) {
4765 validRanges.insert(range);
4766 }
4767 }
4768
4769 // cursor valid? else no new ranges can be found
4770 if (currentCursor.isValid() && currentCursor.line() < doc()->buffer().lines()) {
4771 // now: get current ranges for the line of cursor with an attribute
4772 const QList<Kate::TextRange *> rangesForCurrentCursor = doc()->buffer().rangesForLine(currentCursor.line(), this, false);
4773
4774 // match which ranges really fit the given cursor
4775 for (Kate::TextRange *range : rangesForCurrentCursor) {
4776 // range has no dynamic attribute of right type and no feedback object
4777 auto attribute = range->attribute();
4778 if ((!attribute || !attribute->dynamicAttribute(activationType)) && !range->feedback()) {
4779 continue;
4780 }
4781
4782 // range doesn't contain cursor, not interesting
4783 if ((range->startInternal().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) ? (currentCursor < range->toRange().start())
4784 : (currentCursor <= range->toRange().start())) {
4785 continue;
4786 }
4787
4788 if ((range->endInternal().insertBehavior() == KTextEditor::MovingCursor::StayOnInsert) ? (range->toRange().end() <= currentCursor)
4789 : (range->toRange().end() < currentCursor)) {
4790 continue;
4791 }
4792
4793 // range contains cursor, was it already in old set?
4794 auto it = validRanges.find(range);
4795 if (it != validRanges.end()) {
4796 // insert in new, remove from old, be done with it
4797 newRangesIn.insert(range);
4798 validRanges.erase(it);
4799 continue;
4800 }
4801
4802 // oh, new range, trigger update and insert into new set
4803 newRangesIn.insert(range);
4804
4805 if (attribute && attribute->dynamicAttribute(activationType)) {
4806 notifyAboutRangeChange(range->toLineRange(), true);
4807 }
4808
4809 // feedback
4810 if (range->feedback()) {
4811 if (activationType == KTextEditor::Attribute::ActivateMouseIn) {
4812 range->feedback()->mouseEnteredRange(range, this);
4813 } else {
4814 range->feedback()->caretEnteredRange(range, this);
4815 Q_EMIT caretChangedRange(this);
4816 }
4817 }
4818
4819#ifdef VIEW_RANGE_DEBUG
4820 // found new range for activation
4821 qCDebug(LOG_KTE) << "activated new range" << range << "by" << activationType;
4822#endif
4823 }
4824 }
4825
4826 // now: notify for left ranges!
4827 for (Kate::TextRange *range : std::as_const(validRanges)) {
4828 // range valid + right dynamic attribute, trigger update
4829 if (range->toRange().isValid() && range->attribute() && range->attribute()->dynamicAttribute(activationType)) {
4830 notifyAboutRangeChange(range->toLineRange(), true);
4831 }
4832
4833 // feedback
4834 if (range->feedback()) {
4835 if (activationType == KTextEditor::Attribute::ActivateMouseIn) {
4836 range->feedback()->mouseExitedRange(range, this);
4837 } else {
4838 range->feedback()->caretExitedRange(range, this);
4839 Q_EMIT caretChangedRange(this);
4840 }
4841 }
4842 }
4843
4844 // set new ranges
4845 oldSet = newRangesIn;
4846}
4847
4848void KTextEditor::ViewPrivate::postMessage(KTextEditor::Message *message, QList<std::shared_ptr<QAction>> actions)
4849{
4850 // just forward to KateMessageWidget :-)
4851 auto messageWidget = m_messageWidgets[message->position()];
4852 if (!messageWidget) {
4853 // this branch is used for: TopInView, CenterInView, and BottomInView
4854 messageWidget = new KateMessageWidget(m_viewInternal, true);
4855 m_messageWidgets[message->position()] = messageWidget;
4856 m_notificationLayout->addWidget(messageWidget, message->position());
4857 connect(this, &KTextEditor::ViewPrivate::displayRangeChanged, messageWidget, &KateMessageWidget::startAutoHideTimer);
4859 }
4860 messageWidget->postMessage(message, std::move(actions));
4861}
4862
4863KateMessageWidget *KTextEditor::ViewPrivate::messageWidget()
4864{
4865 return m_messageWidgets[KTextEditor::Message::TopInView];
4866}
4867
4868void KTextEditor::ViewPrivate::saveFoldingState()
4869{
4870 m_savedFoldingState = m_textFolding.exportFoldingRanges();
4871}
4872
4873void KTextEditor::ViewPrivate::clearFoldingState()
4874{
4875 m_savedFoldingState = {};
4876}
4877
4878void KTextEditor::ViewPrivate::applyFoldingState()
4879{
4880 m_textFolding.importFoldingRanges(m_savedFoldingState);
4881 m_savedFoldingState = QJsonDocument();
4882}
4883
4884void KTextEditor::ViewPrivate::exportHtmlToFile(const QString &file)
4885{
4886 KateExporter(this).exportToFile(file);
4887}
4888
4889void KTextEditor::ViewPrivate::exportHtmlToClipboard()
4890{
4891 KateExporter(this).exportToClipboard();
4892}
4893
4894void KTextEditor::ViewPrivate::exportHtmlToFile()
4895{
4896 const QString file = QFileDialog::getSaveFileName(this, i18n("Export File as HTML"), doc()->documentName());
4897 if (!file.isEmpty()) {
4898 KateExporter(this).exportToFile(file);
4899 }
4900}
4901
4902void KTextEditor::ViewPrivate::clearHighlights()
4903{
4904 m_rangesForHighlights.clear();
4905 m_currentTextForHighlights.clear();
4906}
4907
4908void KTextEditor::ViewPrivate::selectionChangedForHighlights()
4909{
4910 QString text;
4911 // if text of selection is still the same, abort
4912 if (selection() && selectionRange().onSingleLine()) {
4913 text = selectionText();
4914 if (text == m_currentTextForHighlights) {
4915 return;
4916 }
4917 }
4918
4919 // text changed: remove all highlights + create new ones
4920 // (do not call clearHighlights(), since this also resets the m_currentTextForHighlights
4921 m_rangesForHighlights.clear();
4922
4923 // do not highlight strings with leading and trailing spaces
4924 if (!text.isEmpty() && (text.at(0).isSpace() || text.at(text.length() - 1).isSpace())) {
4925 return;
4926 }
4927
4928 // trigger creation of ranges for current view range
4929 m_currentTextForHighlights = text;
4930 createHighlights();
4931}
4932
4933void KTextEditor::ViewPrivate::createHighlights()
4934{
4935 // do nothing if no text to highlight
4936 if (m_currentTextForHighlights.isEmpty()) {
4937 return;
4938 }
4939
4940 // clear existing highlighting ranges, otherwise we stack over and over the same ones eventually
4941 m_rangesForHighlights.clear();
4942
4944 attr->setBackground(Qt::yellow);
4945
4946 // set correct highlight color from Kate's color schema
4947 QColor fgColor = defaultStyleAttribute(KSyntaxHighlighting::Theme::TextStyle::Normal)->foreground().color();
4948 QColor bgColor = rendererConfig()->searchHighlightColor();
4949 attr->setForeground(fgColor);
4950 attr->setBackground(bgColor);
4951
4952 KTextEditor::Cursor start(visibleRange().start());
4953 KTextEditor::Range searchRange;
4954
4955 // only add word boundary if we can find the text then
4956 // fixes $lala hl
4957 QString pattern = QRegularExpression::escape(m_currentTextForHighlights);
4958 if (m_currentTextForHighlights.contains(QRegularExpression(QLatin1String("\\b") + pattern, QRegularExpression::UseUnicodePropertiesOption))) {
4959 pattern.prepend(QLatin1String("\\b"));
4960 }
4961
4962 if (m_currentTextForHighlights.contains(QRegularExpression(pattern + QLatin1String("\\b"), QRegularExpression::UseUnicodePropertiesOption))) {
4963 pattern += QLatin1String("\\b");
4964 }
4965
4967 do {
4968 searchRange.setRange(start, visibleRange().end());
4969
4970 matches = doc()->searchText(searchRange, pattern, KTextEditor::Regex);
4971
4972 if (matches.first().isValid()) {
4973 if (matches.first() != selectionRange()) {
4974 std::unique_ptr<KTextEditor::MovingRange> mr(doc()->newMovingRange(matches.first()));
4975 mr->setZDepth(-90000.0); // Set the z-depth to slightly worse than the selection
4976 mr->setAttribute(attr);
4977 mr->setView(this);
4978 mr->setAttributeOnlyForViews(true);
4979 m_rangesForHighlights.push_back(std::move(mr));
4980 }
4981 start = matches.first().end();
4982 }
4983 } while (matches.first().isValid());
4984}
4985
4986KateAbstractInputMode *KTextEditor::ViewPrivate::currentInputMode() const
4987{
4988 return m_viewInternal->m_currentInputMode;
4989}
4990
4991void KTextEditor::ViewPrivate::toggleInputMode()
4992{
4993 if (QAction *a = qobject_cast<QAction *>(sender())) {
4994 setInputMode(static_cast<KTextEditor::View::InputMode>(a->data().toInt()));
4995 }
4996}
4997
4998void KTextEditor::ViewPrivate::cycleInputMode()
4999{
5000 InputMode current = currentInputMode()->viewInputMode();
5001 InputMode to = (current == KTextEditor::View::NormalInputMode) ? KTextEditor::View::ViInputMode : KTextEditor::View::NormalInputMode;
5002 setInputMode(to);
5003}
5004
5005// BEGIN KTextEditor::PrintInterface stuff
5006bool KTextEditor::ViewPrivate::print()
5007{
5008 return KatePrinter::print(this);
5009}
5010
5011void KTextEditor::ViewPrivate::printPreview()
5012{
5013 KatePrinter::printPreview(this);
5014}
5015
5016// END
5017
5018// BEGIN Inline Note Interface
5019void KTextEditor::ViewPrivate::registerInlineNoteProvider(KTextEditor::InlineNoteProvider *provider)
5020{
5021 if (std::find(m_inlineNoteProviders.cbegin(), m_inlineNoteProviders.cend(), provider) == m_inlineNoteProviders.cend()) {
5022 m_inlineNoteProviders.push_back(provider);
5023
5024 connect(provider, &KTextEditor::InlineNoteProvider::inlineNotesReset, this, &KTextEditor::ViewPrivate::inlineNotesReset);
5025 connect(provider, &KTextEditor::InlineNoteProvider::inlineNotesChanged, this, &KTextEditor::ViewPrivate::inlineNotesLineChanged);
5026
5027 inlineNotesReset();
5028 }
5029}
5030
5031void KTextEditor::ViewPrivate::unregisterInlineNoteProvider(KTextEditor::InlineNoteProvider *provider)
5032{
5033 auto it = std::find(m_inlineNoteProviders.cbegin(), m_inlineNoteProviders.cend(), provider);
5034 if (it != m_inlineNoteProviders.cend()) {
5035 m_inlineNoteProviders.erase(it);
5036 provider->disconnect(this);
5037
5038 inlineNotesReset();
5039 }
5040}
5041
5042QVarLengthArray<KateInlineNoteData, 8> KTextEditor::ViewPrivate::inlineNotes(int line) const
5043{
5045 for (KTextEditor::InlineNoteProvider *provider : m_inlineNoteProviders) {
5046 int index = 0;
5047 const auto columns = provider->inlineNotes(line);
5048 for (int column : columns) {
5049 const bool underMouse = Cursor(line, column) == m_viewInternal->m_activeInlineNote.m_position;
5050 KateInlineNoteData note =
5051 {provider, this, {line, column}, index, underMouse, m_viewInternal->renderer()->currentFont(), m_viewInternal->renderer()->lineHeight()};
5052 allInlineNotes.append(note);
5053 index++;
5054 }
5055 }
5056 return allInlineNotes;
5057}
5058
5059QRect KTextEditor::ViewPrivate::inlineNoteRect(const KateInlineNoteData &note) const
5060{
5061 return m_viewInternal->inlineNoteRect(note);
5062}
5063
5064void KTextEditor::ViewPrivate::inlineNotesReset()
5065{
5066 m_viewInternal->m_activeInlineNote = {};
5067 tagLines(KTextEditor::LineRange(0, doc()->lastLine()), true);
5068}
5069
5070void KTextEditor::ViewPrivate::inlineNotesLineChanged(int line)
5071{
5072 if (line == m_viewInternal->m_activeInlineNote.m_position.line()) {
5073 m_viewInternal->m_activeInlineNote = {};
5074 }
5075 tagLines({line, line}, true);
5076}
5077
5078// END Inline Note Interface
5079
5080KTextEditor::Attribute::Ptr KTextEditor::ViewPrivate::defaultStyleAttribute(KSyntaxHighlighting::Theme::TextStyle defaultStyle) const
5081{
5082 KateRendererConfig *renderConfig = const_cast<KTextEditor::ViewPrivate *>(this)->rendererConfig();
5083
5084 KTextEditor::Attribute::Ptr style = doc()->highlight()->attributes(renderConfig->schema()).at(defaultStyle);
5085 if (!style->hasProperty(QTextFormat::BackgroundBrush)) {
5086 // make sure the returned style has the default background color set
5087 style = new KTextEditor::Attribute(*style);
5088 style->setBackground(QBrush(renderConfig->backgroundColor()));
5089 }
5090 return style;
5091}
5092
5093QList<KTextEditor::AttributeBlock> KTextEditor::ViewPrivate::lineAttributes(int line)
5094{
5096
5097 if (line < 0 || line >= doc()->lines()) {
5098 return attribs;
5099 }
5100
5101 const Kate::TextLine kateLine = doc()->kateTextLine(line);
5102 const auto &intAttrs = kateLine.attributesList();
5103 for (qsizetype i = 0; i < intAttrs.size(); ++i) {
5104 if (intAttrs[i].length > 0 && intAttrs[i].attributeValue > 0) {
5105 attribs << KTextEditor::AttributeBlock(intAttrs.at(i).offset, intAttrs.at(i).length, renderer()->attribute(intAttrs.at(i).attributeValue));
5106 }
5107 }
5108
5109 return attribs;
5110}
5111
5112void KTextEditor::ViewPrivate::copyFileLocation() const
5113{
5114 QGuiApplication::clipboard()->setText(m_doc->url().toString(QUrl::PreferLocalFile | QUrl::RemovePassword) + QStringLiteral(":")
5115 + QString::number(cursorPosition().line() + 1));
5116}
5117
5118#include "moc_kateview.cpp"
Q_INVOKABLE QAction * action(const QString &name) const
void addAssociatedWidget(QWidget *widget)
QAction * addAction(const QString &name, const QObject *receiver=nullptr, const char *member=nullptr)
static void setDefaultShortcut(QAction *action, const QKeySequence &shortcut)
static Q_INVOKABLE void setDefaultShortcuts(QAction *action, const QList< QKeySequence > &shortcuts)
QList< QAction * > actions() const
void addAction(QAction *action)
bool hasKey(const char *key) const
void writeEntry(const char *key, const char *value, WriteConfigFlags pFlags=Normal)
void deleteGroup(const QString &group, WriteConfigFlags flags=Normal)
QString readEntry(const char *key, const char *aDefault=nullptr) const
static void setAutoHideCursor(QWidget *w, bool enable, bool customEventFilter=false)
void canceled(const QString &errMsg)
void indexTriggered(int index)
A delegate for rendering line annotation information and handling events.
An model for providing line annotation information.
Attributes of a part of a line.
Definition attribute.h:307
A class which provides customized text decorations.
Definition attribute.h:51
ActivationType
Several automatic activation mechanisms exist for associated attributes.
Definition attribute.h:244
@ ActivateMouseIn
Activate attribute on mouse in.
Definition attribute.h:246
@ ActivateCaretIn
Activate attribute on caret in.
Definition attribute.h:248
An item model for providing code completion, and meta information for enhanced presentation.
The Cursor represents a position in a Document.
Definition cursor.h:75
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
Definition cursor.h:192
void setColumn(int column) noexcept
Set the cursor column to column.
Definition cursor.h:201
void setPosition(Cursor position) noexcept
Set the current cursor position to position.
Definition cursor.h:150
constexpr bool isValid() const noexcept
Returns whether the current position of this cursor is a valid position (line + column must both be >...
Definition cursor.h:102
static constexpr Cursor start() noexcept
Returns a cursor representing the start of any document - i.e., line 0, column 0.
Definition cursor.h:120
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
Definition cursor.h:174
static constexpr Cursor invalid() noexcept
Returns an invalid cursor.
Definition cursor.h:112
Backend of KTextEditor::Document related public KTextEditor interfaces.
bool documentReload() override
Reloads the current document from disk if possible.
bool postMessage(KTextEditor::Message *message) override
Post message to the Document and its Views.
void joinLines(uint first, uint last)
Unwrap a range of lines.
QString text(KTextEditor::Range range, bool blockwise=false) const override
Get the document content within the given range.
void transform(KTextEditor::ViewPrivate *view, KTextEditor::Cursor, TextTransform)
Handling uppercase, lowercase and capitalize for the view.
QString line(int line) const override
Get a single text line.
int lastLine() const
gets the last line number (lines() - 1)
KateBuffer & buffer()
Get access to buffer of this document.
int lines() const override
Get the count of lines of the document.
void bomSetByUser()
Set that the BOM marker is forced via the tool menu.
bool editStart()
Enclose editor actions with editStart() and editEnd() to group them.
KTextEditor::Cursor lastEditingPosition(EditingPositionKind nextOrPrevious, KTextEditor::Cursor)
Returns the next or previous position cursor in this document from the stack depending on the argumen...
KateDocumentConfig * config()
Configuration.
bool editWrapLine(int line, int col, bool newLine=true, bool *newLineAdded=nullptr, bool notify=true)
Wrap line.
void removeAllTrailingSpaces()
This function doesn't check for config and is available for use all the time via an action.
Kate::TextLine kateTextLine(int i)
Same as plainKateTextLine(), except that it is made sure the line is highlighted.
bool editEnd()
End a editor operation.
int findTouchedLine(int startLine, bool down)
Find the next modified/saved line, starting at startLine.
int lineLength(int line) const override
Get the length of a given line in characters.
KTextEditor::Range wordRangeAt(KTextEditor::Cursor cursor) const override
Get the text range for the word located under the text position cursor.
void removeView(KTextEditor::View *)
removes the view from the list of views.
bool wrapParagraph(int first, int last)
Wrap lines touched by the selection with respect of existing paragraphs.
Editing transaction support.
Definition document.h:574
A KParts derived class representing a text document.
Definition document.h:284
void reloaded(KTextEditor::Document *document)
Emitted after the current document was reloaded.
Range documentRange() const
A Range which encompasses the whole document.
Definition document.h:785
void highlightingModeChanged(KTextEditor::Document *document)
Warn anyone listening that the current document's highlighting mode has changed.
void aboutToReload(KTextEditor::Document *document)
Warn anyone listening that the current document is about to reload.
void copyToClipboard(const QString &text, const QString &fileName)
Copy text to clipboard an remember it in the history.
void deregisterView(KTextEditor::ViewPrivate *view)
unregister view at the factory
QTextToSpeech * speechEngine(KTextEditor::ViewPrivate *view)
text to speech engine to be use by the view actions, constructed on demand.
void configDialog(QWidget *parent) override
Configuration management.
void registerView(KTextEditor::ViewPrivate *view)
register view at the factory this allows us to loop over all views for example on config changes
static KTextEditor::EditorPrivate * self()
Kate Part Internal stuff ;)
A source of inline notes for a document.
void inlineNotesReset()
The provider should emit the signal inlineNotesReset() when almost all inline notes changed.
void inlineNotesChanged(int line)
The provider should emit the signal inlineNotesChanged() whenever one or more InlineNotes on the line...
virtual QList< int > inlineNotes(int line) const =0
Get list of inline notes for given line.
An object representing lines from a start line to an end line.
Definition linerange.h:41
constexpr int start() const noexcept
Get the start line of this line range.
Definition linerange.h:105
static constexpr LineRange invalid() noexcept
Returns an invalid line range.
Definition linerange.h:73
constexpr bool isValid() const noexcept
Validity check.
Definition linerange.h:65
constexpr int end() const noexcept
Get the end line of this line range.
Definition linerange.h:115
This class allows the application that embeds the KTextEditor component to allow it to access parts o...
Definition mainwindow.h:47
This class holds a Message to display in Views.
Definition message.h:94
@ BelowView
show message below view.
Definition message.h:121
@ AboveView
show message above view.
Definition message.h:119
@ TopInView
show message as view overlay in the top right corner.
Definition message.h:123
@ BottomInView
show message as view overlay in the bottom right corner.
Definition message.h:125
void setAutoHide(int delay=0)
Set the auto hide time to delay milliseconds.
void setAutoHideMode(KTextEditor::Message::AutoHideMode mode)
Sets the auto hide mode to mode.
MessagePosition position() const
Returns the message position of this message.
@ Information
information message type
Definition message.h:108
@ Immediate
auto-hide is triggered as soon as the message is shown
Definition message.h:136
void setPosition(MessagePosition position)
Sets the position of the message to position.
@ StayOnInsert
stay on insert
@ ExpandRight
Expand to encapsulate new characters to the right of the range.
@ ExpandLeft
Expand to encapsulate new characters to the left of the range.
An object representing a section of text, from one Cursor to another.
constexpr LineRange toLineRange() const noexcept
Convert this Range to a LineRange.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
void setEnd(Cursor end) noexcept
Set the end cursor to end.
constexpr bool isEmpty() const noexcept
Returns true if this range contains no characters, ie.
void setRange(Range range) noexcept
Set the start and end cursors to range.start() and range.end() respectively.
constexpr bool onSingleLine() const noexcept
Check whether this range is wholly contained within one line, ie.
static constexpr Range invalid() noexcept
Returns an invalid range.
constexpr bool isValid() const noexcept
Validity check.
constexpr bool containsLine(int line) const noexcept
Returns true if this range wholly encompasses line.
constexpr bool contains(Range range) const noexcept
Check whether the this range wholly encompasses range.
void setStart(Cursor start) noexcept
Set the start cursor to start.
Class to provide text hints for a View.
A text widget with KXMLGUIClient that represents a Document.
Definition view.h:244
ViewMode
Possible view modes These correspond to various modes the text editor might be in.
Definition view.h:295
InputMode
Possible input modes.
Definition view.h:286
@ ViInputMode
Vi mode.
Definition view.h:288
@ NormalInputMode
Normal Mode.
Definition view.h:287
void cursorPositionChanged(KTextEditor::View *view, KTextEditor::Cursor newPosition)
This signal is emitted whenever the view's cursor position changed.
void selectionChanged(KTextEditor::View *view)
This signal is emitted whenever the view's selection changes.
KXMLGUIClient * parentClient() const
KXMLGUIFactory * factory() const
virtual void setComponentName(const QString &componentName, const QString &componentDisplayName)
void removeClient(KXMLGUIClient *client)
KTextEditor::Range computeFoldingRangeForStartLine(int startLine)
For a given line, compute the folding range that starts there to be used to fold e....
This is the code completion's main widget, and also contains the core interface logic.
Internal data container for KTextEditor::InlineNote interface.
Class to layout KTextEditor::Messages in KateView.
This class implements a message widget based on KMessageWidget.
@ Header
Message positioned at the top of the view.
@ Footer
Message positioned at the bottom of the view.
void startAutoHideTimer()
Start autoHide timer if requested.
Handles all of the work of rendering the text (used for the views and printing)
Tools > Scripts menu This menu is filled with the command line scripts exported via the scripting sup...
Inserts a template and offers advanced snippet features, like navigation and mirroring.
This action provides a list of available indenters and gets plugged into the KTextEditor::ViewPrivate...
QList< TextRange * > rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const
Return the ranges which affect the given line.
Class representing a 'clever' text cursor.
@ Folded
Range is folded away.
Class representing a single text line.
const QString & text() const
Accessor to the text contained in this line.
const QList< Attribute > & attributesList() const
Accessor to attributes.
QString string(int column, int length) const
Returns the substring with length beginning at the given column.
int length() const
Returns the line's length.
QChar at(int column) const
Returns the character at the given column.
int firstChar() const
Returns the position of the first non-whitespace character.
Class representing a 'clever' text range.
Q_SCRIPTABLE Q_NOREPLY void start()
Q_SCRIPTABLE bool focusOut(int ms=-1)
Q_SCRIPTABLE bool focusIn(int ms=-1)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT QString displayName(Akonadi::ETMCalendar *calendar, const Akonadi::Collection &collection)
void update(Part *part, const QByteArray &data, qint64 dataSize)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
KIOCORE_EXPORT CopyJob * copy(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
KIOWIDGETS_EXPORT PasteJob * paste(const QMimeData *mimeData, const QUrl &destDir, JobFlags flags=DefaultFlags)
QWidget * window(QObject *job)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QAction * selectAll(const QObject *recvr, const char *slot, QObject *parent)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & beginningOfLine()
const QList< QKeySequence > & begin()
const QList< QKeySequence > & reload()
const QList< QKeySequence > & zoomIn()
const QList< QKeySequence > & zoomOut()
const QList< QKeySequence > & next()
const QList< QKeySequence > & deleteWordBack()
const QList< QKeySequence > & end()
const QList< QKeySequence > & backwardWord()
const QList< QKeySequence > & endOfLine()
const QList< QKeySequence > & forwardWord()
const QList< QKeySequence > & shortcut(StandardShortcut id)
const QList< QKeySequence > & deleteWordForward()
const QList< QKeySequence > & prior()
const QList< QKeySequence > & pasteSelection()
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
@ Default
Default settings.
Definition document.h:48
@ Regex
Treats the pattern as a regular expression.
Definition document.h:51
void setChecked(bool)
QVariant data() const const
void setEnabled(bool)
void setIcon(const QIcon &icon)
void setText(const QString &text)
void triggered(bool checked)
void setWhatsThis(const QString &what)
bool isLetterOrNumber(char32_t ucs4)
bool isSpace(char32_t ucs4)
void setText(const QString &text, Mode mode)
bool supportsSelection() const const
QString text(Mode mode) const const
QColor fromRgba(QRgb rgba)
Type type() const const
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
virtual void addItem(QLayoutItem *item) override
void addWidget(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment)
void setColumnStretch(int column, int stretch)
void setRowStretch(int row, int stretch)
virtual void setSpacing(int spacing) override
QClipboard * clipboard()
QIcon fromTheme(const QString &name)
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
void setContentsMargins(const QMargins &margins)
void append(QList< T > &&value)
iterator begin()
void clear()
const T & constFirst() const const
bool empty() const const
iterator end()
T & first()
reference front()
bool isEmpty() const const
void push_back(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
void aboutToHide()
void aboutToShow()
QAction * addSeparator()
bool disconnect(const QMetaObject::Connection &connection)
QString escape(QStringView str)
iterator end()
iterator erase(const_iterator pos)
iterator find(const T &value)
iterator insert(const T &value)
const QChar at(qsizetype position) const const
bool isEmpty() const const
bool isRightToLeft() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & prepend(QChar ch)
void reserve(qsizetype size)
PM_DefaultFrameWidth
SH_ScrollView_FrameOnlyAroundContents
void initFrom(const QWidget *widget)
QueuedConnection
StrongFocus
LeftToRight
WidgetWithChildrenShortcut
QTextStream & bom(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
PreferLocalFile
bool canConvert() const const
bool toBool() const const
int toInt(bool *ok) const const
QString toString() const const
int userType() const const
T value() const const
void append(T &&t)
iterator begin()
bool empty() const const
iterator end()
iterator erase(const_iterator begin, const_iterator end)
void push_back(T &&t)
qsizetype size() const const
virtual bool event(QEvent *event) override
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:56:21 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.