📄 editor.cxx
字号:
};
Point Editor::LocationFromPosition(int pos) {
Point pt;
RefreshStyleData();
if (pos == INVALID_POSITION)
return pt;
int line = pdoc->LineFromPosition(pos);
int lineVisible = cs.DisplayFromDoc(line);
//Platform::DebugPrintf("line=%d\n", line);
AutoSurface surface(this);
AutoLineLayout ll(llc, RetrieveLineLayout(line));
if (surface && ll) {
// -1 because of adding in for visible lines in following loop.
pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
pt.x = 0;
unsigned int posLineStart = pdoc->LineStart(line);
LayoutLine(line, surface, vs, ll, wrapWidth);
int posInLine = pos - posLineStart;
// In case of very long line put x at arbitrary large position
if (posInLine > ll->maxLineLength) {
pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
}
for (int subLine = 0; subLine < ll->lines; subLine++) {
if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
}
if (posInLine >= ll->LineStart(subLine)) {
pt.y += vs.lineHeight;
}
}
pt.x += vs.fixedColumnWidth - xOffset;
}
return pt;
}
int Editor::XFromPosition(int pos) {
Point pt = LocationFromPosition(pos);
return pt.x - vs.fixedColumnWidth + xOffset;
}
int Editor::LineFromLocation(Point pt) {
return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
}
void Editor::SetTopLine(int topLineNew) {
topLine = topLineNew;
posTopLine = pdoc->LineStart(topLine);
}
static inline bool IsEOLChar(char ch) {
return (ch == '\r') || (ch == '\n');
}
int Editor::PositionFromLocation(Point pt) {
RefreshStyleData();
pt.x = pt.x - vs.fixedColumnWidth + xOffset;
int visibleLine = pt.y / vs.lineHeight + topLine;
if (pt.y < 0) { // Division rounds towards 0
visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
}
if (visibleLine < 0)
visibleLine = 0;
int lineDoc = cs.DocFromDisplay(visibleLine);
if (lineDoc >= pdoc->LinesTotal())
return pdoc->Length();
unsigned int posLineStart = pdoc->LineStart(lineDoc);
int retVal = posLineStart;
AutoSurface surface(this);
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
if (surface && ll) {
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
int lineStartSet = cs.DisplayFromDoc(lineDoc);
int subLine = visibleLine - lineStartSet;
if (subLine < ll->lines) {
int lineStart = ll->LineStart(subLine);
int lineEnd = ll->LineStart(subLine + 1);
int subLineStart = ll->positions[lineStart];
for (int i = lineStart; i < lineEnd; i++) {
if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
IsEOLChar(ll->chars[i])) {
return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
}
}
return lineEnd + posLineStart;
}
retVal = ll->numCharsInLine + posLineStart;
}
return retVal;
}
// Like PositionFromLocation but INVALID_POSITION returned when not near any text.
int Editor::PositionFromLocationClose(Point pt) {
RefreshStyleData();
PRectangle rcClient = GetTextRectangle();
if (!rcClient.Contains(pt))
return INVALID_POSITION;
if (pt.x < vs.fixedColumnWidth)
return INVALID_POSITION;
if (pt.y < 0)
return INVALID_POSITION;
pt.x = pt.x - vs.fixedColumnWidth + xOffset;
int visibleLine = pt.y / vs.lineHeight + topLine;
if (pt.y < 0) { // Division rounds towards 0
visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
}
int lineDoc = cs.DocFromDisplay(visibleLine);
if (lineDoc < 0)
return INVALID_POSITION;
if (lineDoc >= pdoc->LinesTotal())
return INVALID_POSITION;
AutoSurface surface(this);
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
if (surface && ll) {
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
unsigned int posLineStart = pdoc->LineStart(lineDoc);
int lineStartSet = cs.DisplayFromDoc(lineDoc);
int subLine = visibleLine - lineStartSet;
if (subLine < ll->lines) {
int lineStart = ll->LineStart(subLine);
int lineEnd = ll->LineStart(subLine + 1);
int subLineStart = ll->positions[lineStart];
for (int i = lineStart; i < lineEnd; i++) {
if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
IsEOLChar(ll->chars[i])) {
return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
}
}
}
}
return INVALID_POSITION;
}
/**
* Find the document position corresponding to an x coordinate on a particular document line.
* Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
*/
int Editor::PositionFromLineX(int lineDoc, int x) {
RefreshStyleData();
if (lineDoc >= pdoc->LinesTotal())
return pdoc->Length();
//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
AutoSurface surface(this);
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
int retVal = 0;
if (surface && ll) {
unsigned int posLineStart = pdoc->LineStart(lineDoc);
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
retVal = ll->numCharsInLine + posLineStart;
int subLine = 0;
int lineStart = ll->LineStart(subLine);
int lineEnd = ll->LineStart(subLine + 1);
int subLineStart = ll->positions[lineStart];
for (int i = lineStart; i < lineEnd; i++) {
if (x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
IsEOLChar(ll->chars[i])) {
retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
break;
}
}
}
return retVal;
}
// If painting then abandon the painting because a wider redraw is needed.
// Return true if calling code should stop drawing
bool Editor::AbandonPaint() {
if ((paintState == painting) && !paintingAllText) {
paintState = paintAbandoned;
}
return paintState == paintAbandoned;
}
void Editor::RedrawRect(PRectangle rc) {
//Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
// Clip the redraw rectangle into the client area
PRectangle rcClient = GetClientRectangle();
if (rc.top < rcClient.top)
rc.top = rcClient.top;
if (rc.bottom > rcClient.bottom)
rc.bottom = rcClient.bottom;
if (rc.left < rcClient.left)
rc.left = rcClient.left;
if (rc.right > rcClient.right)
rc.right = rcClient.right;
if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
wMain.InvalidateRectangle(rc);
}
}
void Editor::Redraw() {
//Platform::DebugPrintf("Redraw all\n");
PRectangle rcClient = GetClientRectangle();
wMain.InvalidateRectangle(rcClient);
//wMain.InvalidateAll();
}
void Editor::RedrawSelMargin() {
if (!AbandonPaint()) {
if (vs.maskInLine) {
Redraw();
} else {
PRectangle rcSelMargin = GetClientRectangle();
rcSelMargin.right = vs.fixedColumnWidth;
wMain.InvalidateRectangle(rcSelMargin);
}
}
}
PRectangle Editor::RectangleFromRange(int start, int end) {
int minPos = start;
if (minPos > end)
minPos = end;
int maxPos = start;
if (maxPos < end)
maxPos = end;
int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
int lineDocMax = pdoc->LineFromPosition(maxPos);
int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
PRectangle rcClient = GetTextRectangle();
PRectangle rc;
rc.left = vs.fixedColumnWidth;
rc.top = (minLine - topLine) * vs.lineHeight;
if (rc.top < 0)
rc.top = 0;
rc.right = rcClient.right;
rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
// Ensure PRectangle is within 16 bit space
rc.top = Platform::Clamp(rc.top, -32000, 32000);
rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
return rc;
}
void Editor::InvalidateRange(int start, int end) {
RedrawRect(RectangleFromRange(start, end));
}
int Editor::CurrentPosition() {
return currentPos;
}
bool Editor::SelectionEmpty() {
return anchor == currentPos;
}
int Editor::SelectionStart(int line) {
if ((line == -1) || (selType == selStream)) {
return Platform::Minimum(currentPos, anchor);
} else { // selType == selRectangle
int selStart = SelectionStart();
int selEnd = SelectionEnd();
int lineStart = pdoc->LineFromPosition(selStart);
int lineEnd = pdoc->LineFromPosition(selEnd);
if (line < lineStart || line > lineEnd) {
return -1;
} else {
int minX = Platform::Minimum(xStartSelect, xEndSelect);
return PositionFromLineX(line, minX);
}
}
}
int Editor::SelectionEnd(int line) {
if ((line == -1) || (selType == selStream)) {
return Platform::Maximum(currentPos, anchor);
} else { // selType == selRectangle
int selStart = SelectionStart();
int selEnd = SelectionEnd();
int lineStart = pdoc->LineFromPosition(selStart);
int lineEnd = pdoc->LineFromPosition(selEnd);
if (line < lineStart || line > lineEnd) {
return -1;
} else {
int maxX = Platform::Maximum(xStartSelect, xEndSelect);
// measure line and return character closest to minx
return PositionFromLineX(line, maxX);
}
}
}
void Editor::SetSelection(int currentPos_, int anchor_) {
currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
anchor_ = pdoc->ClampPositionIntoDocument(anchor_);
if ((currentPos != currentPos_) || (anchor != anchor_)) {
int firstAffected = anchor;
if (firstAffected > currentPos)
firstAffected = currentPos;
if (firstAffected > anchor_)
firstAffected = anchor_;
if (firstAffected > currentPos_)
firstAffected = currentPos_;
int lastAffected = anchor;
if (lastAffected < currentPos)
lastAffected = currentPos;
if (lastAffected < anchor_)
lastAffected = anchor_;
if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
lastAffected = (currentPos_ + 1);
currentPos = currentPos_;
anchor = anchor_;
needUpdateUI = true;
InvalidateRange(firstAffected, lastAffected);
}
ClaimSelection();
}
void Editor::SetSelection(int currentPos_) {
currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
if (currentPos != currentPos_) {
int firstAffected = anchor;
if (firstAffected > currentPos)
firstAffected = currentPos;
if (firstAffected > currentPos_)
firstAffected = currentPos_;
int lastAffected = anchor;
if (lastAffected < currentPos)
lastAffected = currentPos;
if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
lastAffected = (currentPos_ + 1);
currentPos = currentPos_;
needUpdateUI = true;
InvalidateRange(firstAffected, lastAffected);
}
ClaimSelection();
}
void Editor::SetEmptySelection(int currentPos_) {
selType = selStream;
SetSelection(currentPos_, currentPos_);
}
bool Editor::RangeContainsProtected(int start, int end) const {
if (vs.ProtectionActive()) {
if (start > end) {
int t = start;
start = end;
end = t;
}
int mask = pdoc->stylingBitsMask;
for (int pos = start; pos < end; pos++) {
if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
return true;
}
}
return false;
}
bool Editor::SelectionContainsProtected() const {
// TODO: make support rectangular selection
return RangeContainsProtected(anchor, currentPos);
}
int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
// Asks document to find a good position and then moves out of any invisible positions
pos = pdoc->MovePositionOutsideChar(pos, moveDir, checkLineEnd);
if (vs.ProtectionActive()) {
int mask = pdoc->stylingBitsMask;
if (moveDir > 0) {
if ((pos > 0) && vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()) {
while ((pos < pdoc->Length()) &&
(vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()))
pos++;
}
} else if (moveDir < 0) {
if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()) {
while ((pos > 0) &&
(vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()))
pos--;
}
}
}
return pos;
}
int Editor::MovePositionTo(int newPos, bool extend, bool ensureVisible) {
int delta = newPos - currentPos;
newPos = pdoc->ClampPositionIntoDocument(newPos);
newPos = MovePositionOutsideChar(newPos, delta);
if (extend) {
SetSelection(newPos);
} else {
SetEmptySelection(newPos);
}
ShowCaretAtCurrentPosition();
if (ensureVisible)
EnsureCaretVisible();
NotifyMove(newPos);
return 0;
}
int Editor::MovePositionSoVisible(int pos, int moveDir) {
pos = pdoc->ClampPositionIntoDocument(pos);
pos = MovePositionOutsideChar(pos, moveDir);
int lineDoc = pdoc->LineFromPosition(pos);
if (cs.GetVisible(lineDoc)) {
return pos;
} else {
int lineDisplay = cs.DisplayFromDoc(lineDoc);
if (moveDir > 0) {
// lineDisplay is already line before fold as lines in fold use display line of line after fold
lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
return pdoc->LineStart(cs.DocFromDisplay(lineDisplay));
} else {
lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
return pdoc->LineEnd(cs.DocFromDisplay(lineDisplay));
}
}
}
// Choose the x position that the caret will try to stick to as it is moves up and down
void Editor::SetLastXChosen() {
Point pt = LocationFromPosition(currentPos);
lastXChosen = pt.x;
}
void Editor::ScrollTo(int line, bool moveThumb) {
int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
if (topLineNew != topLine) {
// Try to optimise small scrolls
int linesToMove = topLine - topLineNew;
SetTopLine(topLineNew);
ShowCaretAtCurrentPosition();
// Perform redraw rather than scroll if many lines would be redrawn anyway.
if (abs(linesToMove) <= 10) {
ScrollText(linesToMove);
} else {
Redraw();
}
if (moveThumb) {
SetVerticalScrollPos();
}
}
}
void Editor::ScrollText(int /* linesToMove */) {
//Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
Redraw();
}
void Editor::HorizontalScrollTo(int xPos) {
//Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
if (xPos < 0)
xPos = 0;
if ((wrapState == eWrapNone) && (xOffset != xPos)) {
xOffset = xPos;
SetHorizontalScrollPos();
RedrawRect(GetClientRectangle());
}
}
void Editor::MoveCaretInsideView(bool ensureVisible) {
PRectangle rcClient = GetTextRectangle();
Point pt = LocationFromPosition(currentPos);
if (pt.y < rcClient.top) {
MovePositionTo(PositionFromLocation(
Point(lastXChosen, rcClient.top)),
false, ensureVisible);
} else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
MovePositionTo(PositionFromLocation(
Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
false, ensureVisible);
}
}
int Editor::DisplayFromPosition(int pos) {
int lineDoc = pdoc->LineFromPosition(pos);
int lineDisplay = cs.DisplayFromDoc(lineDoc);
AutoSurface surface(this);
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
if (surface && ll) {
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
unsigned int posLineStart = pdoc->LineStart(lineDoc);
int posInLine = pos - posLineStart;
lineDisplay--; // To make up for first increment ahead.
for (int subLine = 0; subLine < ll->lines; subLine++) {
if (posInLine >= ll->LineStart(subLine)) {
lineDisplay++;
}
}
}
return lineDisplay;
}
/**
* Ensure the caret is reasonably visible in context.
*
Caret policy in SciTE
If slop is set, we can define a slop value.
This value defines an unwanted zone (UZ) where the caret is... unwanted.
This zone is defined as a number of pixels near the vertical margins,
and as a number of lines near the horizontal margins.
By keeping the caret away from the edges, it is seen within its context,
so it is likely that the identifier that the caret is on can be completely seen,
and that the current line is seen with some of the lines following it which are
often dependent on that line.
If strict is set, the policy is enforced... strictly.
The caret is centred on the display if slop is not set,
and cannot go in the UZ if slop is set.
If jumps is set, the display is moved more energetically
so the caret can move in the same direction longer before the policy is applied again.
'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
If even is not set, instead of having symmetrical UZs,
the left and bottom UZs are extended up to right and top UZs respectively.
This way, we favour the displaying of useful information: the begining of lines,
where most code reside, and the lines after the caret, eg. the body of a function.
| | | | |
slop | strict | jumps | even | Caret can go to the margin | When reaching limit
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -