603 lines
18 KiB
C++
603 lines
18 KiB
C++
#include "SynEdit.h"
|
|
#include <QApplication>
|
|
#include <QFontMetrics>
|
|
#include <algorithm>
|
|
|
|
SynEdit::SynEdit(QWidget *parent, Qt::WindowFlags f) : QFrame(parent,f)
|
|
{
|
|
mPaintLock = 0;
|
|
mPainting = false;
|
|
mLines = std::make_shared<SynEditStringList>();
|
|
mOrigLines = mLines;
|
|
//fPlugins := TList.Create;
|
|
mMouseMoved = false;
|
|
mUndoing = false;
|
|
mLines->connect(mLines.get(), &SynEditStringList::changed, this, &SynEdit::linesChanged);
|
|
mLines->connect(mLines.get(), &SynEditStringList::changing, this, &SynEdit::linesChanging);
|
|
mLines->connect(mLines.get(), &SynEditStringList::cleared, this, &SynEdit::linesCleared);
|
|
mLines->connect(mLines.get(), &SynEditStringList::deleted, this, &SynEdit::linesDeleted);
|
|
mLines->connect(mLines.get(), &SynEditStringList::inserted, this, &SynEdit::linesInserted);
|
|
mLines->connect(mLines.get(), &SynEditStringList::putted, this, &SynEdit::linesPutted);
|
|
#ifdef Q_OS_WIN
|
|
mFontDummy = QFont("Consolas",10);
|
|
#elif Q_OS_LINUX
|
|
mFontDummy = QFont("terminal",14);
|
|
#else
|
|
#error "Not supported!"
|
|
#endif
|
|
mUndoList = std::make_shared<SynEditUndoList>();
|
|
mUndoList->connect(mUndoList.get(), &SynEditUndoList::addedUndo, this, &SynEdit::undoAdded);
|
|
mOrigUndoList = mUndoList;
|
|
mRedoList = std::make_shared<SynEditUndoList>();
|
|
mRedoList->connect(mRedoList.get(), &SynEditUndoList::addedUndo, this, &SynEdit::redoAdded);
|
|
mOrigRedoList = mRedoList;
|
|
//DoubleBuffered = false;
|
|
mActiveLineColor = QColor();
|
|
mSelectedBackground = QColor();
|
|
mSelectedForeground = QColor();
|
|
|
|
mBookMarkOpt.connect(&mBookMarkOpt, &SynBookMarkOpt::changed, this, &SynEdit::bookMarkOptionsChanged);
|
|
// fRightEdge has to be set before FontChanged is called for the first time
|
|
mRightEdge = 80;
|
|
mGutter.setRightOffset(21);
|
|
mGutter.connect(&mGutter, &SynGutter::changed, this, &SynEdit::gutterChanged);
|
|
mGutterWidth = mGutter.width();
|
|
mTextOffset = mGutterWidth + 2;
|
|
//ControlStyle := ControlStyle + [csOpaque, csSetCaption, csNeedsBorderPaint];
|
|
//Height := 150;
|
|
//Width := 200;
|
|
this->setCursor(Qt::CursorShape::IBeamCursor);
|
|
//TabStop := True;
|
|
mInserting = true;
|
|
mMaxScrollWidth = 1024;
|
|
mScrollBars = SynScrollStyle::ssBoth;
|
|
this->setFrameShape(QFrame::Panel);
|
|
this->setFrameShadow(QFrame::Sunken);
|
|
this->setLineWidth(1);
|
|
mInsertCaret = SynEditCaretType::ctVerticalLine;
|
|
mOverwriteCaret = SynEditCaretType::ctBlock;
|
|
mSelectionMode = SynSelectionMode::smNormal;
|
|
mActiveSelectionMode = SynSelectionMode::smNormal;
|
|
//stop qt to auto fill background
|
|
setAutoFillBackground(false);
|
|
//fFocusList := TList.Create;
|
|
//fKbdHandler := TSynEditKbdHandler.Create;
|
|
//fMarkList.OnChange := MarkListChange;
|
|
setDefaultKeystrokes();
|
|
mRightEdgeColor = QColorConstants::Svg::silver;
|
|
/* IME input */
|
|
mImeCount = 0;
|
|
mMBCSStepAside = false;
|
|
/* end of IME input */
|
|
mWantReturns = true;
|
|
mWantTabs = false;
|
|
mTabWidth = 4;
|
|
mLeftChar = 1;
|
|
mTopLine = 1;
|
|
mCaretX = 1;
|
|
mLastCaretX = 1;
|
|
mCaretY = 1;
|
|
mBlockBegin.Char = 1;
|
|
mBlockBegin.Line = 1;
|
|
mBlockEnd = mBlockBegin;
|
|
mOptions = eoAutoIndent | eoDragDropEditing | eoEnhanceEndKey |
|
|
eoScrollPastEol | eoShowScrollHint | eoSmartTabs | eoTabsToSpaces |
|
|
eoSmartTabDelete| eoGroupUndo;
|
|
mScrollTimer = new QTimer(this);
|
|
mScrollTimer->setInterval(100);
|
|
connect(mScrollTimer, &QTimer::timeout,this, &SynEdit::scrollTimerHandler);
|
|
|
|
mScrollHintColor = QColorConstants::Yellow;
|
|
mScrollHintFormat = SynScrollHintFormat::shfTopLineOnly;
|
|
|
|
synFontChanged();
|
|
|
|
}
|
|
|
|
int SynEdit::displayLineCount()
|
|
{
|
|
return lineToRow(mLines->count());
|
|
}
|
|
|
|
BufferCoord SynEdit::caretXY()
|
|
{
|
|
BufferCoord result;
|
|
result.Char = caretX();
|
|
result.Line = caretY();
|
|
return result;
|
|
}
|
|
|
|
int SynEdit::caretX()
|
|
{
|
|
return mCaretX;
|
|
}
|
|
|
|
int SynEdit::caretY()
|
|
{
|
|
return mCaretY;
|
|
}
|
|
|
|
void SynEdit::setCaretXY(const BufferCoord &value)
|
|
{
|
|
setCaretXYCentered(false,value);
|
|
}
|
|
|
|
void SynEdit::setCaretXYEx(bool CallEnsureCursorPos, BufferCoord value)
|
|
{
|
|
bool vTriggerPaint=true; //how to test it?
|
|
|
|
if (vTriggerPaint)
|
|
doOnPaintTransient(SynTransientType::ttBefore);
|
|
int nMaxX = maxScrollWidth() + 1;
|
|
if (value.Line > mLines->count())
|
|
value.Line = mLines->count();
|
|
if (value.Line < 1) {
|
|
// this is just to make sure if Lines stringlist should be empty
|
|
value.Line = 1;
|
|
if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) {
|
|
nMaxX = 1;
|
|
}
|
|
} else {
|
|
if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol))
|
|
nMaxX = mLines->getString(value.Line-1).length();
|
|
}
|
|
if ((value.Char > nMaxX) && (! (mOptions.testFlag(SynEditorOption::eoScrollPastEol)) ||
|
|
!(mOptions.testFlag(SynEditorOption::eoAutoSizeMaxScrollWidth))) )
|
|
value.Char = nMaxX;
|
|
if (value.Char < 1)
|
|
value.Char = 1;
|
|
if ((value.Char != mCaretX) || (value.Line != mCaretY)) {
|
|
incPaintLock();
|
|
auto action = finally([this]{
|
|
decPaintLock();
|
|
});
|
|
// simply include the flags, fPaintLock is > 0
|
|
if (mCaretX != value.Char) {
|
|
mCaretX = value.Char;
|
|
mStatusChanges.setFlag(SynStatusChange::scCaretX);
|
|
}
|
|
if (mCaretY != value.Line) {
|
|
if (!mActiveLineColor.isValid()) {
|
|
invalidateLine(value.Line);
|
|
invalidateLine(mCaretY);
|
|
}
|
|
mCaretY = value.Line;
|
|
mStatusChanges.setFlag(SynStatusChange::scCaretY);
|
|
}
|
|
// Call UpdateLastCaretX before DecPaintLock because the event handler it
|
|
// calls could raise an exception, and we don't want fLastCaretX to be
|
|
// left in an undefined state if that happens.
|
|
updateLastCaretX();
|
|
if (CallEnsureCursorPos)
|
|
ensureCursorPosVisible();
|
|
mStateFlags.setFlag(SynStateFlag::sfCaretChanged);
|
|
mStateFlags.setFlag(SynStateFlag::sfScrollbarChanged);
|
|
} else {
|
|
// Also call UpdateLastCaretX if the caret didn't move. Apps don't know
|
|
// anything about fLastCaretX and they shouldn't need to. So, to avoid any
|
|
// unwanted surprises, always update fLastCaretX whenever CaretXY is
|
|
// assigned to.
|
|
// Note to SynEdit developers: If this is undesirable in some obscure
|
|
// case, just save the value of fLastCaretX before assigning to CaretXY and
|
|
// restore it afterward as appropriate.
|
|
updateLastCaretX();
|
|
}
|
|
if (vTriggerPaint)
|
|
doOnPaintTransient(SynTransientType::ttAfter);
|
|
|
|
}
|
|
|
|
void SynEdit::setCaretXYCentered(bool ForceToMiddle, const BufferCoord &value)
|
|
{
|
|
incPaintLock();
|
|
auto action = finally([this] {
|
|
decPaintLock();
|
|
});
|
|
mStatusChanges.setFlag(SynStatusChange::scSelection);
|
|
setCaretXYEx(ForceToMiddle,value);
|
|
if selAvail() then
|
|
invalidateSelection();
|
|
mBlockBegin.Char = mCaretX;
|
|
mBlockBegin.Line = mCaretY;
|
|
mBlockEnd = mBlockBegin;
|
|
if (ForceToMiddle)
|
|
ensureCursorPosVisibleEx(true); // but here after block has been set
|
|
|
|
}
|
|
|
|
void SynEdit::invalidateGutter()
|
|
{
|
|
invalidateGutterLines(-1, -1);
|
|
}
|
|
|
|
void SynEdit::invalidateGutterLine(int aLine)
|
|
{
|
|
if ((aLine < 1) || (aLine > mLines->count()))
|
|
return;
|
|
|
|
invalidateGutterLines(aLine, aLine);
|
|
}
|
|
|
|
void SynEdit::invalidateGutterLines(int FirstLine, int LastLine)
|
|
{
|
|
QRect rcInval;
|
|
if (!isVisible())
|
|
return;
|
|
if (FirstLine == -1 && LastLine == -1) {
|
|
rcInval = QRect(clientLeft(), clientTop(), mGutterWidth, clientHeight());
|
|
if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging))
|
|
mInvalidateRect = mInvalidateRect.united(rcInval);
|
|
else
|
|
update(rcInval);
|
|
} else {
|
|
// find the visible lines first
|
|
if (LastLine < FirstLine)
|
|
std::swap(LastLine, FirstLine);
|
|
if (mUseCodeFolding) {
|
|
FirstLine = lineToRow(FirstLine);
|
|
if (LastLine <= mLines->count())
|
|
LastLine = lineToRow(LastLine);
|
|
else
|
|
LastLine = INT_MAX;
|
|
}
|
|
FirstLine = std::max(FirstLine, topLine());
|
|
LastLine = std::min(LastLine, topLine() + linesInWindow());
|
|
// any line visible?
|
|
if (LastLine >= FirstLine) {
|
|
rcInval = {clientLeft(), clientTop()+mTextHeight * (FirstLine - topLine()),
|
|
mGutterWidth, mTextHeight * (LastLine - topLine() + 1)};
|
|
if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) {
|
|
mInvalidateRect = mInvalidateRect.united(rcInval);
|
|
} else {
|
|
update(rcInval);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SynEdit::clearAreaList(SynEditingAreaList areaList)
|
|
{
|
|
areaList.clear();
|
|
}
|
|
|
|
void SynEdit::computeCaret(int X, int Y)
|
|
{
|
|
DisplayCoord vCaretNearestPos = pixelsToNearestRowColumn(X, Y);
|
|
vCaretNearestPos.Row = MinMax(vCaretNearestPos.Row, 1, displayLineCount());
|
|
setInternalDisplayXY(vCaretNearestPos);
|
|
}
|
|
|
|
void SynEdit::computeScroll(int X, int Y)
|
|
{
|
|
QRect iScrollBounds; // relative to the client area
|
|
// don't scroll if dragging text from other control
|
|
// if (not MouseCapture) and (not Dragging) then begin
|
|
// fScrollTimer.Enabled := False;
|
|
// Exit;
|
|
// end;
|
|
|
|
iScrollBounds = QRect(mGutterWidth+this->frameWidth(), this->frameWidth(), mCharsInWindow * mCharWidth,
|
|
mLinesInWindow * mTextHeight);
|
|
|
|
if (X < iScrollBounds.left())
|
|
mScrollDeltaX = (X - iScrollBounds.left()) % mCharWidth - 1;
|
|
else if (X >= iScrollBounds.right())
|
|
mScrollDeltaX = (X - iScrollBounds.right()) % mCharWidth + 1;
|
|
else
|
|
mScrollDeltaX = 0;
|
|
|
|
if (Y < iScrollBounds.top())
|
|
mScrollDeltaY = (Y - iScrollBounds.top()) % mTextHeight - 1;
|
|
else if (Y >= iScrollBounds.bottom())
|
|
mScrollDeltaY = (Y - iScrollBounds.bottom()) % mTextHeight + 1;
|
|
else
|
|
mScrollDeltaY = 0;
|
|
|
|
if (mScrollDeltaX!=0 || mScrollDeltaY!=0)
|
|
mScrollTimer->start();
|
|
}
|
|
|
|
void SynEdit::doBlockIndent()
|
|
{
|
|
BufferCoord OrgCaretPos;
|
|
BufferCoord BB, BE;
|
|
QString StrToInsert;
|
|
int Run;
|
|
int e,x,i,InsertStrLen;
|
|
QString Spaces;
|
|
SynSelectionMode OrgSelectionMode;
|
|
BufferCoord InsertionPos;
|
|
|
|
OrgSelectionMode = mActiveSelectionMode;
|
|
OrgCaretPos = caretXY();
|
|
StrToInsert = nullptr;
|
|
if (selAvail()) {
|
|
auto action = finally([&,this]{
|
|
if (BE.Char > 1)
|
|
BE.Char+=Spaces.length();
|
|
setCaretAndSelection(OrgCaretPos,
|
|
{BB.Char + Spaces.length(), BB.Line}, BE);
|
|
setActiveSelectionMode(OrgSelectionMode);
|
|
});
|
|
// keep current selection detail
|
|
BB = blockBegin();
|
|
BE = blockEnd();
|
|
// build text to insert
|
|
if (BE.Char == 1) {
|
|
e = BE.Line - 1;
|
|
x = 1;
|
|
} else {
|
|
e = BE.Line;
|
|
if (mOptions.testFlag(SynEditorOption::eoTabsToSpaces))
|
|
x = caretX() + mTabWidth;
|
|
else
|
|
x = caretX() + 1;
|
|
}
|
|
if (mOptions.testFlag(eoTabsToSpaces)) {
|
|
InsertStrLen = (mTabWidth + 2) * (e - BB.Line) + mTabWidth + 1;
|
|
// chars per line * lines-1 + last line + null char
|
|
StrToInsert.resize(InsertStrLen);
|
|
Run = 0;
|
|
Spaces = QString(mTabWidth,' ') ;
|
|
} else {
|
|
InsertStrLen = 3 * (e - BB.Line) + 2;
|
|
// #9#13#10 * lines-1 + (last line's #9 + null char)
|
|
StrToInsert.resize(InsertStrLen);
|
|
Run = 0;
|
|
Spaces = "\t";
|
|
}
|
|
for (i = BB.Line; i<e;i++) {
|
|
StrToInsert.replace(Run,Spaces.length()+2,Spaces+"\r\n");
|
|
Run+=Spaces.length()+2;
|
|
}
|
|
StrToInsert.replace(Run,Spaces.length(),Spaces);
|
|
|
|
{
|
|
mUndoList->BeginBlock();
|
|
auto action2=finally([this]{
|
|
mUndoList->EndBlock();
|
|
});
|
|
InsertionPos.Line = BB.Line;
|
|
if (mActiveSelectionMode == SynSelectionMode::smColumn)
|
|
InsertionPos.Char = std::min(BB.Char, BE.Char);
|
|
else
|
|
InsertionPos.Char = 1;
|
|
insertBlock(InsertionPos, InsertionPos, StrToInsert, true);
|
|
mUndoList->AddChange(SynChangeReason::crIndent, BB, BE, "", SynSelectionMode::smColumn);
|
|
//We need to save the position of the end block for redo
|
|
mUndoList.AddChange(SynChangeReason::crIndent,
|
|
{BB.Char + Spaces.length(), BB.Line},
|
|
{BE.Char + Spaces.length(), BE.Line},
|
|
"", SynSelectionMode::smColumn);
|
|
//adjust the x position of orgcaretpos appropriately
|
|
OrgCaretPos.Char = X;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void SynEdit::incPaintLock()
|
|
{
|
|
mPaintLock ++ ;
|
|
}
|
|
|
|
void SynEdit::decPaintLock()
|
|
{
|
|
Q_ASSERT(mPaintLock > 0);
|
|
mPaintLock--;
|
|
if (mPaintLock == 0 ) {
|
|
if (mStateFlags.testFlag(SynStateFlag::sfScrollbarChanged))
|
|
updateScrollbars();
|
|
if (mStateFlags.testFlag(SynStateFlag::sfCaretChanged))
|
|
updateCaret();
|
|
if (mStatusChanges!=0)
|
|
doOnStatusChange(mStatusChanges);
|
|
}
|
|
}
|
|
|
|
bool SynEdit::mouseCapture()
|
|
{
|
|
return hasMouseTracking();
|
|
}
|
|
|
|
int SynEdit::clientWidth()
|
|
{
|
|
return frameRect().width()-2*frameWidth();
|
|
}
|
|
|
|
int SynEdit::clientHeight()
|
|
{
|
|
return frameRect().height()-2*frameWidth();
|
|
}
|
|
|
|
int SynEdit::clientTop()
|
|
{
|
|
return frameRect().top()+frameWidth();
|
|
}
|
|
|
|
int SynEdit::clientLeft()
|
|
{
|
|
return frameRect().left()+frameWidth();
|
|
}
|
|
|
|
QRect SynEdit::clientRect()
|
|
{
|
|
return QRect(frameRect().left()+frameWidth(),frameRect().top()+frameWidth(), frameRect().width()-2*frameWidth(), frameRect().height()-2*frameWidth());
|
|
}
|
|
|
|
void SynEdit::bookMarkOptionsChanged()
|
|
{
|
|
invalidateGutter();
|
|
}
|
|
|
|
void SynEdit::linesChanged()
|
|
{
|
|
SynSelectionMode vOldMode;
|
|
mStateFlags.setFlag(SynStateFlag::sfLinesChanging, false);
|
|
if (mUseCodeFolding)
|
|
rescan();
|
|
|
|
updateScrollBars();
|
|
vOldMode = mActiveSelectionMode;
|
|
setBlockBegin(caretXY());
|
|
mActiveSelectionMode = vOldMode;
|
|
update(mInvalidateRect);
|
|
mInvalidateRect = {0,0,0,0};
|
|
|
|
if (mGutter.showLineNumbers() && (mGutter.autoSize()))
|
|
mGutter.autoSizeDigitCount(mLines->count());
|
|
if (!mOptions.testFlag(SynEditorOption::eoScrollPastEof))
|
|
setTopLine(mTopLine);
|
|
}
|
|
|
|
void SynEdit::linesChanging()
|
|
{
|
|
mStateFlags.setFlag(SynStateFlag::sfLinesChanging);
|
|
}
|
|
|
|
void SynEdit::linesCleared()
|
|
{
|
|
if (mUseCodeFolding)
|
|
foldOnListCleared();
|
|
clearUndo();
|
|
// invalidate the *whole* client area
|
|
mInvalidateRect={0,0,0,0};
|
|
update();
|
|
// set caret and selected block to start of text
|
|
setCaretXY({1,1});
|
|
// scroll to start of text
|
|
setTopLine(1);
|
|
setLeftChar(1);
|
|
mStatusChanges.setFlag(SynStatusChange::scAll);
|
|
}
|
|
|
|
void SynEdit::linesDeleted(int index, int count)
|
|
{
|
|
if (mUseCodeFolding)
|
|
foldOnListDeleted(index + 1, count);
|
|
if (mHighlighter && mLines->count() > 0)
|
|
scanFrom(index);
|
|
invalidateLines(index + 1, INT_MAX);
|
|
invalidateGutterLines(index + 1, INT_MAX);
|
|
}
|
|
|
|
void SynEdit::linesInserted(int index, int count)
|
|
{
|
|
if (mUseCodeFolding)
|
|
foldOnListInserted(index + 1, count);
|
|
if (mHighlighter && mLines->count() > 0) {
|
|
int vLastScan = index;
|
|
do {
|
|
vLastScan = scanFrom(vLastScan);
|
|
vLastScan++;
|
|
} while (vLastScan < index + count) ;
|
|
}
|
|
invalidateLines(index + 1, INT_MAX);
|
|
invalidateGutterLines(index + 1, INT_MAX);
|
|
if (mOptions.setFlag(SynEditorOption::eoAutoSizeMaxScrollWidth)) {
|
|
int L = mLines->expandedStringLength(index);
|
|
if (L > mMaxScrollWidth)
|
|
setMaxScrollWidth(L);
|
|
}
|
|
}
|
|
|
|
void SynEdit::linesPutted(int index, int count)
|
|
{
|
|
int vEndLine = index + 1;
|
|
if (mHighlighter) {
|
|
vEndLine = std::max(vEndLine, scanFrom(index) + 1);
|
|
// If this editor is chained then the real owner of text buffer will probably
|
|
// have already parsed the changes, so ScanFrom will return immediately.
|
|
if (mLines != mOrigLines)
|
|
vEndLine = INT_MAX;
|
|
}
|
|
invalidateLines(index + 1, vEndLine);
|
|
|
|
if (mOptions.setFlag(SynEditorOption::eoAutoSizeMaxScrollWidth)) {
|
|
int L = mLines->expandedStringLength(index);
|
|
if (L > mMaxScrollWidth)
|
|
setMaxScrollWidth(L);
|
|
}
|
|
}
|
|
|
|
void SynEdit::undoAdded()
|
|
{
|
|
updateModifiedStatus();
|
|
|
|
// we have to clear the redo information, since adding undo info removes
|
|
// the necessary context to undo earlier edit actions
|
|
if (! mUndoList->insideRedo() &&
|
|
mUndoList->PeekItem() && (mUndoList->PeekItem()->changeReason()!=SynChangeReason::crGroupBreak))
|
|
mRedoList->Clear();
|
|
if (mUndoList->blockCount() == 0 )
|
|
doChange();
|
|
}
|
|
|
|
void SynEdit::redoAdded()
|
|
{
|
|
updateModifiedStatus();
|
|
|
|
if (mRedoList->blockCount() == 0 )
|
|
doChange();
|
|
}
|
|
|
|
void SynEdit::gutterChanged()
|
|
{
|
|
if (mGutter.showLineNumbers() && mGutter.autoSize())
|
|
mGutter.autoSizeDigitCount(mLines->count());
|
|
int nW;
|
|
if (mGutter.useFontStyle()) {
|
|
QFontMetrics fm=QFontMetrics(mGutter.font());
|
|
nW = mGutter.realGutterWidth(fm.averageCharWidth());
|
|
} else {
|
|
nW = mGutter.realGutterWidth(mCharWidth);
|
|
}
|
|
if (nW == mGutterWidth)
|
|
invalidateGutter();
|
|
else
|
|
setGutterWidth(nW);
|
|
}
|
|
|
|
void SynEdit::scrollTimerHandler()
|
|
{
|
|
QPoint iMousePos;
|
|
DisplayCoord C;
|
|
int X, Y;
|
|
|
|
iMousePos = QCursor::pos();
|
|
iMousePos = mapFromGlobal(iMousePos);
|
|
C = pixelsToRowColumn(iMousePos.x(), iMousePos.y());
|
|
C.Row = MinMax(C.Row, 1, displayLineCount());
|
|
if (mScrollDeltaX != 0) {
|
|
setLeftChar(leftChar() + mScrollDeltaX);
|
|
X = leftChar();
|
|
if (mScrollDeltaX > 0) // scrolling right?
|
|
X+=charsInWindow();
|
|
C.Column = X;
|
|
}
|
|
if (mScrollDeltaY != 0) {
|
|
if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
|
|
setTopLine(topLine() + mScrollDeltaY * linesInWindow());
|
|
else
|
|
setTopLine(topLine()) + mScrollDeltaY);
|
|
Y = topLine();
|
|
if (mScrollDeltaY > 0) // scrolling down?
|
|
Y+=linesInWindow() - 1;
|
|
C.Row = MinMax(Y, 1, displayLineCount());
|
|
}
|
|
BufferCoord vCaret = displayToBufferPos(C);
|
|
if ((caretX() <> vCaret.Char) || (caretY() != vCaret.Line)) {
|
|
// changes to line / column in one go
|
|
incPaintLock();
|
|
auto action = finally([this]{
|
|
decPaintLock();
|
|
});
|
|
setInternalCaretXY(vCaret);
|
|
|
|
// if MouseCapture is True we're changing selection. otherwise we're dragging
|
|
if (mouseCapture())
|
|
setBlockEnd(caretXY());
|
|
}
|
|
computeScroll(iMousePos.x(), iMousePos.y());
|
|
}
|