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