aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authornyamatongwe <unknown>2002-02-28 02:52:43 +0000
committernyamatongwe <unknown>2002-02-28 02:52:43 +0000
commit3307089950230f9b4fd52048e27d01280f81c247 (patch)
treed242ef9e669d0f88c10b3651444a1e4f6e223a94 /src
parent63c9ec2b810f77f429972ab272f5d16404bc0625 (diff)
downloadscintilla-mirror-3307089950230f9b4fd52048e27d01280f81c247.tar.gz
Line layout cache feature added.
Diffstat (limited to 'src')
-rw-r--r--src/Document.cxx7
-rw-r--r--src/Document.h2
-rw-r--r--src/Editor.cxx884
-rw-r--r--src/Editor.h59
4 files changed, 619 insertions, 333 deletions
diff --git a/src/Document.cxx b/src/Document.cxx
index 76695efe5..3d3e5a51d 100644
--- a/src/Document.cxx
+++ b/src/Document.cxx
@@ -36,6 +36,7 @@ Document::Document() {
stylingMask = 0;
SetWordChars(0);
endStyled = 0;
+ styleClock = 0;
enteredCount = 0;
enteredReadOnlyCount = 0;
tabInChars = 8;
@@ -1115,6 +1116,12 @@ void Document::SetStyles(int length, char *styles) {
}
bool Document::EnsureStyledTo(int pos) {
+ if (pos > GetEndStyled()) {
+ styleClock++;
+ if (styleClock > 0x100000) {
+ styleClock = 0;
+ }
+ }
// Ask the watchers to style, and stop as soon as one responds.
for (int i = 0; pos > GetEndStyled() && i < lenWatchers; i++)
watchers[i].watcher->NotifyStyleNeeded(this, watchers[i].userData, pos);
diff --git a/src/Document.h b/src/Document.h
index 0d90d4c00..8affe294c 100644
--- a/src/Document.h
+++ b/src/Document.h
@@ -85,6 +85,7 @@ private:
charClassification charClass[256];
char stylingMask;
int endStyled;
+ int styleClock;
int enteredCount;
int enteredReadOnlyCount;
@@ -195,6 +196,7 @@ public:
void SetStyles(int length, char *styles);
int GetEndStyled() { return endStyled; }
bool EnsureStyledTo(int pos);
+ int GetStyleClock() { return styleClock; }
int SetLineState(int line, int state) { return cb.SetLineState(line, state); }
int GetLineState(int line) { return cb.GetLineState(line); }
diff --git a/src/Editor.cxx b/src/Editor.cxx
index 304ed61bc..6bfce655a 100644
--- a/src/Editor.cxx
+++ b/src/Editor.cxx
@@ -32,6 +32,188 @@ active(false), on(false), period(500) {}
Timer::Timer() :
ticking(false), ticksToWait(0), tickerID(0) {}
+LineLayout::LineLayout(int maxLineLength_, int maxDisplayLines) :
+ validity(llInvalid),
+ maxLineLength(-1),
+ lineNumber(-1),
+ inCache(false),
+ numCharsInLine(0),
+ xHighlightGuide(0),
+ highlightColumn(0),
+ selStart(0),
+ selEnd(0),
+ containsCaret(false),
+ edgeColumn(0),
+ chars(0),
+ styles(0),
+ indicators(0),
+ positions(0),
+ widthLine(0),
+ lines(1),
+ maxDisplayLines(maxDisplayLines),
+ lineStarts(0) {
+ Resize(maxLineLength_);
+}
+
+LineLayout::~LineLayout() {
+ Free();
+}
+
+void LineLayout::Resize(int maxLineLength_) {
+ if (maxLineLength_ > maxLineLength) {
+ Free();
+ chars = new char[maxLineLength_ + 1];
+ styles = new char[maxLineLength_ + 1];
+ indicators = new char[maxLineLength_ + 1];
+ positions = new int[maxLineLength_ + 1];
+ lineStarts = new int[maxDisplayLines + 1];
+ maxLineLength = maxLineLength_;
+ }
+}
+
+void LineLayout::Free() {
+ delete []chars;
+ chars = 0;
+ delete []styles;
+ styles = 0;
+ delete []indicators;
+ indicators = 0;
+ delete []positions;
+ positions = 0;
+ delete []lineStarts;
+ lineStarts = 0;
+}
+
+void LineLayout::Invalidate(validLevel validity_) {
+ validity = validity_;
+}
+
+LineLayoutCache::LineLayoutCache() :
+ level(0), length(0), size(0), cache(0),
+ allInvalidated(false), styleClock(-1) {
+ Allocate(0);
+}
+
+LineLayoutCache::~LineLayoutCache() {
+ Deallocate();
+}
+
+void LineLayoutCache::Allocate(int length_) {
+ allInvalidated = false;
+ length = length_;
+ size = length;
+ if (size > 1) {
+ size = (size / 16 + 1) * 16;
+ }
+ if (size > 0) {
+ cache = new LineLayout *[size];
+ }
+ for (int i=0; i<size; i++)
+ cache[i] = 0;
+}
+
+void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) {
+ int lengthForLevel = 0;
+ if (level == llcCaret) {
+ lengthForLevel = 1;
+ } else if (level == llcPage) {
+ lengthForLevel = linesOnScreen + 1;
+ } else if (level == llcDocument) {
+ lengthForLevel = linesInDoc;
+ }
+ if (lengthForLevel > size) {
+ Deallocate();
+ } else if (lengthForLevel != length) {
+ Invalidate(LineLayout::llInvalid);
+ }
+ if (!cache) {
+ Allocate(lengthForLevel);
+ }
+}
+
+void LineLayoutCache::Deallocate() {
+ for (int i=0; i<length; i++)
+ delete cache[i];
+ delete []cache;
+ cache = 0;
+ length = 0;
+}
+
+void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) {
+ if (cache && !allInvalidated) {
+ for (int i=0; i<length; i++) {
+ if (cache[i]) {
+ cache[i]->Invalidate(validity_);
+ }
+ }
+ if (validity_ == LineLayout::llInvalid) {
+ allInvalidated = true;
+ }
+ }
+}
+
+void LineLayoutCache::SetLevel(int level_) {
+ allInvalidated = false;
+ if ((level_ != -1) && (level != level_)) {
+ level = level_;
+ Deallocate();
+ }
+}
+
+LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,
+ int linesOnScreen, int linesInDoc) {
+ AllocateForLevel(linesOnScreen, linesInDoc);
+ if (styleClock != styleClock_) {
+ Invalidate(LineLayout::llInvalid);
+ styleClock = styleClock_;
+ }
+ allInvalidated = false;
+ int pos = -1;
+ LineLayout *ret = 0;
+ if (((level == llcCaret) || (level == llcPage)) && (lineNumber == lineCaret)) {
+ pos = 0;
+ } else if (level == llcPage) {
+ pos = lineNumber % length;
+ } else if (level == llcDocument) {
+ pos = lineNumber;
+ }
+ if (pos >= 0) {
+ if (cache && (pos < length)) {
+ if (cache[pos]) {
+ if ((cache[pos]->lineNumber != lineNumber) ||
+ (cache[pos]->maxLineLength < maxChars)) {
+ delete cache[pos];
+ cache[pos] = 0;
+ }
+ }
+ if (!cache[pos]) {
+ cache[pos] = new LineLayout(maxChars);
+ }
+ if (cache[pos]) {
+ cache[pos]->lineNumber = lineNumber;
+ cache[pos]->inCache = true;
+ ret = cache[pos];
+ }
+ }
+ }
+
+ if (!ret) {
+ ret = new LineLayout(maxChars);
+ ret->lineNumber = lineNumber;
+ }
+
+ return ret;
+}
+
+void LineLayoutCache::Dispose(LineLayout *ll) {
+ allInvalidated = false;
+ if (ll) {
+ if (!ll->inCache) {
+ delete ll;
+ }
+ }
+}
+
Editor::Editor() {
ctrlID = 0;
@@ -121,6 +303,8 @@ Editor::Editor() {
wrapState = eWrapNone;
wrapWidth = wrapWidthInfinite;
docLineLastWrapped = -1;
+
+ llc.SetLevel(LineLayoutCache::llcDocument);
}
Editor::~Editor() {
@@ -150,6 +334,7 @@ void Editor::InvalidateStyleData() {
stylesValid = false;
palette.Release();
DropGraphics();
+ llc.Invalidate(LineLayout::llInvalid);
}
void Editor::InvalidateStyleRedraw() {
@@ -239,28 +424,29 @@ Point Editor::LocationFromPosition(int pos) {
int lineVisible = cs.DisplayFromDoc(line);
//Platform::DebugPrintf("line=%d\n", line);
AutoSurface surface(IsUnicodeMode());
- if (surface) {
+ LineLayout *ll = 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);
- LineLayout ll;
LayoutLine(line, surface, vs, ll, wrapWidth);
int posInLine = pos - posLineStart;
- pt.x = 0;
// In case of very long line put x at arbitrary large position
- if (posInLine > LineLayout::maxLineLength) {
- pt.x = ll.positions[LineLayout::maxLineLength] - ll.positions[ll.lineStarts[ll.lines]];
+ if (posInLine > ll->maxLineLength) {
+ pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->lineStarts[ll->lines]];
}
- for (int subLine=0; subLine<ll.lines; subLine++) {
- if ((posInLine > ll.lineStarts[subLine]) && (posInLine <= ll.lineStarts[subLine+1])) {
- pt.x = ll.positions[posInLine] - ll.positions[ll.lineStarts[subLine]];
+ for (int subLine=0; subLine<ll->lines; subLine++) {
+ if ((posInLine > ll->lineStarts[subLine]) && (posInLine <= ll->lineStarts[subLine+1])) {
+ pt.x = ll->positions[posInLine] - ll->positions[ll->lineStarts[subLine]];
}
- if (posInLine >= ll.lineStarts[subLine]) {
+ if (posInLine >= ll->lineStarts[subLine]) {
pt.y += vs.lineHeight;
}
}
pt.x += vs.fixedColumnWidth - xOffset;
}
+ llc.Dispose(ll);
return pt;
}
@@ -291,28 +477,31 @@ int Editor::PositionFromLocation(Point pt) {
if (lineDoc >= pdoc->LinesTotal())
return pdoc->Length();
AutoSurface surface(IsUnicodeMode());
- if (surface) {
- unsigned int posLineStart = pdoc->LineStart(lineDoc);
-
- LineLayout ll;
+ int retVal = 0;
+ LineLayout *ll = 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.lineStarts[subLine];
- int lineEnd = ll.lineStarts[subLine+1];
- int subLineStart = ll.positions[lineStart];
+ if (subLine < ll->lines) {
+ int lineStart = ll->lineStarts[subLine];
+ int lineEnd = ll->lineStarts[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) ||
- ll.chars[i] == '\r' || ll.chars[i] == '\n') {
+ if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
+ ll->chars[i] == '\r' || ll->chars[i] == '\n') {
+ llc.Dispose(ll);
return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
}
}
+ llc.Dispose(ll);
return lineEnd + posLineStart;
}
- return ll.numCharsInLine + posLineStart;
+ retVal = ll->numCharsInLine + posLineStart;
}
- return 0;
+ llc.Dispose(ll);
+ return retVal;
}
// Like PositionFromLocation but INVALID_POSITION returned when not near any text.
@@ -336,25 +525,26 @@ int Editor::PositionFromLocationClose(Point pt) {
if (lineDoc >= pdoc->LinesTotal())
return INVALID_POSITION;
AutoSurface surface(IsUnicodeMode());
- if (surface) {
- unsigned int posLineStart = pdoc->LineStart(lineDoc);
-
- LineLayout ll;
+ LineLayout *ll = 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.lineStarts[subLine];
- int lineEnd = ll.lineStarts[subLine+1];
- int subLineStart = ll.positions[lineStart];
+ if (subLine < ll->lines) {
+ int lineStart = ll->lineStarts[subLine];
+ int lineEnd = ll->lineStarts[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) ||
- ll.chars[i] == '\r' || ll.chars[i] == '\n') {
+ if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
+ ll->chars[i] == '\r' || ll->chars[i] == '\n') {
+ llc.Dispose(ll);
return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
}
}
}
}
+ llc.Dispose(ll);
return INVALID_POSITION;
}
@@ -367,25 +557,26 @@ int Editor::PositionFromLineX(int lineDoc, int x) {
return pdoc->Length();
//Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
AutoSurface surface(IsUnicodeMode());
- if (surface) {
+ LineLayout *ll = RetrieveLineLayout(lineDoc);
+ int retVal = 0;
+ if (surface && ll) {
unsigned int posLineStart = pdoc->LineStart(lineDoc);
-
- LineLayout ll;
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
+ retVal = ll->numCharsInLine + posLineStart;
int subLine = 0;
- int lineStart = ll.lineStarts[subLine];
- int lineEnd = ll.lineStarts[subLine+1];
- int subLineStart = ll.positions[lineStart];
+ int lineStart = ll->lineStarts[subLine];
+ int lineEnd = ll->lineStarts[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) ||
- ll.chars[i] == '\r' || ll.chars[i] == '\n') {
- return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+ if (x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
+ ll->chars[i] == '\r' || ll->chars[i] == '\n') {
+ retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+ break;
}
}
-
- return ll.numCharsInLine + posLineStart;
}
- return 0;
+ llc.Dispose(ll);
+ return retVal;
}
void Editor::RedrawRect(PRectangle rc) {
@@ -651,18 +842,19 @@ int Editor::DisplayFromPosition(int pos) {
int lineDoc = pdoc->LineFromPosition(pos);
int lineDisplay = cs.DisplayFromDoc(lineDoc);
AutoSurface surface(IsUnicodeMode());
- if (surface) {
+ LineLayout *ll = 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.
- LineLayout ll;
- LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
- for (int subLine=0; subLine<ll.lines; subLine++) {
- if (posInLine >= ll.lineStarts[subLine]) {
+ for (int subLine=0; subLine<ll->lines; subLine++) {
+ if (posInLine >= ll->lineStarts[subLine]) {
lineDisplay++;
}
}
}
+ llc.Dispose(ll);
return lineDisplay;
}
@@ -772,6 +964,7 @@ void Editor::NeedWrapping(int docLineStartWrapping) {
docLineLastWrapped = docLineStartWrapping - 1;
if (docLineLastWrapped < -1)
docLineLastWrapped = -1;
+ llc.Invalidate(LineLayout::llPositions);
}
// Check if wrapping needed and perform any needed wrapping.
@@ -790,6 +983,7 @@ bool Editor::WrapLines() {
}
docLineLastWrapped = 0x7ffffff;
} else {
+ ElapsedTime et;
int lineDocTop = cs.DocFromDisplay(topLine);
int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
PRectangle rcTextArea = GetClientRectangle();
@@ -803,10 +997,16 @@ bool Editor::WrapLines() {
int lastLineToWrap = pdoc->LinesTotal();
while (docLineLastWrapped <= lastLineToWrap) {
docLineLastWrapped++;
- LineLayout ll;
- LayoutLine(docLineLastWrapped, surface, vs, ll, wrapWidth);
- if (cs.SetHeight(docLineLastWrapped, ll.lines))
+ LineLayout *ll = RetrieveLineLayout(docLineLastWrapped);
+ int linesWrapped = 1;
+ if (ll) {
+ LayoutLine(docLineLastWrapped, surface, vs, ll, wrapWidth);
+ linesWrapped = ll->lines;
+ }
+ llc.Dispose(ll);
+ if (cs.SetHeight(docLineLastWrapped, linesWrapped)) {
wrapOccurred = true;
+ }
}
}
goodTopLine = cs.DisplayFromDoc(lineDocTop);
@@ -814,6 +1014,8 @@ bool Editor::WrapLines() {
goodTopLine += subLineTop;
else
goodTopLine += cs.GetHeight(lineDocTop);
+ double durWrap = et.Duration(true);
+ Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
}
}
if (wrapOccurred) {
@@ -1038,168 +1240,185 @@ static bool IsSpaceOrTab(char ch) {
return ch == ' ' || ch == '\t';
}
+LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
+ int posLineStart = pdoc->LineStart(lineNumber);
+ int posLineEnd = pdoc->LineStart(lineNumber + 1);
+ int lineCaret = pdoc->LineFromPosition(currentPos);
+ return llc.Retrieve(lineNumber, lineCaret,
+ posLineEnd - posLineStart, pdoc->GetStyleClock(),
+ LinesOnScreen() + 1, pdoc->LinesTotal());
+}
+
/**
* Fill in the LineLayout data for the given line.
* Copy the given @a line and its styles from the document into local arrays.
* Also determine the x position at which each character starts.
*/
-void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll, int width) {
- ll.widthLine = width;
- ll.lines = 1;
- ll.lineStarts[0] = 0;
- int numCharsInLine = 0;
+void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
+ if (!ll)
+ return;
int posLineStart = pdoc->LineStart(line);
- if (vstyle.edgeState == EDGE_BACKGROUND) {
- ll.edgeColumn = pdoc->FindColumn(line, theEdge);
- if (ll.edgeColumn >= posLineStart) {
- ll.edgeColumn -= posLineStart;
+ if (ll->validity == LineLayout::llInvalid) {
+ ll->widthLine = width;
+ ll->lines = 1;
+ ll->lineStarts[0] = 0;
+ int numCharsInLine = 0;
+ if (vstyle.edgeState == EDGE_BACKGROUND) {
+ ll->edgeColumn = pdoc->FindColumn(line, theEdge);
+ if (ll->edgeColumn >= posLineStart) {
+ ll->edgeColumn -= posLineStart;
+ }
+ } else {
+ ll->edgeColumn = -1;
+ }
+
+ int posLineEnd = pdoc->LineStart(line + 1);
+ Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
+ char styleByte = 0;
+ int styleMask = pdoc->stylingBitsMask;
+ ll->xHighlightGuide = 0;
+ // If the line is very long, limit the treatment to a length that should fit in the viewport
+ if (posLineEnd > (posLineStart + ll->maxLineLength)) {
+ posLineEnd = posLineStart + ll->maxLineLength;
+ }
+ // Fill base line layout
+ for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
+ char chDoc = pdoc->CharAt(charInDoc);
+ styleByte = pdoc->StyleAt(charInDoc);
+ if (vstyle.viewEOL || ((chDoc != '\r') && (chDoc != '\n'))) {
+ ll->chars[numCharsInLine] = chDoc;
+ ll->styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
+ ll->indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
+ if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
+ ll->chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
+ else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
+ ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
+ numCharsInLine++;
+ }
}
- } else {
- ll.edgeColumn = -1;
- }
+ // Extra element at the end of the line to hold end x position and act as
+ ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
+ ll->styles[numCharsInLine] = styleByte; // For eolFilled
+ ll->indicators[numCharsInLine] = 0;
- int posLineEnd = pdoc->LineStart(line + 1);
- Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
- char styleByte = 0;
- int styleMask = pdoc->stylingBitsMask;
- ll.xHighlightGuide = 0;
- // If the line is very long, limit the treatment to a length that should fit in the viewport
- if (posLineEnd > (posLineStart + LineLayout::maxLineLength)) {
- posLineEnd = posLineStart + LineLayout::maxLineLength;
- }
- // Fill base line layout
- for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
- char chDoc = pdoc->CharAt(charInDoc);
- styleByte = pdoc->StyleAt(charInDoc);
- if (vstyle.viewEOL || ((chDoc != '\r') && (chDoc != '\n'))) {
- ll.chars[numCharsInLine] = chDoc;
- ll.styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
- ll.indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
- if (vstyle.styles[ll.styles[numCharsInLine]].caseForce == Style::caseUpper)
- ll.chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
- else if (vstyle.styles[ll.styles[numCharsInLine]].caseForce == Style::caseLower)
- ll.chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
- numCharsInLine++;
- }
- }
- // Extra element at the end of the line to hold end x position and act as
- ll.chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
- ll.styles[numCharsInLine] = styleByte; // For eolFilled
- ll.indicators[numCharsInLine] = 0;
-
- // Layout the line, determining the position of each character,
- // with an extra element at the end for the end of the line.
- int startseg = 0; // Start of the current segment, in char. number
- int startsegx = 0; // Start of the current segment, in pixels
- ll.positions[0] = 0;
- unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
- bool lastSegItalics = false;
-
- for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
- if ((ll.styles[charInLine] != ll.styles[charInLine + 1]) ||
- IsControlCharacter(ll.chars[charInLine]) || IsControlCharacter(ll.chars[charInLine + 1])) {
- ll.positions[startseg] = 0;
- if (vstyle.styles[ll.styles[charInLine]].visible) {
- if (IsControlCharacter(ll.chars[charInLine])) {
- if (ll.chars[charInLine] == '\t') {
- ll.positions[charInLine + 1] = ((((startsegx + 2) /
- tabWidth) + 1) * tabWidth) - startsegx;
- } else if (controlCharSymbol < 32) {
- const char *ctrlChar = ControlCharacterString(ll.chars[charInLine]);
- // +3 For a blank on front and rounded edge each side:
- ll.positions[charInLine + 1] = surface->WidthText(ctrlCharsFont, ctrlChar, strlen(ctrlChar)) + 3;
- } else {
- char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
- surface->MeasureWidths(ctrlCharsFont, cc, 1,
- ll.positions + startseg + 1);
+ // Layout the line, determining the position of each character,
+ // with an extra element at the end for the end of the line.
+ int startseg = 0; // Start of the current segment, in char. number
+ int startsegx = 0; // Start of the current segment, in pixels
+ ll->positions[0] = 0;
+ unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
+ bool lastSegItalics = false;
+
+ for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
+ if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
+ IsControlCharacter(ll->chars[charInLine]) || IsControlCharacter(ll->chars[charInLine + 1])) {
+ ll->positions[startseg] = 0;
+ if (vstyle.styles[ll->styles[charInLine]].visible) {
+ if (IsControlCharacter(ll->chars[charInLine])) {
+ if (ll->chars[charInLine] == '\t') {
+ ll->positions[charInLine + 1] = ((((startsegx + 2) /
+ tabWidth) + 1) * tabWidth) - startsegx;
+ } else if (controlCharSymbol < 32) {
+ const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
+ // +3 For a blank on front and rounded edge each side:
+ ll->positions[charInLine + 1] = surface->WidthText(ctrlCharsFont, ctrlChar, strlen(ctrlChar)) + 3;
+ } else {
+ char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
+ surface->MeasureWidths(ctrlCharsFont, cc, 1,
+ ll->positions + startseg + 1);
+ }
+ lastSegItalics = false;
+ } else { // Regular character
+ lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
+ int lenSeg = charInLine - startseg + 1;
+ if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
+ // Over half the segments are single characters and of these about half are space characters.
+ ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
+ } else {
+ surface->MeasureWidths(vstyle.styles[ll->styles[charInLine]].font, ll->chars + startseg,
+ lenSeg, ll->positions + startseg + 1);
+ }
}
- lastSegItalics = false;
- } else { // Regular character
- lastSegItalics = vstyle.styles[ll.styles[charInLine]].italic;
- int lenSeg = charInLine - startseg + 1;
- if ((lenSeg == 1) && (' ' == ll.chars[startseg])) {
- // Over half the segments are single characters and of these about half are space characters.
- ll.positions[charInLine + 1] = vstyle.styles[ll.styles[charInLine]].spaceWidth;
- } else {
- surface->MeasureWidths(vstyle.styles[ll.styles[charInLine]].font, ll.chars + startseg,
- lenSeg, ll.positions + startseg + 1);
+ } else { // invisible
+ for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
+ ll->positions[posToZero] = 0;
}
}
- } else { // invisible
- for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
- ll.positions[posToZero] = 0;
+ for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
+ ll->positions[posToIncrease] += startsegx;
}
+ startsegx = ll->positions[charInLine + 1];
+ startseg = charInLine + 1;
}
- for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
- ll.positions[posToIncrease] += startsegx;
- }
- startsegx = ll.positions[charInLine + 1];
- startseg = charInLine + 1;
}
+ // Small hack to make lines that end with italics not cut off the edge of the last character
+ if ((startseg > 0) && lastSegItalics) {
+ ll->positions[startseg] += 2;
+ }
+ ll->numCharsInLine = numCharsInLine;
+ ll->validity = LineLayout::llPositions;
}
- if (width == wrapWidthInfinite) {
- ll.lines = 1;
- ll.lineStarts[1] = numCharsInLine;
- ll.widthLine = ll.positions[numCharsInLine];
- } else {
- if (width < 20) { // Hard to cope when too narrow, so just assume there is space
- width = 20;
- ll.widthLine = width;
- }
- ll.lines = 0;
- // Calculate line start positions based upon width.
- // For now this is simplistic - wraps on byte rather than character and
- // in the middle of words. Should search for spaces or style changes.
- int lastGoodBreak = 0;
- int lastLineStart = 0;
- int startOffset = 0;
- int p=0;
- while (p < numCharsInLine) {
- if ((ll.positions[p+1] - startOffset) >= width) {
- if (lastGoodBreak == lastLineStart) {
- // Try moving to start of last character
- if (p > 0) {
- lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
- - posLineStart;
- }
+ if (ll->validity == LineLayout::llPositions) {
+ if (width == wrapWidthInfinite) {
+ ll->lines = 1;
+ ll->lineStarts[1] = ll->numCharsInLine;
+ ll->widthLine = ll->positions[ll->numCharsInLine];
+ } else {
+ if (width < 20) { // Hard to cope when too narrow, so just assume there is space
+ width = 20;
+ ll->widthLine = width;
+ }
+ ll->lines = 0;
+ // Calculate line start positions based upon width.
+ // For now this is simplistic - wraps on byte rather than character and
+ // in the middle of words. Should search for spaces or style changes.
+ int lastGoodBreak = 0;
+ int lastLineStart = 0;
+ int startOffset = 0;
+ int p=0;
+ while (p < ll->numCharsInLine) {
+ if ((ll->positions[p+1] - startOffset) >= width) {
if (lastGoodBreak == lastLineStart) {
- // Ensure at least one character on line.
- lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart +1, 1)
- - posLineStart;
+ // Try moving to start of last character
+ if (p > 0) {
+ lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
+ - posLineStart;
+ }
+ if (lastGoodBreak == lastLineStart) {
+ // Ensure at least one character on line.
+ lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart +1, 1)
+ - posLineStart;
+ }
}
+ lastLineStart = lastGoodBreak;
+ if (ll->lines < ll->maxDisplayLines - 2) {
+ ll->lines++;
+ ll->lineStarts[ll->lines] = lastGoodBreak;
+ } else {
+ break;
+ }
+ startOffset = ll->positions[lastGoodBreak];
+ p = lastGoodBreak + 1;
+ continue;
}
- lastLineStart = lastGoodBreak;
- if (ll.lines < LineLayout::maxDisplayLines - 2) {
- ll.lines++;
- ll.lineStarts[ll.lines] = lastGoodBreak;
- } else {
- break;
- }
- startOffset = ll.positions[lastGoodBreak];
- p = lastGoodBreak + 1;
- continue;
- }
- if (p > 0) {
- if (ll.styles[p] != ll.styles[p-1]) {
- lastGoodBreak = p;
- } else if (IsSpaceOrTab(ll.chars[p-1]) && !IsSpaceOrTab(ll.chars[p])) {
- lastGoodBreak = p;
+ if (p > 0) {
+ if (ll->styles[p] != ll->styles[p-1]) {
+ lastGoodBreak = p;
+ } else if (IsSpaceOrTab(ll->chars[p-1]) && !IsSpaceOrTab(ll->chars[p])) {
+ lastGoodBreak = p;
+ }
}
+ p++;
}
- p++;
+ ll->lines++;
+ ll->lineStarts[ll->lines] = ll->numCharsInLine;
}
- ll.lines++;
- ll.lineStarts[ll.lines] = numCharsInLine;
- }
- // Small hack to make lines that end with italics not cut off the edge of the last character
- if ((startseg > 0) && lastSegItalics) {
- ll.positions[startseg] += 2;
+ ll->validity = LineLayout::llLines;
}
- ll.numCharsInLine = numCharsInLine;
}
void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
- PRectangle rcLine, LineLayout &ll, int subLine) {
+ PRectangle rcLine, LineLayout *ll, int subLine) {
PRectangle rcSegment = rcLine;
@@ -1215,7 +1434,7 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
// multiple markers cause background override, the color for the highest numbered one is used.
bool overrideBackground = false;
ColourAllocated background;
- if (caret.active && vsDraw.showCaretLineBackground && ll.containsCaret) {
+ if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
overrideBackground = true;
background = vsDraw.caretLineBackground.allocated;
}
@@ -1253,34 +1472,34 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
int posLineEnd = pdoc->LineStart(line + 1);
int styleMask = pdoc->stylingBitsMask;
- int startseg = ll.lineStarts[subLine];
- int subLineStart = ll.positions[startseg];
+ int startseg = ll->lineStarts[subLine];
+ int subLineStart = ll->positions[startseg];
int lineStart = 0;
int lineEnd = 0;
- if (subLine < ll.lines) {
- lineStart = ll.lineStarts[subLine];
- lineEnd = ll.lineStarts[subLine+1];
+ if (subLine < ll->lines) {
+ lineStart = ll->lineStarts[subLine];
+ lineEnd = ll->lineStarts[subLine+1];
}
for (int i = lineStart; i < lineEnd; i++) {
int iDoc = i + posLineStart;
// If there is the end of a style run for any reason
- if ((ll.styles[i] != ll.styles[i + 1]) ||
+ if ((ll->styles[i] != ll->styles[i + 1]) ||
i == (lineEnd-1) ||
- IsControlCharacter(ll.chars[i]) || IsControlCharacter(ll.chars[i + 1]) ||
- ((ll.selStart != ll.selEnd) && ((iDoc + 1 == ll.selStart) || (iDoc + 1 == ll.selEnd))) ||
- (i == (ll.edgeColumn - 1))) {
- rcSegment.left = ll.positions[startseg] + xStart - subLineStart;
- rcSegment.right = ll.positions[i + 1] + xStart - subLineStart;
+ IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) ||
+ ((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) ||
+ (i == (ll->edgeColumn - 1))) {
+ rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
+ rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
// Only try to draw if really visible - enhances performance by not calling environment to
// draw strings that are completely past the right side of the window.
//if (rcSegment.left <= rcLine.right) {
if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
- int styleMain = ll.styles[i];
+ int styleMain = ll->styles[i];
ColourAllocated textBack = vsDraw.styles[styleMain].back.allocated;
ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
Font &textFont = vsDraw.styles[styleMain].font;
- bool inSelection = (iDoc >= ll.selStart) && (iDoc < ll.selEnd) && (ll.selStart != ll.selEnd);
+ bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
if (inSelection) {
if (vsDraw.selbackset) {
if (primarySelection)
@@ -1293,21 +1512,21 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
} else {
if (overrideBackground)
textBack = background;
- if ((vsDraw.edgeState == EDGE_BACKGROUND) && (i >= ll.edgeColumn) && (ll.chars[i] != '\n') && (ll.chars[i] != '\r'))
+ if ((vsDraw.edgeState == EDGE_BACKGROUND) && (i >= ll->edgeColumn) && (ll->chars[i] != '\n') && (ll->chars[i] != '\r'))
textBack = vsDraw.edgecolour.allocated;
}
- if (ll.chars[i] == '\t') {
+ if (ll->chars[i] == '\t') {
// Manage tab display
surface->FillRectangle(rcSegment, textBack);
if ((vsDraw.viewWhitespace != wsInvisible) || ((inIndentation && vsDraw.viewIndentationGuides))) {
surface->PenColour(textFore);
}
if (inIndentation && vsDraw.viewIndentationGuides) {
- for (int xIG = ll.positions[i] / indentWidth * indentWidth; xIG < ll.positions[i + 1]; xIG += indentWidth) {
- if (xIG >= ll.positions[i] && xIG > 0) {
+ for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
+ if (xIG >= ll->positions[i] && xIG > 0) {
Point from(0, ((lineVisible & 1) && (vsDraw.lineHeight & 1)) ? 1 : 0);
PRectangle rcCopyArea(xIG + xStart + 1, rcSegment.top, xIG + xStart + 2, rcSegment.bottom);
- surface->Copy(rcCopyArea, from, (ll.xHighlightGuide == xIG) ?
+ surface->Copy(rcCopyArea, from, (ll->xHighlightGuide == xIG) ?
*pixmapIndentGuideHighlight : *pixmapIndentGuide);
}
}
@@ -1319,12 +1538,12 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
}
}
- } else if (IsControlCharacter(ll.chars[i])) {
+ } else if (IsControlCharacter(ll->chars[i])) {
// Manage control character display
inIndentation = false;
if (controlCharSymbol < 32) {
// Draw the character
- const char *ctrlChar = ControlCharacterString(ll.chars[i]);
+ const char *ctrlChar = ControlCharacterString(ll->chars[i]);
surface->FillRectangle(rcSegment, textBack);
int normalCharHeight = surface->Ascent(ctrlCharsFont) -
surface->InternalLeading(ctrlCharsFont);
@@ -1351,15 +1570,15 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
} else {
// Manage normal display
surface->DrawTextNoClip(rcSegment, textFont,
- rcSegment.top + vsDraw.maxAscent, ll.chars + startseg,
+ rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
i - startseg + 1, textFore, textBack);
if (vsDraw.viewWhitespace != wsInvisible ||
(inIndentation && vsDraw.viewIndentationGuides)) {
for (int cpos = 0; cpos <= i - startseg; cpos++) {
- if (ll.chars[cpos + startseg] == ' ') {
+ if (ll->chars[cpos + startseg] == ' ') {
if (vsDraw.viewWhitespace != wsInvisible) {
if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
- int xmid = (ll.positions[cpos + startseg] + ll.positions[cpos + startseg + 1]) / 2;
+ int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
rcDot.right = rcDot.left + 1;
rcDot.bottom = rcDot.top + 1;
@@ -1367,11 +1586,11 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
}
}
if (inIndentation && vsDraw.viewIndentationGuides) {
- int startSpace = ll.positions[cpos + startseg];
+ int startSpace = ll->positions[cpos + startseg];
if (startSpace > 0 && (startSpace % indentWidth == 0)) {
Point from(0, ((lineVisible & 1) && (vsDraw.lineHeight & 1)) ? 1 : 0);
PRectangle rcCopyArea(startSpace + xStart + 1, rcSegment.top, startSpace + xStart + 2, rcSegment.bottom);
- surface->Copy(rcCopyArea, from, (ll.xHighlightGuide == ll.positions[cpos + startseg]) ?
+ surface->Copy(rcCopyArea, from, (ll->xHighlightGuide == ll->positions[cpos + startseg]) ?
*pixmapIndentGuideHighlight : *pixmapIndentGuide);
}
}
@@ -1397,18 +1616,18 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
for (int indica = 0; indica <= INDIC_MAX; indica++)
indStart[indica] = 0;
- for (int indicPos = 0; indicPos < ll.numCharsInLine; indicPos++) {
- if (ll.indicators[indicPos] != ll.indicators[indicPos + 1]) {
+ for (int indicPos = 0; indicPos < ll->numCharsInLine; indicPos++) {
+ if (ll->indicators[indicPos] != ll->indicators[indicPos + 1]) {
int mask = 1 << pdoc->stylingBits;
for (int indicnum = 0; mask < 0x100; indicnum++) {
- if ((ll.indicators[indicPos + 1] & mask) && !(ll.indicators[indicPos] & mask)) {
- indStart[indicnum] = ll.positions[indicPos + 1];
+ if ((ll->indicators[indicPos + 1] & mask) && !(ll->indicators[indicPos] & mask)) {
+ indStart[indicnum] = ll->positions[indicPos + 1];
}
- if (!(ll.indicators[indicPos + 1] & mask) && (ll.indicators[indicPos] & mask)) {
+ if (!(ll->indicators[indicPos + 1] & mask) && (ll->indicators[indicPos] & mask)) {
PRectangle rcIndic(
indStart[indicnum] + xStart,
rcLine.top + vsDraw.maxAscent,
- ll.positions[indicPos + 1] + xStart,
+ ll->positions[indicPos + 1] + xStart,
rcLine.top + vsDraw.maxAscent + 3);
vsDraw.indicators[indicnum].Draw(surface, rcIndic);
}
@@ -1419,11 +1638,11 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
// End of the drawing of the current line
// Fill in a PRectangle representing the end of line characters
- int xEol = ll.positions[lineEnd] - subLineStart;
+ int xEol = ll->positions[lineEnd] - subLineStart;
rcSegment.left = xEol + xStart;
rcSegment.right = xEol + vsDraw.aveCharWidth + xStart;
- bool eolInSelection = (subLine == (ll.lines-1)) &&
- (posLineEnd > ll.selStart) && (posLineEnd <= ll.selEnd) && (ll.selStart != ll.selEnd);
+ bool eolInSelection = (subLine == (ll->lines-1)) &&
+ (posLineEnd > ll->selStart) && (posLineEnd <= ll->selEnd) && (ll->selStart != ll->selEnd);
if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
if (primarySelection)
surface->FillRectangle(rcSegment, vsDraw.selbackground.allocated);
@@ -1432,15 +1651,15 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis
} else if (overrideBackground) {
surface->FillRectangle(rcSegment, background);
} else {
- surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated);
+ surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
}
rcSegment.left = xEol + vsDraw.aveCharWidth + xStart;
rcSegment.right = rcLine.right;
if (overrideBackground) {
surface->FillRectangle(rcSegment, background);
- } else if (vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].eolFilled) {
- surface->FillRectangle(rcSegment, vsDraw.styles[ll.styles[ll.numCharsInLine] & styleMask].back.allocated);
+ } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
+ surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
} else {
surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
}
@@ -1592,8 +1811,9 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
//double durLayout = 0.0;
//double durPaint = 0.0;
//double durCopy = 0.0;
+ //ElapsedTime etWhole;
int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
- LineLayout ll;
+ LineLayout *ll = 0;
while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
int lineDoc = cs.DocFromDisplay(visibleLine);
@@ -1607,111 +1827,115 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
// and determine the x position at which each character starts.
//ElapsedTime et;
if (lineDoc != lineDocPrevious) {
+ llc.Dispose(ll);
+ ll = RetrieveLineLayout(lineDoc);
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
lineDocPrevious = lineDoc;
}
//durLayout += et.Duration(true);
- ll.selStart = SelectionStart(lineDoc);
- ll.selEnd = SelectionEnd(lineDoc);
- ll.containsCaret = lineDoc == lineCaret;
- if (hideSelection) {
- ll.selStart = -1;
- ll.selEnd = -1;
- ll.containsCaret = false;
- }
-
- int posLineStart = pdoc->LineStart(lineDoc);
- int posLineEnd = pdoc->LineStart(lineDoc + 1);
- //Platform::DebugPrintf("line %d %d - %d\n", line, posLineStart, posLineEnd);
-
- PRectangle rcLine = rcClient;
- rcLine.top = ypos;
- rcLine.bottom = ypos + vs.lineHeight;
-
- // Highlight the current braces if any
- if ((braces[0] >= posLineStart) && (braces[0] < posLineEnd)) {
- int braceOffset = braces[0] - posLineStart;
- if (braceOffset < ll.numCharsInLine)
- ll.styles[braceOffset] = static_cast<char>(bracesMatchStyle);
- }
- if ((braces[1] >= posLineStart) && (braces[1] < posLineEnd)) {
- int braceOffset = braces[1] - posLineStart;
- if (braceOffset < ll.numCharsInLine)
- ll.styles[braceOffset] = static_cast<char>(bracesMatchStyle);
- }
- if ((braces[0] >= posLineStart && braces[1] <= posLineEnd) ||
- (braces[1] >= posLineStart && braces[0] <= posLineEnd)) {
- ll.xHighlightGuide = highlightGuideColumn * vs.spaceWidth;
- }
-
- // Draw the line
- DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
- //durPaint += et.Duration(true);
-
- bool expanded = cs.GetExpanded(lineDoc);
- if ( (expanded && (foldFlags & 2)) || (!expanded && (foldFlags & 4)) ) {
- if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
- PRectangle rcFoldLine = rcLine;
- rcFoldLine.bottom = rcFoldLine.top + 1;
- surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+ if (ll) {
+ ll->selStart = SelectionStart(lineDoc);
+ ll->selEnd = SelectionEnd(lineDoc);
+ ll->containsCaret = lineDoc == lineCaret;
+ if (hideSelection) {
+ ll->selStart = -1;
+ ll->selEnd = -1;
+ ll->containsCaret = false;
}
- }
- if ( (expanded && (foldFlags & 8)) || (!expanded && (foldFlags & 16)) ) {
- if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
- PRectangle rcFoldLine = rcLine;
- rcFoldLine.top = rcFoldLine.bottom - 1;
- surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+
+ int posLineStart = pdoc->LineStart(lineDoc);
+ int posLineEnd = pdoc->LineStart(lineDoc + 1);
+ //Platform::DebugPrintf("line %d %d - %d\n", visibleLine, posLineStart, posLineEnd);
+
+ PRectangle rcLine = rcClient;
+ rcLine.top = ypos;
+ rcLine.bottom = ypos + vs.lineHeight;
+
+ // Highlight the current braces if any
+ if ((braces[0] >= posLineStart) && (braces[0] < posLineEnd)) {
+ int braceOffset = braces[0] - posLineStart;
+ if (braceOffset < ll->numCharsInLine)
+ ll->styles[braceOffset] = static_cast<char>(bracesMatchStyle);
}
- }
-
- // Draw the Caret
- if (lineDoc == lineCaret) {
- int offset = Platform::Minimum(posCaret - posLineStart, LineLayout::maxLineLength);
- if ((offset >= ll.lineStarts[subLine]) &&
- ((offset < ll.lineStarts[subLine+1]) || offset == ll.numCharsInLine)) {
- int xposCaret = ll.positions[offset] - ll.positions[ll.lineStarts[subLine]] + xStart;
- int widthOverstrikeCaret;
- if (posCaret == pdoc->Length()) { // At end of document
- widthOverstrikeCaret = vs.aveCharWidth;
- } else if ((posCaret - posLineStart) >= ll.numCharsInLine) { // At end of line
- widthOverstrikeCaret = vs.aveCharWidth;
- } else {
- widthOverstrikeCaret = ll.positions[offset + 1] - ll.positions[offset];
+ if ((braces[1] >= posLineStart) && (braces[1] < posLineEnd)) {
+ int braceOffset = braces[1] - posLineStart;
+ if (braceOffset < ll->numCharsInLine)
+ ll->styles[braceOffset] = static_cast<char>(bracesMatchStyle);
+ }
+ if ((braces[0] >= posLineStart && braces[1] <= posLineEnd) ||
+ (braces[1] >= posLineStart && braces[0] <= posLineEnd)) {
+ ll->xHighlightGuide = highlightGuideColumn * vs.spaceWidth;
+ }
+
+ // Draw the line
+ DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
+ //durPaint += et.Duration(true);
+
+ bool expanded = cs.GetExpanded(lineDoc);
+ if ( (expanded && (foldFlags & 2)) || (!expanded && (foldFlags & 4)) ) {
+ if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
+ PRectangle rcFoldLine = rcLine;
+ rcFoldLine.bottom = rcFoldLine.top + 1;
+ surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
}
- if (widthOverstrikeCaret < 3) // Make sure its visible
- widthOverstrikeCaret = 3;
- if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) {
- PRectangle rcCaret = rcLine;
- int caretWidthOffset = 0;
- if ((offset > 0) && (vs.caretWidth > 1))
- caretWidthOffset = 1; // Move back so overlaps both character cells.
- if (posDrag >= 0) {
- rcCaret.left = xposCaret - caretWidthOffset;
- rcCaret.right = rcCaret.left + vs.caretWidth;
+ }
+ if ( (expanded && (foldFlags & 8)) || (!expanded && (foldFlags & 16)) ) {
+ if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
+ PRectangle rcFoldLine = rcLine;
+ rcFoldLine.top = rcFoldLine.bottom - 1;
+ surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+ }
+ }
+
+ // Draw the Caret
+ if (lineDoc == lineCaret) {
+ int offset = Platform::Minimum(posCaret - posLineStart, ll->maxLineLength);
+ if ((offset >= ll->lineStarts[subLine]) &&
+ ((offset < ll->lineStarts[subLine+1]) || offset == ll->numCharsInLine)) {
+ int xposCaret = ll->positions[offset] - ll->positions[ll->lineStarts[subLine]] + xStart;
+ int widthOverstrikeCaret;
+ if (posCaret == pdoc->Length()) { // At end of document
+ widthOverstrikeCaret = vs.aveCharWidth;
+ } else if ((posCaret - posLineStart) >= ll->numCharsInLine) { // At end of line
+ widthOverstrikeCaret = vs.aveCharWidth;
} else {
- if (inOverstrike) {
- rcCaret.top = rcCaret.bottom - 2;
- rcCaret.left = xposCaret + 1;
- rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
- } else {
+ widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
+ }
+ if (widthOverstrikeCaret < 3) // Make sure its visible
+ widthOverstrikeCaret = 3;
+ if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) {
+ PRectangle rcCaret = rcLine;
+ int caretWidthOffset = 0;
+ if ((offset > 0) && (vs.caretWidth > 1))
+ caretWidthOffset = 1; // Move back so overlaps both character cells.
+ if (posDrag >= 0) {
rcCaret.left = xposCaret - caretWidthOffset;
rcCaret.right = rcCaret.left + vs.caretWidth;
+ } else {
+ if (inOverstrike) {
+ rcCaret.top = rcCaret.bottom - 2;
+ rcCaret.left = xposCaret + 1;
+ rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
+ } else {
+ rcCaret.left = xposCaret - caretWidthOffset;
+ rcCaret.right = rcCaret.left + vs.caretWidth;
+ }
}
+ surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
}
- surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
}
}
+
+ if (bufferedDraw) {
+ Point from(vs.fixedColumnWidth, 0);
+ PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
+ rcClient.right, yposScreen + vs.lineHeight);
+ surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
+ }
+ //durCopy += et.Duration(true);
}
-
- if (bufferedDraw) {
- Point from(vs.fixedColumnWidth, 0);
- PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
- rcClient.right, yposScreen + vs.lineHeight);
- surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
- }
- //durCopy += et.Duration(true);
-
+
if (!bufferedDraw) {
ypos += vs.lineHeight;
}
@@ -1720,7 +1944,9 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
visibleLine++;
//gdk_flush();
}
- //Platform::DebugPrintf("Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g\n", durLayout, durPaint, durLayout / durPaint, durCopy);
+ llc.Dispose(ll);
+ //if (durPaint < 0.00000001)
+ // durPaint = 0.00000001;
// Right column limit indicator
PRectangle rcBeyondEOF = rcClient;
@@ -1736,6 +1962,9 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
}
}
+ //Platform::DebugPrintf(
+ //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
+ //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
NotifyPainted();
}
}
@@ -1858,7 +2087,7 @@ long Editor::FormatRange(bool draw, RangeToFormat *pfr) {
// Copy this line and its styles from the document into local arrays
// and determine the x position at which each character starts.
LineLayout ll;
- LayoutLine(line, surfaceMeasure, vsPrint, ll);
+ LayoutLine(line, surfaceMeasure, vsPrint, &ll);
ll.selStart = -1;
ll.selEnd = -1;
ll.containsCaret = false;
@@ -1885,7 +2114,7 @@ long Editor::FormatRange(bool draw, RangeToFormat *pfr) {
// Draw the line
surface->FlushCachedState();
- DrawLine(surface, vsPrint, line, line, xStart, rcLine, ll);
+ DrawLine(surface, vsPrint, line, line, xStart, rcLine, &ll);
ypos += vsPrint.lineHeight;
line++;
@@ -2276,20 +2505,22 @@ void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
void Editor::CheckModificationForWrap(DocModification mh) {
if ((mh.modificationType & SC_MOD_INSERTTEXT) ||
(mh.modificationType & SC_MOD_DELETETEXT)) {
+ llc.Invalidate(LineLayout::llInvalid);
if (wrapState != eWrapNone) {
int lineDoc = pdoc->LineFromPosition(mh.position);
if (mh.linesAdded == 0) {
AutoSurface surface(IsUnicodeMode());
- if (surface) {
- LineLayout ll;
+ LineLayout *ll = RetrieveLineLayout(lineDoc);
+ if (surface && ll) {
LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
- if (cs.GetHeight(lineDoc) != ll.lines) {
+ if (cs.GetHeight(lineDoc) != ll->lines) {
NeedWrapping(lineDoc-1);
Redraw();
}
} else {
NeedWrapping(lineDoc);
}
+ llc.Dispose(ll);
} else {
NeedWrapping(lineDoc);
}
@@ -3729,7 +3960,7 @@ void Editor::ToggleContraction(int line) {
}
// Recurse up from this line to find any folds that prevent this line from being visible
-// and unfold them all.
+// and unfold them all->
void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
// In case in need of wrapping to ensure DisplayFromDoc works.
@@ -4382,6 +4613,13 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
case SCI_GETWRAPMODE:
return wrapState;
+ case SCI_SETLAYOUTCACHE:
+ llc.SetLevel(wParam);
+ break;
+
+ case SCI_GETLAYOUTCACHE:
+ return llc.GetLevel();
+
case SCI_GETCOLUMN:
return pdoc->GetColumn(wParam);
diff --git a/src/Editor.h b/src/Editor.h
index ec66629f6..a7047ef14 100644
--- a/src/Editor.h
+++ b/src/Editor.h
@@ -35,8 +35,11 @@ public:
*/
class LineLayout {
public:
+ enum validLevel { llInvalid, llPositions, llLines } validity;
/// Drawing is only performed for @a maxLineLength characters on each line.
- enum {maxLineLength = 8000};
+ int maxLineLength;
+ int lineNumber;
+ bool inCache;
int numCharsInLine;
int xHighlightGuide;
bool highlightColumn;
@@ -44,18 +47,51 @@ public:
int selEnd;
bool containsCaret;
int edgeColumn;
- char chars[maxLineLength+1];
- char styles[maxLineLength+1];
- char indicators[maxLineLength+1];
- int positions[maxLineLength+1];
+ char *chars;
+ char *styles;
+ char *indicators;
+ int *positions;
// Wrapped line support
int widthLine;
int lines;
- enum {maxDisplayLines = 400};
- int lineStarts[maxDisplayLines];
+ int maxDisplayLines;
+ int *lineStarts;
+
+ LineLayout(int maxLineLength_ = 8000, int maxDisplayLines=100);
+ virtual ~LineLayout();
+ void Resize(int maxLineLength_);
+ void Free();
+ void Invalidate(validLevel validity_);
+};
- LineLayout() : numCharsInLine(0) {}
+/**
+ */
+class LineLayoutCache {
+ int level;
+ int length;
+ int size;
+ LineLayout **cache;
+ bool allInvalidated;
+ int styleClock;
+ void Allocate(int length_);
+ void AllocateForLevel(int linesOnScreen, int linesInDoc);
+ void Deallocate();
+public:
+ LineLayoutCache();
+ virtual ~LineLayoutCache();
+ enum {
+ llcNone=SC_CACHE_NONE,
+ llcCaret=SC_CACHE_CARET,
+ llcPage=SC_CACHE_PAGE,
+ llcDocument=SC_CACHE_DOCUMENT
+ };
+ void Invalidate(LineLayout::validLevel validity_);
+ void SetLevel(int level_);
+ int GetLevel() { return level; }
+ LineLayout *Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,
+ int linesOnScreen, int linesInDoc);
+ void Dispose(LineLayout *ll);
};
class SelectionText {
@@ -154,6 +190,8 @@ protected: // ScintillaBase subclass needs access to much of Editor
Surface *pixmapIndentGuide;
Surface *pixmapIndentGuideHighlight;
+ LineLayoutCache llc;
+
KeyMap kmap;
Caret caret;
@@ -281,10 +319,11 @@ protected: // ScintillaBase subclass needs access to much of Editor
int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault);
void PaintSelMargin(Surface *surface, PRectangle &rc);
- void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll,
+ LineLayout *RetrieveLineLayout(int lineNumber);
+ void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll,
int width=wrapWidthInfinite);
void DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
- PRectangle rcLine, LineLayout &ll, int subLine=0);
+ PRectangle rcLine, LineLayout *ll, int subLine=0);
void Paint(Surface *surfaceWindow, PRectangle rcArea);
long FormatRange(bool draw, RangeToFormat *pfr);