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;
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)
120 , m_bmLastFlashPos(doc()->newMovingCursor(
KTextEditor::Cursor::invalid()))
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)
138 , m_selectionMode(Default)
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;
282#ifndef QT_NO_ACCESSIBILITY
285 connect(doc(), &KTextEditor::DocumentPrivate::textInsertedRange,
this, &KateViewInternal::documentTextInserted);
286 connect(doc(), &KTextEditor::DocumentPrivate::textRemoved,
this, &KateViewInternal::documentTextRemoved);
292KateViewInternal::~KateViewInternal()
294#ifndef QT_NO_ACCESSIBILITY
299 delete m_textAnimation;
303 m_leftBorder =
nullptr;
306void KateViewInternal::dynWrapChanged()
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()) {
336 for (
int i = qMin(linesDisplayed() - 1, cache()->viewCacheLineCount() - 1); i >= 0; i--) {
339 if (thisLine.line() == -1) {
343 if (thisLine.virtualLine() >= view()->textFolding().visibleLines()) {
346 doc()->lineLength(view()->textFolding().visibleLineToLine(view()->textFolding().visibleLines() - 1)));
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()) {
374 return KateTextLayout::invalid();
377int KateViewInternal::lineToY(
int viewLine)
const
379 return (viewLine - startLine()) * renderer()->lineHeight();
382void KateViewInternal::slotIncFontSizes(qreal step)
387void KateViewInternal::slotDecFontSizes(qreal step)
389 renderer()->decreaseFontSizes(step);
392void KateViewInternal::slotResetFontSizes()
394 renderer()->resetFontSizes();
400void KateViewInternal::scrollLines(
int line)
407void KateViewInternal::scrollViewLines(
int offset)
413 m_lineScroll->
setValue(startLine());
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) {
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())) {
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) {
521 m_madeVisible =
false;
523 if (viewLinesScrolledUsable) {
524 int lines = linesDisplayed();
525 if (view()->textFolding().visibleLines() < lines) {
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);
549 Q_EMIT view()->displayRangeChanged(m_view);
560 Q_EMIT view()->displayRangeChanged(m_view);
564void KateViewInternal::scrollColumns(
int x)
570 if (
x > m_columnScroll->
maximum()) {
578 int dx = startX() -
x;
581 if (qAbs(dx) <
width()) {
589 Q_EMIT view()->displayRangeChanged(m_view);
597void KateViewInternal::updateView(
bool changed,
int viewLinesScrolled)
599 if (!
isVisible() && !viewLinesScrolled && !changed) {
603 view()->doc()->delayAutoReload();
606 int wrapWidth =
width();
607 if (view()->config()->dynWrapAtStaticMarker() && view()->config()->dynWordWrap()) {
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;
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());
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)));
647 if (!view()->dynWordWrap()) {
648 int max = maxLen(startLine()) -
width();
663 visible = ((show_scrollbars == KateViewConfig::AlwaysOn) || ((show_scrollbars == KateViewConfig::ShowWhenNeeded) && (max != 0)));
667 m_columnScroll->
setRange(0, max + (renderer()->spaceWidth() / 2));
677 visible_dummy =
false;
691void KateViewInternal::makeVisible(
const KTextEditor::Cursor c,
int endCol,
bool force,
bool center,
bool calledExternally)
698 const int lnDisp = linesDisplayed();
700 const bool curBelowScreen = (viewLine == -2);
704 scrollPos(
scroll, force, calledExternally);
705 }
else if (center && (c < startPos() || c > endPos())) {
707 scrollPos(
scroll,
false, calledExternally);
708 }
else if ((viewLine >= (lnDisp - m_minLinesVisible)) || (curBelowScreen)) {
710 scrollPos(
scroll,
false, calledExternally);
711 }
else if (c < viewLineOffset(startPos(), m_minLinesVisible)) {
713 scrollPos(
scroll,
false, calledExternally);
717 if (startPos() > max) {
718 scrollPos(max, max.
column(), calledExternally);
722 if (!view()->dynWordWrap() && (endCol != -1 || view()->wrapCursor())) {
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);
758 if (startPos() > max) {
759 scrollPos(max,
false,
false,
false );
763 qint64 foldedRangeId = -1;
764 if (!view()->textFolding().isLineVisible(m_cursor.
line(), &foldedRangeId)) {
769 updateCursor(foldingRange.
start(),
true);
773 updateCursor(m_cursor,
true);
782 Q_EMIT view()->displayRangeChanged(m_view);
785void KateViewInternal::slotRegionBeginEndAddedRemoved(
unsigned int)
792void KateViewInternal::showEvent(
QShowEvent *e)
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()) {
827 if (viewLine < 0 || viewLine >= cache()->viewCacheLineCount()) {
831 const int y = (int)viewLine * renderer()->lineHeight();
835 const auto textLength = doc()->lineLength(
cursor.line());
836 if (
cursor.column() > textLength) {
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);
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)
918 CalculatingCursor(KateViewInternal *vi,
int line,
int col)
919 : m_cursor(line, col)
925 virtual ~CalculatingCursor()
931 return m_cursor.
line();
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())));
969 void toEdge(KateViewInternal::Bias bias)
971 if (bias == KateViewInternal::left) {
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();
1020 KateViewInternal *m_vi;
1023class BoundedCursor final :
public CalculatingCursor
1026 BoundedCursor(KateViewInternal *vi)
1027 : CalculatingCursor(vi)
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) {
1076 for (
int i = 0; i > n; i--) {
1077 if (column() >= thisLine->length()) {
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)
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)
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) {
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();
1171 if (column() > thisLine->length()) {
1182 CalculatingCursor &operator-=(
int n)
override
1184 return operator+=(-n);
1271class CamelCursor final :
public CalculatingCursor
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();
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()) {
1340 jump = col < 0 || (column() == col) ? (column() + 1) : col;
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) {
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;
1427 CalculatingCursor &operator-=(
int n)
override
1429 return operator+=(-n);
1433void KateViewInternal::moveChar(KateViewInternal::Bias bias,
bool sel)
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;
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)
1486 return doc()->characterAt({
cursor.line(),
cursor.column() - 1});
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 (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;
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)
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 (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;
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()) {
1633 if (!doc()->config()->smartHome()) {
1634 BoundedCursor c(
this,
cursor);
1639 if (
cursor.line() < 0 ||
cursor.line() >= doc()->lines()) {
1648 if (lc < 0 || c.
column() == lc) {
1656void KateViewInternal::home(
bool sel)
1659 view()->ensureUniqueCursors(
true);
1660 const auto &secondaryCursors = view()->m_secondaryCursors;
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()) {
1691 if (!doc()->config()->smartHome()) {
1692 BoundedCursor c(
this,
cursor);
1697 if (
cursor.line() < 0 ||
cursor.line() >= doc()->lines()) {
1704 if (
cursor.column() == doc()->lineLength(
cursor.line())) {
1709 BoundedCursor c(
this,
cursor);
1715void KateViewInternal::end(
bool sel)
1718 view()->ensureUniqueCursors(
true);
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);
1747 int currentViewLine = cache()->
viewLine(c);
1749 if (currentViewLine) {
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);
1779 if (!view()->dynWordWrap()) {
1780 KTextEditor::Cursor ret(qMin((
int)view()->textFolding().visibleLines() - 1, virtualCursor.
line() + offset), 0);
1782 if (ret.line() < 0) {
1789 Q_ASSERT(t.isValid());
1791 ret.setColumn(renderer()->xToCursor(t, m_preservedX, !view()->wrapCursor()).column());
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) {
1812 Q_ASSERT(thisLine.virtualLine() == (
int)view()->textFolding().lineToVisibleLine(virtualCursor.
line()));
1816 virtualLine = virtualCursor.
line() + 1;
1820 currentOffset = cursorViewLine;
1821 if (offset <= currentOffset) {
1824 Q_ASSERT(thisLine.virtualLine() == (
int)view()->textFolding().lineToVisibleLine(virtualCursor.
line()));
1828 virtualLine = virtualCursor.
line() - 1;
1833 while (virtualLine >= 0 && virtualLine < (
int)view()->textFolding().visibleLines()) {
1835 KateLineLayout *thisLine = cache()->
line(realLine, virtualLine);
1840 for (
int i = 0; i < thisLine->viewLineCount(); ++i) {
1841 if (offset == currentOffset) {
1846 int requiredViewLine = cache()->lastViewLine(realLine) - thisViewLine.
viewLine();
1847 if (requiredViewLine != thisViewLine.
viewLine()) {
1848 thisViewLine = thisLine->viewLine(requiredViewLine);
1856 realCursor = renderer()->
xToCursor(thisViewLine, m_preservedX, !view()->wrapCursor());
1877 doc()->lineLength(view()->textFolding().visibleLineToLine(view()->textFolding().visibleLines() - 1)));
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);
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());
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);
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());
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()))) {
2020 updateSecondarySelection(i,
cursor, newPos);
2022 view()->clearSecondarySelections();
2024 auto vNewPos = toVirtualCursor(newPos);
2025 tagLines(vCursor.line(), vNewPos.line());
2035 Q_ASSERT((
cursor.line() == thisLine.line()) && (
cursor.column() >= thisLine.startCol()) && (!thisLine.wrap() ||
cursor.column() < thisLine.
endCol()));
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);
2074 Q_ASSERT((m_cursor.
line() == thisLine.line()) && (m_cursor.
column() >= thisLine.startCol()) && (!thisLine.wrap() || m_cursor.
column() < thisLine.
endCol()));
2078 updateSelection(c, sel);
2082void KateViewInternal::cursorToMatchingBracket(
bool sel)
2087 updateSelection(c, sel);
2092void KateViewInternal::topOfView(
bool sel)
2094 view()->clearSecondaryCursors();
2096 updateSelection(toRealCursor(c), sel);
2097 updateCursor(toRealCursor(c));
2100void KateViewInternal::bottomOfView(
bool sel)
2102 view()->clearSecondaryCursors();
2104 updateSelection(toRealCursor(c), sel);
2105 updateCursor(toRealCursor(c));
2109void KateViewInternal::scrollLines(
int lines,
bool sel)
2114 c.
setLine(view()->textFolding().visibleLineToLine(c.
line()));
2116 updateSelection(c, sel);
2121void KateViewInternal::scrollUp()
2127void KateViewInternal::scrollDown()
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());
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) {
2174 scrollPos(newStartPos);
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());
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) {
2225 scrollPos(newStartPos);
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()
2279void KateViewInternal::top(
bool sel)
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)
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();
2310 updateSelection(c, sel);
2314void KateViewInternal::bottom_end(
bool sel)
2316 if (view()->isCompletionActive()) {
2317 view()->completionWidget()->bottom();
2321 view()->clearSecondaryCursors();
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}));
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;
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();
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();
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()) {
2423 if (newCursor.
line() + 1 >= doc()->lines()) {
2424 newCursor.
setColumn(doc()->line(newCursor.
line()).length());
2429 m_selectAnchor = m_selectionCached.
start();
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());
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();
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) {
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) {
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) {
2592 m_fmEnd->setRange(foldingMarkerMatch);
2594 m_fmStart->setRange(foldingMarkerMatch);
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();
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>;
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;
2658 auto doMerge = [](
Range newRange, SecondaryCursor &a, SecondaryCursor &b) {
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 bool isLeft = it->cursor() < it->anchor;
2716 if (it->cursor() < primCursor) {
2717 updateCursor(it->cursor());
2719 m_selectAnchor = qMax(m_selectAnchor, it->anchor);
2721 if (it->cursor() > primCursor) {
2722 updateCursor(it->cursor());
2724 m_selectAnchor = qMin(m_selectAnchor, it->anchor);
2727 setSelection(primarySel);
2730 }
else if (it->pos) {
2734 auto pos = it->cursor();
2735 if (!primarySel.boundaryAtCursor(
pos) && primarySel.contains(
pos)) {
2742 cursors.
erase(std::remove_if(cursors.
begin(),
2744 [](
const SecondaryCursor &c) {
2745 return !c.pos.get();
2750void KateViewInternal::updateCursor(
const KTextEditor::Cursor newCursor,
bool force,
bool center,
bool calledExternally,
bool scroll)
2752 if (!force && (m_cursor.
toCursor() == newCursor)) {
2753 m_displayCursor = toVirtualCursor(newCursor);
2754 if (
scroll && !m_madeVisible && m_view == doc()->activeView()) {
2758 makeVisible(m_displayCursor, m_displayCursor.
column(),
false, center, calledExternally);
2764 if (m_cursor.
line() != newCursor.
line()) {
2765 m_leftBorder->updateForCursorLineChange();
2773 m_displayCursor = toVirtualCursor(newCursor);
2776 if (m_view == doc()->activeView() &&
scroll) {
2777 makeVisible(m_displayCursor, m_displayCursor.
column(),
false, center, calledExternally);
2780 updateBracketMarks();
2782 updateFoldingMarkersHighlighting();
2785 tagLine(oldDisplayCursor);
2786 if (oldDisplayCursor.
line() != m_displayCursor.
line()) {
2787 tagLine(m_displayCursor);
2801 m_preserveX =
false;
2803 m_preservedX = renderer()->
cursorToX(cache()->textLayout(m_cursor), m_cursor, !view()->wrapCursor());
2817void KateViewInternal::updateBracketMarkAttributes()
2820 bracketFill->setBackground(view()->rendererConfig()->highlightedBracketColor());
2821 bracketFill->setBackgroundFillWhitespace(
false);
2822 if (
QFontInfo(renderer()->currentFont()).fixedPitch()) {
2824 bracketFill->setFontBold();
2827 m_bmStart->setAttribute(bracketFill);
2828 m_bmEnd->setAttribute(bracketFill);
2830 if (view()->rendererConfig()->showWholeBracketExpression()) {
2832 expressionFill->setBackground(view()->rendererConfig()->highlightedBracketColor());
2833 expressionFill->setBackgroundFillWhitespace(
false);
2835 m_bm->setAttribute(expressionFill);
2841void KateViewInternal::updateBracketMarks()
2844 const int maxLines = 5000;
2849 if (m_bm->toRange() == newRange) {
2851 hideBracketMatchPreview();
2856 m_bm->setRange(newRange);
2863 if (m_view->config()->
value(KateViewConfig::ShowBracketMatchPreview).
toBool()) {
2864 showBracketMatchPreview();
2868 if (!m_view->rendererConfig()->animateBracketMatching()) {
2872 const KTextEditor::Cursor flashPos = (m_cursor == m_bmStart->start() || m_cursor == m_bmStart->end()) ? m_bmEnd->start() : m_bm->start();
2873 if (flashPos != m_bmLastFlashPos->toCursor()) {
2874 m_bmLastFlashPos->setPosition(flashPos);
2877 attribute->setBackground(view()->rendererConfig()->highlightedBracketColor());
2878 if (m_bmStart->attribute()->fontBold()) {
2879 attribute->setFontBold(
true);
2882 flashChar(flashPos, attribute);
2892 hideBracketMatchPreview();
2898 return tagLines(virtualCursor, virtualCursor,
false);
2901bool KateViewInternal::tagLines(
int start,
int end,
bool realLines)
2909 cache()->relayoutLines(
start.line(),
end.line());
2913 end = toVirtualCursor(end);
2916 cache()->relayoutLines(toRealCursor(
start).line(), toRealCursor(end).line());
2919 if (
end.line() < startLine()) {
2925 if (
start.line() > startLine() + cache()->viewCacheLineCount()) {
2930 cache()->updateViewCache(startPos());
2936 for (
int z = 0; z < cache()->viewCacheLineCount(); z++) {
2938 if ((line.virtualLine() >
start.line() || (line.virtualLine() ==
start.line() && line.
endCol() >=
start.column() &&
start.column() != -1))
2939 && (line.virtualLine() <
end.line() || (line.virtualLine() ==
end.line() && (line.startCol() <=
end.column() ||
end.column() == -1)))) {
2947 if (!m_view->config()->showFoldingOnHoverOnly() && doc()->highlight() && doc()->highlight()->foldingIndentationSensitive()) {
2950 }
else if (!view()->dynWordWrap()) {
2951 int y = lineToY(
start.line());
2953 int h = (
end.line() -
start.line() + 2) * renderer()->lineHeight();
2962 for (
int z = 0; z < cache()->viewCacheLineCount(); z++) {
2965 || ((line.virtualLine() >
start.line() || (line.virtualLine() ==
start.line() && line.
endCol() >=
start.column() &&
start.column() != -1))
2966 && (line.virtualLine() <
end.line() || (line.virtualLine() ==
end.line() && (line.startCol() <=
end.column() ||
end.column() == -1))))) {
2968 m_leftBorder->
update(0, z * renderer()->lineHeight(), m_leftBorder->
width(), m_leftBorder->
height());
2985 return tagLines(range.
start(), range.
end(), realCursors);
2988void KateViewInternal::tagAll()
2993 m_leftBorder->updateFont();
2997void KateViewInternal::paintCursor()
3000 if (tagLine(m_displayCursor)) {
3006 for (
const auto &c : view()->m_secondaryCursors) {
3007 auto p = c.cursor();
3008 if (p.line() >= s - 1 && p.line() <= e + 1 && !updatedLines.
contains(p.line())) {
3010 tagLines(p, p,
true);
3014 if (!updatedLines.
isEmpty()) {
3024 if (!thisLine.isValid()) {
3025 thisLine = cache()->
textLayout(doc()->lines() - 1, -1);
3028 c = renderer()->
xToCursor(thisLine, startX() + p.
x(), !view()->wrapCursor());
3030 if (c.
line() < 0 || c.
line() >= doc()->lines()) {
3035 const auto inlineNotes = view()->inlineNotes(c.
line());
3037 for (
const auto ¬e : inlineNotes) {
3038 auto noteCursor = note.m_position;
3040 if (note.m_position.column() >= doc()->lineLength(c.
line()) || note.m_position.column() == 0) {
3046 const auto caretWidth = renderer()->
caretStyle() == KTextEditor::caretStyles::Line ? 2. : 0.;
3049 const auto halfCharWidth = (charWidth / 2);
3052 const auto totalWidth =
width + halfCharWidth;
3056 if (r.contains(p)) {
3066void KateViewInternal::placeCursor(
const QPoint &p,
bool keepSelection,
bool updateSelection)
3073 if (updateSelection) {
3074 KateViewInternal::updateSelection(c, keepSelection);
3077 int tmp = m_minLinesVisible;
3078 m_minLinesVisible = 0;
3080 m_minLinesVisible = tmp;
3082 if (updateSelection && keepSelection) {
3083 moveCursorToSelectionEdge();
3088bool KateViewInternal::isTargetSelected(
const QPoint &p)
3091 if (!thisLine.isValid()) {
3095 return view()->cursorSelected(renderer()->xToCursor(thisLine, startX() + p.
x(), !view()->wrapCursor()));
3102 switch (e->
type()) {
3118 if (!view()->m_secondaryCursors.empty()) {
3119 view()->clearSecondaryCursors();
3124 if (view()->isCompletionActive()) {
3125 view()->abortCompletion();
3129 }
else if (view()->bottomViewBar()->barWidgetVisible()) {
3130 view()->bottomViewBar()->hideCurrentBarWidget();
3134 }
else if (!view()->config()->persistentSelection() && view()->selection()) {
3135 m_currentInputMode->clearSelection();
3143 if (view()->completionWidget()->handleShortcutOverride(k)) {
3149 if (m_currentInputMode->stealKey(k)) {
3182 QRect doNotScrollRegion(s_scrollMargin, s_scrollMargin,
width() - s_scrollMargin * 2,
height() - s_scrollMargin * 2);
3184 if (!doNotScrollRegion.contains(currentPoint)) {
3199 hideBracketMatchPreview();
3204 scrollPrepareEvent(s);
3221void KateViewInternal::keyPressEvent(
QKeyEvent *e)
3225 view()->emitNavigateLeft();
3230 view()->emitNavigateRight();
3235 view()->emitNavigateUp();
3240 view()->emitNavigateDown();
3245 view()->emitNavigateAccept();
3250 view()->emitNavigateBack();
3255 if (e->
key() ==
Qt::Key_Alt && view()->completionWidget()->isCompletionActive()) {
3256 view()->completionWidget()->toggleDocumentation();
3262 if (m_currentInputMode->keyPress(e)) {
3266 if (!doc()->isReadWrite()) {
3273 view()->keyReturn();
3287 uint tabHandling = doc()->config()->tabHandling();
3289 if (tabHandling == KateDocumentConfig::tabSmart) {
3291 if (view()->selection() && !view()->selectionRange().onSingleLine()) {
3292 tabHandling = KateDocumentConfig::tabIndents;
3302 if (first < 0 || m_cursor.
column() <= first) {
3303 tabHandling = KateDocumentConfig::tabIndents;
3305 tabHandling = KateDocumentConfig::tabInsertsTab;
3311 if (tabHandling == KateDocumentConfig::tabInsertsTab) {
3312 doc()->typeChars(m_view, QStringLiteral(
"\t"));
3315 for (
const auto &c :
std::as_const(m_view->m_secondaryCursors)) {
3316 auto cursor = c.cursor();
3320 doc()->indent(view()->selection() ? view()->selectionRange() :
KTextEditor::
Range(m_cursor.line(), 0, m_cursor.line(), 0), 1);
3327 }
else if (doc()->config()->tabHandling() != KateDocumentConfig::tabInsertsTab) {
3329 doc()->indent(view()->selection() ? view()->selectionRange() :
KTextEditor::
Range(m_cursor.line(), 0, m_cursor.line(), 0), -1);
3336 if (isAcceptableInput(e)) {
3337 doc()->typeChars(m_view, e->
text());
3345void KateViewInternal::keyReleaseEvent(
QKeyEvent *e)
3348 m_shiftKeyPressed =
false;
3350 if (m_selChangedByUser) {
3351 if (view()->selection()) {
3355 m_selChangedByUser =
false;
3363bool KateViewInternal::isAcceptableInput(
const QKeyEvent *e)
3405 makeVisible(m_displayCursor, 0);
3406 p = cursorCoordinates(
false);
3408 }
else if (!view()->selection() || view()->config()->persistentSelection()) {
3409 placeCursor(e->
pos());
3413 QMenu *cm = view()->contextMenu();
3415 view()->spellingMenu()->prepareToBeShown(cm);
3421void KateViewInternal::mousePressEvent(
QMouseEvent *e)
3423 if (sendMouseEventToInputContext(e)) {
3430 if (note.position().isValid()) {
3441 m_selChangedByUser =
false;
3443 if (!view()->isMulticursorNotAllowed() && e->
modifiers() == view()->config()->multiCursorModifiers()) {
3444 auto pos = cursorForPoint(e->
pos());
3445 if (
pos.isValid()) {
3446 view()->addSecondaryCursor(
pos);
3451 view()->clearSecondaryCursors();
3454 if (m_possibleTripleClick) {
3455 m_possibleTripleClick =
false;
3457 m_selectionMode = Line;
3460 updateSelection(m_cursor,
true);
3462 view()->selectLine(m_cursor);
3463 if (view()->selection()) {
3464 m_selectAnchor = view()->selectionRange().
start();
3468 if (view()->selection()) {
3474 if (m_selectAnchor.
line() > view()->selectionRange().
start().line()) {
3476 if (m_selectAnchor == view()->selectionRange().
end() && m_selectAnchor.
column() == 0) {
3481 m_selectionCached.
setEnd(view()->selectionRange().
end());
3484 m_selectionCached.
setStart(view()->selectionRange().
start());
3485 if (view()->selectionRange().
end().line() > view()->selectionRange().
start().line()) {
3488 m_selectionCached.
setEnd(view()->selectionRange().
end());
3492 moveCursorToSelectionEdge();
3496 m_scrollTimer.
start(50);
3500 }
else if (m_selectionMode == Default) {
3501 m_selectionMode = Mouse;
3514 if (!m_selectAnchor.
isValid()) {
3515 m_selectAnchor = m_cursor;
3522 m_dragInfo.state = diPending;
3523 m_dragInfo.start = e->
pos();
3525 m_dragInfo.state = diNone;
3528 placeCursor(e->
pos(),
true,
false);
3531 m_selectAnchor = m_selectionCached.
end();
3533 m_selectAnchor = m_selectionCached.
start();
3539 if (view()->selection()) {
3543 placeCursor(e->
pos());
3549 m_scrollTimer.
start(50);
3556 if (e->
pos().
x() == 0) {
3558 placeCursor(e->
pos());
3569void KateViewInternal::mouseDoubleClickEvent(
QMouseEvent *e)
3571 if (sendMouseEventToInputContext(e)) {
3575 m_selectionMode = Word;
3583 ce = m_selectAnchor.
column();
3584 if (ce > 0 && doc()->highlight()->isInWord(l.
at(ce))) {
3585 for (; ce < l.
length(); ce++) {
3586 if (!doc()->highlight()->isInWord(l.
at(ce))) {
3592 cs = m_selectAnchor.
column() - 1;
3593 if (cs < doc()->lineLength(m_selectAnchor.
line()) && doc()->highlight()->isInWord(l.
at(cs))) {
3594 for (cs--; cs >= 0; cs--) {
3595 if (!doc()->highlight()->isInWord(l.
at(cs))) {
3606 m_selectionCached.
setStart(m_selectAnchor);
3607 m_selectionCached.
setEnd(m_selectAnchor);
3610 placeCursor(e->
pos(),
true);
3617 view()->clearSelection(
false,
false);
3618 placeCursor(e->
pos());
3619 view()->selectWord(m_cursor);
3620 cursorToMatchingBracket(
true);
3622 if (view()->selection()) {
3623 m_selectAnchor = view()->selectionRange().
start();
3624 m_selectionCached = view()->selectionRange();
3626 m_selectAnchor = m_cursor;
3633 if (view()->selection()) {
3638 moveCursorToSelectionEdge();
3639 m_possibleTripleClick =
true;
3645 m_scrollTimer.
start(50);
3653void KateViewInternal::tripleClickTimeout()
3655 m_possibleTripleClick =
false;
3658void KateViewInternal::beginSelectLine(
const QPoint &pos)
3661 m_possibleTripleClick =
true;
3664void KateViewInternal::mouseReleaseEvent(
QMouseEvent *e)
3666 if (sendMouseEventToInputContext(e)) {
3672 m_selectionMode = Default;
3675 if (m_selChangedByUser) {
3676 if (view()->selection()) {
3679 moveCursorToSelectionEdge();
3681 m_selChangedByUser =
false;
3684 if (m_dragInfo.state == diPending) {
3686 }
else if (m_dragInfo.state == diNone) {
3687 m_scrollTimer.
stop();
3690 m_dragInfo.state = diNone;
3693 if (view()->selection() && !view()->m_secondaryCursors.empty()) {
3701 if (!view()->config()->mousePasteAtCursorPosition()) {
3702 placeCursor(e->
pos());
3705 if (doc()->isReadWrite()) {
3707 view()->paste(&clipboard);
3719void KateViewInternal::leaveEvent(
QEvent *)
3721 m_textHintTimer.
stop();
3725 if (m_dragInfo.state == diNone) {
3726 m_scrollTimer.
stop();
3729 hideBracketMatchPreview();
3738 if (includeBorder) {
3739 coord.rx() -= m_leftBorder->
width();
3741 coord.rx() += startX();
3744 if (thisLine.isValid()) {
3745 ret = renderer()->
xToCursor(thisLine, coord.x(), !view()->wrapCursor());
3748 if (ret.
column() > view()->document()->lineLength(ret.
line())) {
3757void KateViewInternal::mouseMoveEvent(
QMouseEvent *e)
3764 if (newPosition != m_mouse) {
3765 m_mouse = newPosition;
3771 auto focusChanged =
false;
3772 if (noteData.m_position.isValid()) {
3773 if (!m_activeInlineNote.m_position.
isValid()) {
3775 tagLine(noteData.m_position);
3776 focusChanged =
true;
3777 noteData.m_underMouse =
true;
3779 m_activeInlineNote = noteData;
3783 }
else if (m_activeInlineNote.m_position.
isValid()) {
3784 tagLine(m_activeInlineNote.m_position);
3785 focusChanged =
true;
3786 m_activeInlineNote.m_underMouse =
false;
3788 m_activeInlineNote = {};
3797 if (m_dragInfo.state == diPending) {
3808 }
else if (m_dragInfo.state == diDragging) {
3819 int d = renderer()->lineHeight();
3825 if (m_mouseX >
width()) {
3834 if (m_mouseY >
height()) {
3846 placeCursor(
QPoint(m_mouseX, m_mouseY),
true);
3849 if (view()->config()->textDragAndDrop() && isTargetSelected(e->
pos())) {
3870 m_textHintTimer.
start(m_textHintDelay);
3871 m_textHintPos = e->
pos();
3876void KateViewInternal::updateDirty()
3878 const int h = renderer()->lineHeight();
3880 int currentRectStart = -1;
3881 int currentRectEnd = -1;
3886 for (
int i = 0; i < cache()->viewCacheLineCount(); ++i) {
3887 if (cache()->
viewLine(i).isDirty()) {
3888 if (currentRectStart == -1) {
3889 currentRectStart = h * i;
3892 currentRectEnd += h;
3895 }
else if (currentRectStart != -1) {
3896 updateRegion +=
QRect(0, currentRectStart,
width(), currentRectEnd);
3897 currentRectStart = -1;
3898 currentRectEnd = -1;
3903 if (currentRectStart != -1) {
3904 updateRegion +=
QRect(0, currentRectStart,
width(), currentRectEnd);
3907 if (!updateRegion.
isEmpty()) {
3908 if (debugPainting) {
3909 qCDebug(LOG_KTE) <<
"Update dirty region " << updateRegion;
3915void KateViewInternal::hideEvent(
QHideEvent *e)
3918 if (view()->isCompletionActive()) {
3919 view()->completionWidget()->abortCompletion();
3925 if (debugPainting) {
3926 qCDebug(LOG_KTE) <<
"GOT PAINT EVENT: Region" << e->
region();
3931 int xStart = startX() + unionRect.
x();
3932 int xEnd = xStart + unionRect.
width();
3933 uint h = renderer()->lineHeight();
3934 uint startz = (unionRect.
y() / h);
3935 uint endz = startz + 1 + (unionRect.
height() / h);
3936 uint lineRangesSize = cache()->viewCacheLineCount();
3947 renderer()->
setCaretStyle(m_currentInputMode->caretStyle());
3948 renderer()->
setShowTabs(doc()->config()->showTabs());
3955 paint.translate(unionRect.
x(), startz * h);
3956 for (uint z = startz; z <= endz; z++) {
3958 if ((z >= lineRangesSize) || (cache()->viewLine(z).line() == -1)) {
3959 if (!(z >= lineRangesSize)) {
3960 cache()->
viewLine(z).setDirty(
false);
3962 paint.fillRect(0, 0, unionRect.
width(), h, m_view->rendererConfig()->backgroundColor());
3976 if (!thisLine.
viewLine() || z == startz) {
3980 const int thisLineTop = h * thisLine.
viewLine();
3981 paint.translate(
QPoint(0, -thisLineTop));
3985 const QRectF lineRect(0, 0, unionRect.
width(), h * thisLine.kateLineLayout()->viewLineCount());
3986 paint.fillRect(lineRect, m_view->rendererConfig()->backgroundColor());
3991 paint.setClipRect(lineRect);
3996 const QRect textClipRect{xStart, thisLineTop, xEnd - xStart,
height()};
3998 renderer()->
paintTextLine(paint, thisLine.kateLineLayout(), xStart, xEnd, textClipRect.toRectF(), &
pos);
4002 thisLine.setDirty(
false);
4007 paint.translate(0, h);
4012 if (m_textAnimation) {
4013 m_textAnimation->draw(paint);
4024 m_madeVisible =
false;
4028 showBracketMatchPreview();
4031 if (heightChanged) {
4032 setAutoCenterLines(m_autoCenterLines,
false);
4036 if (view()->dynWordWrap()) {
4037 bool dirtied =
false;
4039 for (
int i = 0; i < cache()->viewCacheLineCount(); i++) {
4044 if (viewLine.wrap() || viewLine.isRightToLeft() || viewLine.width() >
width()) {
4046 viewLine.setDirty();
4051 if (dirtied || heightChanged) {
4058 if (expandedHorizontally && startX() > 0) {
4065 if (m_cursor.
column() > doc()->lineLength(m_cursor.
line())) {
4069 thisLine.
endCol() + ((
width() - thisLine.xOffset() - (thisLine.width() - startX())) / renderer()->spaceWidth()) - 1);
4071 updateCursor(newCursor);
4076 if (expandedVertically) {
4078 if (startPos() > max) {
4083 Q_EMIT view()->displayRangeChanged(m_view);
4086void KateViewInternal::moveEvent(
QMoveEvent *e)
4089 if (e->
pos() != e->
oldPos() && m_bmPreview) {
4090 showBracketMatchPreview();
4096void KateViewInternal::scrollTimeout()
4098 if (m_scrollX || m_scrollY) {
4099 const int scrollTo = startPos().
line() + (m_scrollY / (int)renderer()->lineHeight());
4100 placeCursor(
QPoint(m_mouseX, m_mouseY),
true);
4101 scrollLines(scrollTo);
4105void KateViewInternal::cursorTimeout()
4107 if (!debugPainting && m_currentInputMode->blinkCaret()) {
4113void KateViewInternal::textHintTimeout()
4115 m_textHintTimer.
stop();
4129 if (!
hint.isEmpty()) {
4135 qCDebug(LOG_KTE) <<
"Hint text: " << textHints;
4137 for (
const QString &str :
std::as_const(textHints)) {
4138 hint += QStringLiteral(
"<p>%1</p>").arg(str);
4140 QPoint pos(startX() + m_textHintPos.
x(), m_textHintPos.
y());
4153 doc()->setActiveView(m_view);
4156 view()->slotGotFocus();
4159void KateViewInternal::focusOutEvent(
QFocusEvent *)
4164 m_cursorTimer.
stop();
4168 m_textHintTimer.
stop();
4170 view()->slotLostFocus();
4172 hideBracketMatchPreview();
4175void KateViewInternal::doDrag()
4177 m_dragInfo.state = diDragging;
4178 m_dragInfo.dragObject =
new QDrag(
this);
4179 std::unique_ptr<QMimeData> mimeData(
new QMimeData());
4180 mimeData->setText(view()->selectionText());
4182 const auto startCur = view()->selectionRange().
start();
4183 const auto endCur = view()->selectionRange().
end();
4184 if (!startCur.isValid() || !endCur.isValid()) {
4188 int startLine = startCur.line();
4189 int endLine = endCur.line();
4200 for (
int l = startLine; l <= endLine; ++l) {
4201 if (l >= firstVisibleLine) {
4206 for (
int l = endLine; l >= startLine; --l) {
4207 if (l <= lastVisibleLine) {
4217 for (
int l = startLine; l <= endLine; ++l) {
4219 h += renderer()->lineHeight();
4221 qreal scale = h > m_view->
height() / 2 ? 0.75 : 1.0;
4225 if (startLine == startCur.line()) {
4226 sX = renderer()->
cursorToX(cache()->textLayout(startCur), startCur, !view()->wrapCursor());
4231 if (endLine == endCur.line()) {
4232 eX = renderer()->
cursorToX(cache()->textLayout(endCur), endCur, !view()->wrapCursor());
4238 if (view()->selection()) {
4239 view()->clearHighlights();
4244 QPixmap pixmap(w * dpr, h * dpr);
4245 if (!pixmap.isNull()) {
4246 pixmap.setDevicePixelRatio(dpr);
4248 renderer()->
paintSelection(&pixmap, startLine, sX, endLine, eX, scale);
4250 if (view()->selection()) {
4265 const int y = lineToY(view()->m_textFolding.lineToVisibleLine(startLine));
4268 m_dragInfo.dragObject->
setPixmap(pixmap);
4270 m_dragInfo.dragObject->
setMimeData(mimeData.release());
4276 if (
event->source() ==
this) {
4279 event->setAccepted((
event->mimeData()->hasText() && doc()->isReadWrite()) ||
event->mimeData()->hasUrls());
4282void KateViewInternal::fixDropEvent(
QDropEvent *event)
4284 if (
event->source() !=
this) {
4297 event->setDropAction(action);
4304 placeCursor(
event->position().toPoint(),
true,
false);
4308 fixDropEvent(
event);
4311void KateViewInternal::dropEvent(
QDropEvent *event)
4314 if (
event->mimeData()->hasUrls()) {
4319 if (
event->mimeData()->hasText() && doc()->isReadWrite()) {
4320 const QString text =
event->mimeData()->text();
4321 const bool blockMode = view()->blockSelection();
4323 fixDropEvent(
event);
4328 std::unique_ptr<KTextEditor::MovingCursor> targetCursor2(doc()->newMovingCursor(m_cursor));
4336 editSetCursor(selRange.end());
4338 view()->clearSelection();
4345 view()->removeSelectedText();
4346 if (targetCursor2->toCursor() != targetCursor) {
4348 targetCursor = targetCursor2->toCursor();
4350 doc()->insertText(targetCursor2->toCursor(), text, blockMode);
4353 doc()->insertText(targetCursor, text, blockMode);
4358 editSetCursor(targetCursor + blockAdjust);
4361 editSetCursor(targetCursor2->toCursor());
4366 event->acceptProposedAction();
4371 m_dragInfo.state = diNone;
4377void KateViewInternal::clear()
4382 view()->clearSecondaryCursors();
4385 m_lineScroll->updatePixmap();
4392 if (m_zoomEventFilter->detectZoomingEvent(e)) {
4394 slotIncFontSizes(qreal(e->
angleDelta().
y()) / (qreal)QWheelEvent::DefaultDeltasPerStep);
4396 slotDecFontSizes(qreal(-e->
angleDelta().
y()) / (qreal)QWheelEvent::DefaultDeltasPerStep);
4408 auto offset = sign * qreal(e->
angleDelta().
y()) / 120.0;
4410 const auto pageStep = m_lineScroll->
pageStep();
4411 offset = qBound(-pageStep,
int(offset * pageStep), pageStep);
4417 m_accumulatedScroll += offset - int(offset);
4418 const auto extraAccumulated = int(m_accumulatedScroll);
4419 m_accumulatedScroll -= extraAccumulated;
4422 scrollViewLines(
int(offset) + extraAccumulated);
4429 if (view()->dynWordWrap()) {
4446 hideBracketMatchPreview();
4451 int lineHeight = renderer()->lineHeight();
4452 event->setViewportSize(
QSizeF(0.0, 0.0));
4453 event->setContentPosRange(
QRectF(0.0, 0.0, 0.0, m_lineScroll->
maximum() * lineHeight));
4454 event->setContentPos(
QPointF(0.0, m_lineScroll->
value() * lineHeight));
4466void KateViewInternal::startDragScroll()
4468 if (!m_dragScrollTimer.
isActive()) {
4469 m_dragScrollTimer.
start(s_scrollTime);
4473void KateViewInternal::stopDragScroll()
4475 m_dragScrollTimer.
stop();
4479void KateViewInternal::doDragScroll()
4485 if (p.
y() < s_scrollMargin) {
4486 dy = p.
y() - s_scrollMargin;
4487 }
else if (p.
y() >
height() - s_scrollMargin) {
4488 dy = s_scrollMargin - (
height() - p.
y());
4491 if (p.
x() < s_scrollMargin) {
4492 dx = p.
x() - s_scrollMargin;
4493 }
else if (p.
x() >
width() - s_scrollMargin) {
4494 dx = s_scrollMargin - (
width() - p.
x());
4500 scrollLines(startLine() + dy);
4503 if (columnScrollingPossible() && dx) {
4504 scrollColumns(qMin(startX() + dx, m_columnScroll->
maximum()));
4514 if (std::find(m_textHintProviders.cbegin(), m_textHintProviders.cend(), provider) == m_textHintProviders.cend()) {
4515 m_textHintProviders.push_back(provider);
4519 m_textHintTimer.
start(m_textHintDelay);
4524 const auto it = std::find(m_textHintProviders.cbegin(), m_textHintProviders.cend(), provider);
4525 if (it != m_textHintProviders.cend()) {
4526 m_textHintProviders.erase(it);
4529 if (m_textHintProviders.empty()) {
4530 m_textHintTimer.
stop();
4534void KateViewInternal::setTextHintDelay(
int delay)
4537 m_textHintDelay = 200;
4539 m_textHintDelay = delay;
4543int KateViewInternal::textHintDelay()
const
4545 return m_textHintDelay;
4548bool KateViewInternal::textHintsEnabled()
4550 return !m_textHintProviders.empty();
4554void KateViewInternal::editStart()
4556 editSessionNumber++;
4558 if (editSessionNumber > 1) {
4562 editIsRunning =
true;
4563 editOldCursor = m_cursor;
4564 editOldSelection = view()->selectionRange();
4567void KateViewInternal::editEnd(
int editTagLineStart,
int editTagLineEnd,
bool tagFrom)
4569 if (editSessionNumber == 0) {
4573 editSessionNumber--;
4575 if (editSessionNumber > 0) {
4582 if (view()->dynWordWrap()) {
4583 if (KateLineLayout *
layout = cache()->line(startLine())) {
4584 int index =
layout->viewLineForColumn(startPos().column());
4585 if (index >= 0 && index < layout->viewLineCount()) {
4586 col =
layout->viewLine(index).startCol();
4592 if (tagFrom && (editTagLineStart <=
int(view()->textFolding().visibleLineToLine(startLine())))) {
4595 tagLines(editTagLineStart, tagFrom ? qMax(doc()->lastLine() + 1, editTagLineEnd) : editTagLineEnd, true);
4598 if (editOldCursor == m_cursor.
toCursor()) {
4599 updateBracketMarks();
4604 if (editOldCursor != m_cursor.
toCursor() || m_view == doc()->activeView()) {
4608 if (m_cursor.
line() >= editTagLineStart && m_cursor.
line() <= editTagLineEnd) {
4609 m_madeVisible =
false;
4610 updateCursor(m_cursor,
true);
4616 if (editOldSelection != view()->selectionRange()
4618 && !(editTagLineStart > editOldSelection.
end().
line() && editTagLineEnd < editOldSelection.
start().
line()))) {
4622 editIsRunning =
false;
4627 if (m_cursor.
toCursor() != _cursor) {
4633void KateViewInternal::viewSelectionChanged()
4635 if (!view()->selection()) {
4638 const auto r = view()->selectionRange();
4639 m_selectAnchor = r.
start() == m_cursor ? r.end() : r.start();
4652 return m_layoutCache;
4664 if (realCursor.
line() < 0) {
4673 return view()->renderer();
4676void KateViewInternal::mouseMoved()
4678 view()->notifyMousePositionChanged(m_mouse);
4682void KateViewInternal::cursorMoved()
4686#ifndef QT_NO_ACCESSIBILITY
4694KTextEditor::DocumentPrivate *KateViewInternal::doc()
4696 return m_view->doc();
4699KTextEditor::DocumentPrivate *KateViewInternal::doc()
const
4701 return m_view->doc();
4704bool KateViewInternal::rangeAffectsView(
KTextEditor::Range range,
bool realCursors)
const
4706 int startLine = KateViewInternal::startLine();
4707 int endLine = startLine + (int)m_visibleLineCount;
4714 return (range.
end().
line() >= startLine) || (range.
start().
line() <= endLine);
4731 auto lineHeight = renderer()->lineHeight();
4732 return QRect(cursorToCoordinate(m_cursor,
true,
false),
QSize(1, lineHeight ? lineHeight : 1));
4739 return m_cursor.
column();
4744 if (view()->selection() && m_selectAnchor.
line() == m_cursor.
line()) {
4745 return m_selectAnchor.
column();
4747 return m_cursor.
column();
4751 return doc()->kateTextLine(m_cursor.
line()).text();
4754 if (view()->selection()) {
4755 return view()->selectionText();
4769 if (doc()->readOnly()) {
4777 if (!m_imPreeditRange) {
4778 m_imPreeditRange.reset(
4782 if (!m_imPreeditRange->toRange().isEmpty()) {
4783 doc()->inputMethodStart();
4784 doc()->removeText(*m_imPreeditRange);
4785 doc()->inputMethodEnd();
4789 view()->removeSelectedText();
4799 if (
start != removeEnd) {
4812 m_imPreeditRange->setRange(preeditRange);
4816 doc()->inputMethodStart();
4817 doc()->insertText(m_imPreeditRange->start(), e->
preeditString());
4818 doc()->inputMethodEnd();
4825 m_imPreeditRange.reset();
4826 m_imPreeditRangeChildren.clear();
4838 bool hideCursor =
false;
4841 if (m_imPreeditRange) {
4842 m_imPreeditRangeChildren.clear();
4844 int decorationColumn = 0;
4846 for (
auto &a : attributes) {
4850 hideCursor = !a.length;
4851 QColor c = qvariant_cast<QColor>(a.value);
4862 const int startLine = preEditRangeStart.
line();
4863 const int startCol = preEditRangeStart.
column();
4865 std::unique_ptr<KTextEditor::MovingRange> formatRange(doc()->newMovingRange(fr));
4867 attribute->merge(f);
4868 formatRange->setAttribute(attribute);
4869 decorationColumn =
end;
4870 m_imPreeditRangeChildren.push_back(std::move(formatRange));
4879 if (newCursor != m_cursor.
toCursor()) {
4880 updateCursor(newCursor);
4890 Q_ASSERT(
pos.isValid());
4894 if (!view()->textFolding().isLineVisible(
pos.line())) {
4899 if (m_textAnimation) {
4900 m_textAnimation->deleteLater();
4905void KateViewInternal::showBracketMatchPreview()
4914 if (m_cursor == openBracketCursor || toVirtualCursor(openBracketCursor).line() >= startLine() || m_cursor.
line() - startLine() < 2) {
4915 hideBracketMatchPreview();
4925 const int previewLine = openBracketCursor.
line();
4927 KateLineLayout *lineLayout(
new KateLineLayout(*renderer_));
4928 lineLayout->setLine(previewLine, -1);
4931 const int col = lineLayout->textLine().firstChar();
4932 if (previewLine > 0 && (col == -1 || col == openBracketCursor.
column())) {
4933 lineLayout->setLine(previewLine - 1, lineLayout->virtualLine() - 1);
4936 renderer_->
layoutLine(lineLayout, -1 ,
false );
4937 const int lineWidth =
4938 qBound(m_view->
width() / 5,
int(lineLayout->width() + renderer_->spaceWidth() * 2), m_view->
width() - m_leftBorder->
width() - m_lineScroll->
width());
4939 m_bmPreview->resize(lineWidth, renderer_->lineHeight() * 2);
4941 m_bmPreview->move(topLeft.
x(), topLeft.
y());
4942 m_bmPreview->setLine(lineLayout->virtualLine());
4943 m_bmPreview->setCenterView(
false);
4944 m_bmPreview->raise();
4945 m_bmPreview->show();
4948void KateViewInternal::hideBracketMatchPreview()
4950 m_bmPreview.reset();
4955#ifndef QT_NO_ACCESSIBILITY
4957 auto doc = view()->doc();
4966#ifndef QT_NO_ACCESSIBILITY
4968 auto doc = view()->doc();
4979 const auto noteWidth = note.width();
4980 auto noteCursor = note.position();
4984 const auto lineLength = view()->document()->
lineLength(noteCursor.line());
4985 int extraOffset = -noteWidth;
4986 if (noteCursor.column() == lineLength) {
4988 }
else if (noteCursor.column() > lineLength) {
4989 extraOffset = (noteCursor.column() - lineLength) * renderer()->spaceWidth();
4990 noteCursor.setColumn(lineLength);
4992 auto noteStartPos =
mapToGlobal(cursorToCoordinate(noteCursor,
true,
false));
4994 if (view()->dynWordWrap()) {
4995 const KateLineLayout *lineLayout = cache()->
line(noteCursor.line());
4999 noteStartPos.rx() -= note.width();
5000 extraOffset = -extraOffset;
5004 auto globalNoteRect =
QRect(noteStartPos +
QPoint{extraOffset, 0},
QSize(noteWidth, renderer()->lineHeight()));
5006 return globalNoteRect;
5013 const auto inlineNotes = view()->inlineNotes(line);
5015 for (
const auto ¬e : inlineNotes) {
5016 auto globalNoteRect = inlineNoteRect(note);
5017 if (globalNoteRect.contains(globalPos)) {
5025bool KateViewInternal::sendMouseEventToInputContext(
QMouseEvent *e)
5027 if (!m_imPreeditRange) {
5032 if (!m_imPreeditRange->contains(c) && c != m_imPreeditRange->end()) {
5036 auto cursorPos = (c - m_imPreeditRange->start());
5038 if (cursorPos.column() >= 0) {
5047void KateViewInternal::commitPreedit()
5049 if (!m_imPreeditRange) {
5056#include "moc_kateviewinternal.cpp"
A class which provides customized text decorations.
@ 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 >...
constexpr bool atStartOfDocument() const noexcept
Determine if this cursor is located at the start of a document (= at position (0, 0)).
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.
A KParts derived class representing a text document.
virtual int lineLength(int line) const =0
Get the length of a given line in characters.
const std::array< std::unique_ptr< KateAbstractInputModeFactory >, KTextEditor::View::ViInputMode+1 > & inputModeFactories()
static KTextEditor::EditorPrivate * self()
Kate Part Internal stuff ;)
virtual void inlineNoteMouseMoveEvent(const InlineNote ¬e, const QPoint &globalPos)
Invoked when the mouse cursor moves inside the note.
virtual void inlineNoteFocusOutEvent(const InlineNote ¬e)
Invoked when the mouse cursor leaves the note.
Describes an inline note.
qreal width() const
Returns the width of this note in pixels.
@ 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.
A Cursor which is bound to a specific Document, and maintains its position.
const Cursor toCursor() const
Convert this clever cursor into a dumb one.
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.
void setEnd(Cursor end) noexcept
Set the end cursor to end.
constexpr bool isEmpty() const noexcept
Returns true if this range contains no characters, ie.
void setRange(Range range) noexcept
Set the start and end cursors to range.start() and range.end() respectively.
static constexpr Range invalid() noexcept
Returns an invalid range.
constexpr bool isValid() const noexcept
Validity check.
void setStart(Cursor start) noexcept
Set the start cursor to start.
Class to provide text hints for a View.
void horizontalScrollPositionChanged(KTextEditor::View *view)
This signal should be emitted whenever the view is scrolled horizontally.
int lastDisplayedLine(LineType lineType=RealLine) const
Get the last displayed line in the view.
int firstDisplayedLine(LineType lineType=RealLine) const
Get the first displayed line in the view.
@ NormalInputMode
Normal Mode.
void cursorPositionChanged(KTextEditor::View *view, KTextEditor::Cursor newPosition)
This signal is emitted whenever the view's cursor position changed.
void selectionChanged(KTextEditor::View *view)
This signal is emitted whenever the view's selection changes.
void verticalScrollPositionChanged(KTextEditor::View *view, KTextEditor::Cursor newPos)
This signal should be emitted whenever the view is scrolled vertically.
QVariant value(const int key) const
Get a config value.
Internal data container for KTextEditor::InlineNote interface.
This class handles Kate's caching of layouting information (in KateLineLayout and KateTextLayout).
KateTextLayout & viewLine(int viewLine)
Returns the layout of the corresponding line in the view.
KateLineLayout * line(int realLine, int virtualLine=-1)
Returns the KateLineLayout for the specified line.
KateTextLayout textLayout(const KTextEditor::Cursor realCursor)
Returns the layout describing the text line which is occupied by realCursor.
int displayViewLine(const KTextEditor::Cursor virtualCursor, bool limitToVisible=false)
Find the view line of the cursor, relative to the display (0 = top line of view, 1 = second line,...
Handles all of the work of rendering the text (used for the views and printing)
void increaseFontSizes(qreal step=1.0) const
Change to a different font (soon to be font set?)
void paintSelection(QPaintDevice *d, int startLine, int xStart, int endLine, int xEnd, qreal scale=1.0)
Paints a range of text into d.
void setShowTabs(bool showTabs)
Set whether a mark should be painted to help identifying tabs.
const QFont & currentFont() const
Access currently used font.
const AttributePtr & attribute(uint pos) const
This takes an in index, and returns all the attributes for it.
KTextEditor::caretStyles caretStyle() const
The style of the caret (text cursor) to be painted.
void setDrawCaret(bool drawCaret)
Set whether the caret (text cursor) will be drawn.
int cursorToX(const KateTextLayout &range, int col, bool returnPastLine=false) const
Returns the x position of cursor col on the line range.
KTextEditor::Cursor xToCursor(const KateTextLayout &range, int x, bool returnPastLine=false) const
Returns the real cursor which is occupied by the specified x value, or that closest to it.
void setShowSpaces(KateDocumentConfig::WhitespaceRendering showSpaces)
Set which spaces should be rendered.
const QFontMetricsF & currentFontMetrics() const
Access currently used font metrics.
void setCaretOverrideColor(const QColor &color)
Set a brush with which to override drawing of the caret.
void setCaretStyle(KTextEditor::caretStyles style)
Set the style of caret to be painted.
void layoutLine(KateLineLayout *line, int maxwidth=-1, bool cacheLayout=false) const
Text width & height calculation functions...
void updateMarkerSize()
Update marker size shown.
void paintTextLine(QPainter &paint, KateLineLayout *range, int xStart, int xEnd, const QRectF &textClipRect=QRectF(), const KTextEditor::Cursor *cursor=nullptr, PaintTextLineFlags flags=PaintTextLineFlags())
This is the ultimate function to perform painting of a text line.
This class is used to flash text in the text view.
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).
This class implements a QAccessible-interface for a KateViewInternal.
int column() const override
Retrieve the column on which this cursor is situated.
void setPosition(const TextCursor &position)
Fast way to set the current cursor position to position.
int line() const override
Retrieve the line on which this cursor is situated.
void foldingRangesChanged()
If the folding state of existing ranges changes or ranges are added/removed, this signal is emitted.
int visibleLines() const
Query number of visible lines.
KTextEditor::Range foldingRange(qint64 id) const
Returns the folding range associated with id.
int visibleLineToLine(int visibleLine) const
Convert a visible line number to a line number in the text buffer.
void ensureLineIsVisible(int line)
Ensure that a given line will be visible.
Class representing a single text line.
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.
Q_SCRIPTABLE Q_NOREPLY void start()
QAction * hint(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & end()
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
void actionTriggered(int action)
void setRange(int min, int max)
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)
Qt::DropAction exec(Qt::DropActions supportedActions)
void setHotSpot(const QPoint &hotspot)
void setMimeData(QMimeData *data)
void setPixmap(const QPixmap &pixmap)
qint64 elapsed() const const
bool isValid() const const
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
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
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
QTextCharFormat toCharFormat() 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
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isActive() const const
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
bool toBool() const const
bool contains(const AT &value) const const
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
qsizetype size() const const
QPoint angleDelta() const const