17#include "kateviewinternal.h"
19#include "kateabstractinputmode.h"
20#include "kateabstractinputmodefactory.h"
21#include "katebuffer.h"
22#include "katecompletionwidget.h"
23#include "kateconfig.h"
24#include "kateglobal.h"
25#include "katehighlight.h"
26#include "katelayoutcache.h"
27#include "katemessagewidget.h"
28#include "katepartdebug.h"
29#include "katerenderer.h"
30#include "katetextanimation.h"
31#include "katetextpreview.h"
33#include "kateviewaccessible.h"
34#include "kateviewhelpers.h"
35#include "spellcheck/spellingmenu.h"
38#include <ktexteditor/documentcursor.h>
39#include <ktexteditor/inlinenoteprovider.h>
40#include <ktexteditor/movingrange.h>
41#include <ktexteditor/texthintinterface.h>
44#include <QApplication>
57static const bool debugPainting =
false;
62 ZoomEventFilter() =
default;
67 if (modState == modifier) {
68 if (m_lastWheelEvent.isValid()) {
69 const qint64 deltaT = m_lastWheelEvent.elapsed();
72 if (m_lastWheelEventUnmodified && deltaT < 200) {
74 }
else if (deltaT > 1000) {
85 m_lastWheelEventUnmodified =
false;
89 modState &= ~modifier;
90 e->setModifiers(modState);
94 m_lastWheelEventUnmodified =
true;
97 m_lastWheelEvent.start();
100 return !m_ignoreZoom && modState == modifier;
104 QElapsedTimer m_lastWheelEvent;
105 bool m_ignoreZoom =
false;
106 bool m_lastWheelEventUnmodified =
false;
109KateViewInternal::KateViewInternal(KTextEditor::ViewPrivate *view)
111 , editSessionNumber(0)
112 , editIsRunning(false)
114 , m_cursor(&doc()->buffer(),
KTextEditor::
Cursor(0, 0), Kate::TextCursor::MoveOnInsert)
116 , m_possibleTripleClick(false)
129 , m_startPos(&doc()->buffer(),
KTextEditor::
Cursor(0, 0), Kate::TextCursor::StayOnInsert)
131 , m_visibleLineCount(0)
132 , m_madeVisible(false)
133 , m_shiftKeyPressed(false)
134 , m_autoCenterLines(0)
135 , m_minLinesVisible(0)
136 , m_selChangedByUser(false)
137 , m_selectAnchor(-1, -1)
142 , m_cachedMaxStartPos(-1, -1)
143 , m_dragScrollTimer(this)
144 , m_scrollTimer(this)
145 , m_cursorTimer(this)
146 , m_textHintTimer(this)
147 , m_textHintDelay(500)
148 , m_textHintPos(-1, -1)
149 , m_imPreeditRange(nullptr)
159 setMinimumSize(0, 0);
167 m_bm->setView(m_view);
168 m_bmStart->setView(m_view);
169 m_bmEnd->setView(m_view);
170 m_bm->setAttributeOnlyForViews(
true);
171 m_bmStart->setAttributeOnlyForViews(
true);
172 m_bmEnd->setAttributeOnlyForViews(
true);
175 m_bm->setZDepth(-1000.0);
176 m_bmStart->setZDepth(-1000.0);
177 m_bmEnd->setZDepth(-1000.0);
180 updateBracketMarkAttributes();
186 m_lineScroll->show();
187 m_lineScroll->setTracking(
true);
193 auto viewScrollLinesSlot = qOverload<int>(&KateViewInternal::scrollLines);
195 connect(m_lineScroll, &KateScrollBar::sliderMMBMoved,
this, viewScrollLinesSlot);
211 m_scroller->setScrollerProperties(prop);
217 m_scroller->grabGesture(
this);
220 if (m_view->dynWordWrap()) {
221 m_columnScroll->hide();
223 m_columnScroll->show();
226 m_columnScroll->setTracking(
true);
233 m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height());
236 if (m_view->dynWordWrap()) {
242 cache()->setWrap(m_view->dynWordWrap());
247 m_leftBorder =
new KateIconBorder(
this, m_view);
248 m_leftBorder->show();
253 m_displayCursor.setPosition(0, 0);
255 setAcceptDrops(
true);
257 m_zoomEventFilter.reset(
new ZoomEventFilter());
259 installEventFilter(
this);
263 setCursor(m_mouseCursor);
266 setMouseTracking(
true);
268 m_dragInfo.state = diNone;
271 connect(&m_dragScrollTimer, &
QTimer::timeout,
this, &KateViewInternal::doDragScroll);
273 connect(&m_scrollTimer, &
QTimer::timeout,
this, &KateViewInternal::scrollTimeout);
275 connect(&m_cursorTimer, &
QTimer::timeout,
this, &KateViewInternal::cursorTimeout);
277 connect(&m_textHintTimer, &
QTimer::timeout,
this, &KateViewInternal::textHintTimeout);
282#ifndef QT_NO_ACCESSIBILITY
292KateViewInternal::~KateViewInternal()
294#ifndef QT_NO_ACCESSIBILITY
299 delete m_textAnimation;
303 m_leftBorder =
nullptr;
306void KateViewInternal::dynWrapChanged()
308 m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height());
309 if (view()->dynWordWrap()) {
310 m_columnScroll->hide();
315 m_columnScroll->show();
319 cache()->setWrap(view()->dynWordWrap());
322 if (view()->dynWordWrap()) {
332 if (!cache()->viewCacheLineCount()) {
333 return KTextEditor::Cursor();
336 for (
int i = qMin(linesDisplayed() - 1, cache()->viewCacheLineCount() - 1); i >= 0; i--) {
337 const KateTextLayout &thisLine = cache()->viewLine(i);
339 if (thisLine.line() == -1) {
343 if (thisLine.virtualLine() >= view()->textFolding().visibleLines()) {
345 return KTextEditor::Cursor(view()->textFolding().visibleLines() - 1,
346 doc()->lineLength(view()->textFolding().visibleLineToLine(view()->textFolding().visibleLines() - 1)));
349 return KTextEditor::Cursor(thisLine.virtualLine(), thisLine.wrap() ? thisLine.
endCol() - 1 : thisLine.
endCol());
353 return KTextEditor::Cursor();
356int KateViewInternal::endLine()
const
358 return endPos().line();
364 return KateTextLayout::invalid();
367 int range =
y / renderer()->lineHeight();
370 if (range >= 0 && range < cache()->viewCacheLineCount()) {
371 return cache()->viewLine(range);
374 return KateTextLayout::invalid();
377int KateViewInternal::lineToY(
int viewLine)
const
379 return (viewLine - startLine()) * renderer()->lineHeight();
382void KateViewInternal::slotIncFontSizes(qreal step)
384 renderer()->increaseFontSizes(step);
387void KateViewInternal::slotDecFontSizes(qreal step)
389 renderer()->decreaseFontSizes(step);
392void KateViewInternal::slotResetFontSizes()
394 renderer()->resetFontSizes();
400void KateViewInternal::scrollLines(
int line)
402 KTextEditor::Cursor newPos(line, 0);
407void KateViewInternal::scrollViewLines(
int offset)
409 KTextEditor::Cursor c = viewLineOffset(startPos(), offset);
412 bool blocked = m_lineScroll->blockSignals(
true);
413 m_lineScroll->setValue(startLine());
414 m_lineScroll->blockSignals(blocked);
417void KateViewInternal::scrollAction(
int action)
446void KateViewInternal::scrollNextPage()
448 scrollViewLines(qMax(linesDisplayed() - 1, 0));
451void KateViewInternal::scrollPrevPage()
453 scrollViewLines(-qMax(linesDisplayed() - 1, 0));
456void KateViewInternal::scrollPrevLine()
461void KateViewInternal::scrollNextLine()
468 cache()->setAcceptDirtyLayouts(
true);
470 if (m_cachedMaxStartPos.line() == -1 || changed) {
471 KTextEditor::Cursor end(view()->textFolding().visibleLines() - 1,
472 doc()->lineLength(view()->textFolding().visibleLineToLine(view()->textFolding().visibleLines() - 1)));
474 if (view()->config()->scrollPastEnd()) {
475 m_cachedMaxStartPos = viewLineOffset(end, -m_minLinesVisible);
477 m_cachedMaxStartPos = viewLineOffset(end, -(linesDisplayed() - 1));
481 cache()->setAcceptDirtyLayouts(
false);
483 return m_cachedMaxStartPos;
487void KateViewInternal::scrollPos(
KTextEditor::Cursor &c,
bool force,
bool calledExternally,
bool emitSignals)
489 if (!force && ((!view()->dynWordWrap() && c.
line() == startLine()) || c == startPos())) {
497 KTextEditor::Cursor limit = maxStartPos();
502 if (!force && ((!view()->dynWordWrap() && c.
line() == startLine()) || c == startPos())) {
507 int viewLinesScrolled = 0;
512 bool viewLinesScrolledUsable = !force && (c.
line() >= startLine() - linesDisplayed() - 1) && (c.
line() <= endLine() + linesDisplayed() + 1);
514 if (viewLinesScrolledUsable) {
515 viewLinesScrolled = cache()->displayViewLine(c);
518 m_startPos.setPosition(c);
521 m_madeVisible =
false;
523 if (viewLinesScrolledUsable) {
524 int lines = linesDisplayed();
525 if (view()->textFolding().visibleLines() < lines) {
526 KTextEditor::Cursor end(view()->textFolding().visibleLines() - 1,
527 doc()->lineLength(view()->textFolding().visibleLineToLine(view()->textFolding().visibleLines() - 1)));
528 lines = qMin(linesDisplayed(), cache()->displayViewLine(end) + 1);
531 Q_ASSERT(lines >= 0);
533 if (!calledExternally && qAbs(viewLinesScrolled) < lines &&
539 updateView(
false, viewLinesScrolled);
541 int scrollHeight = -(viewLinesScrolled * (int)renderer()->lineHeight());
545 m_leftBorder->scroll(0, scrollHeight);
548 Q_EMIT view()->verticalScrollPositionChanged(m_view, c);
549 Q_EMIT view()->displayRangeChanged(m_view);
557 m_leftBorder->update();
559 Q_EMIT view()->verticalScrollPositionChanged(m_view, c);
560 Q_EMIT view()->displayRangeChanged(m_view);
564void KateViewInternal::scrollColumns(
int x)
570 if (
x > m_columnScroll->maximum()) {
571 x = m_columnScroll->maximum();
578 int dx = startX() -
x;
581 if (qAbs(dx) <
width()) {
588 Q_EMIT view()->horizontalScrollPositionChanged(m_view);
589 Q_EMIT view()->displayRangeChanged(m_view);
591 bool blocked = m_columnScroll->blockSignals(
true);
592 m_columnScroll->setValue(startX());
593 m_columnScroll->blockSignals(blocked);
597void KateViewInternal::updateView(
bool changed,
int viewLinesScrolled)
599 if (!
isVisible() && !viewLinesScrolled && !changed) {
603 view()->doc()->delayAutoReload();
604 bool blocked = m_lineScroll->blockSignals(
true);
606 int wrapWidth =
width();
607 if (view()->config()->dynWrapAtStaticMarker() && view()->config()->dynWordWrap()) {
610 s.
fill(QLatin1Char(
'5'), view()->doc()->config()->wordWrapAt());
611 wrapWidth = qMin(
width(),
static_cast<int>(renderer()->currentFontMetrics().boundingRect(s).
width()));
614 if (wrapWidth != cache()->viewWidth()) {
615 cache()->setViewWidth(wrapWidth);
624 int newSize = (qMax(0,
height()) / renderer()->lineHeight()) + 1;
625 cache()->updateViewCache(startPos(), newSize, viewLinesScrolled);
626 m_visibleLineCount = newSize;
628 KTextEditor::Cursor maxStart = maxStartPos(changed);
629 int maxLineScrollRange = maxStart.
line();
630 if (view()->dynWordWrap() && maxStart.
column() != 0) {
631 maxLineScrollRange++;
633 m_lineScroll->setRange(0, maxLineScrollRange);
635 m_lineScroll->setValue(startLine());
636 m_lineScroll->setSingleStep(1);
637 m_lineScroll->setPageStep(qMax(0,
height()) / renderer()->lineHeight());
638 m_lineScroll->blockSignals(blocked);
640 KateViewConfig::ScrollbarMode show_scrollbars =
static_cast<KateViewConfig::ScrollbarMode
>(view()->config()->showScrollbars());
642 bool visible = ((show_scrollbars == KateViewConfig::AlwaysOn) || ((show_scrollbars == KateViewConfig::ShowWhenNeeded) && (maxLineScrollRange != 0)));
645 m_lineScroll->setVisible(
visible);
647 if (!view()->dynWordWrap()) {
648 int max = maxLen(startLine()) -
width();
658 blocked = m_columnScroll->blockSignals(
true);
661 m_columnScroll->setDisabled(max == 0);
663 visible = ((show_scrollbars == KateViewConfig::AlwaysOn) || ((show_scrollbars == KateViewConfig::ShowWhenNeeded) && (max != 0)));
665 m_columnScroll->setVisible(
visible);
667 m_columnScroll->setRange(0, max + (renderer()->spaceWidth() / 2));
669 m_columnScroll->setValue(startX());
672 m_columnScroll->setSingleStep(renderer()->currentFontMetrics().horizontalAdvance(QLatin1Char(
'a')));
673 m_columnScroll->setPageStep(
width());
675 m_columnScroll->blockSignals(blocked);
677 visible_dummy =
false;
680 m_dummy->setVisible(visible_dummy);
691void KateViewInternal::makeVisible(
const KTextEditor::Cursor c,
int endCol,
bool force,
bool center,
bool calledExternally)
698 const int lnDisp = linesDisplayed();
699 const int viewLine = cache()->displayViewLine(c,
true);
700 const bool curBelowScreen = (viewLine == -2);
703 KTextEditor::Cursor
scroll = c;
704 scrollPos(
scroll, force, calledExternally);
705 }
else if (center && (c < startPos() || c > endPos())) {
706 KTextEditor::Cursor
scroll = viewLineOffset(c, -
int(lnDisp) / 2);
707 scrollPos(
scroll,
false, calledExternally);
708 }
else if ((viewLine >= (lnDisp - m_minLinesVisible)) || (curBelowScreen)) {
709 KTextEditor::Cursor
scroll = viewLineOffset(c, -(lnDisp - m_minLinesVisible - 1));
710 scrollPos(
scroll,
false, calledExternally);
711 }
else if (c < viewLineOffset(startPos(), m_minLinesVisible)) {
712 KTextEditor::Cursor
scroll = viewLineOffset(c, -m_minLinesVisible);
713 scrollPos(
scroll,
false, calledExternally);
716 KTextEditor::Cursor max = maxStartPos();
717 if (startPos() > max) {
718 scrollPos(max, max.
column(), calledExternally);
722 if (!view()->dynWordWrap() && (endCol != -1 || view()->wrapCursor())) {
723 KTextEditor::Cursor rc = toRealCursor(c);
724 int sX = renderer()->cursorToX(cache()->textLayout(rc), rc, !view()->wrapCursor());
726 int sXborder = sX - 8;
732 scrollColumns(sXborder);
733 }
else if (sX > startX() +
width()) {
734 scrollColumns(sX -
width() + 8);
738 m_madeVisible = !force;
740#ifndef QT_NO_ACCESSIBILITY
746void KateViewInternal::slotRegionVisibilityChanged()
756 m_cachedMaxStartPos.setLine(-1);
757 KTextEditor::Cursor max = maxStartPos();
758 if (startPos() > max) {
759 scrollPos(max,
false,
false,
false );
763 qint64 foldedRangeId = -1;
764 if (!view()->textFolding().isLineVisible(m_cursor.line(), &foldedRangeId)) {
765 KTextEditor::Range foldingRange = view()->textFolding().foldingRange(foldedRangeId);
769 updateCursor(foldingRange.
start(),
true);
773 updateCursor(m_cursor,
true);
778 m_leftBorder->update();
781 Q_EMIT view()->verticalScrollPositionChanged(m_view, max);
782 Q_EMIT view()->displayRangeChanged(m_view);
785void KateViewInternal::slotRegionBeginEndAddedRemoved(
unsigned int)
789 m_leftBorder->update();
792void KateViewInternal::showEvent(
QShowEvent *e)
802 Kate::TextLine kateLine = doc()->kateTextLine(position.
line());
803 *attrib = *m_view->renderer()->attribute(kateLine.
attribute(position.
column()));
807int KateViewInternal::linesDisplayed()
const
812 int fh = qMax(1, renderer()->lineHeight());
816 return qMax(1, (h - (h % fh)) / fh);
821 if (
cursor.line() >= doc()->lines()) {
822 return QPoint(-1, -1);
825 int viewLine = cache()->displayViewLine(realCursor ? toVirtualCursor(
cursor) :
cursor,
true);
827 if (viewLine < 0 || viewLine >= cache()->viewCacheLineCount()) {
828 return QPoint(-1, -1);
831 const int y = (int)viewLine * renderer()->lineHeight();
833 KateTextLayout
layout = cache()->viewLine(viewLine);
835 const auto textLength = doc()->lineLength(
cursor.line());
836 if (
cursor.column() > textLength) {
837 return QPoint(-1, -1);
844 if (!
layout.isRightToLeft() || (
layout.isRightToLeft() && view()->dynWordWrap())) {
849 x = (int)
layout.lineLayout().cursorToX(textLength -
cursor.column());
856 x += m_leftBorder->width();
864QPoint KateViewInternal::cursorCoordinates(
bool includeBorder)
const
866 return cursorToCoordinate(m_displayCursor,
false, includeBorder);
871 KTextEditor::Cursor c;
873 if (!m_bm->toRange().isValid()) {
877 Q_ASSERT(m_bmEnd->toRange().isValid());
878 Q_ASSERT(m_bmStart->toRange().isValid());
882 if (m_bmEnd->toRange().contains(m_cursor) || m_bmEnd->end() == m_cursor.toCursor()) {
883 c = m_bmStart->
start();
884 }
else if (m_bmStart->toRange().contains(m_cursor) || m_bmStart->end() == m_cursor.toCursor()) {
887 if (doc()->config()->ovr()) {
899class CalculatingCursor
905 CalculatingCursor(KateViewInternal *vi)
911 CalculatingCursor(KateViewInternal *vi,
const KTextEditor::Cursor c)
918 CalculatingCursor(KateViewInternal *vi,
int line,
int col)
919 : m_cursor(line, col)
925 virtual ~CalculatingCursor()
931 return m_cursor.line();
936 return m_cursor.column();
939 operator KTextEditor::Cursor()
const
944 virtual CalculatingCursor &operator+=(
int n) = 0;
946 virtual CalculatingCursor &operator-=(
int n) = 0;
948 CalculatingCursor &operator++()
950 return operator+=(1);
953 CalculatingCursor &operator--()
955 return operator-=(1);
960 m_cursor.setLine(qBound(0, line(),
int(doc()->lines() - 1)));
961 if (view()->wrapCursor()) {
962 m_cursor.setColumn(qBound(0, column(), doc()->lineLength(line())));
964 m_cursor.setColumn(qMax(0, column()));
969 void toEdge(KateViewInternal::Bias bias)
971 if (bias == KateViewInternal::left) {
972 m_cursor.setColumn(0);
973 }
else if (bias == KateViewInternal::right) {
974 m_cursor.setColumn(doc()->lineLength(line()));
980 return atEdge(KateViewInternal::left) || atEdge(KateViewInternal::right);
983 bool atEdge(KateViewInternal::Bias bias)
const
986 case KateViewInternal::left:
987 return column() == 0;
988 case KateViewInternal::none:
990 case KateViewInternal::right:
991 return column() >= doc()->lineLength(line());
1001 return line() >= 0 && line() < doc()->lines() && column() >= 0 && (!view()->wrapCursor() || column() <= doc()->lineLength(line()));
1003 KTextEditor::ViewPrivate *view()
1005 return m_vi->m_view;
1007 const KTextEditor::ViewPrivate *view()
const
1009 return m_vi->m_view;
1011 KTextEditor::DocumentPrivate *doc()
1013 return view()->doc();
1015 const KTextEditor::DocumentPrivate *doc()
const
1017 return view()->doc();
1019 KTextEditor::Cursor m_cursor;
1020 KateViewInternal *m_vi;
1023class BoundedCursor final :
public CalculatingCursor
1026 BoundedCursor(KateViewInternal *vi)
1027 : CalculatingCursor(vi)
1030 BoundedCursor(KateViewInternal *vi,
const KTextEditor::Cursor c)
1031 : CalculatingCursor(vi, c)
1034 BoundedCursor(KateViewInternal *vi,
int line,
int col)
1035 : CalculatingCursor(vi, line, col)
1038 CalculatingCursor &operator+=(
int n)
override
1040 KateLineLayout *thisLine = m_vi->cache()->line(line());
1041 if (!thisLine || !thisLine->isValid()) {
1042 qCWarning(LOG_KTE) <<
"Did not retrieve valid layout for line " << line();
1046 const bool wrapCursor = view()->wrapCursor();
1049 for (
int i = 0; i < n; i++) {
1050 if (column() >= thisLine->length()) {
1054 }
else if (view()->dynWordWrap()) {
1056 if (maxColumn == -1) {
1057 maxColumn = thisLine->length() + ((m_vi->width() - thisLine->widthOfLastLine()) / m_vi->renderer()->spaceWidth()) - 1;
1060 if (column() >= maxColumn) {
1061 m_cursor.setColumn(maxColumn);
1065 m_cursor.setColumn(column() + 1);
1068 m_cursor.setColumn(column() + 1);
1076 for (
int i = 0; i > n; i--) {
1077 if (column() >= thisLine->length()) {
1078 m_cursor.setColumn(column() - 1);
1079 }
else if (column() == 0) {
1090 CalculatingCursor &operator-=(
int n)
override
1092 return operator+=(-n);
1096class WrappingCursor final :
public CalculatingCursor
1099 WrappingCursor(KateViewInternal *vi)
1100 : CalculatingCursor(vi)
1103 WrappingCursor(KateViewInternal *vi,
const KTextEditor::Cursor c)
1104 : CalculatingCursor(vi, c)
1107 WrappingCursor(KateViewInternal *vi,
int line,
int col)
1108 : CalculatingCursor(vi, line, col)
1112 CalculatingCursor &operator+=(
int n)
override
1114 KateLineLayout *thisLine = m_vi->cache()->line(line());
1115 if (!thisLine || !thisLine->isValid()) {
1116 qCWarning(LOG_KTE) <<
"Did not retrieve a valid layout for line " << line();
1121 for (
int i = 0; i < n; i++) {
1122 if (column() >= thisLine->length()) {
1124 if (line() >= doc()->lines() - 1)
1131 m_cursor.setColumn(0);
1132 m_cursor.setLine(line() + 1);
1135 thisLine = m_vi->cache()->line(line());
1136 if (!thisLine || !thisLine->isValid()) {
1137 qCWarning(LOG_KTE) <<
"Did not retrieve a valid layout for line " << line();
1148 for (
int i = 0; i > n; i--) {
1149 if (column() == 0) {
1156 m_cursor.setLine(line() - 1);
1159 thisLine = m_vi->cache()->line(line());
1160 if (!thisLine || !thisLine->isValid()) {
1161 qCWarning(LOG_KTE) <<
"Did not retrieve a valid layout for line " << line();
1166 m_cursor.setColumn(thisLine->length());
1171 if (column() > thisLine->length()) {
1172 m_cursor.setColumn(column() - 1);
1182 CalculatingCursor &operator-=(
int n)
override
1184 return operator+=(-n);
1271class CamelCursor final :
public CalculatingCursor
1274 CamelCursor(KateViewInternal *vi,
const KTextEditor::Cursor c)
1275 : CalculatingCursor(vi, c)
1279 CalculatingCursor &operator+=(
int n)
override
1281 KateLineLayout *thisLine = m_vi->cache()->line(line());
1282 if (!thisLine || !thisLine->isValid()) {
1283 qCWarning(LOG_KTE) <<
"Did not retrieve valid layout for line " << line();
1287 auto isSurrogate = [](QChar c) {
1288 return c.isLowSurrogate() || c.isHighSurrogate();
1292 auto skipCaps = [](QStringView text,
int &col) {
1309 const QString &text = thisLine->textLine().
text();
1312 skipCaps(text, col);
1315 for (
int i = col; i < thisLine->length(); ++i) {
1316 const auto c = text.
at(i);
1317 if (isSurrogate(c)) {
1320 }
else if (c.isUpper() || !c.isLetterOrNumber()) {
1327 if (col < text.
size() && text.
at(col) == QLatin1Char(
'_')) {
1328 while (col < text.
size() && text.
at(col) == QLatin1Char(
'_')) {
1340 jump = col < 0 || (column() == col) ? (column() + 1) : col;
1341 m_cursor.setColumn(jump);
1345 auto skipCapsRev = [](QStringView text,
int &col) {
1347 while (col > 0 && text.
at(col).
isUpper()) {
1355 if (count >= 1 && col >= 0 && !text.
at(col).
isUpper()) {
1360 const QString &text = thisLine->textLine().
text();
1361 int col = std::min<int>(column(), text.
size() - 1);
1367 if (column() == text.
size()) {
1371 if (col >= 0 && text.
at(col).
isSpace()) {
1380 if (col > 0 && text.
at(col).
isSpace()) {
1381 while (text.
at(col).
isSpace() && col > 0) {
1387 if (col > 0 && text.
at(col) == QLatin1Char(
'_')) {
1388 while (col > 0 && text.
at(col) == QLatin1Char(
'_')) {
1393 if (col > 0 && text.
at(col).
isUpper()) {
1394 skipCapsRev(text, col);
1397 for (
int i = col; i > 0; --i) {
1398 const auto c = text.
at(i);
1399 if (isSurrogate(c)) {
1402 }
else if (c.isUpper() || !c.isLetterOrNumber()) {
1414 }
else if (col == column() && column() > 0) {
1415 jump = column() - 1;
1420 m_cursor.setColumn(jump);
1427 CalculatingCursor &operator-=(
int n)
override
1429 return operator+=(-n);
1433void KateViewInternal::moveChar(KateViewInternal::Bias bias,
bool sel)
1435 KTextEditor::Cursor c;
1436 if (view()->wrapCursor()) {
1437 c = WrappingCursor(
this, m_cursor) += bias;
1439 c = BoundedCursor(
this, m_cursor) += bias;
1442 const auto &sc = view()->m_secondaryCursors;
1443 QVarLengthArray<CursorPair, 16> multiCursors;
1444 const int lastLine = doc()->lastLine();
1445 bool shouldEnsureUniqueCursors =
false;
1446 for (
const auto &c : sc) {
1447 auto oldPos = c.cursor();
1448 if (view()->wrapCursor()) {
1449 c.pos->
setPosition(WrappingCursor(
this, oldPos) += bias);
1451 c.pos->
setPosition(BoundedCursor(
this, oldPos) += bias);
1453 const auto newPos = c.pos->toCursor();
1454 multiCursors.
push_back({.oldPos = oldPos, .newPos = newPos});
1456 if (!shouldEnsureUniqueCursors) {
1457 shouldEnsureUniqueCursors = newPos.line() == 0 || newPos.line() == lastLine;
1461 updateSelection(c, sel);
1463 updateSecondaryCursors(multiCursors, sel);
1464 if (shouldEnsureUniqueCursors) {
1465 view()->ensureUniqueCursors();
1469void KateViewInternal::cursorPrevChar(
bool sel)
1471 if (!view()->wrapCursor() && m_cursor.column() == 0) {
1475 moveChar(KateViewInternal::left, sel);
1478void KateViewInternal::cursorNextChar(
bool sel)
1480 moveChar(KateViewInternal::right, sel);
1483void KateViewInternal::wordPrev(
bool sel,
bool subword)
1485 auto characterAtPreviousColumn = [
this](KTextEditor::Cursor
cursor) -> QChar {
1486 return doc()->characterAt({
cursor.line(),
cursor.column() - 1});
1489 auto wordPrevious = [
this, &characterAtPreviousColumn, subword](KTextEditor::Cursor
cursor) -> KTextEditor::Cursor {
1490 WrappingCursor c(
this,
cursor);
1500 KateHighlighting *h = doc()->highlight();
1502 while (!c.atEdge(left) && (c.
column() > doc()->lineLength(c.
line()) || characterAtPreviousColumn(c).isSpace())) {
1506 if (c.atEdge(left)) {
1508 }
else if (h->isInWord(characterAtPreviousColumn(c))) {
1509 if (subword || doc()->config()->camelCursor()) {
1510 CamelCursor cc(
this,
cursor);
1514 while (!c.atEdge(left) && h->isInWord(characterAtPreviousColumn(c))) {
1519 while (!c.atEdge(left)
1520 && !h->isInWord(characterAtPreviousColumn(c))
1523 && !characterAtPreviousColumn(c).isSpace()) {
1531 const auto &secondaryCursors = view()->m_secondaryCursors;
1532 QVarLengthArray<CursorPair, 16> cursorsToUpdate;
1533 for (
const auto &
cursor : secondaryCursors) {
1534 auto oldPos =
cursor.cursor();
1535 auto newCursorPos = wordPrevious(
cursor.cursor());
1536 cursor.pos->setPosition(newCursorPos);
1537 cursorsToUpdate.
push_back({.oldPos = oldPos, .newPos = newCursorPos});
1541 const auto c = wordPrevious(m_cursor);
1542 updateSelection(c, sel);
1546 view()->ensureUniqueCursors();
1548 updateSecondaryCursors(cursorsToUpdate, sel);
1551void KateViewInternal::wordNext(
bool sel,
bool subword)
1553 auto nextWord = [
this, subword](KTextEditor::Cursor
cursor) -> KTextEditor::Cursor {
1554 WrappingCursor c(
this,
cursor);
1564 KateHighlighting *h = doc()->highlight();
1565 if (c.atEdge(right)) {
1567 }
else if (h->isInWord(doc()->characterAt(c))) {
1568 if (subword || doc()->config()->camelCursor()) {
1569 CamelCursor cc(
this,
cursor);
1573 while (!c.atEdge(right) && h->isInWord(doc()->characterAt(c))) {
1578 while (!c.atEdge(right)
1579 && !h->isInWord(doc()->characterAt(c))
1582 && !doc()->characterAt(c).isSpace()) {
1587 while (!c.atEdge(right) && doc()->characterAt(c).isSpace()) {
1594 const auto &secondaryCursors = view()->m_secondaryCursors;
1595 QVarLengthArray<CursorPair, 16> cursorsToUpdate;
1596 for (
const auto &
cursor : secondaryCursors) {
1597 auto oldPos =
cursor.cursor();
1598 auto newCursorPos = nextWord(
cursor.cursor());
1599 cursor.pos->setPosition(newCursorPos);
1600 cursorsToUpdate.
push_back({.oldPos = oldPos, .newPos = newCursorPos});
1604 const auto c = nextWord(m_cursor);
1605 updateSelection(c, sel);
1610 view()->ensureUniqueCursors();
1612 updateSecondaryCursors(cursorsToUpdate, sel);
1615void KateViewInternal::moveEdge(KateViewInternal::Bias bias,
bool sel)
1617 BoundedCursor c(
this, m_cursor);
1619 updateSelection(c, sel);
1625 if (view()->dynWordWrap() && currentLayout(
cursor).startCol()) {
1627 if (
cursor.column() != currentLayout(
cursor).startCol()) {
1628 KTextEditor::Cursor c = currentLayout(
cursor).
start();
1633 if (!doc()->config()->smartHome()) {
1634 BoundedCursor c(
this,
cursor);
1639 if (
cursor.line() < 0 ||
cursor.line() >= doc()->lines()) {
1643 Kate::TextLine l = doc()->kateTextLine(
cursor.line());
1645 KTextEditor::Cursor c =
cursor;
1648 if (lc < 0 || c.
column() == lc) {
1656void KateViewInternal::home(
bool sel)
1659 view()->ensureUniqueCursors(
true);
1660 const auto &secondaryCursors = view()->m_secondaryCursors;
1661 QVarLengthArray<CursorPair, 16> cursorsToUpdate;
1662 for (
const auto &c : secondaryCursors) {
1663 auto oldPos = c.cursor();
1665 auto newPos = moveCursorToLineStart(oldPos);
1667 cursorsToUpdate.
push_back({.oldPos = oldPos, .newPos = newPos});
1671 auto newPos = moveCursorToLineStart(m_cursor);
1672 if (newPos.isValid()) {
1673 updateSelection(newPos, sel);
1674 updateCursor(newPos,
true);
1676 updateSecondaryCursors(cursorsToUpdate, sel);
1683 if (view()->dynWordWrap() &&
layout.wrap()) {
1686 KTextEditor::Cursor c(
cursor.line(),
layout.endCol() - 1);
1691 if (!doc()->config()->smartHome()) {
1692 BoundedCursor c(
this,
cursor);
1697 if (
cursor.line() < 0 ||
cursor.line() >= doc()->lines()) {
1701 Kate::TextLine l = doc()->kateTextLine(
cursor.line());
1704 if (
cursor.column() == doc()->lineLength(
cursor.line())) {
1705 KTextEditor::Cursor c =
cursor;
1709 BoundedCursor c(
this,
cursor);
1715void KateViewInternal::end(
bool sel)
1718 view()->ensureUniqueCursors(
true);
1720 QVarLengthArray<CursorPair, 16> cursorsToUpdate;
1721 const auto &secondaryCursors = view()->m_secondaryCursors;
1722 for (
const auto &c : secondaryCursors) {
1723 auto oldPos = c.cursor();
1725 auto newPos = moveCursorToLineEnd(oldPos);
1727 cursorsToUpdate.
push_back({.oldPos = oldPos, .newPos = newPos});
1730 auto newPos = moveCursorToLineEnd(m_cursor);
1731 if (newPos.isValid()) {
1732 updateSelection(newPos, sel);
1733 updateCursor(newPos);
1736 updateSecondaryCursors(cursorsToUpdate, sel);
1742 return cache()->textLayout(c);
1747 int currentViewLine = cache()->
viewLine(c);
1749 if (currentViewLine) {
1750 return cache()->textLayout(c.
line(), currentViewLine - 1);
1752 return cache()->textLayout(view()->textFolding().visibleLineToLine(toVirtualCursor(c).line() - 1), -1);
1758 int currentViewLine = cache()->viewLine(c) + 1;
1760 const KateLineLayout *thisLine = cache()->line(c.
line());
1761 if (thisLine && currentViewLine >= thisLine->viewLineCount()) {
1762 currentViewLine = 0;
1763 return cache()->textLayout(view()->textFolding().visibleLineToLine(toVirtualCursor(c).line() + 1), currentViewLine);
1765 return cache()->textLayout(c.
line(), currentViewLine);
1779 if (!view()->dynWordWrap()) {
1780 KTextEditor::Cursor ret(qMin((
int)view()->textFolding().visibleLines() - 1, virtualCursor.
line() + offset), 0);
1782 if (ret.line() < 0) {
1787 int realLine = view()->textFolding().visibleLineToLine(ret.line());
1788 KateTextLayout t = cache()->textLayout(realLine, 0);
1789 Q_ASSERT(t.isValid());
1791 ret.setColumn(renderer()->xToCursor(t, m_preservedX, !view()->wrapCursor()).column());
1797 KTextEditor::Cursor realCursor = virtualCursor;
1798 realCursor.
setLine(view()->textFolding().visibleLineToLine(view()->textFolding().lineToVisibleLine(virtualCursor.
line())));
1800 int cursorViewLine = cache()->viewLine(realCursor);
1802 int currentOffset = 0;
1803 int virtualLine = 0;
1805 bool forwards = (offset > 0) ?
true : false;
1808 currentOffset = cache()->lastViewLine(realCursor.
line()) - cursorViewLine;
1809 if (offset <= currentOffset) {
1811 KateTextLayout thisLine = cache()->textLayout(realCursor.
line(), cursorViewLine + offset);
1812 Q_ASSERT(thisLine.virtualLine() == (
int)view()->textFolding().lineToVisibleLine(virtualCursor.
line()));
1813 return KTextEditor::Cursor(virtualCursor.
line(), thisLine.startCol());
1816 virtualLine = virtualCursor.
line() + 1;
1820 currentOffset = cursorViewLine;
1821 if (offset <= currentOffset) {
1823 KateTextLayout thisLine = cache()->textLayout(realCursor.
line(), cursorViewLine - offset);
1824 Q_ASSERT(thisLine.virtualLine() == (
int)view()->textFolding().lineToVisibleLine(virtualCursor.
line()));
1825 return KTextEditor::Cursor(virtualCursor.
line(), thisLine.startCol());
1828 virtualLine = virtualCursor.
line() - 1;
1833 while (virtualLine >= 0 && virtualLine < (
int)view()->textFolding().visibleLines()) {
1834 int realLine = view()->textFolding().visibleLineToLine(virtualLine);
1835 KateLineLayout *thisLine = cache()->line(realLine, virtualLine);
1840 for (
int i = 0; i < thisLine->viewLineCount(); ++i) {
1841 if (offset == currentOffset) {
1842 KateTextLayout thisViewLine = thisLine->viewLine(i);
1846 int requiredViewLine = cache()->lastViewLine(realLine) - thisViewLine.
viewLine();
1847 if (requiredViewLine != thisViewLine.
viewLine()) {
1848 thisViewLine = thisLine->viewLine(requiredViewLine);
1852 KTextEditor::Cursor ret(virtualLine, thisViewLine.startCol());
1856 realCursor = renderer()->xToCursor(thisViewLine, m_preservedX, !view()->wrapCursor());
1876 return KTextEditor::Cursor(view()->textFolding().visibleLines() - 1,
1877 doc()->lineLength(view()->textFolding().visibleLineToLine(view()->textFolding().visibleLines() - 1)));
1879 return KTextEditor::Cursor(0, 0);
1885 if (!view()->wrapCursor() && !range.wrap()) {
1889 int maxX = range.endX();
1891 if (maxX && range.wrap()) {
1892 QChar lastCharInLine = doc()->kateTextLine(range.line()).at(range.
endCol() - 1);
1893 maxX -= renderer()->currentFontMetrics().horizontalAdvance(lastCharInLine);
1901 int maxCol = range.
endCol();
1903 if (maxCol && range.wrap()) {
1910void KateViewInternal::cursorUp(
bool sel)
1912 if (!sel && view()->completionWidget()->isCompletionActive()) {
1913 view()->completionWidget()->cursorUp();
1922 for (
const auto &c : view()->m_secondaryCursors) {
1923 auto cursor = c.pos->toCursor();
1924 auto vCursor = toVirtualCursor(
cursor);
1927 if (vCursor.line() == 0 && (!view()->dynWordWrap() || cache()->viewLine(
cursor) == 0)) {
1928 auto newPos = moveCursorToLineStart(
cursor);
1930 auto newVcursor = toVirtualCursor(newPos);
1932 updateSecondarySelection(i,
cursor, newPos);
1934 view()->clearSecondarySelections();
1936 tagLines(newVcursor.line(), vCursor.line());
1941 auto lineLayout = currentLayout(
cursor);
1942 Q_ASSERT(lineLayout.line() ==
cursor.line());
1943 Q_ASSERT(lineLayout.startCol() <=
cursor.column());
1944 Q_ASSERT(!lineLayout.wrap() ||
cursor.column() < lineLayout.endCol());
1946 KateTextLayout pRange = previousLayout(
cursor);
1948 KTextEditor::Cursor newPos = renderer()->xToCursor(pRange, m_preservedX, !view()->wrapCursor());
1951 auto newVcursor = toVirtualCursor(newPos);
1953 updateSecondarySelection(i,
cursor, newPos);
1955 view()->clearSecondarySelections();
1957 tagLines(newVcursor.line(), vCursor.line());
1961 auto mergeOnFuncEnd = qScopeGuard([
this, sel] {
1965 view()->ensureUniqueCursors();
1972 Q_ASSERT(m_displayCursor.line() < view()->textFolding().visibleLines());
1975 if (m_displayCursor.line() == 0 && (!view()->dynWordWrap() || cache()->viewLine(m_cursor) == 0)) {
1976 auto newPos = moveCursorToLineStart(m_cursor);
1978 updateSelection(newPos, sel);
1979 updateCursor(newPos,
true);
1984 KateTextLayout thisLine = currentLayout(m_cursor);
1986 KateTextLayout pRange = previousLayout(m_cursor);
1989 Q_ASSERT(m_cursor.line() == thisLine.line());
1990 Q_ASSERT(m_cursor.column() >= thisLine.startCol());
1991 Q_ASSERT(!thisLine.wrap() || m_cursor.column() < thisLine.
endCol());
1993 KTextEditor::Cursor c = renderer()->xToCursor(pRange, m_preservedX, !view()->wrapCursor());
1995 updateSelection(c, sel);
1999void KateViewInternal::cursorDown(
bool sel)
2001 if (!sel && view()->completionWidget()->isCompletionActive()) {
2002 view()->completionWidget()->cursorDown();
2010 for (
const auto &c : view()->m_secondaryCursors) {
2011 auto cursor = c.cursor();
2012 auto vCursor = toVirtualCursor(
cursor);
2015 if ((vCursor.line() >= view()->textFolding().visibleLines() - 1)
2016 && (!view()->dynWordWrap() || cache()->viewLine(
cursor) == cache()->lastViewLine(
cursor.line()))) {
2017 KTextEditor::Cursor newPos = moveCursorToLineEnd(
cursor);
2020 updateSecondarySelection(i,
cursor, newPos);
2022 view()->clearSecondarySelections();
2024 auto vNewPos = toVirtualCursor(newPos);
2025 tagLines(vCursor.line(), vNewPos.line());
2030 KateTextLayout thisLine = currentLayout(
cursor);
2032 KateTextLayout nRange = nextLayout(
cursor);
2035 Q_ASSERT((
cursor.line() == thisLine.line()) && (
cursor.column() >= thisLine.startCol()) && (!thisLine.wrap() ||
cursor.column() < thisLine.
endCol()));
2036 KTextEditor::Cursor newPos = renderer()->xToCursor(nRange, m_preservedX, !view()->wrapCursor());
2040 updateSecondarySelection(i,
cursor, newPos);
2042 view()->clearSecondarySelections();
2044 auto vNewPos = toVirtualCursor(newPos);
2045 tagLines(vCursor.line(), vNewPos.line());
2048 auto mergeOnFuncEnd = qScopeGuard([
this, sel] {
2052 view()->ensureUniqueCursors();
2059 if ((m_displayCursor.line() >= view()->textFolding().visibleLines() - 1)
2060 && (!view()->dynWordWrap() || cache()->viewLine(m_cursor) == cache()->lastViewLine(m_cursor.line()))) {
2061 auto newPos = moveCursorToLineEnd(m_cursor);
2063 updateSelection(newPos, sel);
2064 updateCursor(newPos);
2069 KateTextLayout thisLine = currentLayout(m_cursor);
2071 KateTextLayout nRange = nextLayout(m_cursor);
2074 Q_ASSERT((m_cursor.line() == thisLine.line()) && (m_cursor.column() >= thisLine.startCol()) && (!thisLine.wrap() || m_cursor.column() < thisLine.
endCol()));
2076 KTextEditor::Cursor c = renderer()->xToCursor(nRange, m_preservedX, !view()->wrapCursor());
2078 updateSelection(c, sel);
2082void KateViewInternal::cursorToMatchingBracket(
bool sel)
2084 KTextEditor::Cursor c = findMatchingBracket();
2087 updateSelection(c, sel);
2092void KateViewInternal::topOfView(
bool sel)
2094 view()->clearSecondaryCursors();
2095 KTextEditor::Cursor c = viewLineOffset(startPos(), m_minLinesVisible);
2096 updateSelection(toRealCursor(c), sel);
2097 updateCursor(toRealCursor(c));
2100void KateViewInternal::bottomOfView(
bool sel)
2102 view()->clearSecondaryCursors();
2103 KTextEditor::Cursor c = viewLineOffset(endPos(), -m_minLinesVisible);
2104 updateSelection(toRealCursor(c), sel);
2105 updateCursor(toRealCursor(c));
2109void KateViewInternal::scrollLines(
int lines,
bool sel)
2111 KTextEditor::Cursor c = viewLineOffset(m_displayCursor, lines,
true);
2114 c.
setLine(view()->textFolding().visibleLineToLine(c.
line()));
2116 updateSelection(c, sel);
2121void KateViewInternal::scrollUp()
2123 KTextEditor::Cursor newPos = viewLineOffset(startPos(), -1);
2127void KateViewInternal::scrollDown()
2129 KTextEditor::Cursor newPos = viewLineOffset(startPos(), 1);
2133void KateViewInternal::setAutoCenterLines(
int viewLines,
bool updateView)
2135 m_autoCenterLines = viewLines;
2136 m_minLinesVisible = qMin(
int((linesDisplayed() - 1) / 2), m_autoCenterLines);
2138 KateViewInternal::updateView();
2142void KateViewInternal::pageUp(
bool sel,
bool half)
2144 if (view()->isCompletionActive()) {
2145 view()->completionWidget()->pageUp();
2148 view()->clearSecondaryCursors();
2152 if (!view()->visibleRange().contains(m_displayCursor)) {
2153 scrollLines(m_displayCursor.line());
2157 int viewLine = cache()->displayViewLine(m_displayCursor);
2158 bool atTop = startPos().atStartOfDocument();
2161 int lineadj = m_minLinesVisible;
2165 linesToScroll = -qMax((linesDisplayed() - 1) - lineadj, 0);
2167 linesToScroll = -qMax((linesDisplayed() / 2 - 1) - lineadj, 0);
2172 if (!doc()->pageUpDownMovesCursor() && !atTop) {
2173 KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1);
2174 scrollPos(newStartPos);
2177 KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine,
true));
2179 KateTextLayout newLine = cache()->textLayout(newPos);
2181 newPos = renderer()->xToCursor(newLine, m_preservedX, !view()->wrapCursor());
2184 updateSelection(newPos, sel);
2185 updateCursor(newPos);
2188 scrollLines(linesToScroll, sel);
2192void KateViewInternal::pageDown(
bool sel,
bool half)
2194 if (view()->isCompletionActive()) {
2195 view()->completionWidget()->pageDown();
2199 view()->clearSecondaryCursors();
2203 if (!view()->visibleRange().contains(m_displayCursor)) {
2204 scrollLines(m_displayCursor.line());
2208 int viewLine = cache()->displayViewLine(m_displayCursor);
2209 bool atEnd = startPos() >= m_cachedMaxStartPos;
2212 int lineadj = m_minLinesVisible;
2216 linesToScroll = qMax((linesDisplayed() - 1) - lineadj, 0);
2218 linesToScroll = qMax((linesDisplayed() / 2 - 1) - lineadj, 0);
2223 if (!doc()->pageUpDownMovesCursor() && !atEnd) {
2224 KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1);
2225 scrollPos(newStartPos);
2228 KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine,
true));
2230 KateTextLayout newLine = cache()->textLayout(newPos);
2232 newPos = renderer()->xToCursor(newLine, m_preservedX, !view()->wrapCursor());
2235 updateSelection(newPos, sel);
2236 updateCursor(newPos);
2239 scrollLines(linesToScroll, sel);
2243int KateViewInternal::maxLen(
int startLine)
2245 Q_ASSERT(!view()->dynWordWrap());
2247 int displayLines = (view()->height() / renderer()->lineHeight()) + 1;
2251 for (
int z = 0; z < displayLines; z++) {
2252 int virtualLine = startLine + z;
2254 if (virtualLine < 0 || virtualLine >= (
int)view()->textFolding().visibleLines()) {
2258 const KateLineLayout *line = cache()->line(view()->textFolding().visibleLineToLine(virtualLine));
2263 maxLen = qMax(maxLen, line->width());
2269bool KateViewInternal::columnScrollingPossible()
2271 return !view()->dynWordWrap() && m_columnScroll->isEnabled() && (m_columnScroll->maximum() > 0);
2274bool KateViewInternal::lineScrollingPossible()
2276 return m_lineScroll->minimum() != m_lineScroll->maximum();
2279void KateViewInternal::top(
bool sel)
2281 KTextEditor::Cursor newCursor(0, 0);
2283 newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !view()->wrapCursor());
2285 view()->clearSecondaryCursors();
2286 updateSelection(newCursor, sel);
2287 updateCursor(newCursor);
2290void KateViewInternal::bottom(
bool sel)
2292 KTextEditor::Cursor newCursor(doc()->lastLine(), 0);
2294 newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !view()->wrapCursor());
2296 view()->clearSecondaryCursors();
2297 updateSelection(newCursor, sel);
2298 updateCursor(newCursor);
2301void KateViewInternal::top_home(
bool sel)
2303 if (view()->isCompletionActive()) {
2304 view()->completionWidget()->top();
2308 view()->clearSecondaryCursors();
2309 KTextEditor::Cursor c(0, 0);
2310 updateSelection(c, sel);
2314void KateViewInternal::bottom_end(
bool sel)
2316 if (view()->isCompletionActive()) {
2317 view()->completionWidget()->bottom();
2321 view()->clearSecondaryCursors();
2322 KTextEditor::Cursor c(doc()->lastLine(), doc()->lineLength(doc()->lastLine()));
2323 updateSelection(c, sel);
2329 if (m_selectionMode != SelectionMode::Default) {
2330 view()->clearSecondaryCursors();
2333 auto &secondaryCursors = view()->m_secondaryCursors;
2334 if (secondaryCursors.empty()) {
2335 qWarning() <<
"Invalid updateSecondarySelection with no secondaryCursors";
2338 Q_ASSERT(secondaryCursors.size() > (
size_t)cursorIdx);
2340 auto &
cursor = secondaryCursors[cursorIdx];
2341 if (
cursor.cursor() != newPos) {
2342 qWarning() <<
"Unexpected different cursor at cursorIdx" << cursorIdx <<
"found" <<
cursor.cursor() <<
"looking for: " << newPos;
2347 Q_ASSERT(
cursor.anchor.isValid());
2350 cursor.range.reset(view()->newSecondarySelectionRange({old, newPos}));
2357 KTextEditor::Cursor newCursor = _newCursor;
2359 if (!view()->selection()
2360 || (m_selectAnchor.line() == -1)
2363 || (view()->config()->persistentSelection()
2364 && !(view()->selectionRange().contains(m_cursor) || view()->selectionRange().boundaryAtCursor(m_cursor)))) {
2365 m_selectAnchor = m_cursor;
2366 setSelection(KTextEditor::Range(m_cursor, newCursor));
2368 bool doSelect =
true;
2369 switch (m_selectionMode) {
2378 if (!m_selectionCached.isValid()) {
2379 m_selectionCached.setStart(m_selectionCached.end());
2383 if (newCursor > m_selectionCached.start()) {
2384 m_selectAnchor = m_selectionCached.start();
2386 Kate::TextLine l = doc()->kateTextLine(newCursor.
line());
2389 if (c > 0 && doc()->highlight()->isInWord(l.
at(c - 1))) {
2390 for (; c < l.
length(); c++) {
2391 if (!doc()->highlight()->isInWord(l.
at(c))) {
2398 }
else if (newCursor < m_selectionCached.start()) {
2399 m_selectAnchor = m_selectionCached.end();
2401 Kate::TextLine l = doc()->kateTextLine(newCursor.
line());
2404 if (c > 0 && c < doc()->lineLength(newCursor.
line()) && doc()->highlight()->isInWord(l.
at(c))
2405 && doc()->highlight()->isInWord(l.
at(c - 1))) {
2406 for (c -= 2; c >= 0; c--) {
2407 if (!doc()->highlight()->isInWord(l.
at(c))) {
2419 if (!m_selectionCached.isValid()) {
2420 m_selectionCached = KTextEditor::Range(endLine(), 0, endLine(), 0);
2422 if (newCursor.
line() > m_selectionCached.start().line()) {
2423 if (newCursor.
line() + 1 >= doc()->lines()) {
2424 newCursor.
setColumn(doc()->line(newCursor.
line()).length());
2429 m_selectAnchor = m_selectionCached.start();
2430 m_selectAnchor.setColumn(0);
2431 }
else if (newCursor.
line() < m_selectionCached.start().line()) {
2434 m_selectAnchor = m_selectionCached.end();
2435 if (m_selectAnchor.column() > 0) {
2436 if (m_selectAnchor.line() + 1 >= doc()->lines()) {
2437 m_selectAnchor.setColumn(doc()->line(newCursor.
line()).length());
2439 m_selectAnchor.setPosition(m_selectAnchor.line() + 1, 0);
2447 if (!m_selectionCached.isValid()) {
2451 if (newCursor > m_selectionCached.end()) {
2452 m_selectAnchor = m_selectionCached.start();
2453 }
else if (newCursor < m_selectionCached.start()) {
2454 m_selectAnchor = m_selectionCached.end();
2463 setSelection(KTextEditor::Range(m_selectAnchor, newCursor));
2464 }
else if (m_selectionCached.isValid()) {
2465 setSelection(m_selectionCached);
2469 m_selChangedByUser =
true;
2470 }
else if (!view()->config()->persistentSelection()) {
2471 view()->clearSelection();
2477#ifndef QT_NO_ACCESSIBILITY
2487 view()->setSelection(range);
2491void KateViewInternal::moveCursorToSelectionEdge(
bool scroll)
2493 if (!view()->selection()) {
2497 int tmp = m_minLinesVisible;
2498 m_minLinesVisible = 0;
2500 if (view()->selectionRange().
start() < m_selectAnchor) {
2501 updateCursor(view()->selectionRange().
start(),
false,
false,
false,
scroll);
2503 updateCursor(view()->selectionRange().end(),
false,
false,
false,
scroll);
2506 m_madeVisible =
false;
2509 m_minLinesVisible = tmp;
2517 int foldCounter = 0;
2518 int lineCounter = 0;
2519 const auto foldMarkers = m_view->doc()->buffer().computeFoldings(currentCursorPos.
line());
2523 long i = direction == 1 ? 0 : (long)foldMarkers.size() - 1;
2526 for (; i >= 0 && i < (long)foldMarkers.size(); i += direction) {
2527 if ((foldMarkers[i].offset - currentCursorPos.
column()) * direction > 0 && foldMarkers[i].foldingRegion.id() == foldingRegion.
id()) {
2528 if (foldMarkers[i].foldingRegion.
type() == foldingRegion.
type()) {
2530 }
else if (foldCounter > 0) {
2532 }
else if (foldCounter == 0) {
2533 return KTextEditor::Range(currentCursorPos.
line(),
2534 getStartOffset(direction, foldMarkers[i].offset, foldMarkers[i].length),
2535 currentCursorPos.
line(),
2536 getEndOffset(direction, foldMarkers[i].offset, foldMarkers[i].length));
2542 int currentLine = currentCursorPos.
line() + direction;
2543 for (; currentLine >= 0 && currentLine < m_view->doc()->lines() && lineCounter < maxLines; currentLine += direction) {
2545 const auto foldMarkers = m_view->doc()->buffer().computeFoldings(currentLine);
2546 i = direction == 1 ? 0 : (long)foldMarkers.size() - 1;
2549 for (; i >= 0 && i < (long)foldMarkers.size(); i += direction) {
2550 if (foldMarkers[i].foldingRegion.
id() == foldingRegion.
id()) {
2551 if (foldMarkers[i].foldingRegion.
type() == foldingRegion.
type()) {
2553 }
else if (foldCounter != 0) {
2555 }
else if (foldCounter == 0) {
2556 return KTextEditor::Range(currentLine,
2557 getStartOffset(direction, foldMarkers[i].offset, foldMarkers[i].length),
2559 getEndOffset(direction, foldMarkers[i].offset, foldMarkers[i].length));
2571void KateViewInternal::updateFoldingMarkersHighlighting()
2573 const auto foldings = m_view->doc()->buffer().computeFoldings(m_cursor.line());
2574 for (
unsigned long i = 0; i < foldings.size(); i++) {
2579 int startOffset = getStartOffset(-direction, foldings[i].offset, foldings[i].length);
2580 int endOffset = getEndOffset(-direction, foldings[i].offset, foldings[i].length);
2582 if (m_cursor.column() >= startOffset && m_cursor.column() <= endOffset) {
2583 const auto foldingMarkerMatch = findMatchingFoldingMarker(KTextEditor::Cursor(m_cursor.line(), m_cursor.column()), foldings[i].foldingRegion, 2000);
2585 if (!foldingMarkerMatch.isValid()) {
2590 if (direction == 1) {
2591 m_fmStart->setRange(KTextEditor::Range(m_cursor.line(), startOffset, m_cursor.line(), endOffset));
2592 m_fmEnd->setRange(foldingMarkerMatch);
2594 m_fmStart->setRange(foldingMarkerMatch);
2595 m_fmEnd->setRange(KTextEditor::Range(m_cursor.line(), startOffset, m_cursor.line(), endOffset));
2599 fill->setBackground(view()->rendererConfig()->highlightedBracketColor());
2601 m_fmStart->setAttribute(fill);
2602 m_fmEnd->setAttribute(fill);
2613 for (
int i = 0; i < cursors.
size(); ++i) {
2614 updateSecondarySelection(i, cursors[i].oldPos, cursors[i].newPos);
2620 view()->clearSecondarySelections();
2623 QVarLengthArray<int> linesToUpdate;
2624 for (
auto cpair : cursors) {
2625 linesToUpdate.
push_back(cpair.oldPos.line());
2626 linesToUpdate.
push_back(cpair.newPos.line());
2629 std::sort(linesToUpdate.
begin(), linesToUpdate.
end());
2630 auto it = std::unique(linesToUpdate.
begin(), linesToUpdate.
end());
2633 using Range = std::pair<int, int>;
2634 QVarLengthArray<Range> ranges;
2636 for (
auto i = linesToUpdate.
begin(); i != it; ++i) {
2638 if (!ranges.
isEmpty() && prev + 1 == curLine) {
2639 ranges.
back().second++;
2646 for (
auto range : ranges) {
2647 int startLine = range.first;
2648 int endLine = range.first + range.second;
2649 tagLines(startLine, endLine,
true);
2654void KateViewInternal::mergeSelections()
2656 using SecondaryCursor = KTextEditor::ViewPrivate::SecondaryCursor;
2657 using Range = KTextEditor::Range;
2658 auto doMerge = [](
Range newRange, SecondaryCursor &a, SecondaryCursor &b) {
2659 a.range->setRange(newRange);
2665 auto &cursors = view()->m_secondaryCursors;
2666 for (
auto it = cursors.begin(); it != cursors.end(); ++it) {
2669 if (it + 1 == cursors.end()) {
2673 auto n = std::next(it);
2678 auto curRange = it->range->toRange();
2679 auto nextRange = n->range->toRange();
2680 if (!curRange.overlaps(nextRange)) {
2684 bool isLefSel = it->cursor() < it->anchor;
2689 auto curPos = it->cursor();
2690 nextRange.expandToRange(curRange);
2695 n->pos->setPosition(curPos);
2696 n->anchor = qMax(n->anchor, it->anchor);
2698 n->anchor = qMin(n->anchor, it->anchor);
2700 doMerge(nextRange, *n, *it);
2703 if (view()->selection()) {
2704 auto primarySel = view()->m_selection.toRange();
2705 auto primCursor = cursorPosition();
2706 for (
auto it = cursors.begin(); it != cursors.end(); ++it) {
2710 auto curRange = it->range ? it->range->toRange() :
Range::invalid();
2711 if (curRange.isValid() && primarySel.overlaps(curRange)) {
2712 primarySel.expandToRange(curRange);
2713 const bool isLeftSel = it->cursor() < it->anchor;
2714 const bool isPrimaryLeftSel = primCursor < m_selectAnchor;
2715 const bool sameDirection = isLeftSel == isPrimaryLeftSel;
2717 if (sameDirection) {
2719 if (it->cursor() < primCursor) {
2720 updateCursor(it->cursor());
2722 m_selectAnchor = qMax(m_selectAnchor, it->anchor);
2724 if (it->cursor() > primCursor) {
2725 updateCursor(it->cursor());
2727 m_selectAnchor = qMin(m_selectAnchor, it->anchor);
2730 updateCursor(it->anchor);
2731 if (isPrimaryLeftSel) {
2732 m_selectAnchor = qMax(m_selectAnchor, it->anchor);
2734 m_selectAnchor = qMin(m_selectAnchor, it->anchor);
2738 setSelection(primarySel);
2741 }
else if (it->pos && !curRange.isValid() && primarySel.boundaryAtCursor(it->cursor())) {
2744 }
else if (it->pos) {
2748 auto pos = it->cursor();
2749 if (!primarySel.boundaryAtCursor(
pos) && primarySel.contains(
pos)) {
2756 cursors.erase(std::remove_if(cursors.begin(),
2758 [](
const SecondaryCursor &c) {
2759 return !c.pos.get();
2764void KateViewInternal::updateCursor(
const KTextEditor::Cursor newCursor,
bool force,
bool center,
bool calledExternally,
bool scroll)
2766 if (!force && (m_cursor.toCursor() == newCursor)) {
2767 m_displayCursor = toVirtualCursor(newCursor);
2768 if (
scroll && !m_madeVisible && m_view == doc()->activeView()) {
2770 view()->textFolding().ensureLineIsVisible(newCursor.
line());
2772 makeVisible(m_displayCursor, m_displayCursor.column(),
false, center, calledExternally);
2778 if (m_cursor.line() != newCursor.
line()) {
2779 m_leftBorder->updateForCursorLineChange();
2783 view()->textFolding().ensureLineIsVisible(newCursor.
line());
2785 KTextEditor::Cursor oldDisplayCursor = m_displayCursor;
2787 m_displayCursor = toVirtualCursor(newCursor);
2788 m_cursor.setPosition(newCursor);
2790 if (m_view == doc()->activeView() &&
scroll) {
2791 makeVisible(m_displayCursor, m_displayCursor.column(),
false, center, calledExternally);
2794 updateBracketMarks();
2796 updateFoldingMarkersHighlighting();
2799 tagLine(oldDisplayCursor);
2800 if (oldDisplayCursor.
line() != m_displayCursor.line()) {
2801 tagLine(m_displayCursor);
2806 if (m_cursorTimer.isActive()) {
2810 renderer()->setDrawCaret(
true);
2815 m_preserveX =
false;
2817 m_preservedX = renderer()->cursorToX(cache()->textLayout(m_cursor), m_cursor, !view()->wrapCursor());
2827 m_textHintTimer.stop();
2829 Q_EMIT view()->cursorPositionChanged(m_view, m_cursor);
2832void KateViewInternal::updateBracketMarkAttributes()
2835 bracketFill->setBackground(view()->rendererConfig()->highlightedBracketColor());
2836 bracketFill->setBackgroundFillWhitespace(
false);
2837 if (QFontInfo(renderer()->currentFont()).fixedPitch()) {
2839 bracketFill->setFontBold();
2842 m_bmStart->setAttribute(bracketFill);
2843 m_bmEnd->setAttribute(bracketFill);
2845 if (view()->rendererConfig()->showWholeBracketExpression()) {
2847 expressionFill->setBackground(view()->rendererConfig()->highlightedBracketColor());
2848 expressionFill->setBackgroundFillWhitespace(
false);
2850 m_bm->setAttribute(expressionFill);
2856void KateViewInternal::updateBracketMarks()
2859 const int maxLines = 5000;
2860 const KTextEditor::Range newRange = doc()->findMatchingBracket(m_cursor, maxLines);
2864 if (m_bm->toRange() == newRange) {
2866 hideBracketMatchPreview();
2871 m_bm->setRange(newRange);
2874 m_bmStart->setRange(KTextEditor::Range(m_bm->start(), KTextEditor::Cursor(m_bm->start().line(), m_bm->start().column() + 1)));
2875 m_bmEnd->setRange(KTextEditor::Range(m_bm->end(), KTextEditor::Cursor(m_bm->end().line(), m_bm->end().column() + 1)));
2878 if (m_view->config()->value(KateViewConfig::ShowBracketMatchPreview).toBool()) {
2879 showBracketMatchPreview();
2883 if (!m_view->rendererConfig()->animateBracketMatching()) {
2887 const KTextEditor::Cursor flashPos = (m_cursor == m_bmStart->start() || m_cursor == m_bmStart->end()) ? m_bmEnd->start() : m_bm->start();
2888 if (flashPos != m_bmLastFlashPos->toCursor()) {
2889 m_bmLastFlashPos->setPosition(flashPos);
2892 attribute->setBackground(view()->rendererConfig()->highlightedBracketColor());
2893 if (m_bmStart->attribute()->fontBold()) {
2894 attribute->setFontBold(
true);
2897 flashChar(flashPos, attribute);
2907 hideBracketMatchPreview();
2913 return tagLines(virtualCursor, virtualCursor,
false);
2916bool KateViewInternal::tagLines(
int start,
int end,
bool realLines)
2918 return tagLines(KTextEditor::Cursor(
start, 0), KTextEditor::Cursor(end, -1), realLines);
2924 cache()->relayoutLines(
start.line(), end.line());
2928 end = toVirtualCursor(end);
2931 cache()->relayoutLines(toRealCursor(
start).line(), toRealCursor(end).line());
2934 if (end.line() < startLine()) {
2940 if (
start.line() > startLine() + cache()->viewCacheLineCount()) {
2945 cache()->updateViewCache(startPos());
2951 for (
int z = 0; z < cache()->viewCacheLineCount(); z++) {
2952 KateTextLayout &line = cache()->viewLine(z);
2953 if ((line.virtualLine() >
start.line() || (line.virtualLine() ==
start.line() && line.
endCol() >=
start.column() &&
start.column() != -1))
2954 && (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1)))) {
2962 if (!m_view->config()->showFoldingOnHoverOnly() && doc()->highlight() && doc()->highlight()->foldingIndentationSensitive()) {
2964 m_leftBorder->update();
2965 }
else if (!view()->dynWordWrap()) {
2966 int y = lineToY(
start.line());
2968 int h = (end.line() -
start.line() + 2) * renderer()->lineHeight();
2969 if (end.line() >= view()->textFolding().visibleLines() - 1) {
2973 m_leftBorder->update(0,
y, m_leftBorder->width(), h);
2977 for (
int z = 0; z < cache()->viewCacheLineCount(); z++) {
2978 KateTextLayout &line = cache()->
viewLine(z);
2980 || ((line.virtualLine() >
start.line() || (line.virtualLine() ==
start.line() && line.
endCol() >=
start.column() &&
start.column() != -1))
2981 && (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1))))) {
2983 m_leftBorder->update(0, z * renderer()->lineHeight(), m_leftBorder->width(), m_leftBorder->height());
3000 return tagLines(range.
start(), range.
end(), realCursors);
3003void KateViewInternal::tagAll()
3008 m_leftBorder->updateFont();
3009 m_leftBorder->update();
3012void KateViewInternal::paintCursor()
3014 QVarLengthArray<int, 64> updatedLines;
3015 if (tagLine(m_displayCursor)) {
3016 updatedLines.
push_back(m_displayCursor.line());
3019 const int s = view()->firstDisplayedLine();
3020 const int e = view()->lastDisplayedLine();
3021 for (
const auto &c : view()->m_secondaryCursors) {
3022 auto p = c.cursor();
3023 if (p.line() >= s - 1 && p.line() <= e + 1 && !updatedLines.
contains(p.line())) {
3025 tagLines(p, p,
true);
3029 if (!updatedLines.
isEmpty()) {
3036 KateTextLayout thisLine = yToKateTextLayout(p.
y());
3037 KTextEditor::Cursor c;
3039 if (!thisLine.isValid()) {
3040 thisLine = cache()->textLayout(doc()->lines() - 1, -1);
3043 c = renderer()->xToCursor(thisLine, startX() + p.
x(), !view()->wrapCursor());
3045 if (c.
line() < 0 || c.
line() >= doc()->lines()) {
3050 const auto inlineNotes = view()->inlineNotes(c.
line());
3052 for (
const auto ¬e : inlineNotes) {
3053 auto noteCursor = note.m_position;
3055 if (note.m_position.column() >= doc()->lineLength(c.
line()) || note.m_position.column() == 0) {
3061 const auto caretWidth = renderer()->caretStyle() == KTextEditor::caretStyles::Line ? 2. : 0.;
3062 const auto width = KTextEditor::InlineNote(note).width() + caretWidth;
3063 const auto charWidth = renderer()->currentFontMetrics().horizontalAdvance(doc()->characterAt(noteCursor));
3064 const auto halfCharWidth = (charWidth / 2);
3067 const auto totalWidth =
width + halfCharWidth;
3070 QRect r(
start, QSize{(int)halfCharWidth, renderer()->lineHeight()});
3071 if (r.contains(p)) {
3081void KateViewInternal::placeCursor(
const QPoint &p,
bool keepSelection,
bool updateSelection)
3083 KTextEditor::Cursor c = cursorForPoint(p);
3088 if (updateSelection) {
3089 KateViewInternal::updateSelection(c, keepSelection);
3092 int tmp = m_minLinesVisible;
3093 m_minLinesVisible = 0;
3095 m_minLinesVisible = tmp;
3097 if (updateSelection && keepSelection) {
3098 moveCursorToSelectionEdge();
3103bool KateViewInternal::isTargetSelected(
const QPoint &p)
3105 const KateTextLayout &thisLine = yToKateTextLayout(p.
y());
3106 if (!thisLine.isValid()) {
3110 return view()->cursorSelected(renderer()->xToCursor(thisLine, startX() + p.
x(), !view()->wrapCursor()));
3117 switch (e->
type()) {
3120 QChildEvent *c =
static_cast<QChildEvent *
>(e);
3130 QKeyEvent *k =
static_cast<QKeyEvent *
>(e);
3133 if (!view()->m_secondaryCursors.empty()) {
3134 view()->clearSecondaryCursors();
3139 if (view()->isCompletionActive()) {
3140 view()->abortCompletion();
3144 }
else if (view()->bottomViewBar()->barWidgetVisible()) {
3145 view()->bottomViewBar()->hideCurrentBarWidget();
3149 }
else if (!view()->config()->persistentSelection() && view()->selection()) {
3150 m_currentInputMode->clearSelection();
3158 if (view()->completionWidget()->handleShortcutOverride(k)) {
3164 if (m_currentInputMode->stealKey(k)) {
3180 QKeyEvent *k =
static_cast<QKeyEvent *
>(e);
3195 QPoint currentPoint = ((QDragMoveEvent *)e)->position().toPoint();
3197 QRect doNotScrollRegion(s_scrollMargin, s_scrollMargin,
width() - s_scrollMargin * 2,
height() - s_scrollMargin * 2);
3199 if (!doNotScrollRegion.contains(currentPoint)) {
3202 ((QDragMoveEvent *)e)->accept(QRect(0, 0, 0, 0));
3205 dragMoveEvent((QDragMoveEvent *)e);
3214 hideBracketMatchPreview();
3218 QScrollPrepareEvent *s =
static_cast<QScrollPrepareEvent *
>(e);
3219 scrollPrepareEvent(s);
3224 QScrollEvent *s =
static_cast<QScrollEvent *
>(e);
3236void KateViewInternal::keyPressEvent(
QKeyEvent *e)
3240 view()->emitNavigateLeft();
3245 view()->emitNavigateRight();
3250 view()->emitNavigateUp();
3255 view()->emitNavigateDown();
3260 view()->emitNavigateAccept();
3265 view()->emitNavigateBack();
3270 if (e->
key() ==
Qt::Key_Alt && view()->completionWidget()->isCompletionActive()) {
3271 view()->completionWidget()->toggleDocumentation();
3277 if (m_currentInputMode->keyPress(e)) {
3281 if (!doc()->isReadWrite()) {
3288 view()->keyReturn();
3302 uint tabHandling = doc()->config()->tabHandling();
3304 if (tabHandling == KateDocumentConfig::tabSmart) {
3306 if (view()->selection() && !view()->selectionRange().onSingleLine() && !view()->blockSelection()) {
3307 tabHandling = KateDocumentConfig::tabIndents;
3315 Kate::TextLine line = doc()->kateTextLine(m_cursor.line());
3317 if (first < 0 || m_cursor.column() <= first) {
3318 tabHandling = KateDocumentConfig::tabIndents;
3320 tabHandling = KateDocumentConfig::tabInsertsTab;
3326 if (tabHandling == KateDocumentConfig::tabInsertsTab) {
3327 doc()->typeChars(m_view, QStringLiteral(
"\t"));
3330 for (
const auto &c : std::as_const(m_view->m_secondaryCursors)) {
3331 auto cursor = c.cursor();
3332 doc()->indent(KTextEditor::Range(
cursor.line(), 0,
cursor.line(), 0), 1);
3335 doc()->indent(view()->selection() ? view()->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), 1);
3342 }
else if (doc()->config()->tabHandling() != KateDocumentConfig::tabInsertsTab) {
3344 doc()->indent(view()->selection() ? view()->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), -1);
3351 if (isAcceptableInput(e)) {
3352 doc()->typeChars(m_view, e->
text());
3360void KateViewInternal::keyReleaseEvent(
QKeyEvent *e)
3363 m_shiftKeyPressed =
false;
3365 if (m_selChangedByUser) {
3366 if (view()->selection()) {
3370 m_selChangedByUser =
false;
3378bool KateViewInternal::isAcceptableInput(
const QKeyEvent *e)
3382 const QString text = e->
text();
3387 const QChar c = text.
at(0);
3416 m_textHintTimer.stop();
3420 QPoint p = e->
pos();
3423 makeVisible(m_displayCursor, 0);
3424 p = cursorCoordinates(
false);
3426 }
else if (!view()->selection() || view()->config()->persistentSelection()) {
3427 placeCursor(e->
pos());
3431 QMenu *cm = view()->contextMenu();
3433 view()->spellingMenu()->prepareToBeShown(cm);
3439void KateViewInternal::mousePressEvent(
QMouseEvent *e)
3441 if (sendMouseEventToInputContext(e)) {
3447 const KTextEditor::InlineNote note(noteData);
3448 if (note.position().isValid()) {
3459 m_selChangedByUser =
false;
3461 if (!view()->isMulticursorNotAllowed() && e->
modifiers() == view()->config()->multiCursorModifiers()) {
3462 auto pos = cursorForPoint(e->
pos());
3463 if (
pos.isValid()) {
3464 view()->addSecondaryCursor(
pos);
3469 view()->clearSecondaryCursors();
3472 if (m_possibleTripleClick) {
3473 m_possibleTripleClick =
false;
3475 m_selectionMode = Line;
3478 updateSelection(m_cursor,
true);
3480 view()->selectLine(m_cursor);
3481 if (view()->selection()) {
3482 m_selectAnchor = view()->selectionRange().start();
3486 if (view()->selection()) {
3492 if (m_selectAnchor.line() > view()->selectionRange().
start().line()) {
3494 if (m_selectAnchor == view()->selectionRange().end() && m_selectAnchor.column() == 0) {
3495 m_selectionCached.setStart(KTextEditor::Cursor(m_selectAnchor.line() - 1, 0));
3497 m_selectionCached.setStart(KTextEditor::Cursor(m_selectAnchor.line(), 0));
3499 m_selectionCached.setEnd(view()->selectionRange().end());
3502 m_selectionCached.setStart(view()->selectionRange().
start());
3503 if (view()->selectionRange().end().line() > view()->selectionRange().
start().line()) {
3504 m_selectionCached.setEnd(KTextEditor::Cursor(view()->selectionRange().
start().line() + 1, 0));
3506 m_selectionCached.setEnd(view()->selectionRange().end());
3510 moveCursorToSelectionEdge();
3514 m_scrollTimer.start(50);
3518 }
else if (m_selectionMode == Default) {
3519 m_selectionMode = Mouse;
3532 if (!m_selectAnchor.isValid()) {
3533 m_selectAnchor = m_cursor;
3540 m_dragInfo.state = diPending;
3541 m_dragInfo.start = e->
pos();
3543 m_dragInfo.state = diNone;
3546 placeCursor(e->
pos(),
true,
false);
3547 if (m_selectionCached.start().isValid()) {
3548 if (m_cursor.toCursor() < m_selectionCached.start()) {
3549 m_selectAnchor = m_selectionCached.end();
3551 m_selectAnchor = m_selectionCached.start();
3556 setSelection(KTextEditor::Range(m_selectAnchor, m_cursor));
3557 if (view()->selection()) {
3561 placeCursor(e->
pos());
3567 m_scrollTimer.start(50);
3574 if (e->
pos().
x() == 0) {
3576 placeCursor(e->
pos());
3587void KateViewInternal::mouseDoubleClickEvent(
QMouseEvent *e)
3589 if (sendMouseEventToInputContext(e)) {
3593 m_selectionMode = Word;
3599 Kate::TextLine l = doc()->kateTextLine(m_selectAnchor.line());
3601 ce = m_selectAnchor.column();
3602 if (ce > 0 && doc()->highlight()->isInWord(l.
at(ce))) {
3603 for (; ce < l.
length(); ce++) {
3604 if (!doc()->highlight()->isInWord(l.
at(ce))) {
3610 cs = m_selectAnchor.column() - 1;
3611 if (cs < doc()->lineLength(m_selectAnchor.line()) && doc()->highlight()->isInWord(l.
at(cs))) {
3612 for (cs--; cs >= 0; cs--) {
3613 if (!doc()->highlight()->isInWord(l.
at(cs))) {
3621 m_selectionCached.setStart(KTextEditor::Cursor(m_selectAnchor.line(), cs + 1));
3622 m_selectionCached.setEnd(KTextEditor::Cursor(m_selectAnchor.line(), ce));
3624 m_selectionCached.setStart(m_selectAnchor);
3625 m_selectionCached.setEnd(m_selectAnchor);
3628 placeCursor(e->
pos(),
true);
3635 view()->clearSelection(
false,
false);
3636 placeCursor(e->
pos());
3637 view()->selectWord(m_cursor);
3638 cursorToMatchingBracket(
true);
3640 if (view()->selection()) {
3641 m_selectAnchor = view()->selectionRange().start();
3642 m_selectionCached = view()->selectionRange();
3644 m_selectAnchor = m_cursor;
3645 m_selectionCached = KTextEditor::Range(m_cursor, m_cursor);
3651 if (view()->selection()) {
3656 moveCursorToSelectionEdge();
3657 m_possibleTripleClick =
true;
3663 m_scrollTimer.start(50);
3671void KateViewInternal::tripleClickTimeout()
3673 m_possibleTripleClick =
false;
3676void KateViewInternal::beginSelectLine(
const QPoint &pos)
3679 m_possibleTripleClick =
true;
3682void KateViewInternal::mouseReleaseEvent(
QMouseEvent *e)
3684 if (sendMouseEventToInputContext(e)) {
3690 m_selectionMode = Default;
3693 if (m_selChangedByUser) {
3694 if (view()->selection()) {
3697 moveCursorToSelectionEdge();
3699 m_selChangedByUser =
false;
3702 if (m_dragInfo.state == diPending) {
3704 }
else if (m_dragInfo.state == diNone) {
3705 m_scrollTimer.stop();
3708 m_dragInfo.state = diNone;
3711 if (view()->selection() && !view()->m_secondaryCursors.empty()) {
3719 if (!view()->config()->mousePasteAtCursorPosition()) {
3720 placeCursor(e->
pos());
3723 if (doc()->isReadWrite()) {
3725 view()->paste(&clipboard);
3737void KateViewInternal::leaveEvent(
QEvent *)
3739 m_textHintTimer.stop();
3743 if (m_dragInfo.state == diNone) {
3744 m_scrollTimer.stop();
3747 hideBracketMatchPreview();
3752 QPoint coord(_coord);
3756 if (includeBorder) {
3757 coord.rx() -= m_leftBorder->width();
3759 coord.rx() += startX();
3761 const KateTextLayout &thisLine = yToKateTextLayout(coord.y());
3762 if (thisLine.isValid()) {
3763 ret = renderer()->xToCursor(thisLine, coord.x(), !view()->wrapCursor());
3766 if (ret.
column() > view()->document()->lineLength(ret.
line())) {
3775void KateViewInternal::mouseMoveEvent(
QMouseEvent *e)
3781 KTextEditor::Cursor newPosition = coordinatesToCursor(e->
pos(),
false);
3782 if (newPosition != m_mouse) {
3783 m_mouse = newPosition;
3789 auto focusChanged =
false;
3790 if (noteData.m_position.isValid()) {
3791 if (!m_activeInlineNote.m_position.isValid()) {
3793 tagLine(noteData.m_position);
3794 focusChanged =
true;
3795 noteData.m_underMouse =
true;
3796 noteData.m_provider->inlineNoteFocusInEvent(KTextEditor::InlineNote(noteData), e->
globalPosition().
toPoint());
3797 m_activeInlineNote = noteData;
3799 noteData.m_provider->inlineNoteMouseMoveEvent(KTextEditor::InlineNote(noteData), e->
globalPosition().
toPoint());
3801 }
else if (m_activeInlineNote.m_position.isValid()) {
3802 tagLine(m_activeInlineNote.m_position);
3803 focusChanged =
true;
3804 m_activeInlineNote.m_underMouse =
false;
3805 m_activeInlineNote.m_provider->inlineNoteFocusOutEvent(KTextEditor::InlineNote(m_activeInlineNote));
3806 m_activeInlineNote = {};
3815 if (m_dragInfo.state == diPending) {
3818 QPoint p(e->
pos() - m_dragInfo.start);
3826 }
else if (m_dragInfo.state == diDragging) {
3837 int d = renderer()->lineHeight();
3843 if (m_mouseX >
width()) {
3852 if (m_mouseY >
height()) {
3864 placeCursor(QPoint(m_mouseX, m_mouseY),
true);
3867 if (view()->config()->textDragAndDrop() && isTargetSelected(e->
pos())) {
3888 m_textHintTimer.start(m_textHintDelay);
3889 m_textHintPos = e->
pos();
3894void KateViewInternal::updateDirty()
3896 const int h = renderer()->lineHeight();
3898 int currentRectStart = -1;
3899 int currentRectEnd = -1;
3901 QRegion updateRegion;
3904 for (
int i = 0; i < cache()->viewCacheLineCount(); ++i) {
3905 if (cache()->viewLine(i).isDirty()) {
3906 if (currentRectStart == -1) {
3907 currentRectStart = h * i;
3910 currentRectEnd += h;
3913 }
else if (currentRectStart != -1) {
3914 updateRegion += QRect(0, currentRectStart,
width(), currentRectEnd);
3915 currentRectStart = -1;
3916 currentRectEnd = -1;
3921 if (currentRectStart != -1) {
3922 updateRegion += QRect(0, currentRectStart,
width(), currentRectEnd);
3925 if (!updateRegion.
isEmpty()) {
3926 if (debugPainting) {
3927 qCDebug(LOG_KTE) <<
"Update dirty region " << updateRegion;
3933void KateViewInternal::hideEvent(
QHideEvent *e)
3936 if (view()->isCompletionActive()) {
3937 view()->completionWidget()->abortCompletion();
3943 if (debugPainting) {
3944 qCDebug(LOG_KTE) <<
"GOT PAINT EVENT: Region" << e->
region();
3947 const QRect &unionRect = e->
rect();
3949 int xStart = startX() + unionRect.
x();
3950 int xEnd = xStart + unionRect.
width();
3951 uint h = renderer()->lineHeight();
3952 uint startz = (unionRect.
y() / h);
3953 uint endz = startz + 1 + (unionRect.
height() / h);
3954 uint lineRangesSize = cache()->viewCacheLineCount();
3955 const KTextEditor::Cursor
pos = m_cursor;
3957 QPainter paint(
this);
3965 renderer()->setCaretStyle(m_currentInputMode->caretStyle());
3966 renderer()->setShowTabs(doc()->config()->showTabs());
3967 renderer()->setShowSpaces(doc()->config()->showSpaces());
3968 renderer()->updateMarkerSize();
3973 paint.translate(unionRect.
x(), startz * h);
3974 for (uint z = startz; z <= endz; z++) {
3976 if ((z >= lineRangesSize) || (cache()->viewLine(z).line() == -1)) {
3977 if (!(z >= lineRangesSize)) {
3978 cache()->viewLine(z).setDirty(
false);
3980 paint.fillRect(0, 0, unionRect.
width(), h, m_view->rendererConfig()->backgroundColor());
3993 KateTextLayout &thisLine = cache()->
viewLine(z);
3994 if (!thisLine.
viewLine() || z == startz) {
3998 const int thisLineTop = h * thisLine.
viewLine();
3999 paint.translate(QPoint(0, -thisLineTop));
4003 const QRectF lineRect(0, 0, unionRect.
width(), h * thisLine.kateLineLayout()->viewLineCount());
4004 paint.fillRect(lineRect, m_view->rendererConfig()->backgroundColor());
4009 paint.setClipRect(lineRect);
4014 const QRect textClipRect{xStart, thisLineTop, xEnd - xStart,
height()};
4016 renderer()->paintTextLine(paint, thisLine.kateLineLayout(), xStart, xEnd, textClipRect.toRectF(), &
pos);
4020 thisLine.setDirty(
false);
4025 paint.translate(0, h);
4030 if (m_textAnimation) {
4031 m_textAnimation->draw(paint);
4041 m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height());
4042 m_madeVisible =
false;
4046 showBracketMatchPreview();
4049 if (heightChanged) {
4050 setAutoCenterLines(m_autoCenterLines,
false);
4051 m_cachedMaxStartPos.setPosition(-1, -1);
4054 if (view()->dynWordWrap()) {
4055 bool dirtied =
false;
4057 for (
int i = 0; i < cache()->viewCacheLineCount(); i++) {
4060 KateTextLayout viewLine = cache()->viewLine(i);
4062 if (viewLine.wrap() || viewLine.isRightToLeft() || viewLine.width() >
width()) {
4064 viewLine.setDirty();
4069 if (dirtied || heightChanged) {
4071 m_leftBorder->update();
4076 if (expandedHorizontally && startX() > 0) {
4083 if (m_cursor.column() > doc()->lineLength(m_cursor.line())) {
4084 KateTextLayout thisLine = currentLayout(m_cursor);
4086 KTextEditor::Cursor newCursor(m_cursor.line(),
4087 thisLine.
endCol() + ((
width() - thisLine.xOffset() - (thisLine.width() - startX())) / renderer()->spaceWidth()) - 1);
4088 if (newCursor.
column() < m_cursor.column()) {
4089 updateCursor(newCursor);
4094 if (expandedVertically) {
4095 KTextEditor::Cursor max = maxStartPos();
4096 if (startPos() > max) {
4101 Q_EMIT view()->displayRangeChanged(m_view);
4104void KateViewInternal::moveEvent(
QMoveEvent *e)
4107 if (e->
pos() != e->
oldPos() && m_bmPreview) {
4108 showBracketMatchPreview();
4114void KateViewInternal::scrollTimeout()
4116 if (m_scrollX || m_scrollY) {
4117 const int scrollTo = startPos().line() + (m_scrollY / (int)renderer()->lineHeight());
4118 placeCursor(QPoint(m_mouseX, m_mouseY),
true);
4119 scrollLines(scrollTo);
4123void KateViewInternal::cursorTimeout()
4125 if (!debugPainting && m_currentInputMode->blinkCaret()) {
4126 renderer()->setDrawCaret(!renderer()->drawCaret());
4131void KateViewInternal::textHintTimeout()
4133 m_textHintTimer.stop();
4135 KTextEditor::Cursor c = coordinatesToCursor(m_textHintPos,
false);
4140 QStringList textHints;
4141 for (KTextEditor::TextHintProvider *
const p : m_textHintProviders) {
4146 const QString
hint = p->textHint(m_view, c);
4147 if (!
hint.isEmpty()) {
4153 qCDebug(LOG_KTE) <<
"Hint text: " << textHints;
4155 for (
const QString &str : std::as_const(textHints)) {
4156 hint += QStringLiteral(
"<p>%1</p>").arg(str);
4158 QPoint
pos(startX() + m_textHintPos.x(), m_textHintPos.y());
4171 doc()->setActiveView(m_view);
4174 view()->slotGotFocus();
4177void KateViewInternal::focusOutEvent(
QFocusEvent *)
4182 m_cursorTimer.stop();
4183 view()->renderer()->setDrawCaret(
true);
4186 m_textHintTimer.stop();
4188 view()->slotLostFocus();
4190 hideBracketMatchPreview();
4193void KateViewInternal::doDrag()
4195 m_dragInfo.state = diDragging;
4196 m_dragInfo.dragObject =
new QDrag(
this);
4197 std::unique_ptr<QMimeData> mimeData(
new QMimeData());
4198 mimeData->setText(view()->selectionText());
4200 const auto startCur = view()->selectionRange().start();
4201 const auto endCur = view()->selectionRange().end();
4202 if (!startCur.isValid() || !endCur.isValid()) {
4206 int startLine = startCur.line();
4207 int endLine = endCur.line();
4218 for (
int l = startLine; l <= endLine; ++l) {
4219 if (l >= firstVisibleLine) {
4224 for (
int l = endLine; l >= startLine; --l) {
4225 if (l <= lastVisibleLine) {
4234 const QFontMetricsF &fm = renderer()->currentFontMetrics();
4235 for (
int l = startLine; l <= endLine; ++l) {
4238 h += renderer()->lineHeight() * (cache()->viewLine(endCur) + 1);
4240 h += renderer()->lineHeight() * cache()->viewLineCount(l);
4243 qreal scale = h > m_view->height() / 2 ? 0.75 : 1.0;
4247 if (startLine == startCur.line()) {
4248 sX = renderer()->cursorToX(cache()->textLayout(startCur), startCur,
false);
4253 if (endLine == endCur.line()) {
4254 eX = renderer()->cursorToX(cache()->textLayout(endCur), endCur,
false);
4260 if (view()->selection()) {
4261 view()->clearHighlights();
4266 QPixmap pixmap(w * dpr, h * dpr);
4267 if (!pixmap.isNull()) {
4268 pixmap.setDevicePixelRatio(dpr);
4270 renderer()->paintSelection(&pixmap, startLine, sX, endLine, eX, cache()->viewWidth(), scale);
4272 if (view()->selection()) {
4274 Q_EMIT view()->selectionChanged(view());
4287 const int y = lineToY(view()->m_textFolding.lineToVisibleLine(startLine));
4290 m_dragInfo.dragObject->setPixmap(pixmap);
4291 m_dragInfo.dragObject->setHotSpot(
pos);
4292 m_dragInfo.dragObject->setMimeData(mimeData.release());
4298 if (
event->source() ==
this) {
4301 event->setAccepted((
event->mimeData()->hasText() && doc()->isReadWrite()) ||
event->mimeData()->hasUrls());
4304void KateViewInternal::fixDropEvent(
QDropEvent *event)
4306 if (
event->source() !=
this) {
4319 event->setDropAction(action);
4326 placeCursor(
event->position().toPoint(),
true,
false);
4330 fixDropEvent(
event);
4333void KateViewInternal::dropEvent(
QDropEvent *event)
4336 if (
event->mimeData()->hasUrls()) {
4341 if (
event->mimeData()->hasText() && doc()->isReadWrite()) {
4342 const QString text =
event->mimeData()->text();
4343 const bool blockMode = view()->blockSelection();
4345 fixDropEvent(
event);
4348 KTextEditor::Cursor targetCursor(m_cursor);
4350 std::unique_ptr<KTextEditor::MovingCursor> targetCursor2(doc()->newMovingCursor(m_cursor));
4353 const KTextEditor::Range selRange(view()->selectionRange());
4354 const KTextEditor::Cursor blockAdjust(selRange.numberOfLines(), selRange.columnWidth());
4358 editSetCursor(selRange.end());
4360 view()->clearSelection();
4367 view()->removeSelectedText();
4368 if (targetCursor2->toCursor() != targetCursor) {
4370 targetCursor = targetCursor2->toCursor();
4372 doc()->insertText(targetCursor2->toCursor(), text, blockMode);
4375 doc()->insertText(targetCursor, text, blockMode);
4379 setSelection(KTextEditor::Range(targetCursor, targetCursor + blockAdjust));
4380 editSetCursor(targetCursor + blockAdjust);
4382 setSelection(KTextEditor::Range(targetCursor, targetCursor2->toCursor()));
4383 editSetCursor(targetCursor2->toCursor());
4388 event->acceptProposedAction();
4393 m_dragInfo.state = diNone;
4399void KateViewInternal::clear()
4401 m_startPos.setPosition(0, 0);
4402 m_displayCursor = KTextEditor::Cursor(0, 0);
4403 m_cursor.setPosition(0, 0);
4404 view()->clearSecondaryCursors();
4407 m_lineScroll->updatePixmap();
4414 if (m_zoomEventFilter->detectZoomingEvent(e)) {
4416 slotIncFontSizes(qreal(e->
angleDelta().
y()) / (qreal)QWheelEvent::DefaultDeltasPerStep);
4418 slotDecFontSizes(qreal(-e->
angleDelta().
y()) / (qreal)QWheelEvent::DefaultDeltasPerStep);
4429 auto sign = m_lineScroll->invertedControls() ? -1 : 1;
4430 auto offset = sign * qreal(e->
angleDelta().
y()) / 120.0;
4432 const auto pageStep = m_lineScroll->pageStep();
4433 offset = qBound(-pageStep,
int(offset * pageStep), pageStep);
4439 m_accumulatedScroll += offset - int(offset);
4440 const auto extraAccumulated = int(m_accumulatedScroll);
4441 m_accumulatedScroll -= extraAccumulated;
4444 scrollViewLines(
int(offset) + extraAccumulated);
4451 if (view()->dynWordWrap()) {
4468 hideBracketMatchPreview();
4473 int lineHeight = renderer()->lineHeight();
4474 event->setViewportSize(QSizeF(0.0, 0.0));
4475 event->setContentPosRange(QRectF(0.0, 0.0, 0.0, m_lineScroll->maximum() * lineHeight));
4476 event->setContentPos(QPointF(0.0, m_lineScroll->value() * lineHeight));
4483 KTextEditor::Cursor newPos((
int)
event->contentPos().y() / renderer()->lineHeight(), 0);
4488void KateViewInternal::startDragScroll()
4490 if (!m_dragScrollTimer.isActive()) {
4491 m_dragScrollTimer.start(s_scrollTime);
4495void KateViewInternal::stopDragScroll()
4497 m_dragScrollTimer.stop();
4501void KateViewInternal::doDragScroll()
4507 if (p.
y() < s_scrollMargin) {
4508 dy = p.
y() - s_scrollMargin;
4509 }
else if (p.
y() >
height() - s_scrollMargin) {
4510 dy = s_scrollMargin - (
height() - p.
y());
4513 if (p.
x() < s_scrollMargin) {
4514 dx = p.
x() - s_scrollMargin;
4515 }
else if (p.
x() >
width() - s_scrollMargin) {
4516 dx = s_scrollMargin - (
width() - p.
x());
4522 scrollLines(startLine() + dy);
4525 if (columnScrollingPossible() && dx) {
4526 scrollColumns(qMin(startX() + dx, m_columnScroll->maximum()));
4536 if (std::find(m_textHintProviders.cbegin(), m_textHintProviders.cend(), provider) == m_textHintProviders.cend()) {
4537 m_textHintProviders.push_back(provider);
4541 m_textHintTimer.start(m_textHintDelay);
4546 const auto it = std::find(m_textHintProviders.cbegin(), m_textHintProviders.cend(), provider);
4547 if (it != m_textHintProviders.cend()) {
4548 m_textHintProviders.erase(it);
4551 if (m_textHintProviders.empty()) {
4552 m_textHintTimer.stop();
4556void KateViewInternal::setTextHintDelay(
int delay)
4559 m_textHintDelay = 200;
4561 m_textHintDelay = delay;
4565int KateViewInternal::textHintDelay()
const
4567 return m_textHintDelay;
4570bool KateViewInternal::textHintsEnabled()
4572 return !m_textHintProviders.empty();
4576void KateViewInternal::editStart()
4578 editSessionNumber++;
4580 if (editSessionNumber > 1) {
4584 editIsRunning =
true;
4585 editOldCursor = m_cursor;
4586 editOldSelection = view()->selectionRange();
4589void KateViewInternal::editEnd(
int editTagLineStart,
int editTagLineEnd,
bool tagFrom)
4591 if (editSessionNumber == 0) {
4595 editSessionNumber--;
4597 if (editSessionNumber > 0) {
4604 if (view()->dynWordWrap()) {
4605 if (KateLineLayout *
layout = cache()->line(startLine())) {
4606 int index =
layout->viewLineForColumn(startPos().column());
4607 if (index >= 0 && index < layout->viewLineCount()) {
4608 col =
layout->viewLine(index).startCol();
4612 m_startPos.setPosition(startLine(), col);
4614 if (tagFrom && (editTagLineStart <=
int(view()->textFolding().visibleLineToLine(startLine())))) {
4617 tagLines(editTagLineStart, tagFrom ? qMax(doc()->lastLine() + 1, editTagLineEnd) : editTagLineEnd,
true);
4620 if (editOldCursor == m_cursor.toCursor()) {
4621 updateBracketMarks();
4626 if (editOldCursor != m_cursor.toCursor() || m_view == doc()->activeView()) {
4630 if (m_cursor.line() >= editTagLineStart && m_cursor.line() <= editTagLineEnd) {
4631 m_madeVisible =
false;
4632 updateCursor(m_cursor,
true);
4638 if (editOldSelection != view()->selectionRange()
4639 || (editOldSelection.isValid() && !editOldSelection.isEmpty()
4640 && !(editTagLineStart > editOldSelection.end().line() && editTagLineEnd < editOldSelection.start().line()))) {
4641 Q_EMIT view()->selectionChanged(m_view);
4644 editIsRunning =
false;
4649 if (m_cursor.toCursor() != _cursor) {
4650 m_cursor.setPosition(_cursor);
4655void KateViewInternal::viewSelectionChanged()
4657 if (!view()->selection()) {
4660 const auto r = view()->selectionRange();
4661 m_selectAnchor = r.start() == m_cursor ? r.end() : r.start();
4674 return m_layoutCache;
4679 return KTextEditor::Cursor(view()->textFolding().visibleLineToLine(virtualCursor.
line()), virtualCursor.
column());
4686 if (realCursor.
line() < 0) {
4690 return KTextEditor::Cursor(view()->textFolding().lineToVisibleLine(realCursor.
line()), realCursor.
column());
4695 return view()->renderer();
4698void KateViewInternal::mouseMoved()
4700 view()->notifyMousePositionChanged(m_mouse);
4704void KateViewInternal::cursorMoved()
4708#ifndef QT_NO_ACCESSIBILITY
4718 return m_view->doc();
4723 return m_view->doc();
4726bool KateViewInternal::rangeAffectsView(
KTextEditor::Range range,
bool realCursors)
const
4728 int startLine = KateViewInternal::startLine();
4729 int endLine = startLine + (int)m_visibleLineCount;
4732 startLine = (int)view()->textFolding().visibleLineToLine(startLine);
4733 endLine = (int)view()->textFolding().visibleLineToLine(endLine);
4736 return (range.
end().
line() >= startLine) || (range.
start().line() <= endLine);
4753 auto lineHeight = renderer()->lineHeight();
4754 return QRect(cursorToCoordinate(m_cursor,
true,
false), QSize(1, lineHeight ? lineHeight : 1));
4758 return renderer()->currentFont();
4761 return m_cursor.column();
4766 if (view()->selection() && m_selectAnchor.line() == m_cursor.line()) {
4767 return m_selectAnchor.column();
4769 return m_cursor.column();
4773 return doc()->kateTextLine(m_cursor.line()).text();
4776 if (view()->selection()) {
4777 return view()->selectionText();
4791 if (doc()->readOnly()) {
4799 if (!m_imPreeditRange) {
4800 m_imPreeditRange.reset(
4804 if (!m_imPreeditRange->toRange().isEmpty()) {
4805 doc()->inputMethodStart();
4806 doc()->removeText(*m_imPreeditRange);
4807 doc()->inputMethodEnd();
4811 view()->removeSelectedText();
4815 KTextEditor::Range preeditRange = *m_imPreeditRange;
4817 KTextEditor::Cursor
start(m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + e->
replacementStart());
4821 if (
start != removeEnd) {
4822 doc()->removeText(KTextEditor::Range(
start, removeEnd));
4834 m_imPreeditRange->setRange(preeditRange);
4838 doc()->inputMethodStart();
4839 doc()->insertText(m_imPreeditRange->start(), e->
preeditString());
4840 doc()->inputMethodEnd();
4847 m_imPreeditRange.reset();
4848 m_imPreeditRangeChildren.clear();
4851 renderer()->setDrawCaret(
false);
4853 renderer()->setCaretOverrideColor(QColor());
4859 KTextEditor::Cursor newCursor = m_cursor;
4860 bool hideCursor =
false;
4863 if (m_imPreeditRange) {
4864 m_imPreeditRangeChildren.clear();
4866 int decorationColumn = 0;
4868 for (
auto &a : attributes) {
4871 newCursor = m_imPreeditRange->
start() + KTextEditor::Cursor(0,
cursor);
4872 hideCursor = !a.length;
4873 QColor c = qvariant_cast<QColor>(a.value);
4879 QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
4883 const KTextEditor::MovingCursor &preEditRangeStart = m_imPreeditRange->start();
4884 const int startLine = preEditRangeStart.
line();
4885 const int startCol = preEditRangeStart.
column();
4886 KTextEditor::Range fr(startLine, startCol +
start, startLine, startCol + end);
4887 std::unique_ptr<KTextEditor::MovingRange> formatRange(doc()->newMovingRange(fr));
4889 attribute->merge(f);
4890 formatRange->setAttribute(attribute);
4891 decorationColumn = end;
4892 m_imPreeditRangeChildren.push_back(std::move(formatRange));
4898 renderer()->setDrawCaret(hideCursor);
4899 renderer()->setCaretOverrideColor(caretColor);
4901 if (newCursor != m_cursor.toCursor()) {
4902 updateCursor(newCursor);
4912 Q_ASSERT(
pos.isValid());
4916 if (!view()->textFolding().isLineVisible(
pos.line())) {
4920 KTextEditor::Range range(
pos, KTextEditor::Cursor(
pos.line(),
pos.column() + 1));
4921 if (m_textAnimation) {
4922 m_textAnimation->deleteLater();
4924 m_textAnimation =
new KateTextAnimation(range, std::move(attribute),
this);
4927void KateViewInternal::showBracketMatchPreview()
4934 const KTextEditor::Cursor openBracketCursor = m_bmStart->start();
4936 if (m_cursor == openBracketCursor || toVirtualCursor(openBracketCursor).line() >= startLine() || m_cursor.line() - startLine() < 2) {
4937 hideBracketMatchPreview();
4942 m_bmPreview.reset(
new KateTextPreview(m_view,
this));
4947 const int previewLine = openBracketCursor.
line();
4948 KateRenderer *
const renderer_ = renderer();
4949 KateLineLayout *lineLayout(
new KateLineLayout(*renderer_));
4950 lineLayout->setLine(previewLine, -1);
4953 const int col = lineLayout->textLine().firstChar();
4954 if (previewLine > 0 && (col == -1 || col == openBracketCursor.
column())) {
4955 lineLayout->setLine(previewLine - 1, lineLayout->virtualLine() - 1);
4958 renderer_->
layoutLine(lineLayout, -1 ,
false );
4959 const int lineWidth =
4960 qBound(m_view->width() / 5,
int(lineLayout->width() + renderer_->spaceWidth() * 2), m_view->width() - m_leftBorder->width() - m_lineScroll->width());
4961 m_bmPreview->resize(lineWidth, renderer_->lineHeight() * 2);
4963 m_bmPreview->move(topLeft.
x(), topLeft.
y());
4964 m_bmPreview->setLine(lineLayout->virtualLine());
4965 m_bmPreview->setCenterView(
false);
4966 m_bmPreview->raise();
4967 m_bmPreview->show();
4970void KateViewInternal::hideBracketMatchPreview()
4972 m_bmPreview.reset();
4977#ifndef QT_NO_ACCESSIBILITY
4979 auto doc = view()->doc();
4980 QAccessibleTextInsertEvent ev(
this, doc->cursorToOffset(range.
start()), doc->text(range));
4988#ifndef QT_NO_ACCESSIBILITY
4990 auto doc = view()->doc();
4991 QAccessibleTextRemoveEvent ev(
this, doc->cursorToOffset(range.
start()), oldText);
4999 KTextEditor::InlineNote note(noteData);
5001 const auto noteWidth = note.width();
5002 auto noteCursor = note.position();
5006 const auto lineLength = view()->document()->lineLength(noteCursor.line());
5007 int extraOffset = -noteWidth;
5008 if (noteCursor.column() == lineLength) {
5010 }
else if (noteCursor.column() > lineLength) {
5011 extraOffset = (noteCursor.column() - lineLength) * renderer()->spaceWidth();
5012 noteCursor.setColumn(lineLength);
5014 auto noteStartPos =
mapToGlobal(cursorToCoordinate(noteCursor,
true,
false));
5016 if (view()->dynWordWrap()) {
5017 const KateLineLayout *lineLayout = cache()->line(noteCursor.line());
5021 noteStartPos.rx() -= note.width();
5022 extraOffset = -extraOffset;
5026 auto globalNoteRect = QRect(noteStartPos + QPoint{extraOffset, 0}, QSize(noteWidth, renderer()->lineHeight()));
5028 return globalNoteRect;
5034 const int line = coordinatesToCursor(
mapFromGlobal(globalPos)).line();
5035 const auto inlineNotes = view()->inlineNotes(line);
5037 for (
const auto ¬e : inlineNotes) {
5038 auto globalNoteRect = inlineNoteRect(note);
5039 if (globalNoteRect.contains(globalPos)) {
5047bool KateViewInternal::sendMouseEventToInputContext(
QMouseEvent *e)
5049 if (!m_imPreeditRange) {
5053 KTextEditor::Cursor c = cursorForPoint(e->
pos());
5054 if (!m_imPreeditRange->contains(c) && c != m_imPreeditRange->end()) {
5058 auto cursorPos = (c - m_imPreeditRange->
start());
5060 if (cursorPos.column() >= 0) {
5069void KateViewInternal::commitPreedit()
5071 if (!m_imPreeditRange) {
5078#include "moc_kateviewinternal.cpp"
The Cursor represents a position in a Document.
@ ActivateMouseIn
Activate attribute on mouse in.
@ ActivateCaretIn
Activate attribute on caret in.
QExplicitlySharedDataPointer< Attribute > Ptr
Shared data pointer for Attribute.
The Cursor represents a position in a Document.
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
void setColumn(int column) noexcept
Set the cursor column to column.
void setPosition(Cursor position) noexcept
Set the current cursor position to position.
constexpr bool isValid() const noexcept
Returns whether the current position of this cursor is a valid position (line + column must both be >...
static constexpr Cursor start() noexcept
Returns a cursor representing the start of any document - i.e., line 0, column 0.
void setLine(int line) noexcept
Set the cursor line to line.
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
static constexpr Cursor invalid() noexcept
Returns an invalid cursor.
Backend of KTextEditor::Document related public KTextEditor interfaces.
void textRemoved(KTextEditor::Document *document, KTextEditor::Range range, const QString &oldText)
The document emits this signal whenever range was removed, i.e.
void textInsertedRange(KTextEditor::Document *document, KTextEditor::Range range)
The document emits this signal whenever text was inserted.
A KParts derived class representing a text document.
static KTextEditor::EditorPrivate * self()
Kate Part Internal stuff ;)
@ TopInView
show message as view overlay in the top right corner.
@ CenterInView
show message as view overlay in the center of the view.
@ BottomInView
show message as view overlay in the bottom right corner.
virtual int column() const =0
Retrieve the column on which this cursor is situated.
virtual int line() const =0
Retrieve the line on which this cursor is situated.
@ ExpandRight
Expand to encapsulate new characters to the right of the range.
@ ExpandLeft
Expand to encapsulate new characters to the left of the range.
An object representing a section of text, from one Cursor to another.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
static constexpr Range invalid() noexcept
Returns an invalid range.
constexpr bool isValid() const noexcept
Validity check.
Class to provide text hints for a View.
@ NormalInputMode
Normal Mode.
void selectionChanged(KTextEditor::View *view)
This signal is emitted whenever the view's selection changes.
Internal data container for KTextEditor::InlineNote interface.
This class handles Kate's caching of layouting information (in KateLineLayout and KateTextLayout).
Handles all of the work of rendering the text (used for the views and printing)
void layoutLine(KateLineLayout *line, int maxwidth=-1, bool cacheLayout=false) const
Text width & height calculation functions...
This class represents one visible line of text; with dynamic wrapping, many KateTextLayouts can be ne...
int endCol(bool indicateEOL=false) const
Return the end column of this text line.
int viewLine() const
Return the index of this visual line inside the document line (KateLineLayout).
void foldingRangesChanged()
If the folding state of existing ranges changes or ranges are added/removed, this signal is emitted.
int attribute(int pos) const
Gets the attribute at the given position use KRenderer::attributes to get the KTextAttribute for this...
const QString & text() const
Accessor to the text contained in this line.
int length() const
Returns the line's length.
int lastChar() const
Returns the position of the last non-whitespace character.
QChar at(int column) const
Returns the character at the given column.
int firstChar() const
Returns the position of the first non-whitespace character.
An object representing a section of text, from one Cursor to another.
void setRange(Range range) noexcept
Set the start and end cursors to range.start() and range.end() respectively.
static constexpr Range invalid() noexcept
Returns an invalid range.
Q_SCRIPTABLE QString start(QString train="")
Q_SCRIPTABLE Q_NOREPLY void start()
QAction * hint(const QObject *recvr, const char *slot, QObject *parent)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
@ Default
Default settings.
void actionTriggered(int action)
void sliderMoved(int value)
void valueChanged(int value)
void installFactory(InterfaceFactory factory)
QAccessibleInterface * queryAccessibleInterface(QObject *object)
void removeFactory(InterfaceFactory factory)
void updateAccessibility(QAccessibleEvent *event)
Category category(char32_t ucs4)
bool isHighSurrogate(char32_t ucs4)
bool isLetter(char32_t ucs4)
bool isLetterOrNumber(char32_t ucs4)
bool isLowSurrogate(char32_t ucs4)
bool isPrint(char32_t ucs4)
bool isSpace(char32_t ucs4)
bool isUpper(char32_t ucs4)
QObject * child() const const
bool removed() const const
void setText(const QString &text, Mode mode)
QString text(Mode mode) const const
bool isValid() const const
bool sendEvent(QObject *receiver, QEvent *event)
bool isAccepted() const const
const T * constData() const const
qreal horizontalAdvance(QChar ch) const const
Qt::KeyboardModifiers modifiers() const const
QString text() const const
void append(QList< T > &&value)
bool isEmpty() const const
const QPoint & oldPos() const const
const QPoint & pos() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
virtual bool eventFilter(QObject *watched, QEvent *event)
void installEventFilter(QObject *filterObj)
void removeEventFilter(QObject *obj)
qreal devicePixelRatioF() const const
const QRect & rect() const const
const QRegion & region() const const
int manhattanLength() const const
virtual void setAccepted(bool accepted) override
QPoint toPoint() const const
bool isEmpty() const const
const QSize & oldSize() const const
QPointF globalPosition() const const
QPointF position() const const
const QChar at(qsizetype position) const const
QString & fill(QChar ch, qsizetype size)
bool isEmpty() const const
qsizetype length() const const
qsizetype size() const const
QChar at(qsizetype n) const const
qsizetype size() const const
SH_RequestSoftwareInputPanel
typedef KeyboardModifiers
bool isValid() const const
int nextCursorPosition(int oldPos, CursorMode mode) const const
int previousCursorPosition(int oldPos, CursorMode mode) const const
const QTextOption & textOption() const const
Qt::LayoutDirection textDirection() const const
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
bool contains(const AT &value) const const
bool isEmpty() const const
qsizetype size() const const
QPoint angleDelta() const const