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 <KStandardAction>
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(i: 0);
92 return doc->isComment(line: 0, column: 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(sender: this, signal: &KTextEditor::ViewPrivate::delayedUpdateOfView, context: this, slot: &KTextEditor::ViewPrivate::slotDelayedUpdateOfView, type: Qt::QueuedConnection);
140
141 m_delayedUpdateTimer.setSingleShot(true);
142 m_delayedUpdateTimer.setInterval(0);
143 connect(sender: &m_delayedUpdateTimer, signal: &QTimer::timeout, context: this, slot: &KTextEditor::ViewPrivate::delayedUpdateOfView);
144
145 KXMLGUIClient::setComponentName(componentName: KTextEditor::EditorPrivate::self()->aboutData().componentName(),
146 componentDisplayName: KTextEditor::EditorPrivate::self()->aboutData().displayName());
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
154 KTextEditor::EditorPrivate::self()->registerView(view: this);
155
156 // try to let the main window, if any, create a view bar for this view
157 QWidget *bottomBarParent = m_mainWindow->createViewBar(view: 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(filterObj: m_viewInternal);
168
169 // add KateMessageWidget for KTE::MessageInterface immediately above view
170 m_messageWidgets[KTextEditor::Message::AboveView] = new KateMessageWidget(this);
171 m_messageWidgets[KTextEditor::Message::AboveView]->setPosition(KateMessageWidget::Position::Header);
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);
176 m_messageWidgets[KTextEditor::Message::BelowView]->setPosition(KateMessageWidget::Position::Footer);
177 m_messageWidgets[KTextEditor::Message::BelowView]->hide();
178
179 // add bottom viewbar...
180 if (bottomBarParent) {
181 m_mainWindow->addWidgetToViewBar(view: this, bar: m_bottomViewBar);
182 }
183
184 // add layout for floating message widgets to KateViewInternal
185 m_notificationLayout = new KateMessageLayout(m_viewInternal);
186 m_notificationLayout->setContentsMargins(left: 20, top: 20, right: 20, bottom: 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(w: m_viewInternal, enable: true);
217
218 for (auto messageWidget : m_messageWidgets) {
219 if (messageWidget) {
220 // user interaction (scrolling) starts notification auto-hide timer
221 connect(sender: this, signal: &KTextEditor::ViewPrivate::displayRangeChanged, context: messageWidget, slot: &KateMessageWidget::startAutoHideTimer);
222
223 // user interaction (cursor navigation) starts notification auto-hide timer
224 connect(sender: this, signal: &KTextEditor::ViewPrivate::cursorPositionChanged, context: messageWidget, slot: &KateMessageWidget::startAutoHideTimer);
225 }
226 }
227
228 // folding restoration on reload
229 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::aboutToReload, context: this, slot: &KTextEditor::ViewPrivate::saveFoldingState);
230 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::reloaded, context: this, slot: &KTextEditor::ViewPrivate::applyFoldingState);
231
232 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::reloaded, context: this, slot: &KTextEditor::ViewPrivate::slotDocumentReloaded);
233 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::aboutToReload, context: this, slot: &KTextEditor::ViewPrivate::slotDocumentAboutToReload);
234
235 // update highlights on scrolling and co
236 connect(sender: this, signal: &KTextEditor::ViewPrivate::displayRangeChanged, context: this, slot: &KTextEditor::ViewPrivate::createHighlights);
237
238 // clear highlights on reload
239 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::aboutToReload, context: this, slot: &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);
253 KTextEditor::EditorPrivate::self()->deregisterView(view: this);
254
255 delete m_completionWidget;
256
257 // remove from xmlgui factory, to be safe
258 if (factory()) {
259 factory()->removeClient(client: 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(view: 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(barWidget: m_statusBar);
279 delete m_statusBar;
280 m_statusBar = nullptr;
281 Q_EMIT statusBarEnabledChanged(view: this, enabled: false);
282 return;
283 }
284
285 // else: create it
286 m_statusBar = new KateStatusBar(this);
287 bottomViewBar()->addPermanentBarWidget(barWidget: m_statusBar);
288 Q_EMIT statusBarEnabledChanged(view: this, enabled: 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
306 QStyleOptionFrame opt;
307 opt.initFrom(w: this);
308 opt.frameShape = QFrame::StyledPanel;
309 opt.state |= QStyle::State_Sunken;
310 const int margin = style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth, option: &opt, widget: this);
311 m_topSpacer->changeSize(w: 0, h: margin, hData: QSizePolicy::Minimum, vData: QSizePolicy::Fixed);
312 m_leftSpacer->changeSize(w: margin, h: 0, hData: QSizePolicy::Fixed, vData: QSizePolicy::Minimum);
313 m_rightSpacer->changeSize(w: margin, h: 0, hData: QSizePolicy::Fixed, vData: QSizePolicy::Minimum);
314 m_bottomSpacer->changeSize(w: 0, h: margin, hData: QSizePolicy::Minimum, vData: QSizePolicy::Fixed);
315
316 // define layout
317 QGridLayout *layout = new QGridLayout(this);
318 layout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
319 layout->setSpacing(0);
320
321 const bool frameAroundContents = style()->styleHint(stylehint: QStyle::SH_ScrollView_FrameOnlyAroundContents, opt: &opt, widget: this);
322 if (frameAroundContents) {
323 // top message widget
324 layout->addWidget(m_messageWidgets[KTextEditor::Message::AboveView], row: 0, column: 0, rowSpan: 1, columnSpan: 5);
325
326 // top spacer
327 layout->addItem(item: m_topSpacer, row: 1, column: 0, rowSpan: 1, columnSpan: 4);
328
329 // left spacer
330 layout->addItem(item: m_leftSpacer, row: 2, column: 0, rowSpan: 1, columnSpan: 1);
331
332 // left border
333 layout->addWidget(m_viewInternal->m_leftBorder, row: 2, column: 1, rowSpan: 1, columnSpan: 1);
334
335 // view
336 layout->addWidget(m_viewInternal, row: 2, column: 2, rowSpan: 1, columnSpan: 1);
337
338 // right spacer
339 layout->addItem(item: m_rightSpacer, row: 2, column: 3, rowSpan: 1, columnSpan: 1);
340
341 // bottom spacer
342 layout->addItem(item: m_bottomSpacer, row: 3, column: 0, rowSpan: 1, columnSpan: 4);
343
344 // vertical scrollbar
345 layout->addWidget(m_viewInternal->m_lineScroll, row: 1, column: 4, rowSpan: 3, columnSpan: 1);
346
347 // horizontal scrollbar
348 layout->addWidget(m_viewInternal->m_columnScroll, row: 4, column: 0, rowSpan: 1, columnSpan: 4);
349
350 // dummy
351 layout->addWidget(m_viewInternal->m_dummy, row: 4, column: 4, rowSpan: 1, columnSpan: 1);
352
353 // bottom message
354 layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], row: 5, column: 0, rowSpan: 1, columnSpan: 5);
355
356 // bottom viewbar
357 if (m_bottomViewBar->parentWidget() == this) {
358 layout->addWidget(m_bottomViewBar, row: 6, column: 0, rowSpan: 1, columnSpan: 5);
359 }
360
361 // stretch
362 layout->setColumnStretch(column: 2, stretch: 1);
363 layout->setRowStretch(row: 2, stretch: 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], row: 0, column: 0, rowSpan: 1, columnSpan: 5);
375
376 // top spacer
377 layout->addItem(item: m_topSpacer, row: 1, column: 0, rowSpan: 1, columnSpan: 5);
378
379 // left spacer
380 layout->addItem(item: m_leftSpacer, row: 2, column: 0, rowSpan: 1, columnSpan: 1);
381
382 // left border
383 layout->addWidget(m_viewInternal->m_leftBorder, row: 2, column: 1, rowSpan: 1, columnSpan: 1);
384
385 // view
386 layout->addWidget(m_viewInternal, row: 2, column: 2, rowSpan: 1, columnSpan: 1);
387
388 // vertical scrollbar
389 layout->addWidget(m_viewInternal->m_lineScroll, row: 2, column: 3, rowSpan: 1, columnSpan: 1);
390
391 // right spacer
392 layout->addItem(item: m_rightSpacer, row: 2, column: 4, rowSpan: 1, columnSpan: 1);
393
394 // horizontal scrollbar
395 layout->addWidget(m_viewInternal->m_columnScroll, row: 3, column: 1, rowSpan: 1, columnSpan: 2);
396
397 // dummy
398 layout->addWidget(m_viewInternal->m_dummy, row: 3, column: 3, rowSpan: 1, columnSpan: 1);
399
400 // bottom spacer
401 layout->addItem(item: m_bottomSpacer, row: 4, column: 0, rowSpan: 1, columnSpan: 5);
402
403 // bottom message
404 layout->addWidget(m_messageWidgets[KTextEditor::Message::BelowView], row: 5, column: 0, rowSpan: 1, columnSpan: 5);
405
406 // bottom viewbar
407 if (m_bottomViewBar->parentWidget() == this) {
408 layout->addWidget(m_bottomViewBar, row: 6, column: 0, rowSpan: 1, columnSpan: 5);
409 }
410
411 // stretch
412 layout->setColumnStretch(column: 2, stretch: 1);
413 layout->setRowStretch(row: 2, stretch: 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(sender: m_doc, signal: &KTextEditor::DocumentPrivate::undoChanged, context: this, slot: &KTextEditor::ViewPrivate::slotUpdateUndo);
427 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::highlightingModeChanged, context: this, slot: &KTextEditor::ViewPrivate::slotHlChanged);
428 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::canceled, context: this, slot: &KTextEditor::ViewPrivate::slotSaveCanceled);
429 connect(sender: m_viewInternal, signal: &KateViewInternal::dropEventPass, context: this, slot: &KTextEditor::ViewPrivate::dropEventPass);
430
431 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::annotationModelChanged, context: m_viewInternal->m_leftBorder, slot: &KateIconBorder::annotationModelChanged);
432}
433
434void KTextEditor::ViewPrivate::goToPreviousEditingPosition()
435{
436 auto c = doc()->lastEditingPosition(nextOrPrevious: KTextEditor::DocumentPrivate::Previous, cursorPosition());
437 if (c.isValid()) {
438 setCursorPosition(c);
439 }
440}
441
442void KTextEditor::ViewPrivate::goToNextEditingPosition()
443{
444 auto c = doc()->lastEditingPosition(nextOrPrevious: 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(actionType: KStandardAction::Cut, receiver: this, SLOT(cut()));
457 a->setWhatsThis(i18n("Cut the selected text and move it to the clipboard"));
458
459 m_paste = a = ac->addAction(actionType: KStandardAction::Paste, receiver: this, SLOT(paste()));
460 a->setWhatsThis(i18n("Paste previously copied or cut clipboard contents"));
461
462 m_copy = a = ac->addAction(actionType: KStandardAction::Copy, receiver: this, SLOT(copy()));
463 a->setWhatsThis(i18n("Use this command to copy the currently selected text to the system clipboard."));
464
465 m_clipboardHistory = a = ac->addAction(QStringLiteral("clipboard_history_paste"), receiver: this, slot: [this] {
466 ClipboardHistoryDialog chd(mainWindow()->window(), this);
467 chd.openDialog(clipboardHistory: KTextEditor::EditorPrivate::self()->clipboardHistory());
468 });
469 a->setText(i18n("Clipboard &History Paste"));
470 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::ALT | Qt::Key_V));
471
472 if (QApplication::clipboard()->supportsSelection()) {
473 m_pasteSelection = a = ac->addAction(QStringLiteral("edit_paste_selection"), receiver: this, SLOT(pasteSelection()));
474 a->setText(i18n("Paste Selection"));
475 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::pasteSelection());
476 a->setWhatsThis(i18n("Paste previously mouse selection contents"));
477 }
478
479 m_swapWithClipboard = a = ac->addAction(QStringLiteral("edit_swap_with_clipboard"), receiver: this, SLOT(swapWithClipboard()));
480 a->setText(i18n("Swap with Clipboard Contents"));
481 a->setWhatsThis(i18n("Swap the selected text with the clipboard contents"));
482
483 m_screenshotSelection = a = ac->addAction(QStringLiteral("text_screenshot_selection"), receiver: this, slot: &KTextEditor::ViewPrivate::screenshot);
484 a->setText(i18n("Take Screenshot of Selection"));
485
486 if (!doc()->readOnly()) {
487 a = ac->addAction(actionType: KStandardAction::Save, receiver: m_doc, SLOT(documentSave()));
488 a->setWhatsThis(i18n("Save the current document"));
489
490 a = m_editUndo = ac->addAction(actionType: KStandardAction::Undo, receiver: m_doc, SLOT(undo()));
491 a->setWhatsThis(i18n("Revert the most recent editing actions"));
492
493 a = m_editRedo = ac->addAction(actionType: KStandardAction::Redo, receiver: m_doc, SLOT(redo()));
494 a->setWhatsThis(i18n("Revert the most recent undo operation"));
495
496 // Tools > Scripts
497 // stored inside scoped pointer to ensure we destruct it early enough as it does internal cleanups of other child objects
498 m_scriptActionMenu.reset(p: new KateScriptActionMenu(this, i18n("&Scripts")));
499 ac->addAction(QStringLiteral("tools_scripts"), action: m_scriptActionMenu.get());
500
501 a = ac->addAction(QStringLiteral("tools_apply_wordwrap"));
502 a->setText(i18n("Apply &Word Wrap"));
503 a->setWhatsThis(
504 i18n("Use this to wrap the current line, or to reformat the selected lines as paragraph, "
505 "to fit the 'Wrap words at' setting in the configuration dialog.<br /><br />"
506 "This is a static word wrap, meaning the document is changed."));
507 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::applyWordWrap);
508
509 a = ac->addAction(QStringLiteral("tools_cleanIndent"));
510 a->setText(i18n("&Clean Indentation"));
511 a->setWhatsThis(
512 i18n("Use this to clean the indentation of a selected block of text (only tabs/only spaces).<br /><br />"
513 "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
514 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::cleanIndent);
515
516 a = ac->addAction(QStringLiteral("tools_formatIndent"));
517 a->setText(i18n("&Format Indentation"));
518 a->setWhatsThis(i18n("Use this to auto indent the current line or block of text to its proper indent level."));
519 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::formatIndent);
520
521 a = ac->addAction(QStringLiteral("tools_alignOn"));
522 a->setText(i18n("&Align On..."));
523 a->setWhatsThis(
524 i18n("This command aligns lines in the selected block or whole document on the column given by a regular expression "
525 "that you will be prompted for.<br /><br />"
526 "If you give an empty pattern it will align on the first non-blank character by default.<br />"
527 "If the pattern has a capture it will indent on the captured match.<br /><br />"
528 "<i>Examples</i>:<br />"
529 "With '-' it will insert spaces before the first '-' of each lines to align them all on the same column.<br />"
530 "With ':\\s+(.)' it will insert spaces before the first non-blank character that occurs after a colon to align "
531 "them all on the same column."));
532 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::alignOn);
533
534 a = ac->addAction(QStringLiteral("tools_comment"));
535 a->setText(i18n("C&omment"));
536 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_D));
537 a->setWhatsThis(
538 i18n("This command comments out the current line or a selected block of text.<br /><br />"
539 "The characters for single/multiple line comments are defined within the language's highlighting."));
540 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::comment);
541
542 a = ac->addAction(QStringLiteral("Previous Editing Line"));
543 a->setText(i18n("Go to Previous Editing Location"));
544 a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
545 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_E));
546 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::goToPreviousEditingPosition);
547
548 a = ac->addAction(QStringLiteral("Next Editing Line"));
549 a->setText(i18n("Go to Next Editing Location"));
550 a->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
551 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_E));
552 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::goToNextEditingPosition);
553
554 a = ac->addAction(QStringLiteral("tools_uncomment"));
555 a->setText(i18n("Unco&mment"));
556 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D));
557 a->setWhatsThis(
558 i18n("This command removes comments from the current line or a selected block of text.<br /><br />"
559 "The characters for single/multiple line comments are defined within the language's highlighting."));
560 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::uncomment);
561
562 a = ac->addAction(QStringLiteral("tools_toggle_comment"));
563 a->setText(i18n("Toggle Comment"));
564 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_Slash));
565 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleComment);
566
567 a = m_toggleWriteLock = new KToggleAction(i18n("&Read Only Mode"), this);
568 a->setWhatsThis(i18n("Lock/unlock the document for writing"));
569 a->setChecked(!doc()->isReadWrite());
570 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleWriteLock);
571 ac->addAction(QStringLiteral("tools_toggle_write_lock"), action: a);
572
573 a = m_forceRTLDirection = new KToggleAction(i18n("&Force RTL Direction"), this);
574 a->setWhatsThis(i18n("Force RTL Text Direction"));
575 connect(sender: a, signal: &QAction::triggered, context: this, slot: [this](bool checked) {
576 m_forceRTL = checked;
577 tagAll();
578 updateView(changed: true);
579 });
580 ac->addAction(QStringLiteral("force_rtl_direction"), action: a);
581
582 a = ac->addAction(QStringLiteral("tools_uppercase"));
583 a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-uppercase")));
584 a->setText(i18n("Uppercase"));
585 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_U));
586 a->setWhatsThis(
587 i18n("Convert the selection to uppercase, or the character to the "
588 "right of the cursor if no text is selected."));
589 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::uppercase);
590
591 a = ac->addAction(QStringLiteral("tools_lowercase"));
592 a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-lowercase")));
593 a->setText(i18n("Lowercase"));
594 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_U));
595 a->setWhatsThis(
596 i18n("Convert the selection to lowercase, or the character to the "
597 "right of the cursor if no text is selected."));
598 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::lowercase);
599
600 a = ac->addAction(QStringLiteral("tools_capitalize"));
601 a->setIcon(QIcon::fromTheme(QStringLiteral("format-text-capitalize")));
602 a->setText(i18n("Capitalize"));
603 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_U));
604 a->setWhatsThis(
605 i18n("Capitalize the selection, or the word under the "
606 "cursor if no text is selected."));
607 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::capitalize);
608
609 a = ac->addAction(QStringLiteral("tools_join_lines"));
610 a->setText(i18n("Join Lines"));
611 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_J));
612 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::joinLines);
613
614 a = ac->addAction(QStringLiteral("tools_invoke_code_completion"));
615 a->setText(i18n("Invoke Code Completion"));
616 a->setWhatsThis(i18n("Manually invoke command completion, usually by using a shortcut bound to this action."));
617 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_Space));
618 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::userInvokedCompletion);
619
620 a = ac->addAction(QStringLiteral("remove_trailing_spaces"));
621 a->setText(i18n("Remove Trailing Spaces"));
622 connect(sender: a, signal: &QAction::triggered, context: this, slot: [this] {
623 doc()->removeAllTrailingSpaces();
624 });
625 } else {
626 for (auto *action : {m_cut, m_paste, m_clipboardHistory, m_swapWithClipboard}) {
627 action->setEnabled(false);
628 }
629
630 if (m_pasteSelection) {
631 m_pasteSelection->setEnabled(false);
632 }
633
634 m_editUndo = nullptr;
635 m_editRedo = nullptr;
636 }
637
638 a = ac->addAction(actionType: KStandardAction::Print, receiver: this, SLOT(print()));
639 a->setWhatsThis(i18n("Print the current document."));
640
641 a = ac->addAction(actionType: KStandardAction::PrintPreview, receiver: this, SLOT(printPreview()));
642 a->setWhatsThis(i18n("Show print preview of current document"));
643
644 a = ac->addAction(QStringLiteral("file_reload"));
645 a->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
646 a->setText(i18n("Reloa&d"));
647 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::reload());
648 a->setWhatsThis(i18n("Reload the current document from disk."));
649 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::reloadFile);
650
651 a = ac->addAction(actionType: KStandardAction::SaveAs, receiver: m_doc, SLOT(documentSaveAs()));
652 a->setWhatsThis(i18n("Save the current document to disk, with a name of your choice."));
653
654 a = new KateViewEncodingAction(m_doc, this, i18n("Save As with Encoding..."), this, true /* special mode for save as */);
655 a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
656 ac->addAction(QStringLiteral("file_save_as_with_encoding"), action: a);
657
658 a = ac->addAction(QStringLiteral("file_save_copy_as"));
659 a->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
660 a->setText(i18n("Save &Copy As..."));
661 a->setWhatsThis(i18n("Save a copy of the current document to disk."));
662 connect(sender: a, signal: &QAction::triggered, context: m_doc, slot: &KTextEditor::DocumentPrivate::documentSaveCopyAs);
663
664 a = ac->addAction(actionType: KStandardAction::GotoLine, receiver: this, SLOT(gotoLine()));
665 a->setWhatsThis(i18n("This command opens a dialog and lets you choose a line that you want the cursor to move to."));
666
667 a = ac->addAction(QStringLiteral("modified_line_up"));
668 a->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
669 a->setText(i18n("Go to Previous Modified Line"));
670 a->setWhatsThis(i18n("Move upwards to the previous modified line."));
671 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toPrevModifiedLine);
672
673 a = ac->addAction(QStringLiteral("modified_line_down"));
674 a->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
675 a->setText(i18n("Go to Next Modified Line"));
676 a->setWhatsThis(i18n("Move downwards to the next modified line."));
677 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toNextModifiedLine);
678
679 a = ac->addAction(QStringLiteral("set_confdlg"));
680 a->setText(i18n("&Configure Editor..."));
681 a->setIcon(QIcon::fromTheme(QStringLiteral("preferences-other")));
682 a->setWhatsThis(i18n("Configure various aspects of this editor."));
683 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::slotConfigDialog);
684
685 m_modeAction = new KateModeMenu(i18n("&Mode"), this);
686 ac->addAction(QStringLiteral("tools_mode"), action: m_modeAction);
687 m_modeAction->setWhatsThis(i18n(
688 "Here you can choose which mode should be used for the current document. This will influence the highlighting and folding being used, for example."));
689 m_modeAction->updateMenu(doc: m_doc);
690
691 KateHighlightingMenu *menu = new KateHighlightingMenu(i18n("&Highlighting"), this);
692 ac->addAction(QStringLiteral("tools_highlighting"), action: menu);
693 menu->setWhatsThis(i18n("Here you can choose how the current document should be highlighted."));
694 menu->updateMenu(doc: m_doc);
695
696 KateViewSchemaAction *schemaMenu = new KateViewSchemaAction(i18n("&Editor Color Theme"), this);
697 schemaMenu->setIcon(QIcon::fromTheme(QStringLiteral("kcolorchooser")));
698 ac->addAction(QStringLiteral("view_schemas"), action: schemaMenu);
699 schemaMenu->updateMenu(view: this);
700
701 // indentation menu
702 KateViewIndentationAction *indentMenu = new KateViewIndentationAction(m_doc, i18n("&Indentation"), this);
703 ac->addAction(QStringLiteral("tools_indentation"), action: indentMenu);
704
705 m_selectAll = ac->addAction(actionType: KStandardAction::SelectAll);
706 connect(sender: m_selectAll, signal: &QAction::triggered, context: this, slot: [this] {
707 selectAll();
708 qApp->clipboard()->setText(selectionText(), mode: QClipboard::Selection);
709 });
710 a->setWhatsThis(i18n("Select the entire text of the current document."));
711
712 m_deSelect = a = ac->addAction(actionType: KStandardAction::Deselect, receiver: this, SLOT(clearSelection()));
713 a->setWhatsThis(i18n("If you have selected something within the current document, this will no longer be selected."));
714
715 a = ac->addAction(QStringLiteral("view_inc_font_sizes"));
716 a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
717 a->setText(i18n("Enlarge Font"));
718 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::zoomIn());
719 a->setWhatsThis(i18n("This increases the display font size."));
720 connect(sender: a, signal: &QAction::triggered, context: m_viewInternal, slot: [this]() {
721 m_viewInternal->slotIncFontSizes();
722 });
723
724 a = ac->addAction(QStringLiteral("view_dec_font_sizes"));
725 a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
726 a->setText(i18n("Shrink Font"));
727 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::zoomOut());
728 a->setWhatsThis(i18n("This decreases the display font size."));
729 connect(sender: a, signal: &QAction::triggered, context: m_viewInternal, slot: [this]() {
730 m_viewInternal->slotDecFontSizes();
731 });
732
733 a = ac->addAction(QStringLiteral("view_reset_font_sizes"));
734 a->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
735 a->setText(i18n("Reset Font Size"));
736 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::shortcut(id: KStandardShortcut::ActualSize));
737 a->setWhatsThis(i18n("This resets the display font size."));
738 connect(sender: a, signal: &QAction::triggered, context: m_viewInternal, slot: &KateViewInternal::slotResetFontSizes);
739
740 a = m_toggleBlockSelection = new KToggleAction(i18n("Bl&ock Selection Mode"), this);
741 ac->addAction(QStringLiteral("set_verticalSelect"), action: a);
742 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_B));
743 a->setWhatsThis(i18n("This command allows switching between the normal (line based) selection mode and the block selection mode."));
744 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleBlockSelection);
745
746 a = ac->addAction(QStringLiteral("switch_next_input_mode"));
747 a->setText(i18n("Switch to Next Input Mode"));
748 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_V));
749 a->setWhatsThis(i18n("Switch to the next input mode."));
750 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::cycleInputMode);
751
752 a = m_toggleInsert = new KToggleAction(i18n("Overwr&ite Mode"), this);
753 ac->addAction(QStringLiteral("set_insert"), action: a);
754 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::Key_Insert));
755 a->setWhatsThis(i18n("Choose whether you want the text you type to be inserted or to overwrite existing text."));
756 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleInsert);
757
758 KToggleAction *toggleAction;
759 a = m_toggleDynWrap = toggleAction = new KToggleAction(i18n("&Dynamic Word Wrap"), this);
760 a->setIcon(QIcon::fromTheme(QStringLiteral("text-wrap")));
761 ac->addAction(QStringLiteral("view_dynamic_word_wrap"), action: a);
762 a->setWhatsThis(
763 i18n("If this option is checked, the text lines will be wrapped at the view border on the screen.<br /><br />"
764 "This is only a view option, meaning the document will not changed."));
765 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleDynWordWrap);
766
767 a = m_setDynWrapIndicators = new KSelectAction(i18n("Dynamic Word Wrap Indicators"), this);
768 ac->addAction(QStringLiteral("dynamic_word_wrap_indicators"), action: a);
769 a->setWhatsThis(i18n("Choose when the Dynamic Word Wrap Indicators should be displayed"));
770 connect(sender: m_setDynWrapIndicators, signal: &KSelectAction::indexTriggered, context: this, slot: &KTextEditor::ViewPrivate::setDynWrapIndicators);
771 const QStringList list2{i18n("&Off"), i18n("Follow &Line Numbers"), i18n("&Always On")};
772 m_setDynWrapIndicators->setItems(list2);
773 m_setDynWrapIndicators->setEnabled(m_toggleDynWrap->isChecked()); // only synced on real change, later
774
775 a = toggleAction = new KToggleAction(i18n("Static Word Wrap"), this);
776 ac->addAction(QStringLiteral("view_static_word_wrap"), action: a);
777 a->setWhatsThis(i18n("If this option is checked, the text lines will be wrapped at the column defined in the editing properties."));
778 connect(sender: a, signal: &KToggleAction::triggered, slot: [this] {
779 if (m_doc) {
780 m_doc->setWordWrap(!m_doc->wordWrap());
781 }
782 });
783
784 a = toggleAction = m_toggleWWMarker = new KToggleAction(i18n("Show Static &Word Wrap Marker"), this);
785 ac->addAction(QStringLiteral("view_word_wrap_marker"), action: a);
786 a->setWhatsThis(
787 i18n("Show/hide the Word Wrap Marker, a vertical line drawn at the word "
788 "wrap column as defined in the editing properties"));
789 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleWWMarker);
790
791 a = toggleAction = m_toggleFoldingMarkers = new KToggleAction(i18n("Show Folding &Markers"), this);
792 ac->addAction(QStringLiteral("view_folding_markers"), action: a);
793 a->setWhatsThis(i18n("You can choose if the codefolding marks should be shown, if codefolding is possible."));
794 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleFoldingMarkers);
795
796 a = m_toggleIconBar = toggleAction = new KToggleAction(i18n("Show &Icon Border"), this);
797 ac->addAction(QStringLiteral("view_border"), action: a);
798 a->setWhatsThis(i18n("Show/hide the icon border.<br /><br />The icon border shows bookmark symbols, for instance."));
799 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleIconBorder);
800
801 a = toggleAction = m_toggleLineNumbers = new KToggleAction(i18n("Show &Line Numbers"), this);
802 ac->addAction(QStringLiteral("view_line_numbers"), action: a);
803 a->setWhatsThis(i18n("Show/hide the line numbers on the left hand side of the view."));
804 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleLineNumbersOn);
805
806 a = m_toggleScrollBarMarks = toggleAction = new KToggleAction(i18n("Show Scroll&bar Marks"), this);
807 ac->addAction(QStringLiteral("view_scrollbar_marks"), action: a);
808 a->setWhatsThis(i18n("Show/hide the marks on the vertical scrollbar.<br /><br />The marks show bookmarks, for instance."));
809 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleScrollBarMarks);
810
811 a = m_toggleScrollBarMiniMap = toggleAction = new KToggleAction(i18n("Show Scrollbar Mini-Map"), this);
812 ac->addAction(QStringLiteral("view_scrollbar_minimap"), action: a);
813 a->setWhatsThis(i18n("Show/hide the mini-map on the vertical scrollbar.<br /><br />The mini-map shows an overview of the whole document."));
814 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleScrollBarMiniMap);
815
816 a = m_doc->autoReloadToggleAction();
817 ac->addAction(QStringLiteral("view_auto_reload"), action: a);
818
819 // a = m_toggleScrollBarMiniMapAll = toggleAction = new KToggleAction(i18n("Show the whole document in the Mini-Map"), this);
820 // ac->addAction(QLatin1String("view_scrollbar_minimap_all"), a);
821 // 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
822 // mini-map.")); connect(a, SIGNAL(triggered(bool)), SLOT(toggleScrollBarMiniMapAll())); connect(m_toggleScrollBarMiniMap, SIGNAL(triggered(bool)),
823 // m_toggleScrollBarMiniMapAll, SLOT(setEnabled(bool)));
824
825 a = m_toggleNPSpaces = new KToggleAction(i18n("Show Non-Printable Spaces"), this);
826 ac->addAction(QStringLiteral("view_non_printable_spaces"), action: a);
827 a->setWhatsThis(i18n("Show/hide bounding box around non-printable spaces"));
828 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleNPSpaces);
829
830 a = m_switchCmdLine = ac->addAction(QStringLiteral("switch_to_cmd_line"));
831 a->setText(i18n("Switch to Command Line"));
832 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::Key_F7));
833 a->setWhatsThis(i18n("Show/hide the command line on the bottom of the view."));
834 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::switchToCmdLine);
835
836 KActionMenu *am = new KActionMenu(i18n("Input Modes"), this);
837 m_inputModeActions = new QActionGroup(am);
838 ac->addAction(QStringLiteral("view_input_modes"), action: am);
839 auto switchInputModeAction = ac->action(QStringLiteral("switch_next_input_mode"));
840 am->addAction(action: switchInputModeAction);
841 am->addSeparator();
842 for (const auto &mode : m_viewInternal->m_inputModes) {
843 a = new QAction(mode->viewInputModeHuman(), m_inputModeActions);
844 am->addAction(action: a);
845 a->setWhatsThis(i18n("Activate/deactivate %1", mode->viewInputModeHuman()));
846 const InputMode im = mode->viewInputMode();
847 a->setData(static_cast<int>(im));
848 a->setCheckable(true);
849 if (im == m_config->inputMode()) {
850 a->setChecked(true);
851 }
852 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleInputMode);
853 }
854
855 a = m_setEndOfLine = new KSelectAction(i18n("&End of Line"), this);
856 ac->addAction(QStringLiteral("set_eol"), action: a);
857 a->setWhatsThis(i18n("Choose which line endings should be used, when you save the document"));
858 const QStringList list{i18nc("@item:inmenu End of Line", "&UNIX"),
859 i18nc("@item:inmenu End of Line", "&Windows/DOS"),
860 i18nc("@item:inmenu End of Line", "&Macintosh")};
861 m_setEndOfLine->setItems(list);
862 m_setEndOfLine->setCurrentItem(doc()->config()->eol());
863 connect(sender: m_setEndOfLine, signal: &KSelectAction::indexTriggered, context: this, slot: &KTextEditor::ViewPrivate::setEol);
864
865 a = m_addBom = new KToggleAction(i18n("Add &Byte Order Mark (BOM)"), this);
866 m_addBom->setChecked(doc()->config()->bom());
867 ac->addAction(QStringLiteral("add_bom"), action: a);
868 a->setWhatsThis(i18n("Enable/disable adding of byte order marks for UTF-8/UTF-16 encoded files while saving"));
869 connect(sender: m_addBom, signal: &KToggleAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::setAddBom);
870
871 // encoding menu
872 m_encodingAction = new KateViewEncodingAction(m_doc, this, i18n("E&ncoding"), this);
873 ac->addAction(QStringLiteral("set_encoding"), action: m_encodingAction);
874
875 a = ac->addAction(actionType: KStandardAction::Find, receiver: this, SLOT(find()));
876 a->setWhatsThis(i18n("Look up the first occurrence of a piece of text or regular expression."));
877 addAction(action: a);
878
879 a = ac->addAction(QStringLiteral("edit_find_selected"));
880 a->setText(i18n("Find Selected"));
881 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_H));
882 a->setWhatsThis(i18n("Finds next occurrence of selected text."));
883 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::findSelectedForwards);
884
885 a = ac->addAction(QStringLiteral("edit_find_selected_backwards"));
886 a->setText(i18n("Find Selected Backwards"));
887 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_H));
888 a->setWhatsThis(i18n("Finds previous occurrence of selected text."));
889 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::findSelectedBackwards);
890
891 a = ac->addAction(QStringLiteral("edit_find_multicursor_next_occurrence"));
892 a->setText(i18n("Find and Select Next Occurrence"));
893 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::Key_J));
894 a->setWhatsThis(i18n("Finds next occurrence of the word under cursor and add it to selection."));
895 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::findNextOccurunceAndSelect);
896
897 a = ac->addAction(QStringLiteral("edit_skip_multicursor_current_occurrence"));
898 a->setText(i18n("Mark Currently Selected Occurrence as Skipped"));
899 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::Key_K));
900 a->setWhatsThis(i18n("Marks the currently selected word as skipped."));
901 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::skipCurrentOccurunceSelection);
902
903 a = ac->addAction(QStringLiteral("edit_find_multicursor_all_occurrences"));
904 a->setText(i18n("Find and Select All Occurrences"));
905 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::SHIFT | Qt::CTRL | Qt::Key_J));
906 a->setWhatsThis(i18n("Finds all occurrences of the word under cursor and selects them."));
907 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::findAllOccuruncesAndSelect);
908
909 a = ac->addAction(actionType: KStandardAction::FindNext, receiver: this, SLOT(findNext()));
910 a->setWhatsThis(i18n("Look up the next occurrence of the search phrase."));
911 addAction(action: a);
912
913 a = ac->addAction(actionType: KStandardAction::FindPrev, QStringLiteral("edit_find_prev"), receiver: this, SLOT(findPrevious()));
914 a->setWhatsThis(i18n("Look up the previous occurrence of the search phrase."));
915 addAction(action: a);
916
917 a = ac->addAction(actionType: KStandardAction::Replace, receiver: this, SLOT(replace()));
918 a->setWhatsThis(i18n("Look up a piece of text or regular expression and replace the result with some given text."));
919
920 a = ac->addAction(QStringLiteral("edit_create_multi_cursor_from_sel"));
921 a->setText(i18n("Add Cursors to Line Ends"));
922 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_I));
923 a->setWhatsThis(i18n("Creates a cursor at the end of every line in selection."));
924 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::createMultiCursorsFromSelection);
925
926 a = ac->addAction(QStringLiteral("edit_create_multi_cursor_down"));
927 a->setText(i18n("Add Caret below Cursor"));
928 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_Down));
929 a->setWhatsThis(i18n("Adds a caret in the line below the current caret."));
930 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::addSecondaryCursorDown);
931
932 a = ac->addAction(QStringLiteral("edit_create_multi_cursor_up"));
933 a->setText(i18n("Add Caret above Cursor"));
934 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::CTRL | Qt::Key_Up));
935 a->setWhatsThis(i18n("Adds a caret in the line above the current caret."));
936 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::addSecondaryCursorUp);
937
938 a = ac->addAction(QStringLiteral("edit_toggle_camel_case_cursor"));
939 a->setText(i18n("Toggle Camel Case Cursor Movement"));
940 a->setWhatsThis(i18n("Toggle between normal word movement and camel case cursor movement."));
941 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleCamelCaseCursor);
942
943 a = ac->addAction(QStringLiteral("edit_remove_cursors_from_empty_lines"));
944 a->setText(i18n("Remove Cursors from Empty Lines"));
945 a->setWhatsThis(i18n("Remove cursors from empty lines"));
946 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::removeCursorsFromEmptyLines);
947
948 m_spell->createActions(ac);
949 m_toggleOnTheFlySpellCheck = new KToggleAction(i18n("Automatic Spell Checking"), this);
950 m_toggleOnTheFlySpellCheck->setWhatsThis(i18n("Enable/disable automatic spell checking"));
951 connect(sender: m_toggleOnTheFlySpellCheck, signal: &KToggleAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toggleOnTheFlySpellCheck);
952 ac->addAction(QStringLiteral("tools_toggle_automatic_spell_checking"), action: m_toggleOnTheFlySpellCheck);
953 ac->setDefaultShortcut(action: m_toggleOnTheFlySpellCheck, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_O));
954
955 a = ac->addAction(QStringLiteral("tools_change_dictionary"));
956 a->setText(i18n("Change Dictionary..."));
957 a->setWhatsThis(i18n("Change the dictionary that is used for spell checking."));
958 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::changeDictionary);
959
960 a = ac->addAction(QStringLiteral("tools_clear_dictionary_ranges"));
961 a->setText(i18n("Clear Dictionary Ranges"));
962 a->setEnabled(false);
963 a->setWhatsThis(i18n("Remove all the separate dictionary ranges that were set for spell checking."));
964 connect(sender: a, signal: &QAction::triggered, context: m_doc, slot: &KTextEditor::DocumentPrivate::clearDictionaryRanges);
965 connect(sender: m_doc, signal: &KTextEditor::DocumentPrivate::dictionaryRangesPresent, context: a, slot: &QAction::setEnabled);
966
967 m_copyHtmlAction = ac->addAction(QStringLiteral("edit_copy_html"), receiver: this, SLOT(exportHtmlToClipboard()));
968 m_copyHtmlAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
969 m_copyHtmlAction->setText(i18n("Copy as &HTML"));
970 m_copyHtmlAction->setWhatsThis(i18n("Use this command to copy the currently selected text as HTML to the system clipboard."));
971
972 a = ac->addAction(QStringLiteral("file_export_html"), receiver: this, SLOT(exportHtmlToFile()));
973 a->setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
974 a->setText(i18n("E&xport as HTML..."));
975 a->setWhatsThis(
976 i18n("This command allows you to export the current document"
977 " with all highlighting information into a HTML document."));
978
979 m_spellingMenu->createActions(ac);
980
981 m_bookmarks->createActions(ac);
982
983 slotSelectionChanged();
984
985 // Now setup the editing actions before adding the associated
986 // widget and setting the shortcut context
987 setupEditActions();
988 setupCodeFolding();
989 setupSpeechActions();
990
991 ac->addAssociatedWidget(widget: m_viewInternal);
992
993 const auto actions = ac->actions();
994 for (QAction *action : actions) {
995 action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
996 }
997
998 connect(sender: this, signal: &KTextEditor::ViewPrivate::selectionChanged, context: this, slot: &KTextEditor::ViewPrivate::slotSelectionChanged);
999}
1000
1001void KTextEditor::ViewPrivate::slotConfigDialog()
1002{
1003 // invoke config dialog, will auto-save configuration to katepartrc
1004 KTextEditor::EditorPrivate::self()->configDialog(parent: this);
1005}
1006
1007void KTextEditor::ViewPrivate::setupEditActions()
1008{
1009 // If you add an editing action to this
1010 // function make sure to include the line
1011 // m_editActions << a after creating the action
1012 KActionCollection *ac = actionCollection();
1013
1014 QAction *a = ac->addAction(QStringLiteral("word_left"));
1015 a->setText(i18n("Move Word Left"));
1016 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::backwardWord());
1017 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::wordLeft);
1018 m_editActions.push_back(x: a);
1019
1020 a = ac->addAction(QStringLiteral("select_char_left"));
1021 a->setText(i18n("Select Character Left"));
1022 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_Left));
1023 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftCursorLeft);
1024 m_editActions.push_back(x: a);
1025
1026 a = ac->addAction(QStringLiteral("select_word_left"));
1027 a->setText(i18n("Select Word Left"));
1028 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Left));
1029 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftWordLeft);
1030 m_editActions.push_back(x: a);
1031
1032 a = ac->addAction(QStringLiteral("word_right"));
1033 a->setText(i18n("Move Word Right"));
1034 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::forwardWord());
1035 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::wordRight);
1036 m_editActions.push_back(x: a);
1037
1038 a = ac->addAction(QStringLiteral("select_char_right"));
1039 a->setText(i18n("Select Character Right"));
1040 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_Right));
1041 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftCursorRight);
1042 m_editActions.push_back(x: a);
1043
1044 a = ac->addAction(QStringLiteral("select_word_right"));
1045 a->setText(i18n("Select Word Right"));
1046 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Right));
1047 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftWordRight);
1048 m_editActions.push_back(x: a);
1049
1050 a = ac->addAction(QStringLiteral("mark_selection"));
1051 a->setText(i18n("Start the Marked Selection"));
1052 a->setWhatsThis(i18n("Emulate the Emacs-like selection mode, where the beginning is marked and then the selection is continuously updated."));
1053 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::markSelection);
1054 m_editActions.push_back(x: a);
1055
1056 a = ac->addAction(QStringLiteral("beginning_of_line"));
1057 a->setText(i18n("Move to Beginning of Line"));
1058 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::beginningOfLine());
1059 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::home);
1060 m_editActions.push_back(x: a);
1061
1062 a = ac->addAction(QStringLiteral("beginning_of_document"));
1063 a->setText(i18n("Move to Beginning of Document"));
1064 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::begin());
1065 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::top);
1066 m_editActions.push_back(x: a);
1067
1068 a = ac->addAction(QStringLiteral("select_beginning_of_line"));
1069 a->setText(i18n("Select to Beginning of Line"));
1070 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_Home));
1071 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftHome);
1072 m_editActions.push_back(x: a);
1073
1074 a = ac->addAction(QStringLiteral("select_beginning_of_document"));
1075 a->setText(i18n("Select to Beginning of Document"));
1076 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_Home));
1077 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftTop);
1078 m_editActions.push_back(x: a);
1079
1080 a = ac->addAction(QStringLiteral("end_of_line"));
1081 a->setText(i18n("Move to End of Line"));
1082 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::endOfLine());
1083 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::end);
1084 m_editActions.push_back(x: a);
1085
1086 a = ac->addAction(QStringLiteral("end_of_document"));
1087 a->setText(i18n("Move to End of Document"));
1088 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::end());
1089 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::bottom);
1090 m_editActions.push_back(x: a);
1091
1092 a = ac->addAction(QStringLiteral("select_end_of_line"));
1093 a->setText(i18n("Select to End of Line"));
1094 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_End));
1095 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftEnd);
1096 m_editActions.push_back(x: a);
1097
1098 a = ac->addAction(QStringLiteral("select_end_of_document"));
1099 a->setText(i18n("Select to End of Document"));
1100 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_End));
1101 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftBottom);
1102 m_editActions.push_back(x: a);
1103
1104 a = ac->addAction(QStringLiteral("select_line_up"));
1105 a->setText(i18n("Select to Previous Line"));
1106 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_Up));
1107 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftUp);
1108 m_editActions.push_back(x: a);
1109
1110 a = ac->addAction(QStringLiteral("scroll_line_up"));
1111 a->setText(i18n("Scroll Line Up"));
1112 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_Up));
1113 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::scrollUp);
1114 m_editActions.push_back(x: a);
1115
1116 a = ac->addAction(QStringLiteral("move_line_down"));
1117 a->setText(i18n("Move to Next Line"));
1118 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::Key_Down));
1119 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::down);
1120 m_editActions.push_back(x: a);
1121
1122 a = ac->addAction(QStringLiteral("move_line_up"));
1123 a->setText(i18n("Move to Previous Line"));
1124 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::Key_Up));
1125 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::up);
1126 m_editActions.push_back(x: a);
1127
1128 a = ac->addAction(QStringLiteral("move_cursor_right"));
1129 a->setText(i18n("Move Cursor Right"));
1130 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::Key_Right));
1131 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::cursorRight);
1132 m_editActions.push_back(x: a);
1133
1134 a = ac->addAction(QStringLiteral("move_cursor_left"));
1135 a->setText(i18n("Move Cursor Left"));
1136 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::Key_Left));
1137 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::cursorLeft);
1138 m_editActions.push_back(x: a);
1139
1140 a = ac->addAction(QStringLiteral("select_line_down"));
1141 a->setText(i18n("Select to Next Line"));
1142 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_Down));
1143 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftDown);
1144 m_editActions.push_back(x: a);
1145
1146 a = ac->addAction(QStringLiteral("scroll_line_down"));
1147 a->setText(i18n("Scroll Line Down"));
1148 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_Down));
1149 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::scrollDown);
1150 m_editActions.push_back(x: a);
1151
1152 a = ac->addAction(QStringLiteral("scroll_page_up"));
1153 a->setText(i18n("Scroll Page Up"));
1154 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::prior());
1155 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::pageUp);
1156 m_editActions.push_back(x: a);
1157
1158 a = ac->addAction(QStringLiteral("select_page_up"));
1159 a->setText(i18n("Select Page Up"));
1160 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_PageUp));
1161 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftPageUp);
1162 m_editActions.push_back(x: a);
1163
1164 a = ac->addAction(QStringLiteral("move_top_of_view"));
1165 a->setText(i18n("Move to Top of View"));
1166 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::Key_Home));
1167 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::topOfView);
1168 m_editActions.push_back(x: a);
1169
1170 a = ac->addAction(QStringLiteral("select_top_of_view"));
1171 a->setText(i18n("Select to Top of View"));
1172 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_Home));
1173 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftTopOfView);
1174 m_editActions.push_back(x: a);
1175
1176 a = ac->addAction(QStringLiteral("scroll_page_down"));
1177 a->setText(i18n("Scroll Page Down"));
1178 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::next());
1179 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::pageDown);
1180 m_editActions.push_back(x: a);
1181
1182 a = ac->addAction(QStringLiteral("select_page_down"));
1183 a->setText(i18n("Select Page Down"));
1184 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::Key_PageDown));
1185 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftPageDown);
1186 m_editActions.push_back(x: a);
1187
1188 a = ac->addAction(QStringLiteral("move_bottom_of_view"));
1189 a->setText(i18n("Move to Bottom of View"));
1190 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::Key_End));
1191 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::bottomOfView);
1192 m_editActions.push_back(x: a);
1193
1194 a = ac->addAction(QStringLiteral("select_bottom_of_view"));
1195 a->setText(i18n("Select to Bottom of View"));
1196 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_End));
1197 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftBottomOfView);
1198 m_editActions.push_back(x: a);
1199
1200 a = ac->addAction(QStringLiteral("to_matching_bracket"));
1201 a->setText(i18n("Go to Matching Bracket"));
1202 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_6));
1203 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::toMatchingBracket);
1204 // m_editActions << a;
1205
1206 a = ac->addAction(QStringLiteral("select_matching_bracket"));
1207 a->setText(i18n("Select to Matching Bracket"));
1208 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::SHIFT | Qt::CTRL | Qt::Key_6));
1209 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::shiftToMatchingBracket);
1210 // m_editActions << a;
1211
1212 // anders: shortcuts doing any changes should not be created in read-only mode
1213 if (!doc()->readOnly()) {
1214 a = ac->addAction(QStringLiteral("transpose_char"));
1215 a->setText(i18n("Transpose Characters"));
1216 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::transpose);
1217 m_editActions.push_back(x: a);
1218
1219 a = ac->addAction(QStringLiteral("transpose_word"));
1220 a->setText(i18n("Transpose Words"));
1221 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::transposeWord);
1222 m_editActions.push_back(x: a);
1223
1224 a = ac->addAction(QStringLiteral("delete_line"));
1225 a->setText(i18n("Delete Line"));
1226 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_K));
1227 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::killLine);
1228 m_editActions.push_back(x: a);
1229
1230 a = ac->addAction(QStringLiteral("delete_word_left"));
1231 a->setText(i18n("Delete Word Left"));
1232 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::deleteWordBack());
1233 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::deleteWordLeft);
1234 m_editActions.push_back(x: a);
1235
1236 a = ac->addAction(QStringLiteral("delete_word_right"));
1237 a->setText(i18n("Delete Word Right"));
1238 ac->setDefaultShortcuts(action: a, shortcuts: KStandardShortcut::deleteWordForward());
1239 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::deleteWordRight);
1240 m_editActions.push_back(x: a);
1241
1242 a = ac->addAction(QStringLiteral("delete_next_character"));
1243 a->setText(i18n("Delete Next Character"));
1244 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::Key_Delete));
1245 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::keyDelete);
1246 m_editActions.push_back(x: a);
1247
1248 a = ac->addAction(QStringLiteral("backspace"));
1249 a->setText(i18n("Backspace"));
1250 QList<QKeySequence> scuts;
1251 scuts << QKeySequence(Qt::Key_Backspace) << QKeySequence(Qt::SHIFT | Qt::Key_Backspace);
1252 ac->setDefaultShortcuts(action: a, shortcuts: scuts);
1253 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::backspace);
1254 m_editActions.push_back(x: a);
1255
1256 a = ac->addAction(QStringLiteral("insert_tabulator"));
1257 a->setText(i18n("Insert Tab Character"));
1258 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::insertTab);
1259 m_editActions.push_back(x: a);
1260
1261 a = ac->addAction(QStringLiteral("smart_newline"));
1262 a->setText(i18n("Insert Smart Newline"));
1263 a->setWhatsThis(i18n("Insert newline including leading characters of the current line which are not letters or numbers."));
1264 scuts.clear();
1265 scuts << QKeySequence(Qt::SHIFT | Qt::Key_Return) << QKeySequence(Qt::SHIFT | Qt::Key_Enter);
1266 ac->setDefaultShortcuts(action: a, shortcuts: scuts);
1267 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::smartNewline);
1268 m_editActions.push_back(x: a);
1269
1270 a = ac->addAction(QStringLiteral("no_indent_newline"));
1271 a->setText(i18n("Insert a Non-Indented Newline"));
1272 a->setWhatsThis(i18n("Insert a new line without indentation, regardless of indentation settings."));
1273 scuts.clear();
1274 scuts << QKeySequence(Qt::CTRL | Qt::Key_Return) << QKeySequence(Qt::CTRL | Qt::Key_Enter);
1275 ac->setDefaultShortcuts(action: a, shortcuts: scuts);
1276 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::noIndentNewline);
1277 m_editActions.push_back(x: a);
1278
1279 a = ac->addAction(QStringLiteral("newline_above"));
1280 a->setText(i18n("Insert a Newline Above Current Line"));
1281 a->setWhatsThis(i18n("Insert a new line above current line without modifying the current line."));
1282 scuts.clear();
1283 scuts << QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Return) << QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Enter);
1284 ac->setDefaultShortcuts(action: a, shortcuts: scuts);
1285 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::newLineAbove);
1286 m_editActions.push_back(x: a);
1287
1288 a = ac->addAction(QStringLiteral("newline_below"));
1289 a->setText(i18n("Insert a Newline Below Current Line"));
1290 a->setWhatsThis(i18n("Insert a new line below current line without modifying the current line."));
1291 scuts.clear();
1292 scuts << QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Return) << QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Enter);
1293 ac->setDefaultShortcuts(action: a, shortcuts: scuts);
1294 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::newLineBelow);
1295 m_editActions.push_back(x: a);
1296
1297 a = ac->addAction(QStringLiteral("tools_indent"));
1298 a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-more")));
1299 a->setText(i18n("&Indent"));
1300 a->setWhatsThis(
1301 i18n("Use this to indent a selected block of text.<br /><br />"
1302 "You can configure whether tabs should be honored and used or replaced with spaces, in the configuration dialog."));
1303 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::Key_I));
1304 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::indent);
1305
1306 a = ac->addAction(QStringLiteral("tools_unindent"));
1307 a->setIcon(QIcon::fromTheme(QStringLiteral("format-indent-less")));
1308 a->setText(i18n("&Unindent"));
1309 a->setWhatsThis(i18n("Use this to unindent a selected block of text."));
1310 ac->setDefaultShortcut(action: a, shortcut: QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_I));
1311 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::unIndent);
1312 }
1313
1314 if (hasFocus()) {
1315 slotGotFocus();
1316 } else {
1317 slotLostFocus();
1318 }
1319}
1320
1321void KTextEditor::ViewPrivate::setupCodeFolding()
1322{
1323 KActionCollection *ac = this->actionCollection();
1324 QAction *a;
1325
1326 a = ac->addAction(QStringLiteral("folding_toplevel"));
1327 a->setText(i18n("Fold Toplevel Nodes"));
1328 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::slotFoldToplevelNodes);
1329
1330 a = ac->addAction(QStringLiteral("folding_expandtoplevel"));
1331 a->setText(i18n("Unfold Toplevel Nodes"));
1332 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::slotExpandToplevelNodes);
1333
1334 a = ac->addAction(QStringLiteral("folding_toggle_current"));
1335 a->setText(i18n("Toggle Current Node"));
1336 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::slotToggleFolding);
1337
1338 a = ac->addAction(QStringLiteral("folding_toggle_in_current"));
1339 a->setText(i18n("Toggle Contained Nodes"));
1340 connect(sender: a, signal: &QAction::triggered, context: this, slot: &KTextEditor::ViewPrivate::slotToggleFoldingsInRange);
1341}
1342
1343void KTextEditor::ViewPrivate::setupSpeechActions()
1344{
1345 KActionCollection *ac = actionCollection();
1346
1347 QAction *a = ac->addAction(QStringLiteral("tools_speech_say"));
1348 a->setText(i18n("Say current selection or document"));
1349 connect(sender: a, signal: &QAction::triggered, context: this, slot: [this]() {
1350 if (selection()) {
1351 KTextEditor::EditorPrivate::self()->speechEngine(view: this)->say(text: selectionText());
1352 } else {
1353 KTextEditor::EditorPrivate::self()->speechEngine(view: this)->say(text: document()->text());
1354 }
1355 });
1356
1357 a = ac->addAction(QStringLiteral("tools_speech_stop"));
1358 a->setText(i18n("Stop current output"));
1359 connect(sender: a, signal: &QAction::triggered, context: this, slot: [this]() {
1360 KTextEditor::EditorPrivate::self()->speechEngine(view: this)->stop();
1361 });
1362
1363 a = ac->addAction(QStringLiteral("tools_speech_pause"));
1364 a->setText(i18n("Pause current output"));
1365 connect(sender: a, signal: &QAction::triggered, context: this, slot: [this]() {
1366 KTextEditor::EditorPrivate::self()->speechEngine(view: this)->pause();
1367 });
1368
1369 a = ac->addAction(QStringLiteral("tools_speech_resume"));
1370 a->setText(i18n("Resume current output"));
1371 connect(sender: a, signal: &QAction::triggered, context: this, slot: [this]() {
1372 KTextEditor::EditorPrivate::self()->speechEngine(view: this)->resume();
1373 });
1374}
1375
1376void KTextEditor::ViewPrivate::slotFoldToplevelNodes()
1377{
1378 for (int line = 0; line < doc()->lines(); ++line) {
1379 if (textFolding().isLineVisible(line)) {
1380 foldLine(line);
1381 }
1382 }
1383}
1384
1385void KTextEditor::ViewPrivate::slotExpandToplevelNodes()
1386{
1387 const auto topLevelRanges(textFolding().foldingRangesForParentRange());
1388 for (const auto &range : topLevelRanges) {
1389 textFolding().unfoldRange(id: range.first);
1390 }
1391}
1392
1393void KTextEditor::ViewPrivate::slotToggleFolding()
1394{
1395 int line = cursorPosition().line();
1396 bool actionDone = false;
1397 while (!actionDone && (line > -1)) {
1398 actionDone = unfoldLine(line);
1399 if (!actionDone) {
1400 actionDone = foldLine(line: line--).isValid();
1401 }
1402 }
1403}
1404
1405void KTextEditor::ViewPrivate::slotToggleFoldingsInRange()
1406{
1407 int line = cursorPosition().line();
1408 while (!toggleFoldingsInRange(line) && (line > -1)) {
1409 --line;
1410 }
1411}
1412
1413KTextEditor::Range KTextEditor::ViewPrivate::foldLine(int line)
1414{
1415 KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(startLine: line);
1416 if (!foldingRange.isValid()) {
1417 return foldingRange;
1418 }
1419
1420 // Ensure not to fold the end marker to avoid a deceptive look, but only on token based folding
1421 // ensure we don't compute an invalid line by moving outside of the foldingRange range by checking onSingleLine(), see bug 417890
1422 if (!m_doc->buffer().isFoldingStartingOnLine(startLine: line).second && !foldingRange.onSingleLine()) {
1423 const int adjustedLine = foldingRange.end().line() - 1;
1424 foldingRange.setEnd(KTextEditor::Cursor(adjustedLine, doc()->buffer().plainLine(lineno: adjustedLine).length()));
1425 }
1426
1427 // Check if the discovered fold is already folded up.
1428 // If so, we should not fold the same range again!
1429 // This can lead to issues where we need to open the fold multiple times
1430 // in order to actually open it.
1431 auto folds = textFolding().foldingRangesStartingOnLine(line);
1432 for (int i = 0; i < folds.size(); ++i) {
1433 KTextEditor::Range fold = textFolding().foldingRange(id: folds[i].first);
1434 if (fold == foldingRange) {
1435 return foldingRange;
1436 }
1437 }
1438
1439 // Don't try to fold a single line, which can happens due to adjustment above
1440 // FIXME Avoid to offer such a folding marker
1441 if (!foldingRange.onSingleLine()) {
1442 textFolding().newFoldingRange(range: foldingRange, flags: Kate::TextFolding::Folded);
1443 }
1444
1445 return foldingRange;
1446}
1447
1448bool KTextEditor::ViewPrivate::unfoldLine(int line)
1449{
1450 bool actionDone = false;
1451 const KTextEditor::Cursor currentCursor = cursorPosition();
1452
1453 // ask the folding info for this line, if any folds are around!
1454 // auto = QList<QPair<qint64, Kate::TextFolding::FoldingRangeFlags>>
1455 auto startingRanges = textFolding().foldingRangesStartingOnLine(line);
1456 for (int i = 0; i < startingRanges.size() && !actionDone; ++i) {
1457 // Avoid jumping view in case of a big unfold and ensure nice highlight of folding marker
1458 setCursorPosition(textFolding().foldingRange(id: startingRanges[i].first).start());
1459
1460 actionDone |= textFolding().unfoldRange(id: startingRanges[i].first);
1461 }
1462
1463 if (!actionDone) {
1464 // Nothing unfolded? Restore old cursor position!
1465 setCursorPosition(currentCursor);
1466 }
1467
1468 return actionDone;
1469}
1470
1471bool KTextEditor::ViewPrivate::toggleFoldingOfLine(int line)
1472{
1473 bool actionDone = unfoldLine(line);
1474 if (!actionDone) {
1475 actionDone = foldLine(line).isValid();
1476 }
1477
1478 return actionDone;
1479}
1480
1481bool KTextEditor::ViewPrivate::toggleFoldingsInRange(int line)
1482{
1483 KTextEditor::Range foldingRange = doc()->buffer().computeFoldingRangeForStartLine(startLine: line);
1484 if (!foldingRange.isValid()) {
1485 // Either line is not valid or there is no start range
1486 return false;
1487 }
1488
1489 bool actionDone = false; // Track success
1490 const KTextEditor::Cursor currentCursor = cursorPosition();
1491
1492 // Don't be too eager but obliging! Only toggle containing ranges which are
1493 // visible -> Be done when the range is folded
1494 actionDone |= unfoldLine(line);
1495
1496 if (!actionDone) {
1497 // Unfold all in range, but not the range itself
1498 for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1499 actionDone |= unfoldLine(line: ln);
1500 }
1501
1502 if (actionDone) {
1503 // In most cases we want now a not moved cursor
1504 setCursorPosition(currentCursor);
1505 }
1506 }
1507
1508 if (!actionDone) {
1509 // Fold all in range, but not the range itself
1510 for (int ln = foldingRange.start().line() + 1; ln < foldingRange.end().line(); ++ln) {
1511 KTextEditor::Range fr = foldLine(line: ln);
1512 if (fr.isValid()) {
1513 // qMax to avoid infinite loop in case of range without content
1514 ln = qMax(a: ln, b: fr.end().line() - 1);
1515 actionDone = true;
1516 }
1517 }
1518 }
1519
1520 if (!actionDone) {
1521 // At this point was an unfolded range clicked which contains no "childs"
1522 // We assume the user want to fold it by the wrong button, be obliging!
1523 actionDone |= foldLine(line).isValid();
1524 }
1525
1526 // At this point we should be always true
1527 return actionDone;
1528}
1529
1530KTextEditor::View::ViewMode KTextEditor::ViewPrivate::viewMode() const
1531{
1532 return currentInputMode()->viewMode();
1533}
1534
1535QString KTextEditor::ViewPrivate::viewModeHuman() const
1536{
1537 QString currentMode = currentInputMode()->viewModeHuman();
1538
1539 // append read-only if needed
1540 if (!doc()->isReadWrite()) {
1541 currentMode = i18n("(R/O) %1", currentMode);
1542 }
1543
1544 // return full mode
1545 return currentMode;
1546}
1547
1548KTextEditor::View::InputMode KTextEditor::ViewPrivate::viewInputMode() const
1549{
1550 return currentInputMode()->viewInputMode();
1551}
1552
1553QString KTextEditor::ViewPrivate::viewInputModeHuman() const
1554{
1555 return currentInputMode()->viewInputModeHuman();
1556}
1557
1558void KTextEditor::ViewPrivate::setInputMode(KTextEditor::View::InputMode mode, const bool rememberInConfig)
1559{
1560 if (currentInputMode()->viewInputMode() == mode) {
1561 return;
1562 }
1563
1564 // No multi cursors for vi
1565 if (mode == KTextEditor::View::InputMode::ViInputMode) {
1566 clearSecondaryCursors();
1567 }
1568
1569 m_viewInternal->m_currentInputMode->deactivate();
1570 m_viewInternal->m_currentInputMode = m_viewInternal->m_inputModes[mode].get();
1571 m_viewInternal->m_currentInputMode->activate();
1572
1573 // remember in local config if requested, we skip this for the calls in updateConfig
1574 if (rememberInConfig) {
1575 config()->setValue(key: KateViewConfig::InputMode, value: mode);
1576 }
1577
1578 /* small duplication, but need to do this if not toggled by action */
1579 const auto inputModeActions = m_inputModeActions->actions();
1580 for (QAction *action : inputModeActions) {
1581 if (static_cast<InputMode>(action->data().toInt()) == mode) {
1582 action->setChecked(true);
1583 break;
1584 }
1585 }
1586
1587 /* inform the rest of the system about the change */
1588 Q_EMIT viewInputModeChanged(view: this, mode);
1589 Q_EMIT viewModeChanged(view: this, mode: viewMode());
1590}
1591
1592void KTextEditor::ViewPrivate::slotDocumentAboutToReload()
1593{
1594 if (doc()->isAutoReload()) {
1595 const int lastVisibleLine = m_viewInternal->endLine();
1596 const int currentLine = cursorPosition().line();
1597 m_gotoBottomAfterReload = (lastVisibleLine == currentLine) && (currentLine == doc()->lastLine());
1598 if (!m_gotoBottomAfterReload) {
1599 // Ensure the view jumps not back when user scrolls around
1600 const int firstVisibleLine = 1 + lastVisibleLine - m_viewInternal->linesDisplayed();
1601 const int newLine = qBound(min: firstVisibleLine, val: currentLine, max: lastVisibleLine);
1602 setCursorPositionVisual(KTextEditor::Cursor(newLine, cursorPosition().column()));
1603 }
1604 } else {
1605 m_gotoBottomAfterReload = false;
1606 }
1607}
1608
1609void KTextEditor::ViewPrivate::slotDocumentReloaded()
1610{
1611 if (m_gotoBottomAfterReload) {
1612 bottom();
1613 }
1614}
1615
1616void KTextEditor::ViewPrivate::slotGotFocus()
1617{
1618 // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotGotFocus";
1619 currentInputMode()->gotFocus();
1620
1621 // update current view and scrollbars
1622 // it is needed for styles that implement different frame and scrollbar
1623 // rendering when focused
1624 update();
1625 if (m_viewInternal->m_lineScroll->isVisible()) {
1626 m_viewInternal->m_lineScroll->update();
1627 }
1628
1629 if (m_viewInternal->m_columnScroll->isVisible()) {
1630 m_viewInternal->m_columnScroll->update();
1631 }
1632
1633 Q_EMIT focusIn(view: this);
1634}
1635
1636void KTextEditor::ViewPrivate::slotLostFocus()
1637{
1638 // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::slotLostFocus";
1639 currentInputMode()->lostFocus();
1640
1641 // update current view and scrollbars
1642 // it is needed for styles that implement different frame and scrollbar
1643 // rendering when focused
1644 update();
1645 if (m_viewInternal->m_lineScroll->isVisible()) {
1646 m_viewInternal->m_lineScroll->update();
1647 }
1648
1649 if (m_viewInternal->m_columnScroll->isVisible()) {
1650 m_viewInternal->m_columnScroll->update();
1651 }
1652
1653 if (doc()->config()->autoSave() && doc()->config()->autoSaveOnFocusOut() && doc()->isModified() && doc()->url().isLocalFile()) {
1654 doc()->documentSave();
1655 }
1656
1657 Q_EMIT focusOut(view: this);
1658}
1659
1660void KTextEditor::ViewPrivate::setDynWrapIndicators(int mode)
1661{
1662 config()->setValue(key: KateViewConfig::DynWordWrapIndicators, value: mode);
1663}
1664
1665bool KTextEditor::ViewPrivate::isOverwriteMode() const
1666{
1667 return doc()->config()->ovr();
1668}
1669
1670void KTextEditor::ViewPrivate::reloadFile()
1671{
1672 // bookmarks and cursor positions are temporarily saved by the document
1673 doc()->documentReload();
1674}
1675
1676void KTextEditor::ViewPrivate::slotReadWriteChanged()
1677{
1678 if (m_toggleWriteLock) {
1679 m_toggleWriteLock->setChecked(!doc()->isReadWrite());
1680 }
1681
1682 m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
1683 m_paste->setEnabled(doc()->isReadWrite());
1684 if (m_pasteSelection) {
1685 m_pasteSelection->setEnabled(doc()->isReadWrite());
1686 }
1687 m_swapWithClipboard->setEnabled(doc()->isReadWrite());
1688 m_setEndOfLine->setEnabled(doc()->isReadWrite());
1689
1690 static const auto l = {QStringLiteral("edit_replace"),
1691 QStringLiteral("tools_spelling"),
1692 QStringLiteral("tools_indent"),
1693 QStringLiteral("tools_unindent"),
1694 QStringLiteral("tools_cleanIndent"),
1695 QStringLiteral("tools_formatIndet"),
1696 QStringLiteral("tools_alignOn"),
1697 QStringLiteral("tools_comment"),
1698 QStringLiteral("tools_uncomment"),
1699 QStringLiteral("tools_toggle_comment"),
1700 QStringLiteral("tools_uppercase"),
1701 QStringLiteral("tools_lowercase"),
1702 QStringLiteral("tools_capitalize"),
1703 QStringLiteral("tools_join_lines"),
1704 QStringLiteral("tools_apply_wordwrap"),
1705 QStringLiteral("tools_spelling_from_cursor"),
1706 QStringLiteral("tools_spelling_selection")};
1707
1708 for (const auto &action : l) {
1709 QAction *a = actionCollection()->action(name: action);
1710 if (a) {
1711 a->setEnabled(doc()->isReadWrite());
1712 }
1713 }
1714 slotUpdateUndo();
1715
1716 currentInputMode()->readWriteChanged(rw: doc()->isReadWrite());
1717
1718 // => view mode changed
1719 Q_EMIT viewModeChanged(view: this, mode: viewMode());
1720 Q_EMIT viewInputModeChanged(view: this, mode: viewInputMode());
1721}
1722
1723void KTextEditor::ViewPrivate::toggleCamelCaseCursor()
1724{
1725 const auto enabled = doc()->config()->camelCursor();
1726 doc()->config()->setCamelCursor(!enabled);
1727 KTextEditor::Message *m;
1728 if (enabled) {
1729 m = new KTextEditor::Message(i18n("Camel case movement disabled"));
1730 } else {
1731 m = new KTextEditor::Message(i18n("Camel case movement enabled"));
1732 }
1733 m->setPosition(KTextEditor::Message::TopInView);
1734 m->setAutoHide(1000);
1735 m->setAutoHideMode(KTextEditor::Message::Immediate);
1736 doc()->postMessage(message: m);
1737}
1738
1739void KTextEditor::ViewPrivate::slotUpdateUndo()
1740{
1741 if (doc()->readOnly()) {
1742 return;
1743 }
1744
1745 m_editUndo->setEnabled(doc()->isReadWrite() && doc()->undoCount() > 0);
1746 m_editRedo->setEnabled(doc()->isReadWrite() && doc()->redoCount() > 0);
1747}
1748
1749bool KTextEditor::ViewPrivate::setCursorPositionInternal(const KTextEditor::Cursor position, uint tabwidth, bool calledExternally)
1750{
1751 if (position.line() < 0 || position.line() >= doc()->lines()) {
1752 return false;
1753 }
1754
1755 Kate::TextLine l = doc()->kateTextLine(i: position.line());
1756 const QString line_str = l.text();
1757
1758 int x = 0;
1759 int z = 0;
1760 for (; z < line_str.length() && z < position.column(); z++) {
1761 if (line_str[z] == QLatin1Char('\t')) {
1762 x += tabwidth - (x % tabwidth);
1763 } else {
1764 x++;
1765 }
1766 }
1767
1768 if (blockSelection()) {
1769 if (z < position.column()) {
1770 x += position.column() - z;
1771 }
1772 }
1773
1774 m_viewInternal->updateCursor(newCursor: KTextEditor::Cursor(position.line(), x),
1775 force: false,
1776 center: calledExternally /* force center for external calls, see bug 408418 */,
1777 calledExternally);
1778
1779 return true;
1780}
1781
1782void KTextEditor::ViewPrivate::toggleInsert()
1783{
1784 doc()->config()->setOvr(!doc()->config()->ovr());
1785 m_toggleInsert->setChecked(isOverwriteMode());
1786
1787 // No multi cursors for overwrite mode
1788 if (isOverwriteMode()) {
1789 clearSecondaryCursors();
1790 }
1791
1792 Q_EMIT viewModeChanged(view: this, mode: viewMode());
1793 Q_EMIT viewInputModeChanged(view: this, mode: viewInputMode());
1794}
1795
1796void KTextEditor::ViewPrivate::slotSaveCanceled(const QString &error)
1797{
1798 if (!error.isEmpty()) { // happens when canceling a job
1799 KMessageBox::error(parent: this, text: error);
1800 }
1801}
1802
1803void KTextEditor::ViewPrivate::gotoLine()
1804{
1805 gotoBar()->updateData();
1806 bottomViewBar()->showBarWidget(barWidget: gotoBar());
1807}
1808
1809void KTextEditor::ViewPrivate::changeDictionary()
1810{
1811 dictionaryBar()->updateData();
1812 bottomViewBar()->showBarWidget(barWidget: dictionaryBar());
1813}
1814
1815void KTextEditor::ViewPrivate::joinLines()
1816{
1817 int first = selectionRange().start().line();
1818 int last = selectionRange().end().line();
1819 // int left = doc()->line( last ).length() - doc()->selEndCol();
1820 if (first == last) {
1821 first = cursorPosition().line();
1822 last = first + 1;
1823 }
1824 doc()->joinLines(first, last);
1825}
1826
1827void KTextEditor::ViewPrivate::readSessionConfig(const KConfigGroup &config, const QSet<QString> &flags)
1828{
1829 Q_UNUSED(flags)
1830
1831 // cursor position
1832 KTextEditor::Cursor savedPosition(config.readEntry(key: "CursorLine", defaultValue: 0), config.readEntry(key: "CursorColumn", defaultValue: 0));
1833 setCursorPositionInternal(position: savedPosition);
1834
1835 // scroll position
1836 const int scroll = config.readEntry(key: "ScrollLine", defaultValue: -1);
1837 if (scroll >= 0 && scroll < doc()->lines() && savedPosition.line() < doc()->lines()) {
1838 setScrollPositionInternal(KTextEditor::Cursor(scroll, 0));
1839 }
1840
1841 m_config->setDynWordWrap(config.readEntry(key: "Dynamic Word Wrap", defaultValue: false));
1842
1843 // restore text folding
1844 m_savedFoldingState = QJsonDocument::fromJson(json: config.readEntry(key: "TextFolding", defaultValue: QByteArray()));
1845 applyFoldingState();
1846
1847 m_forceRTL = config.readEntry(key: "Force RTL Direction", defaultValue: false);
1848 m_forceRTLDirection->setChecked(m_forceRTL);
1849
1850 for (const auto &mode : m_viewInternal->m_inputModes) {
1851 mode->readSessionConfig(config);
1852 }
1853}
1854
1855void KTextEditor::ViewPrivate::writeSessionConfig(KConfigGroup &config, const QSet<QString> &)
1856{
1857 // ensure we don't amass stuff
1858 config.deleteGroup();
1859
1860 // cursor position
1861 const auto cursor = cursorPosition();
1862 if (cursor.isValid() && cursor != KTextEditor::Cursor(0, 0)) {
1863 config.writeEntry(key: "CursorLine", value: cursor.line());
1864 config.writeEntry(key: "CursorColumn", value: cursor.column());
1865 }
1866
1867 // save scroll position if its different from cursorPosition
1868 const int scrollLine = firstDisplayedLineInternal(lineType: LineType::RealLine);
1869 if (scrollLine > 0 && scrollLine != cursor.line()) {
1870 config.writeEntry(key: "ScrollLine", value: scrollLine);
1871 }
1872
1873 // only write if set in this view
1874 if (m_config->isSet(key: KateViewConfig::DynamicWordWrap)) {
1875 config.writeEntry(key: "Dynamic Word Wrap", value: m_config->dynWordWrap());
1876 }
1877
1878 // save text folding state
1879 saveFoldingState();
1880 if (!m_savedFoldingState.object().value(key: QLatin1String("ranges")).toArray().isEmpty()) {
1881 config.writeEntry(key: "TextFolding", value: m_savedFoldingState.toJson(format: QJsonDocument::Compact));
1882 m_savedFoldingState = QJsonDocument();
1883 }
1884
1885 if (m_forceRTL) {
1886 config.writeEntry(key: "Force RTL Direction", value: m_forceRTL);
1887 }
1888
1889 for (const auto &mode : m_viewInternal->m_inputModes) {
1890 mode->writeSessionConfig(config);
1891 }
1892}
1893
1894int KTextEditor::ViewPrivate::getEol() const
1895{
1896 return doc()->config()->eol();
1897}
1898
1899QMenu *KTextEditor::ViewPrivate::getEolMenu()
1900{
1901 return m_setEndOfLine->menu();
1902}
1903
1904void KTextEditor::ViewPrivate::setEol(int eol)
1905{
1906 if (!doc()->isReadWrite()) {
1907 return;
1908 }
1909
1910 if (m_updatingDocumentConfig) {
1911 return;
1912 }
1913
1914 if (eol != doc()->config()->eol()) {
1915 doc()->setModified(true); // mark modified (bug #143120)
1916 doc()->config()->setEol(eol);
1917 }
1918}
1919
1920void KTextEditor::ViewPrivate::setAddBom(bool enabled)
1921{
1922 if (!doc()->isReadWrite()) {
1923 return;
1924 }
1925
1926 if (m_updatingDocumentConfig) {
1927 return;
1928 }
1929
1930 doc()->config()->setBom(enabled);
1931 doc()->bomSetByUser();
1932}
1933
1934void KTextEditor::ViewPrivate::setIconBorder(bool enable)
1935{
1936 config()->setValue(key: KateViewConfig::ShowIconBar, value: enable);
1937}
1938
1939void KTextEditor::ViewPrivate::toggleIconBorder()
1940{
1941 config()->setValue(key: KateViewConfig::ShowIconBar, value: !config()->iconBar());
1942}
1943
1944void KTextEditor::ViewPrivate::setLineNumbersOn(bool enable)
1945{
1946 config()->setValue(key: KateViewConfig::ShowLineNumbers, value: enable);
1947}
1948
1949void KTextEditor::ViewPrivate::toggleLineNumbersOn()
1950{
1951 config()->setValue(key: KateViewConfig::ShowLineNumbers, value: !config()->lineNumbers());
1952}
1953
1954void KTextEditor::ViewPrivate::setScrollBarMarks(bool enable)
1955{
1956 config()->setValue(key: KateViewConfig::ShowScrollBarMarks, value: enable);
1957}
1958
1959void KTextEditor::ViewPrivate::toggleScrollBarMarks()
1960{
1961 config()->setValue(key: KateViewConfig::ShowScrollBarMarks, value: !config()->scrollBarMarks());
1962}
1963
1964void KTextEditor::ViewPrivate::setScrollBarMiniMap(bool enable)
1965{
1966 config()->setValue(key: KateViewConfig::ShowScrollBarMiniMap, value: enable);
1967}
1968
1969void KTextEditor::ViewPrivate::toggleScrollBarMiniMap()
1970{
1971 config()->setValue(key: KateViewConfig::ShowScrollBarMiniMap, value: !config()->scrollBarMiniMap());
1972}
1973
1974void KTextEditor::ViewPrivate::setScrollBarMiniMapAll(bool enable)
1975{
1976 config()->setValue(key: KateViewConfig::ShowScrollBarMiniMapAll, value: enable);
1977}
1978
1979void KTextEditor::ViewPrivate::toggleScrollBarMiniMapAll()
1980{
1981 config()->setValue(key: KateViewConfig::ShowScrollBarMiniMapAll, value: !config()->scrollBarMiniMapAll());
1982}
1983
1984void KTextEditor::ViewPrivate::setScrollBarMiniMapWidth(int width)
1985{
1986 config()->setValue(key: KateViewConfig::ScrollBarMiniMapWidth, value: width);
1987}
1988
1989void KTextEditor::ViewPrivate::toggleDynWordWrap()
1990{
1991 config()->setDynWordWrap(!config()->dynWordWrap());
1992}
1993
1994void KTextEditor::ViewPrivate::toggleWWMarker()
1995{
1996 m_renderer->config()->setWordWrapMarker(!m_renderer->config()->wordWrapMarker());
1997}
1998
1999void KTextEditor::ViewPrivate::toggleNPSpaces()
2000{
2001 m_renderer->setShowNonPrintableSpaces(!m_renderer->showNonPrintableSpaces());
2002 m_viewInternal->update(); // force redraw
2003}
2004
2005void KTextEditor::ViewPrivate::toggleWordCount(bool on)
2006{
2007 config()->setShowWordCount(on);
2008}
2009
2010void KTextEditor::ViewPrivate::setFoldingMarkersOn(bool enable)
2011{
2012 config()->setValue(key: KateViewConfig::ShowFoldingBar, value: enable);
2013}
2014
2015void KTextEditor::ViewPrivate::toggleFoldingMarkers()
2016{
2017 config()->setValue(key: KateViewConfig::ShowFoldingBar, value: !config()->foldingBar());
2018}
2019
2020bool KTextEditor::ViewPrivate::iconBorder()
2021{
2022 return m_viewInternal->m_leftBorder->iconBorderOn();
2023}
2024
2025bool KTextEditor::ViewPrivate::lineNumbersOn()
2026{
2027 return m_viewInternal->m_leftBorder->lineNumbersOn();
2028}
2029
2030bool KTextEditor::ViewPrivate::scrollBarMarks()
2031{
2032 return m_viewInternal->m_lineScroll->showMarks();
2033}
2034
2035bool KTextEditor::ViewPrivate::scrollBarMiniMap()
2036{
2037 return m_viewInternal->m_lineScroll->showMiniMap();
2038}
2039
2040int KTextEditor::ViewPrivate::dynWrapIndicators()
2041{
2042 return m_viewInternal->m_leftBorder->dynWrapIndicators();
2043}
2044
2045bool KTextEditor::ViewPrivate::foldingMarkersOn()
2046{
2047 return m_viewInternal->m_leftBorder->foldingMarkersOn();
2048}
2049
2050bool KTextEditor::ViewPrivate::forceRTLDirection() const
2051{
2052 return m_forceRTL;
2053}
2054
2055void KTextEditor::ViewPrivate::toggleWriteLock()
2056{
2057 doc()->setReadWrite(!doc()->isReadWrite());
2058}
2059
2060void KTextEditor::ViewPrivate::registerTextHintProvider(KTextEditor::TextHintProvider *provider)
2061{
2062 m_viewInternal->registerTextHintProvider(provider);
2063}
2064
2065void KTextEditor::ViewPrivate::unregisterTextHintProvider(KTextEditor::TextHintProvider *provider)
2066{
2067 m_viewInternal->unregisterTextHintProvider(provider);
2068}
2069
2070void KTextEditor::ViewPrivate::setTextHintDelay(int delay)
2071{
2072 m_viewInternal->setTextHintDelay(delay);
2073}
2074
2075int KTextEditor::ViewPrivate::textHintDelay() const
2076{
2077 return m_viewInternal->textHintDelay();
2078}
2079
2080// NOLINTNEXTLINE(readability-make-member-function-const)
2081void KTextEditor::ViewPrivate::find()
2082{
2083 currentInputMode()->find();
2084}
2085
2086// NOLINTNEXTLINE(readability-make-member-function-const)
2087void KTextEditor::ViewPrivate::findSelectedForwards()
2088{
2089 currentInputMode()->findSelectedForwards();
2090}
2091
2092// NOLINTNEXTLINE(readability-make-member-function-const)
2093void KTextEditor::ViewPrivate::findSelectedBackwards()
2094{
2095 currentInputMode()->findSelectedBackwards();
2096}
2097
2098void KTextEditor::ViewPrivate::skipCurrentOccurunceSelection()
2099{
2100 if (isMulticursorNotAllowed()) {
2101 return;
2102 }
2103 m_skipCurrentSelection = true;
2104}
2105
2106void KTextEditor::ViewPrivate::findNextOccurunceAndSelect()
2107{
2108 if (isMulticursorNotAllowed()) {
2109 return;
2110 }
2111
2112 const auto text = selection() ? doc()->text(range: selectionRange()) : QString();
2113 if (text.isEmpty()) {
2114 const auto selection = doc()->wordRangeAt(cursor: cursorPosition());
2115 // We don't want matching word highlights
2116 setSelection(selection);
2117 setCursorPosition(selection.end());
2118 clearHighlights();
2119
2120 for (auto &c : m_secondaryCursors) {
2121 const auto range = doc()->wordRangeAt(cursor: c.cursor());
2122 if (!c.range && !c.anchor.isValid()) {
2123 c.anchor = range.start();
2124 c.range.reset(p: newSecondarySelectionRange(range));
2125 c.pos->setPosition(range.end());
2126 }
2127 tagLines(range);
2128 }
2129 return;
2130 } else if (!m_rangesForHighlights.empty()) {
2131 clearHighlights();
2132 }
2133
2134 // Use selection range end as starting point
2135 const auto lastSelectionRange = selectionRange();
2136
2137 KTextEditor::Range searchRange(lastSelectionRange.end(), doc()->documentRange().end());
2138 QList<KTextEditor::Range> matches = doc()->searchText(range: searchRange, pattern: text, options: KTextEditor::Default);
2139 if (!matches.isEmpty() && !matches.constFirst().isValid()) {
2140 searchRange.setRange(start: doc()->documentRange().start(), end: lastSelectionRange.end());
2141 matches = doc()->searchText(range: searchRange, pattern: text, options: KTextEditor::Default);
2142 }
2143
2144 // No match found or only one possible match
2145 if (matches.empty() || !matches.constFirst().isValid() || matches.constFirst() == selectionRange()) {
2146 return;
2147 }
2148
2149 auto it = std::find_if(first: m_secondaryCursors.begin(), last: m_secondaryCursors.end(), pred: [&](const SecondaryCursor &c) {
2150 return c.range && c.range->toRange() == matches.constFirst();
2151 });
2152
2153 if (it != m_secondaryCursors.end()) {
2154 m_secondaryCursors.erase(position: it);
2155 }
2156
2157 // Move our primary to cursor to this match and select it
2158 // Ensure we don't create occurence highlights
2159 setSelection(matches.constFirst());
2160 setCursorPosition(matches.constFirst().end());
2161 clearHighlights();
2162
2163 // If we are skipping this selection, then we don't have to do anything
2164 if (!m_skipCurrentSelection) {
2165 PlainSecondaryCursor c;
2166 c.pos = lastSelectionRange.end();
2167 c.range = lastSelectionRange;
2168 // make our previous primary selection a secondary
2169 addSecondaryCursorsWithSelection(cursorsWithSelection: {c});
2170 }
2171 // reset value
2172 m_skipCurrentSelection = false;
2173}
2174
2175void KTextEditor::ViewPrivate::findAllOccuruncesAndSelect()
2176{
2177 if (isMulticursorNotAllowed()) {
2178 return;
2179 }
2180
2181 QString text = selection() ? doc()->text(range: selectionRange()) : QString();
2182 if (text.isEmpty()) {
2183 const auto selection = doc()->wordRangeAt(cursor: cursorPosition());
2184 setSelection(selection);
2185 setCursorPosition(selection.end());
2186 clearHighlights();
2187 text = doc()->text(range: selection);
2188
2189 for (auto &c : m_secondaryCursors) {
2190 const auto range = doc()->wordRangeAt(cursor: c.cursor());
2191 if (!c.range && !c.anchor.isValid()) {
2192 c.anchor = range.start();
2193 c.range.reset(p: newSecondarySelectionRange(range));
2194 c.pos->setPosition(range.end());
2195 }
2196 tagLines(range);
2197 }
2198 }
2199
2200 KTextEditor::Range searchRange(doc()->documentRange());
2201 QList<KTextEditor::Range> matches;
2202 QList<PlainSecondaryCursor> resultRanges;
2203 do {
2204 matches = doc()->searchText(range: searchRange, pattern: text, options: KTextEditor::Default);
2205
2206 if (matches.constFirst().isValid()) {
2207 // Dont add if matches primary selection
2208 if (matches.constFirst() != selectionRange()) {
2209 PlainSecondaryCursor c;
2210 c.pos = matches.constFirst().end();
2211 c.range = matches.constFirst();
2212 resultRanges.push_back(t: c);
2213 }
2214 searchRange.setStart(matches.constFirst().end());
2215 }
2216 } while (matches.first().isValid());
2217
2218 // ensure to clear occurence highlights
2219 if (!resultRanges.empty()) {
2220 clearHighlights();
2221 }
2222
2223 clearSecondaryCursors();
2224 addSecondaryCursorsWithSelection(cursorsWithSelection: resultRanges);
2225}
2226
2227// NOLINTNEXTLINE(readability-make-member-function-const)
2228void KTextEditor::ViewPrivate::replace()
2229{
2230 currentInputMode()->findReplace();
2231}
2232
2233// NOLINTNEXTLINE(readability-make-member-function-const)
2234void KTextEditor::ViewPrivate::findNext()
2235{
2236 currentInputMode()->findNext();
2237}
2238
2239// NOLINTNEXTLINE(readability-make-member-function-const)
2240void KTextEditor::ViewPrivate::findPrevious()
2241{
2242 currentInputMode()->findPrevious();
2243}
2244
2245void KTextEditor::ViewPrivate::showSearchWrappedHint(bool isReverseSearch)
2246{
2247 // show message widget when wrapping
2248 const QIcon icon = isReverseSearch ? QIcon::fromTheme(QStringLiteral("go-up-search")) : QIcon::fromTheme(QStringLiteral("go-down-search"));
2249
2250 if (!m_wrappedMessage || m_isLastSearchReversed != isReverseSearch) {
2251 m_isLastSearchReversed = isReverseSearch;
2252 m_wrappedMessage = new KTextEditor::Message(i18n("Search wrapped"), KTextEditor::Message::Information);
2253 m_wrappedMessage->setIcon(icon);
2254 m_wrappedMessage->setPosition(KTextEditor::Message::BottomInView);
2255 m_wrappedMessage->setAutoHide(2000);
2256 m_wrappedMessage->setAutoHideMode(KTextEditor::Message::Immediate);
2257 m_wrappedMessage->setView(this);
2258 this->doc()->postMessage(message: m_wrappedMessage);
2259 }
2260}
2261
2262void KTextEditor::ViewPrivate::createMultiCursorsFromSelection()
2263{
2264 if (!selection() || selectionRange().isEmpty()) {
2265 return;
2266 }
2267 // Is this really needed?
2268 // Lets just clear them now for simplicity
2269 clearSecondaryCursors();
2270
2271 const auto range = selectionRange();
2272 QList<KTextEditor::Cursor> cursorsToAdd;
2273 const auto start = range.start().line() < 0 ? 0 : range.start().line();
2274 const auto end = range.end().line() > doc()->lines() ? doc()->lines() : range.end().line();
2275 const auto currentLine = cursorPosition().line();
2276 setCursorPosition({currentLine, doc()->lineLength(line: currentLine)});
2277 for (int line = start; line <= end; ++line) {
2278 if (line != currentLine) {
2279 cursorsToAdd.push_back(t: {line, doc()->lineLength(line)});
2280 }
2281 }
2282 // clear selection
2283 setSelection({});
2284 setSecondaryCursors(cursorsToAdd);
2285}
2286
2287void KTextEditor::ViewPrivate::removeCursorsFromEmptyLines()
2288{
2289 if (!m_secondaryCursors.empty()) {
2290 std::vector<KTextEditor::Cursor> cursorsToRemove;
2291 for (const auto &c : m_secondaryCursors) {
2292 auto cursor = c.cursor();
2293 if (doc()->lineLength(line: cursor.line()) == 0) {
2294 cursorsToRemove.push_back(x: cursor);
2295 }
2296 }
2297 removeSecondaryCursors(cursorToRemove: cursorsToRemove);
2298 }
2299}
2300
2301void KTextEditor::ViewPrivate::slotSelectionChanged()
2302{
2303 m_copy->setEnabled(selection() || m_config->smartCopyCut());
2304 m_deSelect->setEnabled(selection());
2305 m_copyHtmlAction->setEnabled(selection());
2306
2307 // update highlighting of current selected word
2308 selectionChangedForHighlights();
2309
2310 if (doc()->readOnly()) {
2311 return;
2312 }
2313
2314 m_cut->setEnabled(selection() || m_config->smartCopyCut());
2315 m_screenshotSelection->setVisible(selection());
2316 m_screenshotSelection->setEnabled(selection());
2317}
2318
2319// NOLINTNEXTLINE(readability-make-member-function-const)
2320void KTextEditor::ViewPrivate::switchToCmdLine()
2321{
2322 currentInputMode()->activateCommandLine();
2323}
2324
2325KateRenderer *KTextEditor::ViewPrivate::renderer()
2326{
2327 return m_renderer;
2328}
2329
2330KateRendererConfig *KTextEditor::ViewPrivate::rendererConfig()
2331{
2332 return m_renderer->config();
2333}
2334
2335void KTextEditor::ViewPrivate::updateConfig()
2336{
2337 if (m_startingUp) {
2338 return;
2339 }
2340
2341 // dyn. word wrap & markers
2342 if (m_hasWrap != config()->dynWordWrap()) {
2343 m_hasWrap = config()->dynWordWrap();
2344
2345 m_viewInternal->dynWrapChanged();
2346
2347 m_setDynWrapIndicators->setEnabled(config()->dynWordWrap());
2348 m_toggleDynWrap->setChecked(config()->dynWordWrap());
2349 }
2350
2351 m_viewInternal->m_leftBorder->setDynWrapIndicators(config()->dynWordWrapIndicators());
2352 m_setDynWrapIndicators->setCurrentItem(config()->dynWordWrapIndicators());
2353
2354 // line numbers
2355 m_viewInternal->m_leftBorder->setLineNumbersOn(config()->lineNumbers());
2356 m_toggleLineNumbers->setChecked(config()->lineNumbers());
2357
2358 // icon bar
2359 m_viewInternal->m_leftBorder->setIconBorderOn(config()->iconBar());
2360 m_toggleIconBar->setChecked(config()->iconBar());
2361
2362 // scrollbar marks
2363 m_viewInternal->m_lineScroll->setShowMarks(config()->scrollBarMarks());
2364 m_toggleScrollBarMarks->setChecked(config()->scrollBarMarks());
2365
2366 // scrollbar mini-map
2367 m_viewInternal->m_lineScroll->setShowMiniMap(config()->scrollBarMiniMap());
2368 m_toggleScrollBarMiniMap->setChecked(config()->scrollBarMiniMap());
2369
2370 // scrollbar mini-map - (whole document)
2371 m_viewInternal->m_lineScroll->setMiniMapAll(config()->scrollBarMiniMapAll());
2372 // m_toggleScrollBarMiniMapAll->setChecked( config()->scrollBarMiniMapAll() );
2373
2374 // scrollbar mini-map.width
2375 m_viewInternal->m_lineScroll->setMiniMapWidth(config()->scrollBarMiniMapWidth());
2376
2377 // misc edit
2378 m_toggleBlockSelection->setChecked(blockSelection());
2379 m_toggleInsert->setChecked(isOverwriteMode());
2380
2381 updateFoldingConfig();
2382
2383 // bookmark
2384 m_bookmarks->setSorting((KateBookmarks::Sorting)config()->bookmarkSort());
2385
2386 m_viewInternal->setAutoCenterLines(viewLines: config()->autoCenterLines());
2387
2388 for (const auto &input : m_viewInternal->m_inputModes) {
2389 input->updateConfig();
2390 }
2391
2392 setInputMode(mode: config()->inputMode(), rememberInConfig: false /* don't remember in config for these calls */);
2393
2394 reflectOnTheFlySpellCheckStatus(enabled: doc()->isOnTheFlySpellCheckingEnabled());
2395
2396 // register/unregister word completion...
2397 bool wc = config()->wordCompletion();
2398 if (wc != isCompletionModelRegistered(model: KTextEditor::EditorPrivate::self()->wordCompletionModel())) {
2399 if (wc) {
2400 registerCompletionModel(model: KTextEditor::EditorPrivate::self()->wordCompletionModel());
2401 } else {
2402 unregisterCompletionModel(model: KTextEditor::EditorPrivate::self()->wordCompletionModel());
2403 }
2404 }
2405
2406 bool kc = config()->keywordCompletion();
2407 if (kc != isCompletionModelRegistered(model: KTextEditor::EditorPrivate::self()->keywordCompletionModel())) {
2408 if (kc) {
2409 registerCompletionModel(model: KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2410 } else {
2411 unregisterCompletionModel(model: KTextEditor::EditorPrivate::self()->keywordCompletionModel());
2412 }
2413 }
2414
2415 m_cut->setEnabled(doc()->isReadWrite() && (selection() || m_config->smartCopyCut()));
2416 m_copy->setEnabled(selection() || m_config->smartCopyCut());
2417
2418 m_accessibilityEnabled = m_config->value(key: KateViewConfig::EnableAccessibility).toBool();
2419
2420 // if not disabled, update status bar
2421 if (m_statusBar) {
2422 m_statusBar->updateStatus();
2423 }
2424
2425 // now redraw...
2426 m_viewInternal->cache()->clear();
2427 tagAll();
2428 updateView(changed: true);
2429
2430 Q_EMIT configChanged(view: this);
2431}
2432
2433void KTextEditor::ViewPrivate::updateDocumentConfig()
2434{
2435 if (m_startingUp) {
2436 return;
2437 }
2438
2439 m_updatingDocumentConfig = true;
2440
2441 m_setEndOfLine->setCurrentItem(doc()->config()->eol());
2442
2443 m_addBom->setChecked(doc()->config()->bom());
2444
2445 m_updatingDocumentConfig = false;
2446
2447 // maybe block selection or wrap-cursor mode changed
2448 ensureCursorColumnValid();
2449
2450 // first change this
2451 m_renderer->setTabWidth(doc()->config()->tabWidth());
2452 m_renderer->setIndentWidth(doc()->config()->indentationWidth());
2453
2454 // now redraw...
2455 m_viewInternal->cache()->clear();
2456 tagAll();
2457 updateView(changed: true);
2458}
2459
2460void KTextEditor::ViewPrivate::updateRendererConfig()
2461{
2462 if (m_startingUp) {
2463 return;
2464 }
2465
2466 m_toggleWWMarker->setChecked(m_renderer->config()->wordWrapMarker());
2467
2468 m_viewInternal->updateBracketMarkAttributes();
2469 m_viewInternal->updateBracketMarks();
2470
2471 // now redraw...
2472 m_viewInternal->cache()->clear();
2473 tagAll();
2474 m_viewInternal->updateView(changed: true);
2475
2476 // update the left border right, for example linenumbers
2477 m_viewInternal->m_leftBorder->updateFont();
2478 m_viewInternal->m_leftBorder->repaint();
2479
2480 m_viewInternal->m_lineScroll->queuePixmapUpdate();
2481
2482 currentInputMode()->updateRendererConfig();
2483
2484 // @@ showIndentLines is not cached anymore.
2485 // m_renderer->setShowIndentLines (m_renderer->config()->showIndentationLines());
2486 Q_EMIT configChanged(view: this);
2487}
2488
2489void KTextEditor::ViewPrivate::updateFoldingConfig()
2490{
2491 // folding bar
2492 m_viewInternal->m_leftBorder->setFoldingMarkersOn(config()->foldingBar());
2493 m_toggleFoldingMarkers->setChecked(config()->foldingBar());
2494
2495 if (hasCommentInFirstLine(doc: m_doc)) {
2496 if (config()->foldFirstLine() && !m_autoFoldedFirstLine) {
2497 foldLine(line: 0);
2498 m_autoFoldedFirstLine = true;
2499 } else if (!config()->foldFirstLine() && m_autoFoldedFirstLine) {
2500 unfoldLine(line: 0);
2501 m_autoFoldedFirstLine = false;
2502 }
2503 } else {
2504 m_autoFoldedFirstLine = false;
2505 }
2506
2507#if 0
2508 // FIXME: FOLDING
2509 const QStringList l = {
2510 QStringLiteral("folding_toplevel")
2511 , QStringLiteral("folding_expandtoplevel")
2512 , QStringLiteral("folding_toggle_current")
2513 , QStringLiteral("folding_toggle_in_current")
2514 };
2515
2516 QAction *a = 0;
2517 for (int z = 0; z < l.size(); z++)
2518 if ((a = actionCollection()->action(l[z].toAscii().constData()))) {
2519 a->setEnabled(doc()->highlight() && doc()->highlight()->allowsFolding());
2520 }
2521#endif
2522}
2523
2524void KTextEditor::ViewPrivate::ensureCursorColumnValid()
2525{
2526 KTextEditor::Cursor c = m_viewInternal->cursorPosition();
2527
2528 // make sure the cursor is valid:
2529 // - in block selection mode or if wrap cursor is off, the column is arbitrary
2530 // - otherwise: it's bounded by the line length
2531 if (!blockSelection() && wrapCursor() && (!c.isValid() || c.column() > doc()->lineLength(line: c.line()))) {
2532 c.setColumn(doc()->lineLength(line: cursorPosition().line()));
2533 setCursorPosition(c);
2534 }
2535}
2536
2537// BEGIN EDIT STUFF
2538void KTextEditor::ViewPrivate::editStart()
2539{
2540 m_viewInternal->editStart();
2541}
2542
2543void KTextEditor::ViewPrivate::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom)
2544{
2545 m_viewInternal->editEnd(editTagLineStart, editTagLineEnd, tagFrom);
2546 textFolding().editEnd(startLine: editTagLineStart, endLine: editTagLineEnd, isLineFoldingStart: [this](int line) {
2547 return m_doc->buffer().isFoldingStartingOnLine(startLine: line).first;
2548 });
2549}
2550
2551void KTextEditor::ViewPrivate::editSetCursor(const KTextEditor::Cursor cursor)
2552{
2553 m_viewInternal->editSetCursor(cursor);
2554}
2555// END
2556
2557// BEGIN TAG & CLEAR
2558bool KTextEditor::ViewPrivate::tagLine(const KTextEditor::Cursor virtualCursor)
2559{
2560 return m_viewInternal->tagLine(virtualCursor);
2561}
2562
2563bool KTextEditor::ViewPrivate::tagRange(KTextEditor::Range range, bool realLines)
2564{
2565 return m_viewInternal->tagRange(range, realCursors: realLines);
2566}
2567
2568bool KTextEditor::ViewPrivate::tagLines(KTextEditor::LineRange lineRange, bool realLines)
2569{
2570 return m_viewInternal->tagLines(start: lineRange.start(), end: lineRange.end(), realLines);
2571}
2572
2573bool KTextEditor::ViewPrivate::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors)
2574{
2575 return m_viewInternal->tagLines(start, end, realCursors);
2576}
2577
2578void KTextEditor::ViewPrivate::tagAll()
2579{
2580 m_viewInternal->tagAll();
2581}
2582
2583void KTextEditor::ViewPrivate::clear()
2584{
2585 m_viewInternal->clear();
2586}
2587
2588void KTextEditor::ViewPrivate::repaintText(bool paintOnlyDirty)
2589{
2590 if (paintOnlyDirty) {
2591 m_viewInternal->updateDirty();
2592 } else {
2593 m_viewInternal->update();
2594 }
2595}
2596
2597void KTextEditor::ViewPrivate::updateView(bool changed)
2598{
2599 // qCDebug(LOG_KTE) << "KTextEditor::ViewPrivate::updateView";
2600 m_viewInternal->updateView(changed);
2601 m_viewInternal->m_leftBorder->update();
2602}
2603
2604// END
2605
2606void KTextEditor::ViewPrivate::slotHlChanged()
2607{
2608 KateHighlighting *hl = doc()->highlight();
2609 bool ok(!hl->getCommentStart(attrib: 0).isEmpty() || !hl->getCommentSingleLineStart(attrib: 0).isEmpty());
2610
2611 if (actionCollection()->action(QStringLiteral("tools_comment"))) {
2612 actionCollection()->action(QStringLiteral("tools_comment"))->setEnabled(ok);
2613 }
2614
2615 if (actionCollection()->action(QStringLiteral("tools_uncomment"))) {
2616 actionCollection()->action(QStringLiteral("tools_uncomment"))->setEnabled(ok);
2617 }
2618
2619 if (actionCollection()->action(QStringLiteral("tools_toggle_comment"))) {
2620 actionCollection()->action(QStringLiteral("tools_toggle_comment"))->setEnabled(ok);
2621 }
2622
2623 // show folding bar if "view defaults" says so, otherwise enable/disable only the menu entry
2624 updateFoldingConfig();
2625}
2626
2627int KTextEditor::ViewPrivate::virtualCursorColumn() const
2628{
2629 return doc()->toVirtualColumn(m_viewInternal->cursorPosition());
2630}
2631
2632void KTextEditor::ViewPrivate::notifyMousePositionChanged(const KTextEditor::Cursor newPosition)
2633{
2634 Q_EMIT mousePositionChanged(view: this, newPosition);
2635}
2636
2637// BEGIN KTextEditor::SelectionInterface stuff
2638
2639bool KTextEditor::ViewPrivate::setSelection(KTextEditor::Range selection)
2640{
2641 // anything to do?
2642 if (selection == m_selection) {
2643 return true;
2644 }
2645
2646 // backup old range
2647 KTextEditor::Range oldSelection = m_selection;
2648
2649 // set new range
2650 m_selection.setRange(selection.isEmpty() ? KTextEditor::Range::invalid() : selection);
2651
2652 // trigger update of correct area
2653 tagSelection(oldSelection);
2654 repaintText(paintOnlyDirty: true);
2655
2656 // emit holy signal
2657 Q_EMIT selectionChanged(view: this);
2658
2659 // be done
2660 return true;
2661}
2662
2663bool KTextEditor::ViewPrivate::clearSelection()
2664{
2665 return clearSelection(redraw: true);
2666}
2667
2668bool KTextEditor::ViewPrivate::clearSelection(bool redraw, bool finishedChangingSelection)
2669{
2670 // no selection, nothing to do...
2671 if (!selection()) {
2672 return false;
2673 }
2674
2675 // backup old range
2676 KTextEditor::Range oldSelection = m_selection;
2677
2678 // invalidate current selection
2679 m_selection.setRange(KTextEditor::Range::invalid());
2680
2681 // trigger update of correct area
2682 tagSelection(oldSelection);
2683 if (redraw) {
2684 repaintText(paintOnlyDirty: true);
2685 }
2686
2687 // emit holy signal
2688 if (finishedChangingSelection) {
2689 Q_EMIT selectionChanged(view: this);
2690 }
2691
2692 m_viewInternal->m_selChangedByUser = false;
2693 // be done
2694 return true;
2695}
2696
2697bool KTextEditor::ViewPrivate::selection() const
2698{
2699 if (!wrapCursor()) {
2700 return m_selection != KTextEditor::Range::invalid();
2701 } else {
2702 return m_selection.toRange().isValid();
2703 }
2704}
2705
2706QString KTextEditor::ViewPrivate::selectionText() const
2707{
2708 if (blockSelect) {
2709 return doc()->text(range: m_selection, blockwise: blockSelect);
2710 }
2711
2712 QVarLengthArray<KTextEditor::Range, 16>