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)
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
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);
564void KateViewInternal::scrollColumns(
int x)
570 if (
x > m_columnScroll->
maximum()) {
578 int dx = startX() -
x;
581 if (qAbs(dx) <
width()) {
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);
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();
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:
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;
1013 return view()->doc();
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)
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()) {
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;
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) {
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()
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) {
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()) {
2772 makeVisible(m_displayCursor, m_displayCursor.
column(),
false, center, calledExternally);
2778 if (m_cursor.
line() != newCursor.
line()) {
2779 m_leftBorder->updateForCursorLineChange();
2787 m_displayCursor = toVirtualCursor(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);
2815 m_preserveX =
false;
2817 m_preservedX = renderer()->
cursorToX(cache()->textLayout(m_cursor), m_cursor, !view()->wrapCursor());
2831void KateViewInternal::updateBracketMarkAttributes()
2834 bracketFill->setBackground(view()->rendererConfig()->highlightedBracketColor());
2835 bracketFill->setBackgroundFillWhitespace(
false);
2836 if (
QFontInfo(renderer()->currentFont()).fixedPitch()) {
2838 bracketFill->setFontBold();
2841 m_bmStart->setAttribute(bracketFill);
2842 m_bmEnd->setAttribute(bracketFill);
2844 if (view()->rendererConfig()->showWholeBracketExpression()) {
2846 expressionFill->setBackground(view()->rendererConfig()->highlightedBracketColor());
2847 expressionFill->setBackgroundFillWhitespace(
false);
2849 m_bm->setAttribute(expressionFill);
2855void KateViewInternal::updateBracketMarks()
2858 const int maxLines = 5000;
2863 if (m_bm->toRange() == newRange) {
2865 hideBracketMatchPreview();
2870 m_bm->setRange(newRange);
2877 if (m_view->config()->
value(KateViewConfig::ShowBracketMatchPreview).
toBool()) {
2878 showBracketMatchPreview();
2882 if (!m_view->rendererConfig()->animateBracketMatching()) {
2886 const KTextEditor::Cursor flashPos = (m_cursor == m_bmStart->start() || m_cursor == m_bmStart->end()) ? m_bmEnd->start() : m_bm->start();
2887 if (flashPos != m_bmLastFlashPos->toCursor()) {
2888 m_bmLastFlashPos->setPosition(flashPos);
2891 attribute->setBackground(view()->rendererConfig()->highlightedBracketColor());
2892 if (m_bmStart->attribute()->fontBold()) {
2893 attribute->setFontBold(
true);
2896 flashChar(flashPos, attribute);
2906 hideBracketMatchPreview();
2912 return tagLines(virtualCursor, virtualCursor,
false);
2915bool KateViewInternal::tagLines(
int start,
int end,
bool realLines)
2923 cache()->relayoutLines(
start.line(),
end.line());
2927 end = toVirtualCursor(end);
2930 cache()->relayoutLines(toRealCursor(
start).line(), toRealCursor(end).line());
2933 if (
end.line() < startLine()) {
2939 if (
start.line() > startLine() + cache()->viewCacheLineCount()) {
2944 cache()->updateViewCache(startPos());
2950 for (
int z = 0; z < cache()->viewCacheLineCount(); z++) {
2952 if ((line.virtualLine() >
start.line() || (line.virtualLine() ==
start.line() && line.
endCol() >=
start.column() &&
start.column() != -1))
2953 && (line.virtualLine() <
end.line() || (line.virtualLine() ==
end.line() && (line.startCol() <=
end.column() ||
end.column() == -1)))) {
2961 if (!m_view->config()->showFoldingOnHoverOnly() && doc()->highlight() && doc()->highlight()->foldingIndentationSensitive()) {
2964 }
else if (!view()->dynWordWrap()) {
2965 int y = lineToY(
start.line());
2967 int h = (
end.line() -
start.line() + 2) * renderer()->lineHeight();
2968 if (
end.line() >= view()->textFolding().visibleLines() - 1) {
2976 for (
int z = 0; z < cache()->viewCacheLineCount(); z++) {
2979 || ((line.virtualLine() >
start.line() || (line.virtualLine() ==
start.line() && line.
endCol() >=
start.column() &&
start.column() != -1))
2980 && (line.virtualLine() <
end.line() || (line.virtualLine() ==
end.line() && (line.startCol() <=
end.column() ||
end.column() == -1))))) {
2982 m_leftBorder->
update(0, z * renderer()->lineHeight(), m_leftBorder->
width(), m_leftBorder->
height());
2999 return tagLines(range.
start(), range.
end(), realCursors);
3002void KateViewInternal::tagAll()
3007 m_leftBorder->updateFont();
3011void KateViewInternal::paintCursor()
3014 if (tagLine(m_displayCursor)) {
3020 for (
const auto &c : view()->m_secondaryCursors) {
3021 auto p = c.cursor();
3022 if (p.line() >= s - 1 && p.line() <= e + 1 && !updatedLines.
contains(p.line())) {
3024 tagLines(p, p,
true);
3028 if (!updatedLines.
isEmpty()) {
3038 if (!thisLine.isValid()) {
3039 thisLine = cache()->
textLayout(doc()->lines() - 1, -1);
3042 c = renderer()->
xToCursor(thisLine, startX() + p.
x(), !view()->wrapCursor());
3044 if (c.
line() < 0 || c.
line() >= doc()->lines()) {
3049 const auto inlineNotes = view()->inlineNotes(c.
line());
3051 for (
const auto ¬e : inlineNotes) {
3052 auto noteCursor = note.m_position;
3054 if (note.m_position.column() >= doc()->lineLength(c.
line()) || note.m_position.column() == 0) {
3060 const auto caretWidth = renderer()->
caretStyle() == KTextEditor::caretStyles::Line ? 2. : 0.;
3063 const auto halfCharWidth = (charWidth / 2);
3066 const auto totalWidth =
width + halfCharWidth;
3070 if (r.contains(p)) {
3080void KateViewInternal::placeCursor(
const QPoint &p,
bool keepSelection,
bool updateSelection)
3087 if (updateSelection) {
3088 KateViewInternal::updateSelection(c, keepSelection);
3091 int tmp = m_minLinesVisible;
3092 m_minLinesVisible = 0;
3094 m_minLinesVisible = tmp;
3096 if (updateSelection && keepSelection) {
3097 moveCursorToSelectionEdge();
3102bool KateViewInternal::isTargetSelected(
const QPoint &p)
3105 if (!thisLine.isValid()) {
3109 return view()->cursorSelected(renderer()->xToCursor(thisLine, startX() + p.
x(), !view()->wrapCursor()));
3116 switch (e->
type()) {
3132 if (!view()->m_secondaryCursors.empty()) {
3133 view()->clearSecondaryCursors();
3138 if (view()->isCompletionActive()) {
3139 view()->abortCompletion();
3143 }
else if (view()->bottomViewBar()->barWidgetVisible()) {
3144 view()->bottomViewBar()->hideCurrentBarWidget();
3148 }
else if (!view()->config()->persistentSelection() && view()->selection()) {
3149 m_currentInputMode->clearSelection();
3157 if (view()->completionWidget()->handleShortcutOverride(k)) {
3163 if (m_currentInputMode->stealKey(k)) {
3196 QRect doNotScrollRegion(s_scrollMargin, s_scrollMargin,
width() - s_scrollMargin * 2,
height() - s_scrollMargin * 2);
3198 if (!doNotScrollRegion.contains(currentPoint)) {
3213 hideBracketMatchPreview();
3218 scrollPrepareEvent(s);
3235void KateViewInternal::keyPressEvent(
QKeyEvent *e)
3239 view()->emitNavigateLeft();
3244 view()->emitNavigateRight();
3249 view()->emitNavigateUp();
3254 view()->emitNavigateDown();
3259 view()->emitNavigateAccept();
3264 view()->emitNavigateBack();
3269 if (e->
key() ==
Qt::Key_Alt && view()->completionWidget()->isCompletionActive()) {
3270 view()->completionWidget()->toggleDocumentation();
3276 if (m_currentInputMode->keyPress(e)) {
3280 if (!doc()->isReadWrite()) {
3287 view()->keyReturn();
3301 uint tabHandling = doc()->
config()->tabHandling();
3303 if (tabHandling == KateDocumentConfig::tabSmart) {
3305 if (view()->selection() && !view()->selectionRange().onSingleLine() && !view()->blockSelection()) {
3306 tabHandling = KateDocumentConfig::tabIndents;
3316 if (first < 0 || m_cursor.
column() <= first) {
3317 tabHandling = KateDocumentConfig::tabIndents;
3319 tabHandling = KateDocumentConfig::tabInsertsTab;
3325 if (tabHandling == KateDocumentConfig::tabInsertsTab) {
3326 doc()->
typeChars(m_view, QStringLiteral(
"\t"));
3329 for (
const auto &c : std::as_const(m_view->m_secondaryCursors)) {
3330 auto cursor = c.cursor();
3334 doc()->indent(view()->selection() ? view()->selectionRange() :
KTextEditor::Range(m_cursor.
line(), 0, m_cursor.
line(), 0), 1);
3341 }
else if (doc()->config()->tabHandling() != KateDocumentConfig::tabInsertsTab) {
3343 doc()->indent(view()->selection() ? view()->selectionRange() :
KTextEditor::Range(m_cursor.
line(), 0, m_cursor.
line(), 0), -1);
3350 if (isAcceptableInput(e)) {
3359void KateViewInternal::keyReleaseEvent(
QKeyEvent *e)
3362 m_shiftKeyPressed =
false;
3364 if (m_selChangedByUser) {
3365 if (view()->selection()) {
3369 m_selChangedByUser =
false;
3377bool KateViewInternal::isAcceptableInput(
const QKeyEvent *e)
3419 makeVisible(m_displayCursor, 0);
3420 p = cursorCoordinates(
false);
3422 }
else if (!view()->selection() || view()->config()->persistentSelection()) {
3423 placeCursor(e->
pos());
3427 QMenu *cm = view()->contextMenu();
3429 view()->spellingMenu()->prepareToBeShown(cm);
3435void KateViewInternal::mousePressEvent(
QMouseEvent *e)
3437 if (sendMouseEventToInputContext(e)) {
3444 if (note.position().isValid()) {
3455 m_selChangedByUser =
false;
3457 if (!view()->isMulticursorNotAllowed() && e->
modifiers() == view()->config()->multiCursorModifiers()) {
3458 auto pos = cursorForPoint(e->
pos());
3459 if (
pos.isValid()) {
3460 view()->addSecondaryCursor(
pos);
3465 view()->clearSecondaryCursors();
3468 if (m_possibleTripleClick) {
3469 m_possibleTripleClick =
false;
3471 m_selectionMode = Line;
3474 updateSelection(m_cursor,
true);
3476 view()->selectLine(m_cursor);
3477 if (view()->selection()) {
3478 m_selectAnchor = view()->selectionRange().
start();
3482 if (view()->selection()) {
3488 if (m_selectAnchor.line() > view()->selectionRange().
start().line()) {
3490 if (m_selectAnchor == view()->selectionRange().
end() && m_selectAnchor.column() == 0) {
3495 m_selectionCached.
setEnd(view()->selectionRange().
end());
3498 m_selectionCached.
setStart(view()->selectionRange().
start());
3499 if (view()->selectionRange().
end().line() > view()->selectionRange().
start().line()) {
3502 m_selectionCached.
setEnd(view()->selectionRange().
end());
3506 moveCursorToSelectionEdge();
3510 m_scrollTimer.
start(50);
3514 }
else if (m_selectionMode == Default) {
3515 m_selectionMode = Mouse;
3528 if (!m_selectAnchor.isValid()) {
3529 m_selectAnchor = m_cursor;
3536 m_dragInfo.state = diPending;
3537 m_dragInfo.start = e->
pos();
3539 m_dragInfo.state = diNone;
3542 placeCursor(e->
pos(),
true,
false);
3545 m_selectAnchor = m_selectionCached.
end();
3547 m_selectAnchor = m_selectionCached.
start();
3553 if (view()->selection()) {
3557 placeCursor(e->
pos());
3563 m_scrollTimer.
start(50);
3570 if (e->
pos().
x() == 0) {
3572 placeCursor(e->
pos());
3583void KateViewInternal::mouseDoubleClickEvent(
QMouseEvent *e)
3585 if (sendMouseEventToInputContext(e)) {
3589 m_selectionMode = Word;
3597 ce = m_selectAnchor.column();
3598 if (ce > 0 && doc()->highlight()->isInWord(l.
at(ce))) {
3599 for (; ce < l.
length(); ce++) {
3600 if (!doc()->highlight()->isInWord(l.
at(ce))) {
3606 cs = m_selectAnchor.column() - 1;
3607 if (cs < doc()->lineLength(m_selectAnchor.line()) && doc()->highlight()->isInWord(l.
at(cs))) {
3608 for (cs--; cs >= 0; cs--) {
3609 if (!doc()->highlight()->isInWord(l.
at(cs))) {
3620 m_selectionCached.
setStart(m_selectAnchor);
3621 m_selectionCached.
setEnd(m_selectAnchor);
3624 placeCursor(e->
pos(),
true);
3631 view()->clearSelection(
false,
false);
3632 placeCursor(e->
pos());
3633 view()->selectWord(m_cursor);
3634 cursorToMatchingBracket(
true);
3636 if (view()->selection()) {
3637 m_selectAnchor = view()->selectionRange().
start();
3638 m_selectionCached = view()->selectionRange();
3640 m_selectAnchor = m_cursor;
3647 if (view()->selection()) {
3652 moveCursorToSelectionEdge();
3653 m_possibleTripleClick =
true;
3659 m_scrollTimer.
start(50);
3667void KateViewInternal::tripleClickTimeout()
3669 m_possibleTripleClick =
false;
3672void KateViewInternal::beginSelectLine(
const QPoint &pos)
3675 m_possibleTripleClick =
true;
3678void KateViewInternal::mouseReleaseEvent(
QMouseEvent *e)
3680 if (sendMouseEventToInputContext(e)) {
3686 m_selectionMode = Default;
3689 if (m_selChangedByUser) {
3690 if (view()->selection()) {
3693 moveCursorToSelectionEdge();
3695 m_selChangedByUser =
false;
3698 if (m_dragInfo.state == diPending) {
3700 }
else if (m_dragInfo.state == diNone) {
3701 m_scrollTimer.
stop();
3704 m_dragInfo.state = diNone;
3707 if (view()->selection() && !view()->m_secondaryCursors.empty()) {
3715 if (!view()->config()->mousePasteAtCursorPosition()) {
3716 placeCursor(e->
pos());
3719 if (doc()->isReadWrite()) {
3721 view()->paste(&clipboard);
3733void KateViewInternal::leaveEvent(
QEvent *)
3735 m_textHintTimer.
stop();
3739 if (m_dragInfo.state == diNone) {
3740 m_scrollTimer.
stop();
3743 hideBracketMatchPreview();
3752 if (includeBorder) {
3753 coord.rx() -= m_leftBorder->
width();
3755 coord.rx() += startX();
3758 if (thisLine.isValid()) {
3759 ret = renderer()->
xToCursor(thisLine, coord.x(), !view()->wrapCursor());
3762 if (ret.
column() > view()->document()->lineLength(ret.
line())) {
3771void KateViewInternal::mouseMoveEvent(
QMouseEvent *e)
3778 if (newPosition != m_mouse) {
3779 m_mouse = newPosition;
3785 auto focusChanged =
false;
3786 if (noteData.m_position.isValid()) {
3787 if (!m_activeInlineNote.m_position.
isValid()) {
3789 tagLine(noteData.m_position);
3790 focusChanged =
true;
3791 noteData.m_underMouse =
true;
3793 m_activeInlineNote = noteData;
3797 }
else if (m_activeInlineNote.m_position.
isValid()) {
3798 tagLine(m_activeInlineNote.m_position);
3799 focusChanged =
true;
3800 m_activeInlineNote.m_underMouse =
false;
3802 m_activeInlineNote = {};
3811 if (m_dragInfo.state == diPending) {
3822 }
else if (m_dragInfo.state == diDragging) {
3833 int d = renderer()->lineHeight();
3839 if (m_mouseX >
width()) {
3848 if (m_mouseY >
height()) {
3860 placeCursor(
QPoint(m_mouseX, m_mouseY),
true);
3863 if (view()->config()->textDragAndDrop() && isTargetSelected(e->
pos())) {
3884 m_textHintTimer.
start(m_textHintDelay);
3885 m_textHintPos = e->
pos();
3890void KateViewInternal::updateDirty()
3892 const int h = renderer()->lineHeight();
3894 int currentRectStart = -1;
3895 int currentRectEnd = -1;
3900 for (
int i = 0; i < cache()->viewCacheLineCount(); ++i) {
3901 if (cache()->viewLine(i).isDirty()) {
3902 if (currentRectStart == -1) {
3903 currentRectStart = h * i;
3906 currentRectEnd += h;
3909 }
else if (currentRectStart != -1) {
3910 updateRegion +=
QRect(0, currentRectStart,
width(), currentRectEnd);
3911 currentRectStart = -1;
3912 currentRectEnd = -1;
3917 if (currentRectStart != -1) {
3918 updateRegion +=
QRect(0, currentRectStart,
width(), currentRectEnd);
3921 if (!updateRegion.
isEmpty()) {
3922 if (debugPainting) {
3923 qCDebug(LOG_KTE) <<
"Update dirty region " << updateRegion;
3929void KateViewInternal::hideEvent(
QHideEvent *e)
3932 if (view()->isCompletionActive()) {
3933 view()->completionWidget()->abortCompletion();
3939 if (debugPainting) {
3940 qCDebug(LOG_KTE) <<
"GOT PAINT EVENT: Region" << e->
region();
3945 int xStart = startX() + unionRect.
x();
3946 int xEnd = xStart + unionRect.
width();
3947 uint h = renderer()->lineHeight();
3948 uint startz = (unionRect.
y() / h);
3949 uint endz = startz + 1 + (unionRect.
height() / h);
3950 uint lineRangesSize = cache()->viewCacheLineCount();
3961 renderer()->
setCaretStyle(m_currentInputMode->caretStyle());
3962 renderer()->
setShowTabs(doc()->config()->showTabs());
3969 paint.translate(unionRect.
x(), startz * h);
3970 for (uint z = startz; z <= endz; z++) {
3972 if ((z >= lineRangesSize) || (cache()->viewLine(z).line() == -1)) {
3973 if (!(z >= lineRangesSize)) {
3974 cache()->
viewLine(z).setDirty(
false);
3976 paint.fillRect(0, 0, unionRect.
width(), h, m_view->rendererConfig()->backgroundColor());
3990 if (!thisLine.
viewLine() || z == startz) {
3994 const int thisLineTop = h * thisLine.
viewLine();
3995 paint.translate(
QPoint(0, -thisLineTop));
3999 const QRectF lineRect(0, 0, unionRect.
width(), h * thisLine.kateLineLayout()->viewLineCount());
4000 paint.fillRect(lineRect, m_view->rendererConfig()->backgroundColor());
4005 paint.setClipRect(lineRect);
4010 const QRect textClipRect{xStart, thisLineTop, xEnd - xStart,
height()};
4012 renderer()->
paintTextLine(paint, thisLine.kateLineLayout(), xStart, xEnd, textClipRect.toRectF(), &
pos);
4016 thisLine.setDirty(
false);
4021 paint.translate(0, h);
4026 if (m_textAnimation) {
4027 m_textAnimation->draw(paint);
4038 m_madeVisible =
false;
4042 showBracketMatchPreview();
4045 if (heightChanged) {
4046 setAutoCenterLines(m_autoCenterLines,
false);
4050 if (view()->dynWordWrap()) {
4051 bool dirtied =
false;
4053 for (
int i = 0; i < cache()->viewCacheLineCount(); i++) {
4058 if (viewLine.wrap() || viewLine.isRightToLeft() || viewLine.width() >
width()) {
4060 viewLine.setDirty();
4065 if (dirtied || heightChanged) {
4072 if (expandedHorizontally && startX() > 0) {
4079 if (m_cursor.
column() > doc()->lineLength(m_cursor.
line())) {
4083 thisLine.
endCol() + ((
width() - thisLine.xOffset() - (thisLine.width() - startX())) / renderer()->spaceWidth()) - 1);
4085 updateCursor(newCursor);
4090 if (expandedVertically) {
4092 if (startPos() > max) {
4100void KateViewInternal::moveEvent(
QMoveEvent *e)
4103 if (e->
pos() != e->
oldPos() && m_bmPreview) {
4104 showBracketMatchPreview();
4110void KateViewInternal::scrollTimeout()
4112 if (m_scrollX || m_scrollY) {
4113 const int scrollTo = startPos().
line() + (m_scrollY / (int)renderer()->lineHeight());
4114 placeCursor(
QPoint(m_mouseX, m_mouseY),
true);
4115 scrollLines(scrollTo);
4119void KateViewInternal::cursorTimeout()
4121 if (!debugPainting && m_currentInputMode->blinkCaret()) {
4127void KateViewInternal::textHintTimeout()
4129 m_textHintTimer.
stop();
4143 if (!
hint.isEmpty()) {
4149 qCDebug(LOG_KTE) <<
"Hint text: " << textHints;
4151 for (
const QString &str : std::as_const(textHints)) {
4152 hint += QStringLiteral(
"<p>%1</p>").arg(str);
4154 QPoint pos(startX() + m_textHintPos.
x(), m_textHintPos.
y());
4167 doc()->setActiveView(m_view);
4170 view()->slotGotFocus();
4173void KateViewInternal::focusOutEvent(
QFocusEvent *)
4178 m_cursorTimer.
stop();
4182 m_textHintTimer.
stop();
4184 view()->slotLostFocus();
4186 hideBracketMatchPreview();
4189void KateViewInternal::doDrag()
4191 m_dragInfo.state = diDragging;
4192 m_dragInfo.dragObject =
new QDrag(
this);
4193 std::unique_ptr<QMimeData> mimeData(
new QMimeData());
4194 mimeData->setText(view()->selectionText());
4196 const auto startCur = view()->selectionRange().
start();
4197 const auto endCur = view()->selectionRange().
end();
4198 if (!startCur.isValid() || !endCur.isValid()) {
4202 int startLine = startCur.line();
4203 int endLine = endCur.line();
4214 for (
int l = startLine; l <= endLine; ++l) {
4215 if (l >= firstVisibleLine) {
4220 for (
int l = endLine; l >= startLine; --l) {
4221 if (l <= lastVisibleLine) {
4231 for (
int l = startLine; l <= endLine; ++l) {
4234 h += renderer()->lineHeight() * (cache()->
viewLine(endCur) + 1);
4236 h += renderer()->lineHeight() * cache()->viewLineCount(l);
4239 qreal scale = h > m_view->
height() / 2 ? 0.75 : 1.0;
4243 if (startLine == startCur.line()) {
4244 sX = renderer()->
cursorToX(cache()->textLayout(startCur), startCur,
false);
4249 if (endLine == endCur.line()) {
4250 eX = renderer()->
cursorToX(cache()->textLayout(endCur), endCur,
false);
4256 if (view()->selection()) {
4257 view()->clearHighlights();
4262 QPixmap pixmap(w * dpr, h * dpr);
4263 if (!pixmap.isNull()) {
4264 pixmap.setDevicePixelRatio(dpr);
4266 renderer()->
paintSelection(&pixmap, startLine, sX, endLine, eX, cache()->viewWidth(), scale);
4268 if (view()->selection()) {
4283 const int y = lineToY(view()->m_textFolding.lineToVisibleLine(startLine));
4286 m_dragInfo.dragObject->
setPixmap(pixmap);
4288 m_dragInfo.dragObject->
setMimeData(mimeData.release());
4294 if (
event->source() ==
this) {
4297 event->setAccepted((
event->mimeData()->hasText() && doc()->isReadWrite()) ||
event->mimeData()->hasUrls());
4300void KateViewInternal::fixDropEvent(
QDropEvent *event)
4302 if (
event->source() !=
this) {
4315 event->setDropAction(action);
4322 placeCursor(
event->position().toPoint(),
true,
false);
4326 fixDropEvent(
event);
4329void KateViewInternal::dropEvent(
QDropEvent *event)
4332 if (
event->mimeData()->hasUrls()) {
4337 if (
event->mimeData()->hasText() && doc()->isReadWrite()) {
4338 const QString text =
event->mimeData()->text();
4339 const bool blockMode = view()->blockSelection();
4341 fixDropEvent(
event);
4346 std::unique_ptr<KTextEditor::MovingCursor> targetCursor2(doc()->newMovingCursor(m_cursor));
4354 editSetCursor(selRange.end());
4356 view()->clearSelection();
4363 view()->removeSelectedText();
4364 if (targetCursor2->toCursor() != targetCursor) {
4366 targetCursor = targetCursor2->toCursor();
4368 doc()->insertText(targetCursor2->toCursor(), text, blockMode);
4371 doc()->insertText(targetCursor, text, blockMode);
4376 editSetCursor(targetCursor + blockAdjust);
4379 editSetCursor(targetCursor2->toCursor());
4384 event->acceptProposedAction();
4389 m_dragInfo.state = diNone;
4395void KateViewInternal::clear()
4400 view()->clearSecondaryCursors();
4403 m_lineScroll->updatePixmap();
4410 if (m_zoomEventFilter->detectZoomingEvent(e)) {
4412 slotIncFontSizes(qreal(e->
angleDelta().
y()) / (qreal)QWheelEvent::DefaultDeltasPerStep);
4414 slotDecFontSizes(qreal(-e->
angleDelta().
y()) / (qreal)QWheelEvent::DefaultDeltasPerStep);
4426 auto offset = sign * qreal(e->
angleDelta().
y()) / 120.0;
4428 const auto pageStep = m_lineScroll->
pageStep();
4429 offset = qBound(-pageStep,
int(offset * pageStep), pageStep);
4435 m_accumulatedScroll += offset - int(offset);
4436 const auto extraAccumulated = int(m_accumulatedScroll);
4437 m_accumulatedScroll -= extraAccumulated;
4440 scrollViewLines(
int(offset) + extraAccumulated);
4447 if (view()->dynWordWrap()) {
4464 hideBracketMatchPreview();
4469 int lineHeight = renderer()->lineHeight();
4470 event->setViewportSize(
QSizeF(0.0, 0.0));
4471 event->setContentPosRange(
QRectF(0.0, 0.0, 0.0, m_lineScroll->
maximum() * lineHeight));
4472 event->setContentPos(
QPointF(0.0, m_lineScroll->
value() * lineHeight));
4484void KateViewInternal::startDragScroll()
4486 if (!m_dragScrollTimer.
isActive()) {
4487 m_dragScrollTimer.
start(s_scrollTime);
4491void KateViewInternal::stopDragScroll()
4493 m_dragScrollTimer.
stop();
4497void KateViewInternal::doDragScroll()
4503 if (p.
y() < s_scrollMargin) {
4504 dy = p.
y() - s_scrollMargin;
4505 }
else if (p.
y() >
height() - s_scrollMargin) {
4506 dy = s_scrollMargin - (
height() - p.
y());
4509 if (p.
x() < s_scrollMargin) {
4510 dx = p.
x() - s_scrollMargin;
4511 }
else if (p.
x() >
width() - s_scrollMargin) {
4512 dx = s_scrollMargin - (
width() - p.
x());
4518 scrollLines(startLine() + dy);
4521 if (columnScrollingPossible() && dx) {
4522 scrollColumns(qMin(startX() + dx, m_columnScroll->
maximum()));
4532 if (std::find(m_textHintProviders.cbegin(), m_textHintProviders.cend(), provider) == m_textHintProviders.cend()) {
4533 m_textHintProviders.push_back(provider);
4537 m_textHintTimer.
start(m_textHintDelay);
4542 const auto it = std::find(m_textHintProviders.cbegin(), m_textHintProviders.cend(), provider);
4543 if (it != m_textHintProviders.cend()) {
4544 m_textHintProviders.erase(it);
4547 if (m_textHintProviders.empty()) {
4548 m_textHintTimer.
stop();
4552void KateViewInternal::setTextHintDelay(
int delay)
4555 m_textHintDelay = 200;
4557 m_textHintDelay = delay;
4561int KateViewInternal::textHintDelay()
const
4563 return m_textHintDelay;
4566bool KateViewInternal::textHintsEnabled()
4568 return !m_textHintProviders.empty();
4572void KateViewInternal::editStart()
4574 editSessionNumber++;
4576 if (editSessionNumber > 1) {
4580 editIsRunning =
true;
4581 editOldCursor = m_cursor;
4582 editOldSelection = view()->selectionRange();
4585void KateViewInternal::editEnd(
int editTagLineStart,
int editTagLineEnd,
bool tagFrom)
4587 if (editSessionNumber == 0) {
4591 editSessionNumber--;
4593 if (editSessionNumber > 0) {
4600 if (view()->dynWordWrap()) {
4601 if (KateLineLayout *
layout = cache()->line(startLine())) {
4602 int index =
layout->viewLineForColumn(startPos().column());
4603 if (index >= 0 && index < layout->viewLineCount()) {
4604 col =
layout->viewLine(index).startCol();
4610 if (tagFrom && (editTagLineStart <=
int(view()->textFolding().visibleLineToLine(startLine())))) {
4613 tagLines(editTagLineStart, tagFrom ? qMax(doc()->lastLine() + 1, editTagLineEnd) : editTagLineEnd,
true);
4616 if (editOldCursor == m_cursor.
toCursor()) {
4617 updateBracketMarks();
4622 if (editOldCursor != m_cursor.
toCursor() || m_view == doc()->activeView()) {
4626 if (m_cursor.
line() >= editTagLineStart && m_cursor.
line() <= editTagLineEnd) {
4627 m_madeVisible =
false;
4628 updateCursor(m_cursor,
true);
4634 if (editOldSelection != view()->selectionRange()
4636 && !(editTagLineStart > editOldSelection.
end().
line() && editTagLineEnd < editOldSelection.
start().
line()))) {
4640 editIsRunning =
false;
4645 if (m_cursor.
toCursor() != _cursor) {
4651void KateViewInternal::viewSelectionChanged()
4653 if (!view()->selection()) {
4656 const auto r = view()->selectionRange();
4657 m_selectAnchor = r.
start() == m_cursor ? r.end() : r.start();
4670 return m_layoutCache;
4682 if (realCursor.
line() < 0) {
4691 return view()->renderer();
4694void KateViewInternal::mouseMoved()
4696 view()->notifyMousePositionChanged(m_mouse);
4700void KateViewInternal::cursorMoved()
4704#ifndef QT_NO_ACCESSIBILITY
4714 return m_view->doc();
4719 return m_view->doc();
4722bool KateViewInternal::rangeAffectsView(
KTextEditor::Range range,
bool realCursors)
const
4724 int startLine = KateViewInternal::startLine();
4725 int endLine = startLine + (int)m_visibleLineCount;
4732 return (range.
end().
line() >= startLine) || (range.
start().line() <= endLine);
4749 auto lineHeight = renderer()->lineHeight();
4750 return QRect(cursorToCoordinate(m_cursor,
true,
false),
QSize(1, lineHeight ? lineHeight : 1));
4757 return m_cursor.
column();
4762 if (view()->selection() && m_selectAnchor.line() == m_cursor.
line()) {
4763 return m_selectAnchor.column();
4765 return m_cursor.
column();
4772 if (view()->selection()) {
4773 return view()->selectionText();
4787 if (doc()->readOnly()) {
4795 if (!m_imPreeditRange) {
4796 m_imPreeditRange.reset(
4800 if (!m_imPreeditRange->toRange().isEmpty()) {
4801 doc()->inputMethodStart();
4802 doc()->removeText(*m_imPreeditRange);
4803 doc()->inputMethodEnd();
4807 view()->removeSelectedText();
4817 if (
start != removeEnd) {
4830 m_imPreeditRange->setRange(preeditRange);
4834 doc()->inputMethodStart();
4835 doc()->insertText(m_imPreeditRange->start(), e->
preeditString());
4836 doc()->inputMethodEnd();
4843 m_imPreeditRange.reset();
4844 m_imPreeditRangeChildren.clear();
4856 bool hideCursor =
false;
4859 if (m_imPreeditRange) {
4860 m_imPreeditRangeChildren.clear();
4862 int decorationColumn = 0;
4864 for (
auto &a : attributes) {
4868 hideCursor = !a.length;
4869 QColor c = qvariant_cast<QColor>(a.value);
4880 const int startLine = preEditRangeStart.
line();
4881 const int startCol = preEditRangeStart.
column();
4883 std::unique_ptr<KTextEditor::MovingRange> formatRange(doc()->newMovingRange(fr));
4885 attribute->merge(f);
4886 formatRange->setAttribute(attribute);
4887 decorationColumn =
end;
4888 m_imPreeditRangeChildren.push_back(std::move(formatRange));
4897 if (newCursor != m_cursor.
toCursor()) {
4898 updateCursor(newCursor);
4908 Q_ASSERT(
pos.isValid());
4912 if (!view()->textFolding().isLineVisible(
pos.line())) {
4917 if (m_textAnimation) {
4918 m_textAnimation->deleteLater();
4923void KateViewInternal::showBracketMatchPreview()
4932 if (m_cursor == openBracketCursor || toVirtualCursor(openBracketCursor).line() >= startLine() || m_cursor.
line() - startLine() < 2) {
4933 hideBracketMatchPreview();
4943 const int previewLine = openBracketCursor.
line();
4945 KateLineLayout *lineLayout(
new KateLineLayout(*renderer_));
4946 lineLayout->setLine(previewLine, -1);
4949 const int col = lineLayout->textLine().firstChar();
4950 if (previewLine > 0 && (col == -1 || col == openBracketCursor.
column())) {
4951 lineLayout->setLine(previewLine - 1, lineLayout->virtualLine() - 1);
4954 renderer_->
layoutLine(lineLayout, -1 ,
false );
4955 const int lineWidth =
4956 qBound(m_view->
width() / 5,
int(lineLayout->width() + renderer_->spaceWidth() * 2), m_view->
width() - m_leftBorder->
width() - m_lineScroll->
width());
4957 m_bmPreview->resize(lineWidth, renderer_->lineHeight() * 2);
4959 m_bmPreview->move(topLeft.
x(), topLeft.
y());
4960 m_bmPreview->setLine(lineLayout->virtualLine());
4961 m_bmPreview->setCenterView(
false);
4962 m_bmPreview->raise();
4963 m_bmPreview->show();
4966void KateViewInternal::hideBracketMatchPreview()
4968 m_bmPreview.reset();
4973#ifndef QT_NO_ACCESSIBILITY
4975 auto doc = view()->doc();
4984#ifndef QT_NO_ACCESSIBILITY
4986 auto doc = view()->doc();
4997 const auto noteWidth = note.width();
4998 auto noteCursor = note.position();
5002 const auto lineLength = view()->document()->
lineLength(noteCursor.line());
5003 int extraOffset = -noteWidth;
5004 if (noteCursor.column() == lineLength) {
5006 }
else if (noteCursor.column() > lineLength) {
5007 extraOffset = (noteCursor.column() - lineLength) * renderer()->spaceWidth();
5008 noteCursor.setColumn(lineLength);
5010 auto noteStartPos =
mapToGlobal(cursorToCoordinate(noteCursor,
true,
false));
5012 if (view()->dynWordWrap()) {
5013 const KateLineLayout *lineLayout = cache()->
line(noteCursor.line());
5017 noteStartPos.rx() -= note.width();
5018 extraOffset = -extraOffset;
5022 auto globalNoteRect =
QRect(noteStartPos +
QPoint{extraOffset, 0},
QSize(noteWidth, renderer()->lineHeight()));
5024 return globalNoteRect;
5031 const auto inlineNotes = view()->inlineNotes(line);
5033 for (
const auto ¬e : inlineNotes) {
5034 auto globalNoteRect = inlineNoteRect(note);
5035 if (globalNoteRect.contains(globalPos)) {
5043bool KateViewInternal::sendMouseEventToInputContext(
QMouseEvent *e)
5045 if (!m_imPreeditRange) {
5050 if (!m_imPreeditRange->contains(c) && c != m_imPreeditRange->end()) {
5054 auto cursorPos = (c - m_imPreeditRange->start());
5056 if (cursorPos.column() >= 0) {
5065void KateViewInternal::commitPreedit()
5067 if (!m_imPreeditRange) {
5074#include "moc_kateviewinternal.cpp"
A class which provides customized text decorations.
QExplicitlySharedDataPointer< Attribute > Ptr
Shared data pointer for Attribute.
@ ActivateMouseIn
Activate attribute on mouse in.
@ ActivateCaretIn
Activate attribute on caret in.
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)).
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.
QString text(KTextEditor::Range range, bool blockwise=false) const override
Get the document content within the given range.
QChar characterAt(KTextEditor::Cursor position) const override
Get the character at text position cursor.
qsizetype cursorToOffset(KTextEditor::Cursor c) const override
Retrives the offset for the given cursor position NOTE: It will return -1 if the cursor was invalid o...
int lastLine() const
gets the last line number (lines() - 1)
KateBuffer & buffer()
Get access to buffer of this document.
int lines() const override
Get the count of lines of the document.
void textRemoved(KTextEditor::Document *document, KTextEditor::Range range, const QString &oldText)
The document emits this signal whenever range was removed, i.e.
bool editStart()
Enclose editor actions with editStart() and editEnd() to group them.
KateDocumentConfig * config()
Configuration.
void typeChars(KTextEditor::ViewPrivate *view, QString chars)
Type chars in a view.
void textInsertedRange(KTextEditor::Document *document, KTextEditor::Range range)
The document emits this signal whenever text was inserted.
Kate::TextLine kateTextLine(int i)
Same as plainKateTextLine(), except that it is made sure the line is highlighted.
bool editEnd()
End a editor operation.
int lineLength(int line) const override
Get the length of a given line in characters.
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 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.
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.
void displayRangeChanged(KTextEditor::View *view)
This signal is emitted whenever the displayed range changes.
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.
KateHighlighting::Foldings computeFoldings(int line)
Compute folding vector for the given line, will internally do a re-highlighting.
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 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 paintSelection(QPaintDevice *d, int startLine, int xStart, int endLine, int xEnd, int viewWidth, qreal scale=1.0)
Paints a range of text into d.
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.
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...
@ Default
Default settings.
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
bool isEmpty() const const
qsizetype size() const const
QPoint angleDelta() const const