diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Editor.cxx | 458 | ||||
-rw-r--r-- | src/Editor.h | 22 |
2 files changed, 353 insertions, 127 deletions
diff --git a/src/Editor.cxx b/src/Editor.cxx index 215ba6232..10321a29f 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -117,6 +117,10 @@ Editor::Editor() { recordingMacro = false; foldFlags = 0; + + wrapState = eWrapNone; + wrapWidth = wrapWidthInfinite; + needWrap = true; } Editor::~Editor() { @@ -236,18 +240,26 @@ Point Editor::LocationFromPosition(int pos) { //Platform::DebugPrintf("line=%d\n", line); AutoSurface surface(IsUnicodeMode()); if (surface) { - surface->Init(); - surface->SetUnicodeMode(SC_CP_UTF8 == pdoc->dbcsCodePage); - pt.y = (lineVisible - topLine) * vs.lineHeight; // + half a lineheight? + // -1 because of adding in for visible lines in following loop. + pt.y = (lineVisible - topLine - 1) * vs.lineHeight; unsigned int posLineStart = pdoc->LineStart(line); LineLayout ll; - LayoutLine(line, surface, vs, ll); - if ((pos - posLineStart) > LineLayout::maxLineLength) { - // very long line so put x at arbitrary large position - pt.x = ll.positions[LineLayout::maxLineLength] + vs.fixedColumnWidth - xOffset; - } else { - pt.x = ll.positions[pos - posLineStart] + vs.fixedColumnWidth - xOffset; + 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]]; + } + 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]) { + pt.y += vs.lineHeight; + } } + pt.x += vs.fixedColumnWidth - xOffset; } return pt; } @@ -269,12 +281,13 @@ void Editor::SetTopLine(int topLineNew) { int Editor::PositionFromLocation(Point pt) { RefreshStyleData(); pt.x = pt.x - vs.fixedColumnWidth + xOffset; - int lineDoc = cs.DocFromDisplay(pt.y / vs.lineHeight + topLine); + int visibleLine = pt.y / vs.lineHeight + topLine; if (pt.y < 0) { // Division rounds towards 0 - lineDoc = cs.DocFromDisplay((pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine); + visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine; } - if (lineDoc < 0) - return 0; + if (visibleLine < 0) + visibleLine = 0; + int lineDoc = cs.DocFromDisplay(visibleLine); if (lineDoc >= pdoc->LinesTotal()) return pdoc->Length(); AutoSurface surface(IsUnicodeMode()); @@ -282,12 +295,20 @@ int Editor::PositionFromLocation(Point pt) { unsigned int posLineStart = pdoc->LineStart(lineDoc); LineLayout ll; - LayoutLine(lineDoc, surface, vs, ll); - for (int i = 0; i < ll.numCharsInLine; i++) { - if (pt.x < ((ll.positions[i] + ll.positions[i + 1]) / 2) || - ll.chars[i] == '\r' || ll.chars[i] == '\n') { - return i + posLineStart; + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + 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]; + 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') { + return i + posLineStart; + } } + return lineEnd + posLineStart; } return ll.numCharsInLine + posLineStart; } @@ -305,24 +326,32 @@ int Editor::PositionFromLocationClose(Point pt) { if (pt.y < 0) return INVALID_POSITION; pt.x = pt.x - vs.fixedColumnWidth + xOffset; - int line = cs.DocFromDisplay(pt.y / vs.lineHeight + topLine); + int visibleLine = pt.y / vs.lineHeight + topLine; if (pt.y < 0) { // Division rounds towards 0 - line = cs.DocFromDisplay((pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine); + visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine; } - if (line < 0) + int lineDoc = cs.DocFromDisplay(visibleLine); + if (lineDoc < 0) return INVALID_POSITION; - if (line >= pdoc->LinesTotal()) + if (lineDoc >= pdoc->LinesTotal()) return INVALID_POSITION; AutoSurface surface(IsUnicodeMode()); if (surface) { - unsigned int posLineStart = pdoc->LineStart(line); + unsigned int posLineStart = pdoc->LineStart(lineDoc); LineLayout ll; - LayoutLine(line, surface, vs, ll); - for (int i = 0; i < ll.numCharsInLine; i++) { - if (pt.x < ((ll.positions[i] + ll.positions[i + 1]) / 2) || - ll.chars[i] == '\r' || ll.chars[i] == '\n') { - return i + posLineStart; + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + 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]; + 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') { + return i + posLineStart; + } } } } @@ -330,19 +359,23 @@ int Editor::PositionFromLocationClose(Point pt) { return INVALID_POSITION; } -int Editor::PositionFromLineX(int line, int x) { +int Editor::PositionFromLineX(int lineDoc, int x) { RefreshStyleData(); - if (line >= pdoc->LinesTotal()) + 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(IsUnicodeMode()); if (surface) { - unsigned int posLineStart = pdoc->LineStart(line); + unsigned int posLineStart = pdoc->LineStart(lineDoc); LineLayout ll; - LayoutLine(line, surface, vs, ll); - for (int i = 0; i < ll.numCharsInLine; i++) { - if (x < ((ll.positions[i] + ll.positions[i + 1]) / 2) || + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + int subLine = 0; + 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 i + posLineStart; } @@ -395,7 +428,8 @@ PRectangle Editor::RectangleFromRange(int start, int end) { if (maxPos < end) maxPos = end; int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos)); - int maxLine = cs.DisplayFromDoc(pdoc->LineFromPosition(maxPos)); + int lineDocMax = pdoc->LineFromPosition(maxPos); + int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1; PRectangle rcClient = GetTextRectangle(); PRectangle rc; rc.left = vs.fixedColumnWidth; @@ -667,7 +701,7 @@ void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) { } // Horizontal positioning - if (horiz) { + if (horiz && (wrapState == eWrapNone)) { int xOffsetNew = xOffset; if (pt.x < rcClient.left) { xOffsetNew = xOffset - (offsetLeft - pt.x); @@ -712,6 +746,49 @@ void Editor::InvalidateCaret() { InvalidateRange(currentPos, currentPos + 1); } +// Check if wrapping needed and perform any needed wrapping. +// Return true if wrapping occurred. +bool Editor::WrapLines(int *goodTopLine) { + *goodTopLine = topLine; + bool wrapOccurred = false; + if (needWrap) { + if (wrapState == eWrapNone) { + if (wrapWidth != wrapWidthInfinite) { + wrapWidth = wrapWidthInfinite; + for (int lineDoc=0; lineDoc<pdoc->LinesTotal(); lineDoc++) { + cs.SetHeight(lineDoc, 1); + } + wrapOccurred = true; + } + } else { + int lineDocTop = cs.DocFromDisplay(topLine); + int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop); + PRectangle rcTextArea = GetClientRectangle(); + rcTextArea.left = vs.fixedColumnWidth; + rcTextArea.right -= vs.rightMarginWidth; + wrapWidth = rcTextArea.Width(); + // Ensure all of the document is styled. + pdoc->EnsureStyledTo(pdoc->Length()); + AutoSurface surface(IsUnicodeMode()); + if (surface) { + for (int lineDoc=0; lineDoc<pdoc->LinesTotal(); lineDoc++) { + LineLayout ll; + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + if (cs.SetHeight(lineDoc, ll.lines)) + wrapOccurred = true; + } + } + *goodTopLine = cs.DisplayFromDoc(lineDocTop); + if (subLineTop < cs.GetHeight(lineDocTop)) + *goodTopLine += subLineTop; + else + *goodTopLine += cs.GetHeight(lineDocTop); + } + } + needWrap = false; + return wrapOccurred; +} + int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) { if (vs.markers[markerCheck].markType == SC_MARK_EMPTY) return markerDefault; @@ -762,16 +839,15 @@ void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { } int visibleLine = topLine; - int line = cs.DocFromDisplay(visibleLine); int yposScreen = 0; // Work out whether the top line is whitespace located after a // lessening of fold level which implies a 'fold tail' but which should not // be displayed until the last of a sequence of whitespace. bool needWhiteClosure = false; - int level = pdoc->GetLevel(line); + int level = pdoc->GetLevel(cs.DocFromDisplay(topLine)); if (level & SC_FOLDLEVELWHITEFLAG) { - int lineBack = line; + int lineBack = cs.DocFromDisplay(topLine); int levelPrev = level; while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) { lineBack--; @@ -791,23 +867,35 @@ void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) { + PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed()); + + int lineDoc = cs.DocFromDisplay(visibleLine); + PLATFORM_ASSERT(cs.GetVisible(lineDoc)); + bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc); + // Decide which fold indicator should be displayed - level = pdoc->GetLevel(line); - int levelNext = pdoc->GetLevel(line+1); - int marks = pdoc->GetMark(line); + level = pdoc->GetLevel(lineDoc); + int levelNext = pdoc->GetLevel(lineDoc+1); + int marks = pdoc->GetMark(lineDoc); + if (!firstSubLine) + marks = 0; int levelNum = level & SC_FOLDLEVELNUMBERMASK; int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK; if (level & SC_FOLDLEVELHEADERFLAG) { - if (cs.GetExpanded(line)) { - if (levelNum == SC_FOLDLEVELBASE) - marks |= 1 << SC_MARKNUM_FOLDEROPEN; - else - marks |= 1 << folderOpenMid; + if (firstSubLine) { + if (cs.GetExpanded(lineDoc)) { + if (levelNum == SC_FOLDLEVELBASE) + marks |= 1 << SC_MARKNUM_FOLDEROPEN; + else + marks |= 1 << folderOpenMid; + } else { + if (levelNum == SC_FOLDLEVELBASE) + marks |= 1 << SC_MARKNUM_FOLDER; + else + marks |= 1 << folderEnd; + } } else { - if (levelNum == SC_FOLDLEVELBASE) - marks |= 1 << SC_MARKNUM_FOLDER; - else - marks |= 1 << folderEnd; + marks |= 1 << SC_MARKNUM_FOLDERSUB; } needWhiteClosure = false; } else if (level & SC_FOLDLEVELWHITEFLAG) { @@ -855,20 +943,19 @@ void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { if (!vs.ms[margin].symbol) { char number[100]; number[0] = '\0'; - sprintf(number, "%d", line + 1); + if (firstSubLine) + sprintf(number, "%d", lineDoc + 1); if (foldFlags & 64) - sprintf(number, "%X", pdoc->GetLevel(line)); + sprintf(number, "%X", pdoc->GetLevel(lineDoc)); PRectangle rcNumber = rcMarker; // Right justify int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, strlen(number)); int xpos = rcNumber.right - width - 3; rcNumber.left = xpos; - if ((visibleLine < cs.LinesDisplayed()) && cs.GetVisible(line)) { - surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font, - rcNumber.top + vs.maxAscent, number, strlen(number), - vs.styles[STYLE_LINENUMBER].fore.allocated, - vs.styles[STYLE_LINENUMBER].back.allocated); - } + surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font, + rcNumber.top + vs.maxAscent, number, strlen(number), + vs.styles[STYLE_LINENUMBER].fore.allocated, + vs.styles[STYLE_LINENUMBER].back.allocated); } if (marks) { @@ -881,7 +968,6 @@ void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) { } visibleLine++; - line = cs.DocFromDisplay(visibleLine); yposScreen += vs.lineHeight; } } @@ -909,12 +995,19 @@ void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) { surface->LineTo(xhead, ymid + ydiff); } +static bool IsSpaceOrTab(char ch) { + return ch == ' ' || ch == '\t'; +} + /** * 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) { +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; int posLineStart = pdoc->LineStart(line); int posLineEnd = pdoc->LineStart(line + 1); @@ -996,6 +1089,60 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou startseg = charInLine + 1; } } + 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 (lastGoodBreak == lastLineStart) { + // Ensure at least one character on line. + lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart +1, 1) + - posLineStart; + } + } + 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; + } + } + p++; + } + 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; @@ -1004,7 +1151,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou } void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart, - PRectangle rcLine, LineLayout &ll) { + PRectangle rcLine, LineLayout &ll, int subLine) { PRectangle rcSegment = rcLine; @@ -1058,17 +1205,25 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis int posLineEnd = pdoc->LineStart(line + 1); int styleMask = pdoc->stylingBitsMask; - int startseg = 0; - for (int i = 0; i < ll.numCharsInLine; i++) { + 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]; + } + 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]) || + 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; - rcSegment.right = ll.positions[i + 1] + xStart; + 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) { @@ -1216,7 +1371,7 @@ 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[ll.numCharsInLine]; + int xEol = ll.positions[lineEnd] - subLineStart; rcSegment.left = xEol + xStart; rcSegment.right = xEol + vsDraw.aveCharWidth + xStart; bool eolInSelection = (posLineEnd > ll.selStart) && (posLineEnd <= ll.selEnd) && (ll.selStart != ll.selEnd); @@ -1257,6 +1412,17 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n", // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + int goodTopLine = topLine; + if (WrapLines(&goodTopLine)) { + SetScrollBars(); + SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos())); + SetVerticalScrollPos(); + // The wrapping process has changed the height of some lines so abandon this + // paint for a complete repaint. + paintState = paintAbandoned; + return; + } + if (!pixmapSelPattern->Initialised()) { pixmapSelPattern->InitPixMap(8, 8, surfaceWindow); // This complex procedure is to reproduce the checker board dithered pattern used by windows @@ -1357,7 +1523,6 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { surface->SetUnicodeMode(IsUnicodeMode()); int visibleLine = topLine + screenLinePaintFirst; - int line = cs.DocFromDisplay(visibleLine); int posCaret = currentPos; if (posDrag >= 0) @@ -1376,17 +1541,24 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { double durPaint = 0.0; double durCopy = 0.0; while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) { - //g_timer_start(tim); + + int lineDoc = cs.DocFromDisplay(visibleLine); //Platform::DebugPrintf("Painting line %d\n", line); + // Only visible lines should be handled by the code within the loop + PLATFORM_ASSERT(cs.GetVisible(lineDoc)); + int lineStartSet = cs.DisplayFromDoc(lineDoc); + int subLine = visibleLine - lineStartSet; // 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, surface, vs, ll); + ElapsedTime et; + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + durLayout += et.Duration(true); - ll.selStart = SelectionStart(line); - ll.selEnd = SelectionEnd(line); - ll.containsCaret = line == lineCaret; + ll.selStart = SelectionStart(lineDoc); + ll.selEnd = SelectionEnd(lineDoc); + ll.containsCaret = lineDoc == lineCaret; if (hideSelection) { ll.selStart = -1; ll.selEnd = -1; @@ -1395,8 +1567,8 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { // Need to fix this up so takes account of Unicode and DBCS ll.edgeColumn = theEdge; - int posLineStart = pdoc->LineStart(line); - int posLineEnd = pdoc->LineStart(line + 1); + int posLineStart = pdoc->LineStart(lineDoc); + int posLineEnd = pdoc->LineStart(lineDoc + 1); //Platform::DebugPrintf("line %d %d - %d\n", line, posLineStart, posLineEnd); PRectangle rcLine = rcClient; @@ -1420,19 +1592,19 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { } // Draw the line - if (cs.GetVisible(line)) - DrawLine(surface, vs, line, visibleLine, xStart, rcLine, ll); + DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine); + durPaint += et.Duration(true); - bool expanded = cs.GetExpanded(line); + bool expanded = cs.GetExpanded(lineDoc); if ( (expanded && (foldFlags & 2)) || (!expanded && (foldFlags & 4)) ) { - if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { + if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) { PRectangle rcFoldLine = rcLine; rcFoldLine.bottom = rcFoldLine.top + 1; surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); } } if ( (expanded && (foldFlags & 8)) || (!expanded && (foldFlags & 16)) ) { - if (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) { + if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) { PRectangle rcFoldLine = rcLine; rcFoldLine.top = rcFoldLine.bottom - 1; surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated); @@ -1440,49 +1612,51 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { } // Draw the Caret - if (line == lineCaret) { + if (lineDoc == lineCaret) { int offset = Platform::Minimum(posCaret - posLineStart, LineLayout::maxLineLength); - int xposCaret = ll.positions[offset] + 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 (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 ((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 (cs.GetVisible(line)) { - if (bufferedDraw) { - Point from(vs.fixedColumnWidth, 0); - PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen, - rcClient.right, yposScreen + vs.lineHeight); - surfaceWindow->Copy(rcCopyArea, from, *pixmapLine); - } + 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; @@ -1490,15 +1664,11 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { yposScreen += vs.lineHeight; visibleLine++; - line = cs.DocFromDisplay(visibleLine); //gdk_flush(); } Platform::DebugPrintf("Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g\n", durLayout, durPaint, durLayout / durPaint, durCopy); // Right column limit indicator - - - PRectangle rcBeyondEOF = rcClient; rcBeyondEOF.left = vs.fixedColumnWidth; rcBeyondEOF.right = rcBeyondEOF.right; @@ -1676,11 +1846,11 @@ long Editor::FormatRange(bool draw, RangeToFormat *pfr) { // Empty method is overridden on GTK+ to show / hide scrollbars void Editor::ReconfigureScrollBars() {} -void Editor::SetScrollBarsTo(PRectangle) { +void Editor::SetScrollBars() { RefreshStyleData(); int nMax = cs.LinesDisplayed(); - int nPage = cs.LinesDisplayed() - MaxScrollPos() + 1; + int nPage = nMax - MaxScrollPos() + 1; bool modified = ModifyScrollBars(nMax, nPage); // TODO: ensure always showing as many lines as possible @@ -1695,9 +1865,13 @@ void Editor::SetScrollBarsTo(PRectangle) { //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage); } -void Editor::SetScrollBars() { - PRectangle rsClient = GetClientRectangle(); - SetScrollBarsTo(rsClient); +void Editor::ChangeSize() { + needWrap = true; + DropGraphics(); + SetScrollBars(); + if (wrapState != eWrapNone) { + Redraw(); + } } void Editor::AddChar(char ch) { @@ -2040,6 +2214,30 @@ void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) { NotifySavePoint(atSavePoint); } +void Editor::CheckModificationForWrap(DocModification mh) { + if ((mh.modificationType & SC_MOD_INSERTTEXT) || + (mh.modificationType & SC_MOD_DELETETEXT)) { + if (wrapState != eWrapNone) { + if (mh.linesAdded == 0) { + int lineDoc = pdoc->LineFromPosition(mh.position); + AutoSurface surface(IsUnicodeMode()); + if (surface) { + LineLayout ll; + LayoutLine(lineDoc, surface, vs, ll, wrapWidth); + if (cs.GetHeight(lineDoc) != ll.lines) { + needWrap = true; + Redraw(); + } + } else { + needWrap = true; + } + } else { + needWrap = true; + } + } + } +} + // Move a position so it is still after the same character as before the insertion. static inline int MovePositionForInsertion(int position, int startInsertion, int length) { if (position > startInsertion) { @@ -2068,6 +2266,7 @@ void Editor::NotifyModified(Document*, DocModification mh, void *) { if (paintState == painting) { CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length)); } else if (paintState == notPainting) { + CheckModificationForWrap(mh); if (mh.modificationType & SC_MOD_CHANGESTYLE) { if (mh.position < pdoc->LineStart(topLine)) { // Styling performed before this view @@ -2142,8 +2341,6 @@ void Editor::NotifyModified(Document*, DocModification mh, void *) { NotifyChange(); // Send EN_CHANGE } - - SCNotification scn; scn.nmhdr.code = SCN_MODIFIED; scn.position = mh.position; @@ -3802,11 +3999,13 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_SETMARGINLEFT: vs.leftMarginWidth = lParam; + needWrap = true; InvalidateStyleRedraw(); break; case SCI_SETMARGINRIGHT: vs.rightMarginWidth = lParam; + needWrap = true; InvalidateStyleRedraw(); break; @@ -4107,6 +4306,16 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_WORDENDPOSITION: return pdoc->ExtendWordSelect(wParam, 1); + case SCI_SETWRAPMODE: + wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone; + needWrap = true; + InvalidateStyleRedraw(); + ReconfigureScrollBars(); + break; + + case SCI_GETWRAPMODE: + return wrapState; + case SCI_GETCOLUMN: return pdoc->GetColumn(wParam); @@ -4223,6 +4432,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_SETMARGINWIDTHN: if (ValidMargin(wParam)) { vs.ms[wParam].width = lParam; + needWrap = true; InvalidateStyleRedraw(); } break; diff --git a/src/Editor.h b/src/Editor.h index f43332e90..8d60abffc 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -48,6 +48,12 @@ public: char styles[maxLineLength+1]; char indicators[maxLineLength+1]; int positions[maxLineLength+1]; + + // Wrapped line support + int widthLine; + int lines; + enum {maxDisplayLines = 100}; + int lineStarts[maxDisplayLines]; }; class SelectionText { @@ -207,6 +213,12 @@ protected: // ScintillaBase subclass needs access to much of Editor int foldFlags; ContractionState cs; + // Wrapping support + enum { eWrapNone, eWrapWord } wrapState; + enum { wrapWidthInfinite = 0x7ffffff}; + int wrapWidth; + bool needWrap; + Document *pdoc; Editor(); @@ -261,11 +273,14 @@ protected: // ScintillaBase subclass needs access to much of Editor void DropCaret(); void InvalidateCaret(); + bool WrapLines(int *goodTopLine); + int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault); void PaintSelMargin(Surface *surface, PRectangle &rc); - void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout &ll); + 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); + PRectangle rcLine, LineLayout &ll, int subLine=0); void Paint(Surface *surfaceWindow, PRectangle rcArea); long FormatRange(bool draw, RangeToFormat *pfr); @@ -273,8 +288,8 @@ protected: // ScintillaBase subclass needs access to much of Editor virtual void SetHorizontalScrollPos() = 0; virtual bool ModifyScrollBars(int nMax, int nPage) = 0; virtual void ReconfigureScrollBars(); - void SetScrollBarsTo(PRectangle rsClient); void SetScrollBars(); + void ChangeSize(); void AddChar(char ch); virtual void AddCharUTF(char *s, unsigned int len); @@ -312,6 +327,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void NotifyModifyAttempt(Document *document, void *userData); void NotifySavePoint(Document *document, void *userData, bool atSavePoint); + void CheckModificationForWrap(DocModification mh); void NotifyModified(Document *document, DocModification mh, void *userData); void NotifyDeleted(Document *document, void *userData); void NotifyStyleNeeded(Document *doc, void *userData, int endPos); |