diff options
author | nyamatongwe <unknown> | 2009-07-03 05:48:47 +0000 |
---|---|---|
committer | nyamatongwe <unknown> | 2009-07-03 05:48:47 +0000 |
commit | 43deaf5e127a3bb92e1e2c72fa9cd8f8233578a9 (patch) | |
tree | f88ba56f27bd5111e0b9c9aaf12426f33dc992d5 /src | |
parent | 2c6815995a898438dd58aaa05af9261fc8b85da6 (diff) | |
download | scintilla-mirror-43deaf5e127a3bb92e1e2c72fa9cd8f8233578a9.tar.gz |
Discontiguoues selection and virtual space initial commit.
Diffstat (limited to 'src')
-rw-r--r-- | src/Document.cxx | 4 | ||||
-rw-r--r-- | src/Document.h | 1 | ||||
-rw-r--r-- | src/Editor.cxx | 1767 | ||||
-rw-r--r-- | src/Editor.h | 46 | ||||
-rw-r--r-- | src/PositionCache.cxx | 22 | ||||
-rw-r--r-- | src/PositionCache.h | 6 | ||||
-rw-r--r-- | src/ScintillaBase.cxx | 41 | ||||
-rw-r--r-- | src/Selection.cxx | 345 | ||||
-rw-r--r-- | src/Selection.h | 142 |
9 files changed, 1600 insertions, 774 deletions
diff --git a/src/Document.cxx b/src/Document.cxx index 211d19315..eca4511de 100644 --- a/src/Document.cxx +++ b/src/Document.cxx @@ -217,6 +217,10 @@ int Document::LineEndPosition(int position) const { return LineEnd(LineFromPosition(position)); } +bool Document::IsLineEndPosition(int position) const { + return LineEnd(LineFromPosition(position)) == position; +} + int Document::VCHomePosition(int position) const { int line = LineFromPosition(position); int startPosition = LineStart(line); diff --git a/src/Document.h b/src/Document.h index 58615bb19..4751cd2c8 100644 --- a/src/Document.h +++ b/src/Document.h @@ -239,6 +239,7 @@ public: int LineStart(int line) const; int LineEnd(int line) const; int LineEndPosition(int position) const; + bool IsLineEndPosition(int position) const; int VCHomePosition(int position) const; int SetLevel(int line, int level); diff --git a/src/Editor.cxx b/src/Editor.cxx index 6f7e6eb4b..7af0e7325 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -28,6 +28,7 @@ #include "CharClassify.h" #include "Decoration.h" #include "Document.h" +#include "Selection.h" #include "PositionCache.h" #include "Editor.h" @@ -116,10 +117,6 @@ Editor::Editor() { lineAnchor = 0; originalAnchorPos = 0; - selType = selStream; - moveExtendsSelection = false; - xStartSelect = 0; - xEndSelect = 0; primarySelection = true; caretXPolicy = CARET_SLOP | CARET_EVEN; @@ -139,6 +136,10 @@ Editor::Editor() { verticalScrollBarVisible = true; endAtLastLine = true; caretSticky = false; + multiLineCaret = false; + multiLineCaretBlinks = true; + virtualSpaceOptions = SCVS_NONE; + virtualSpaceOptions = SCVS_USERACCESSIBLE; pixmapLine = Surface::Allocate(); pixmapSelMargin = Surface::Allocate(); @@ -146,9 +147,6 @@ Editor::Editor() { pixmapIndentGuide = Surface::Allocate(); pixmapIndentGuideHighlight = Surface::Allocate(); - currentPos = 0; - anchor = 0; - targetStart = 0; targetEnd = 0; searchFlags = 0; @@ -227,10 +225,7 @@ void Editor::InvalidateStyleData() { palette.Release(); llc.Invalidate(LineLayout::llInvalid); posCache.Clear(); - if (selType == selRectangle) { - xStartSelect = XFromPosition(anchor); - xEndSelect = XFromPosition(currentPos); - } + SetRectangularRange(); } void Editor::InvalidateStyleRedraw() { @@ -347,91 +342,18 @@ public: } }; -#ifdef SCI_NAMESPACE -namespace Scintilla { -#endif - -/** - * Allows to iterate through the lines of a selection. - * Althought it can be called for a stream selection, in most cases - * it is inefficient and it should be used only for - * a rectangular or a line selection. - */ -class SelectionLineIterator { -private: - Editor *ed; - int line; ///< Current line within the iteration. - bool forward; ///< True if iterating by increasing line number, false otherwise. - int selStart, selEnd; ///< Positions of the start and end of the selection relative to the start of the document. - int minX, maxX; ///< Left and right of selection rectangle. - -public: - int lineStart, lineEnd; ///< Line numbers, first and last lines of the selection. - int startPos, endPos; ///< Positions of the beginning and end of the selection on the current line. - - void Reset() { - if (forward) { - line = lineStart; - } else { - line = lineEnd; - } - } - - SelectionLineIterator(Editor *ed_, bool forward_ = true) : line(0), startPos(0), endPos(0) { - ed = ed_; - forward = forward_; - selStart = ed->SelectionStart(); - selEnd = ed->SelectionEnd(); - lineStart = ed->pdoc->LineFromPosition(selStart); - lineEnd = ed->pdoc->LineFromPosition(selEnd); - // Left of rectangle - minX = Platform::Minimum(ed->xStartSelect, ed->xEndSelect); - // Right of rectangle - maxX = Platform::Maximum(ed->xStartSelect, ed->xEndSelect); - Reset(); - } - ~SelectionLineIterator() {} - - void SetAt(int line) { - if (line < lineStart || line > lineEnd) { - startPos = endPos = INVALID_POSITION; - } else { - if (ed->selType == ed->selRectangle) { - // Measure line and return character closest to minX - startPos = ed->PositionFromLineX(line, minX); - // Measure line and return character closest to maxX - endPos = ed->PositionFromLineX(line, maxX); - } else if (ed->selType == ed->selLines) { - startPos = ed->pdoc->LineStart(line); - endPos = ed->pdoc->LineStart(line + 1); - } else { // Stream selection, here only for completion - if (line == lineStart) { - startPos = selStart; - } else { - startPos = ed->pdoc->LineStart(line); - } - if (line == lineEnd) { - endPos = selEnd; - } else { - endPos = ed->pdoc->LineStart(line + 1); - } - } - } - } - bool Iterate() { - SetAt(line); - if (forward) { - line++; - } else { - line--; - } - return startPos != INVALID_POSITION; +SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const { + if (sp.Position() < 0) { + return SelectionPosition(0); + } else if (sp.Position() > pdoc->Length()) { + return SelectionPosition(pdoc->Length()); + } else { + // If not at end of line then set offset to 0 + if (!pdoc->IsLineEndPosition(sp.Position())) + sp.SetVirtualSpace(0); + return sp; } -}; - -#ifdef SCI_NAMESPACE } -#endif Point Editor::LocationFromPosition(int pos) { Point pt; @@ -478,6 +400,11 @@ int Editor::XFromPosition(int pos) { return pt.x - vs.fixedColumnWidth + xOffset; } +int Editor::XFromPosition(SelectionPosition sp) { + Point pt = LocationFromPosition(sp.Position()); + return pt.x + sp.VirtualSpace() * vs.spaceWidth - vs.fixedColumnWidth + xOffset; +} + int Editor::LineFromLocation(Point pt) { return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine); } @@ -487,16 +414,16 @@ void Editor::SetTopLine(int topLineNew) { posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine)); } -int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) { +SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) { RefreshStyleData(); if (canReturnInvalid) { PRectangle rcClient = GetTextRectangle(); if (!rcClient.Contains(pt)) - return INVALID_POSITION; + return SelectionPosition(INVALID_POSITION); if (pt.x < vs.fixedColumnWidth) - return INVALID_POSITION; + return SelectionPosition(INVALID_POSITION); if (pt.y < 0) - return INVALID_POSITION; + return SelectionPosition(INVALID_POSITION); } pt.x = pt.x - vs.fixedColumnWidth + xOffset; int visibleLine = pt.y / vs.lineHeight + topLine; @@ -507,11 +434,11 @@ int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosit visibleLine = 0; int lineDoc = cs.DocFromDisplay(visibleLine); if (canReturnInvalid && (lineDoc < 0)) - return INVALID_POSITION; + return SelectionPosition(INVALID_POSITION); if (lineDoc >= pdoc->LinesTotal()) - return canReturnInvalid ? INVALID_POSITION : pdoc->Length(); + return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length()); unsigned int posLineStart = pdoc->LineStart(lineDoc); - int retVal = canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart); + SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart)); AutoSurface surface(this); AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc)); if (surface && ll) { @@ -531,29 +458,38 @@ int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosit while (i < lineEnd) { if (charPosition) { if ((pt.x + subLineStart) < (ll->positions[i + 1])) { - return pdoc->MovePositionOutsideChar(i + posLineStart, 1); + return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1)); } } else { if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) { - return pdoc->MovePositionOutsideChar(i + posLineStart, 1); + return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1)); } } i++; } - if (canReturnInvalid) { + if (virtualSpace) { + const int spaceWidth = static_cast<int>(vs.spaceWidth); + int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / + spaceWidth; + return SelectionPosition(lineEnd + posLineStart, spaceOffset); + } else if (canReturnInvalid) { if (pt.x < (ll->positions[lineEnd] - subLineStart)) { - return pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1); + return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1)); } } else { - return lineEnd + posLineStart; + return SelectionPosition(lineEnd + posLineStart); } } if (!canReturnInvalid) - return ll->numCharsInLine + posLineStart; + return SelectionPosition(ll->numCharsInLine + posLineStart); } return retVal; } +int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) { + return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).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. @@ -592,6 +528,45 @@ int Editor::PositionFromLineX(int lineDoc, int x) { } /** + * 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. + */ +SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) { + RefreshStyleData(); + if (lineDoc >= pdoc->LinesTotal()) + return SelectionPosition(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); + int subLine = 0; + int lineStart = ll->LineStart(subLine); + int lineEnd = ll->LineLastVisible(subLine); + int subLineStart = ll->positions[lineStart]; + + if (ll->wrapIndent != 0) { + if (lineStart != 0) // Wrapped + x -= ll->wrapIndent; + } + int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd); + while (i < lineEnd) { + if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) { + retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1); + return SelectionPosition(retVal); + } + i++; + } + const int spaceWidth = static_cast<int>(vs.spaceWidth); + int spaceOffset = (x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth; + return SelectionPosition(lineEnd + posLineStart, spaceOffset); + } + return SelectionPosition(retVal); +} + +/** * If painting then abandon the painting because a wider redraw is needed. * @return true if calling code should stop drawing. */ @@ -676,80 +651,124 @@ void Editor::InvalidateRange(int start, int end) { } int Editor::CurrentPosition() { - return currentPos; + return sel.MainCaret(); } bool Editor::SelectionEmpty() { - return anchor == currentPos; + return sel.Empty(); } int Editor::SelectionStart() { - return Platform::Minimum(currentPos, anchor); + return sel.RangeMain().Start().Position(); } int Editor::SelectionEnd() { - return Platform::Maximum(currentPos, anchor); + return sel.RangeMain().End().Position(); } void Editor::SetRectangularRange() { - if (selType == selRectangle) { - xStartSelect = XFromPosition(anchor); - xEndSelect = XFromPosition(currentPos); + if (sel.IsRectangular()) { + int xAnchor = XFromPosition(sel.Rectangular().anchor); + int xCaret = XFromPosition(sel.Rectangular().caret); + bool anchorTop = sel.Rectangular().anchor < sel.Rectangular().caret; + bool anchorLeft = xAnchor < xCaret; + if (sel.selType == Selection::selThin) { + xCaret = xAnchor; + } + SelectionPosition selStart = sel.Rectangular().Start(); + SelectionPosition selEnd = sel.Rectangular().End(); + int lineStart = pdoc->LineFromPosition(selStart.Position()); + int lineEnd = pdoc->LineFromPosition(selEnd.Position()); + // Left of rectangle + int minX = Platform::Minimum(xAnchor, xCaret); + // Right of rectangle + int maxX = Platform::Maximum(xAnchor, xCaret); + sel.EmptyRanges(); + if (anchorTop) { + for (int line=lineStart; line<=lineEnd; line++) { + SelectionPosition spMin(SPositionFromLineX(line, minX)); + SelectionPosition spMax(SPositionFromLineX(line, maxX)); + sel.AddSelection(spMin, spMax, anchorLeft); + } + } else { + for (int line=lineEnd; line>=lineStart; line--) { + SelectionPosition spMin(SPositionFromLineX(line, minX)); + SelectionPosition spMax(SPositionFromLineX(line, maxX)); + sel.AddSelection(spMin, spMax, anchorLeft); + } + } } } -void Editor::InvalidateSelection(int currentPos_, int anchor_, bool invalidateWholeSelection) { - if (anchor != anchor_ || selType == selRectangle) { +void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) { + if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) { invalidateWholeSelection = true; } - int firstAffected = currentPos; + int firstAffected = newMain.Start().Position(); + // +1 for lastAffected ensures caret repainted + int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position()); if (invalidateWholeSelection) { - if (firstAffected > anchor) - firstAffected = anchor; - if (firstAffected > anchor_) - firstAffected = anchor_; - } - if (firstAffected > currentPos_) - firstAffected = currentPos_; - int lastAffected = currentPos; - if (invalidateWholeSelection) { - if (lastAffected < anchor) - lastAffected = anchor; - if (lastAffected < anchor_) - lastAffected = anchor_; + for (size_t r=0; r<sel.Count(); r++) { + firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position()); + firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position()); + lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1); + lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position()); + } } - if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted - lastAffected = (currentPos_ + 1); needUpdateUI = true; InvalidateRange(firstAffected, lastAffected); } -void Editor::SetSelection(int currentPos_, int anchor_) { - currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); - anchor_ = pdoc->ClampPositionIntoDocument(anchor_); - if ((currentPos != currentPos_) || (anchor != anchor_)) { - InvalidateSelection(currentPos_, anchor_, true); - currentPos = currentPos_; - anchor = anchor_; +void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) { + SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_), + ClampPositionIntoDocument(anchor_)); + if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) { + InvalidateSelection(rangeNew); } + sel.RangeMain() = rangeNew; SetRectangularRange(); ClaimSelection(); } +void Editor::SetSelection(int currentPos_, int anchor_) { + SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_)); +} + +// Just move the caret on the main selection +void Editor::SetSelection(SelectionPosition currentPos_) { + currentPos_ = ClampPositionIntoDocument(currentPos_); + if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) { + InvalidateSelection(SelectionRange(currentPos_)); + } + if (sel.IsRectangular()) { + sel.Rectangular() = + SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor); + SetRectangularRange(); + } else { + sel.RangeMain() = + SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor); + } + ClaimSelection(); +} + void Editor::SetSelection(int currentPos_) { - currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_); - if (currentPos != currentPos_) { - InvalidateSelection(currentPos_, anchor, false); - currentPos = currentPos_; + SetSelection(SelectionPosition(currentPos_)); +} + +void Editor::SetEmptySelection(SelectionPosition currentPos_) { + SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_)); + if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) { + InvalidateSelection(rangeNew); } + sel.Clear(); + sel.RangeMain() = rangeNew; SetRectangularRange(); ClaimSelection(); + } void Editor::SetEmptySelection(int currentPos_) { - selType = selStream; - moveExtendsSelection = false; - SetSelection(currentPos_, currentPos_); + SetEmptySelection(SelectionPosition(currentPos_)); } bool Editor::RangeContainsProtected(int start, int end) const { @@ -769,20 +788,13 @@ bool Editor::RangeContainsProtected(int start, int end) const { } bool Editor::SelectionContainsProtected() { - // DONE, but untested...: make support rectangular selection - bool scp = false; - if (selType == selStream) { - scp = RangeContainsProtected(anchor, currentPos); - } else { - SelectionLineIterator lineIterator(this); - while (lineIterator.Iterate()) { - if (RangeContainsProtected(lineIterator.startPos, lineIterator.endPos)) { - scp = true; - break; - } + for (size_t r=0; r<sel.Count(); r++) { + if (RangeContainsProtected(sel.Range(r).Start().Position(), + sel.Range(r).End().Position())) { + return true; } } - return scp; + return false; } /** @@ -809,14 +821,43 @@ int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) con return pos; } -int Editor::MovePositionTo(int newPos, selTypes sel, bool ensureVisible) { - int delta = newPos - currentPos; - newPos = pdoc->ClampPositionIntoDocument(newPos); - newPos = MovePositionOutsideChar(newPos, delta); - if (sel != noSel) { - selType = sel; +SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const { + int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd); + if (posMoved != pos.Position()) + pos.SetPosition(posMoved); + if (vs.ProtectionActive()) { + int mask = pdoc->stylingBitsMask; + if (moveDir > 0) { + if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) { + while ((pos.Position() < pdoc->Length()) && + (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected())) + pos.Add(1); + } + } else if (moveDir < 0) { + if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) { + while ((pos.Position() > 0) && + (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected())) + pos.Add(-1); + } + } + } + return pos; +} + +int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) { + //int delta = newPos.Position() - sel.MainCaret(); + newPos = ClampPositionIntoDocument(newPos); + //newPos = MovePositionOutsideChar(newPos, delta); + if (!sel.IsRectangular() && (selt == Selection::selRectangle)) { + // Switching to rectangular + SelectionRange rangeMain = sel.RangeMain(); + sel.Clear(); + sel.Rectangular() = rangeMain; + } + if (selt != Selection::noSel) { + sel.selType = selt; } - if (sel != noSel || moveExtendsSelection) { + if (selt != Selection::noSel || sel.MoveExtends()) { SetSelection(newPos); } else { SetEmptySelection(newPos); @@ -828,10 +869,14 @@ int Editor::MovePositionTo(int newPos, selTypes sel, bool ensureVisible) { return 0; } -int Editor::MovePositionSoVisible(int pos, int moveDir) { - pos = pdoc->ClampPositionIntoDocument(pos); - pos = MovePositionOutsideChar(pos, moveDir); - int lineDoc = pdoc->LineFromPosition(pos); +int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) { + return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible); +} + +SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) { + pos = ClampPositionIntoDocument(pos); + //pos = MovePositionOutsideChar(pos, moveDir); + int lineDoc = pdoc->LineFromPosition(pos.Position()); if (cs.GetVisible(lineDoc)) { return pos; } else { @@ -839,20 +884,24 @@ int Editor::MovePositionSoVisible(int pos, int moveDir) { 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)); + return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay))); } else { lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed()); - return pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)); + return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay))); } } } +SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) { + return MovePositionSoVisible(SelectionPosition(pos), moveDir); +} + /** * Choose the x position that the caret will try to stick to * as it moves up and down. */ void Editor::SetLastXChosen() { - Point pt = LocationFromPosition(currentPos); + Point pt = LocationFromPosition(sel.MainCaret()); lastXChosen = pt.x; } @@ -897,16 +946,16 @@ void Editor::HorizontalScrollTo(int xPos) { void Editor::MoveCaretInsideView(bool ensureVisible) { PRectangle rcClient = GetTextRectangle(); - Point pt = LocationFromPosition(currentPos); + Point pt = LocationFromPosition(sel.MainCaret()); if (pt.y < rcClient.top) { - MovePositionTo(PositionFromLocation( + MovePositionTo(SPositionFromLocation( Point(lastXChosen, rcClient.top)), - noSel, ensureVisible); + Selection::noSel, ensureVisible); } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) { int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight; - MovePositionTo(PositionFromLocation( + MovePositionTo(SPositionFromLocation( Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)), - noSel, ensureVisible); + Selection::noSel, ensureVisible); } } @@ -978,7 +1027,7 @@ void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) { //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " "); PRectangle rcClient = GetTextRectangle(); //int rcClientFullWidth = rcClient.Width(); - int posCaret = currentPos; + int posCaret = sel.MainCaret(); if (posDrag >= 0) { posCaret = posDrag; } @@ -1222,10 +1271,13 @@ void Editor::DropCaret() { } void Editor::InvalidateCaret() { - if (posDrag >= 0) + if (posDrag >= 0) { InvalidateRange(posDrag, posDrag + 1); - else - InvalidateRange(currentPos, currentPos + 1); + } else { + for (size_t r=0; r<sel.Count(); r++) { + InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1); + } + } UpdateSystemCaret(); } @@ -1757,7 +1809,7 @@ LineLayout *Editor::RetrieveLineLayout(int lineNumber) { int posLineStart = pdoc->LineStart(lineNumber); int posLineEnd = pdoc->LineStart(lineNumber + 1); PLATFORM_ASSERT(posLineEnd >= posLineStart); - int lineCaret = pdoc->LineFromPosition(currentPos); + int lineCaret = pdoc->LineFromPosition(sel.MainCaret()); return llc.Retrieve(lineNumber, lineCaret, posLineEnd - posLineStart, pdoc->GetStyleClock(), LinesOnScreen() + 1, pdoc->LinesTotal()); @@ -1884,6 +1936,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou ll->widthLine = LineLayout::wrapWidthInfinite; ll->lines = 1; int numCharsInLine = 0; + int numCharsBeforeEOL = 0; if (vstyle.edgeState == EDGE_BACKGROUND) { ll->edgeColumn = pdoc->FindColumn(line, theEdge); if (ll->edgeColumn >= posLineStart) { @@ -1910,6 +1963,8 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower) ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc)); numCharsInLine++; + if (!IsEOLChar(chDoc)) + numCharsBeforeEOL++; } } ll->xHighlightGuide = 0; @@ -1992,6 +2047,7 @@ void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayou ll->positions[startseg] += 2; } ll->numCharsInLine = numCharsInLine; + ll->numCharsBeforeEOL = numCharsBeforeEOL; ll->validity = LineLayout::llPositions; } // Hard to cope when too narrow, so just assume there is space @@ -2186,15 +2242,49 @@ void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, Lin int styleMask = pdoc->stylingBitsMask; PRectangle rcSegment = rcLine; + int virtualSpace = 0; + if (subLine == (ll->lines - 1)) + virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * vsDraw.spaceWidth; + // Fill in a PRectangle representing the end of line characters int xEol = ll->positions[lineEnd] - subLineStart; - rcSegment.left = xEol + xStart; - rcSegment.right = xEol + vsDraw.aveCharWidth + xStart; - int posLineEnd = pdoc->LineStart(line + 1); - bool eolInSelection = (subLine == (ll->lines - 1)) && - (posLineEnd > ll->selStart) && (posLineEnd <= ll->selEnd) && (ll->selStart != ll->selEnd); - if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { + if (virtualSpace) { + rcSegment.left = xEol + xStart; + rcSegment.right = xEol + xStart + virtualSpace; + // TODO: draw selection where needed + surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated); + } + + int posAfterLineEnd = pdoc->LineStart(line + 1); + bool eolInSelection = (subLine == (ll->lines - 1)) && sel.InSelectionForEOL(posAfterLineEnd); + + for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) { + rcSegment.left = xStart + ll->positions[eolPos] + virtualSpace; + rcSegment.right = xStart + ll->positions[eolPos+1] + virtualSpace; + const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]); + bool inSelection = false; + bool inHotspot = false; + int styleMain = ll->styles[eolPos]; + ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, eolPos, ll); + ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated; + if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) { + if (vsDraw.selAlpha == SC_ALPHA_NOALPHA) { + surface->FillRectangle(rcSegment, SelectionBackground(vsDraw)); + } else { + surface->FillRectangle(rcSegment, textBack); + SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha); + } + } else { + surface->FillRectangle(rcSegment, textBack); + } + DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw); + } + + rcSegment.left = ll->positions[ll->numCharsInLine] + xStart + virtualSpace; + rcSegment.right = ll->positions[ll->numCharsInLine] + vsDraw.aveCharWidth + xStart + virtualSpace; + + if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { surface->FillRectangle(rcSegment, SelectionBackground(vsDraw)); } else { if (overrideBackground) { @@ -2202,15 +2292,15 @@ void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, Lin } else { surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated); } - if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) { + if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) { SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha); } } - rcSegment.left = xEol + vsDraw.aveCharWidth + xStart; + rcSegment.left = ll->positions[ll->numCharsInLine] + vsDraw.aveCharWidth + xStart + virtualSpace; rcSegment.right = rcLine.right; - if (vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { + if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) { surface->FillRectangle(rcSegment, SelectionBackground(vsDraw)); } else { if (overrideBackground) { @@ -2229,7 +2319,7 @@ void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, Lin PRectangle rcPlace = rcSegment; if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) { - rcPlace.left = xEol + xStart; + rcPlace.left = ll->positions[ll->numCharsInLine] + xStart + virtualSpace; rcPlace.right = rcPlace.left + vsDraw.aveCharWidth; } else { // draw left of the right text margin, to avoid clipping by the current clip rect @@ -2427,6 +2517,9 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis if (subLine < ll->lines) { lineStart = ll->LineStart(subLine); lineEnd = ll->LineStart(subLine + 1); + if (subLine == ll->lines - 1) { + lineEnd = ll->numCharsBeforeEOL; + } } ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated; @@ -2481,6 +2574,7 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis // Does not take margin into account but not significant int xStartVisible = subLineStart - xStart; + ll->psel = &sel; BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible); int next = bfBack.First(); @@ -2502,7 +2596,7 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right); int styleMain = ll->styles[i]; - bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); + const bool inSelection = !hideSelection && sel.CharacterInSelection(iDoc); bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd); ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll); if (ll->chars[i] == '\t') { @@ -2595,7 +2689,7 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis if (vsDraw.hotspotForegroundSet) textFore = vsDraw.hotspotForeground.allocated; } - bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd); + const bool inSelection = !hideSelection && sel.CharacterInSelection(iDoc); if (inSelection && (vsDraw.selforeset)) { textFore = vsDraw.selforeground.allocated; } @@ -2767,15 +2861,21 @@ void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVis xStart, subLine, subLineStart, overrideBackground, background, drawWrapMarkEnd, wrapColour); } - if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) && (ll->selStart >= 0) && (ll->selEnd >= 0)) { - int startPosSel = (ll->selStart < posLineStart) ? posLineStart : ll->selStart; - int endPosSel = (ll->selEnd < (lineEnd + posLineStart)) ? ll->selEnd : (lineEnd + posLineStart); - if (startPosSel < endPosSel) { - rcSegment.left = xStart + ll->positions[startPosSel - posLineStart] - subLineStart; - rcSegment.right = xStart + ll->positions[endPosSel - posLineStart] - subLineStart; - rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left); - rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right); - SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha); + if (!hideSelection && vsDraw.selAlpha != SC_ALPHA_NOALPHA) { + // For each selection draw + for (size_t r=0; r<sel.Count(); r++) { + SelectionPosition spStart; + SelectionPosition spEnd; + if (sel.Range(r).Intersect(posLineStart, posLineStart + lineEnd, spStart, spEnd)) { + if (!(spStart == spEnd)) { + rcSegment.left = xStart + ll->positions[spStart.Position() - posLineStart] - subLineStart + spStart.VirtualSpace() * vsDraw.spaceWidth; + rcSegment.right = xStart + ll->positions[spEnd.Position() - posLineStart] - subLineStart + spEnd.VirtualSpace() * vsDraw.spaceWidth; + rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left); + rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right); + SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), + (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAlpha / 2); + } + } } } @@ -2937,6 +3037,84 @@ void Editor::RefreshPixMaps(Surface *surfaceWindow) { } } +void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart, + PRectangle rcLine, LineLayout *ll, int subLine) { + // When drag is active it is the only caret drawn + bool drawDrag = posDrag != INVALID_POSITION; + if (hideSelection && !drawDrag) + return; + Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1)); + // For each selection draw + for (size_t r=0; (r<sel.Count()) || drawDrag; r++) { + const bool mainCaret = r == sel.Main(); + const int posCaret = (drawDrag ? posDrag : sel.Range(r).caret.Position()); + const int offset = posCaret - rangeLine.start; + const int virtualOffset = drawDrag ? 0 : sel.Range(r).caret.VirtualSpace() * vsDraw.spaceWidth; + if (ll->InLine(offset, subLine) && offset <= ll->numCharsInLine) { + int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)] + xStart; + if (ll->wrapIndent != 0) { + int lineStart = ll->LineStart(subLine); + if (lineStart != 0) // Wrapped + xposCaret += ll->wrapIndent; + } + if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) && + ((posDrag >= 0) || ((caret.active && caret.on) || (!multiLineCaretBlinks && !mainCaret)))) { + bool caretAtEOF = false; + bool caretAtEOL = false; + bool drawBlockCaret = false; + int widthOverstrikeCaret; + int caretWidthOffset = 0; + PRectangle rcCaret = rcLine; + + if (posCaret == pdoc->Length()) { // At end of document + caretAtEOF = true; + widthOverstrikeCaret = vsDraw.aveCharWidth; + } else if ((posCaret - rangeLine.start) >= ll->numCharsInLine) { // At end of line + caretAtEOL = true; + widthOverstrikeCaret = vsDraw.aveCharWidth; + } else { + widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset]; + } + if (widthOverstrikeCaret < 3) // Make sure its visible + widthOverstrikeCaret = 3; + + if (offset > ll->LineStart(subLine)) + caretWidthOffset = 1; // Move back so overlaps both character cells. + if (posDrag >= 0) { + /* Dragging text, use a line caret */ + rcCaret.left = xposCaret - caretWidthOffset; + rcCaret.right = rcCaret.left + vsDraw.caretWidth; + } else if (inOverstrike) { + /* Overstrike (insert mode), use a modified bar caret */ + rcCaret.top = rcCaret.bottom - 2; + rcCaret.left = xposCaret + 1; + rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1; + } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) { + /* Block caret */ + rcCaret.left = xposCaret; + if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) { + drawBlockCaret = true; + rcCaret.right = xposCaret + widthOverstrikeCaret; + } else { + rcCaret.right = xposCaret + vsDraw.aveCharWidth; + } + } else { + /* Line caret */ + rcCaret.left = xposCaret - caretWidthOffset; + rcCaret.right = rcCaret.left + vsDraw.caretWidth; + } + if (drawBlockCaret) { + DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret, rcCaret); + } else { + surface->FillRectangle(rcCaret, mainCaret ? vsDraw.caretcolour.allocated : vsDraw.selbackground2.allocated); + } + } + } + if (drawDrag) + break; + } +} + void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n", // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom); @@ -3036,7 +3214,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { int visibleLine = topLine + screenLinePaintFirst; - int posCaret = currentPos; + int posCaret = sel.MainCaret(); if (posDrag >= 0) posCaret = posDrag; int lineCaret = pdoc->LineFromPosition(posCaret); @@ -3055,7 +3233,6 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { //ElapsedTime etWhole; int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times AutoLineLayout ll(llc, 0); - SelectionLineIterator lineIterator(this); while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) { int lineDoc = cs.DocFromDisplay(visibleLine); @@ -3069,8 +3246,6 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { //ElapsedTime et; if (lineDoc != lineDocPrevious) { ll.Set(0); - // For rectangular selection this accesses the layout cache so should be after layout returned. - lineIterator.SetAt(lineDoc); ll.Set(RetrieveLineLayout(lineDoc)); LayoutLine(lineDoc, surface, vs, ll, wrapWidth); lineDocPrevious = lineDoc; @@ -3078,17 +3253,8 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { //durLayout += et.Duration(true); if (ll) { - if (selType == selStream) { - ll->selStart = SelectionStart(); - ll->selEnd = SelectionEnd(); - } else { - ll->selStart = lineIterator.startPos; - ll->selEnd = lineIterator.endPos; - } ll->containsCaret = lineDoc == lineCaret; if (hideSelection) { - ll->selStart = -1; - ll->selEnd = -1; ll->containsCaret = false; } @@ -3132,71 +3298,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) { } } - // Draw the Caret - if (lineDoc == lineCaret) { - int offset = Platform::Minimum(posCaret - rangeLine.start, ll->maxLineLength); - if (ll->InLine(offset, subLine)) { - int xposCaret = ll->positions[offset] - ll->positions[ll->LineStart(subLine)] + xStart; - - if (ll->wrapIndent != 0) { - int lineStart = ll->LineStart(subLine); - if (lineStart != 0) // Wrapped - xposCaret += ll->wrapIndent; - } - if ((xposCaret >= 0) && (vs.caretWidth > 0) && (vs.caretStyle != CARETSTYLE_INVISIBLE) && - ((posDrag >= 0) || (caret.active && caret.on))) { - bool caretAtEOF = false; - bool caretAtEOL = false; - bool drawBlockCaret = false; - int widthOverstrikeCaret; - int caretWidthOffset = 0; - PRectangle rcCaret = rcLine; - - if (posCaret == pdoc->Length()) { // At end of document - caretAtEOF = true; - widthOverstrikeCaret = vs.aveCharWidth; - } else if ((posCaret - rangeLine.start) >= ll->numCharsInLine) { // At end of line - caretAtEOL = true; - widthOverstrikeCaret = vs.aveCharWidth; - } else { - widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset]; - } - if (widthOverstrikeCaret < 3) // Make sure its visible - widthOverstrikeCaret = 3; - - if (offset > ll->LineStart(subLine)) - caretWidthOffset = 1; // Move back so overlaps both character cells. - if (posDrag >= 0) { - /* Dragging text, use a line caret */ - rcCaret.left = xposCaret - caretWidthOffset; - rcCaret.right = rcCaret.left + vs.caretWidth; - } else if (inOverstrike) { - /* Overstrike (insert mode), use a modified bar caret */ - rcCaret.top = rcCaret.bottom - 2; - rcCaret.left = xposCaret + 1; - rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1; - } else if (vs.caretStyle == CARETSTYLE_BLOCK) { - /* Block caret */ - rcCaret.left = xposCaret; - if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) { - drawBlockCaret = true; - rcCaret.right = xposCaret + widthOverstrikeCaret; - } else { - rcCaret.right = xposCaret + vs.aveCharWidth; - } - } else { - /* Line caret */ - rcCaret.left = xposCaret - caretWidthOffset; - rcCaret.right = rcCaret.left + vs.caretWidth; - } - if (drawBlockCaret) { - DrawBlockCaret(surface, vs, ll, subLine, xStart, offset, posCaret, rcCaret); - } else { - surface->FillRectangle(rcCaret, vs.caretcolour.allocated); - } - } - } - } + DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine); if (bufferedDraw) { Point from(vs.fixedColumnWidth, 0); @@ -3375,8 +3477,6 @@ long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) { LineLayout ll(8000); LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint); - ll.selStart = -1; - ll.selEnd = -1; ll.containsCaret = false; PRectangle rcLine; @@ -3508,32 +3608,61 @@ void Editor::AddChar(char ch) { // AddCharUTF inserts an array of bytes which may or may not be in UTF-8. void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) { - bool wasSelection = currentPos != anchor; - ClearSelection(); - bool charReplaceAction = false; - if (inOverstrike && !wasSelection && !RangeContainsProtected(currentPos, currentPos + 1)) { - if (currentPos < (pdoc->Length())) { - if (!IsEOLChar(pdoc->CharAt(currentPos))) { - charReplaceAction = true; - pdoc->BeginUndoAction(); - pdoc->DelChar(currentPos); + const bool undoBracketNeeded = (sel.Count() > 1) || !sel.Empty() || inOverstrike; + if (undoBracketNeeded) + pdoc->BeginUndoAction(); + for (size_t r=0; r<sel.Count(); r++) { + if (!RangeContainsProtected(sel.Range(r).Start().Position(), + sel.Range(r).End().Position())) { + int positionInsert = sel.Range(r).Start().Position(); + if (!sel.Range(r).Empty()) { + if (sel.Range(r).Length()) { + pdoc->DeleteChars(positionInsert, sel.Range(r).Length()); + sel.ClearVirtualSpace(r); + } else { + // Range is all virtual so collapse to start of virtual space + sel.Range(r).MinimizeVirtualSpace(); + } + } else if (inOverstrike) { + if (positionInsert < pdoc->Length()) { + if (!IsEOLChar(pdoc->CharAt(positionInsert))) { + pdoc->DelChar(positionInsert); + sel.ClearVirtualSpace(r); + } + } + } + const unsigned int spaces = sel.Range(r).caret.VirtualSpace(); + if (spaces > 0) { + char *spaceText = new char[spaces]; + memset(spaceText, ' ', spaces); + pdoc->InsertString(positionInsert, spaceText, spaces); + positionInsert += spaces; + delete []spaceText; + } + if (pdoc->InsertString(positionInsert, s, len)) { + sel.Range(r).caret.SetPosition(positionInsert + len); + sel.Range(r).anchor.SetPosition(positionInsert + len); + } + sel.ClearVirtualSpace(r); + // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information + if (wrapState != eWrapNone) { + AutoSurface surface(this); + if (surface) { + WrapOneLine(surface, pdoc->LineFromPosition(positionInsert)); + } } } } - if (pdoc->InsertString(currentPos, s, len)) { - SetEmptySelection(currentPos + len); - } - if (charReplaceAction) { + if (undoBracketNeeded) pdoc->EndUndoAction(); - } - // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information if (wrapState != eWrapNone) { - AutoSurface surface(this); - if (surface) { - WrapOneLine(surface, pdoc->LineFromPosition(currentPos)); - } SetScrollBars(); } + if (sel.IsRectangular()) { + sel.selType = Selection::selThin; + sel.Rectangular() = SelectionRange(sel.Rectangular().caret, sel.Range(0).anchor); + } + // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information EnsureCaretVisible(); // Avoid blinking during rapid typing: ShowCaretAtCurrentPosition(); @@ -3580,30 +3709,19 @@ void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) { } void Editor::ClearSelection() { - if (!SelectionContainsProtected()) { - int startPos = SelectionStart(); - if (selType == selStream) { - unsigned int chars = SelectionEnd() - startPos; - if (0 != chars) { - pdoc->BeginUndoAction(); - pdoc->DeleteChars(startPos, chars); - pdoc->EndUndoAction(); - } - } else { - pdoc->BeginUndoAction(); - SelectionLineIterator lineIterator(this, false); - while (lineIterator.Iterate()) { - startPos = lineIterator.startPos; - unsigned int chars = lineIterator.endPos - startPos; - if (0 != chars) { - pdoc->DeleteChars(startPos, chars); - } + pdoc->BeginUndoAction(); + for (size_t r=0; r<sel.Count(); r++) { + if (!sel.Range(r).Empty()) { + if (!RangeContainsProtected(sel.Range(r).Start().Position(), + sel.Range(r).End().Position())) { + pdoc->DeleteChars(sel.Range(r).Start().Position(), + sel.Range(r).Length()); + sel.ClearVirtualSpace(r); } - pdoc->EndUndoAction(); - selType = selStream; } - SetEmptySelection(startPos); } + pdoc->EndUndoAction(); + } void Editor::ClearAll() { @@ -3617,8 +3735,7 @@ void Editor::ClearAll() { pdoc->MarginClearAll(); } pdoc->EndUndoAction(); - anchor = 0; - currentPos = 0; + sel.Clear(); SetTopLine(0); SetVerticalScrollPos(); InvalidateStyleRedraw(); @@ -3655,13 +3772,14 @@ void Editor::Cut() { } } -void Editor::PasteRectangular(int pos, const char *ptr, int len) { +void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) { if (pdoc->IsReadOnly() || SelectionContainsProtected()) { return; } - currentPos = pos; - int xInsert = XFromPosition(currentPos); - int line = pdoc->LineFromPosition(currentPos); + sel.Clear(); + sel.RangeMain() = SelectionRange(pos, pos); + int xInsert = XFromPosition(sel.RangeMain().caret); + int line = pdoc->LineFromPosition(sel.MainCaret()); bool prevCr = false; pdoc->BeginUndoAction(); for (int i = 0; i < len; i++) { @@ -3675,17 +3793,17 @@ void Editor::PasteRectangular(int pos, const char *ptr, int len) { pdoc->InsertChar(pdoc->Length(), '\n'); } // Pad the end of lines with spaces if required - currentPos = PositionFromLineX(line, xInsert); - if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) { - while (XFromPosition(currentPos) < xInsert) { - pdoc->InsertChar(currentPos, ' '); - currentPos++; + sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert)); + if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) { + while (XFromPosition(sel.MainCaret()) < xInsert) { + pdoc->InsertChar(sel.MainCaret(), ' '); + sel.RangeMain().caret.Add(1); } } prevCr = ptr[i] == '\r'; } else { - pdoc->InsertString(currentPos, ptr + i, 1); - currentPos++; + pdoc->InsertString(sel.MainCaret(), ptr + i, 1); + sel.RangeMain().caret.Add(1); prevCr = false; } } @@ -3698,14 +3816,28 @@ bool Editor::CanPaste() { } void Editor::Clear() { - if (currentPos == anchor) { - if (!RangeContainsProtected(currentPos, currentPos + 1)) { - DelChar(); + const bool undoBracketNeeded = (sel.Count() > 1) || !sel.Empty(); + // If multiple selections, don't delete EOLS + if (undoBracketNeeded) + pdoc->BeginUndoAction(); + if (sel.Empty()) { + pdoc->BeginUndoAction(); + for (size_t r=0; r<sel.Count(); r++) { + if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) { + if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) { + pdoc->DelChar(sel.Range(r).caret.Position()); + sel.ClearVirtualSpace(r); + } // else multiple selection so don't eat line ends + } else { + sel.ClearVirtualSpace(r); + } } + pdoc->EndUndoAction(); } else { ClearSelection(); } - SetEmptySelection(currentPos); + if (undoBracketNeeded) + pdoc->EndUndoAction(); } void Editor::SelectAll() { @@ -3733,20 +3865,57 @@ void Editor::Redo() { } void Editor::DelChar() { - if (!RangeContainsProtected(currentPos, currentPos + 1)) { - pdoc->DelChar(currentPos); + if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) { + pdoc->DelChar(sel.MainCaret()); } // Avoid blinking during rapid typing: ShowCaretAtCurrentPosition(); } void Editor::DelCharBack(bool allowLineStartDeletion) { - if (currentPos == anchor) { - if (!RangeContainsProtected(currentPos - 1, currentPos)) { - int lineCurrentPos = pdoc->LineFromPosition(currentPos); - if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != currentPos)) { - if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) && - pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) { + const bool undoBracketNeeded = (sel.Count() > 1) || !sel.Empty(); + if (undoBracketNeeded) + pdoc->BeginUndoAction(); + if (sel.Empty()) { + for (size_t r=0; r<sel.Count(); r++) { + if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) { + if (sel.Range(r).caret.VirtualSpace()) { + sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1); + sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace()); + } else { + int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position()); + if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) { + if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) && + pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) { + if (!undoBracketNeeded) + pdoc->BeginUndoAction(); + int indentation = pdoc->GetLineIndentation(lineCurrentPos); + int indentationStep = pdoc->IndentSize(); + if (indentation % indentationStep == 0) { + pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep); + } else { + pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep)); + } + // SetEmptySelection + sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos), + pdoc->GetLineIndentPosition(lineCurrentPos)); + if (!undoBracketNeeded) + pdoc->EndUndoAction(); + } else { + pdoc->DelCharBack(sel.Range(r).caret.Position()); + } + } + } + } else { + sel.ClearVirtualSpace(r); + } + } + /* + if (!RangeContainsProtected(sel.MainCaret() - 1, sel.MainCaret())) { + int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret()); + if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.MainCaret())) { + if (pdoc->GetColumn(sel.MainCaret()) <= pdoc->GetLineIndentation(lineCurrentPos) && + pdoc->GetColumn(sel.MainCaret()) > 0 && pdoc->backspaceUnindents) { pdoc->BeginUndoAction(); int indentation = pdoc->GetLineIndentation(lineCurrentPos); int indentationStep = pdoc->IndentSize(); @@ -3758,14 +3927,17 @@ void Editor::DelCharBack(bool allowLineStartDeletion) { SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos)); pdoc->EndUndoAction(); } else { - pdoc->DelCharBack(currentPos); + pdoc->DelCharBack(sel.MainCaret()); } } } + */ } else { ClearSelection(); - SetEmptySelection(currentPos); + SetEmptySelection(sel.MainCaret()); } + if (undoBracketNeeded) + pdoc->EndUndoAction(); // Avoid blinking during rapid typing: ShowCaretAtCurrentPosition(); } @@ -3990,13 +4162,11 @@ void Editor::NotifyModified(Document*, DocModification mh, void *) { } else { // Move selection and brace highlights if (mh.modificationType & SC_MOD_INSERTTEXT) { - currentPos = MovePositionForInsertion(currentPos, mh.position, mh.length); - anchor = MovePositionForInsertion(anchor, mh.position, mh.length); + sel.MovePositions(true, mh.position, mh.length); braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length); braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length); } else if (mh.modificationType & SC_MOD_DELETETEXT) { - currentPos = MovePositionForDeletion(currentPos, mh.position, mh.length); - anchor = MovePositionForDeletion(anchor, mh.position, mh.length); + sel.MovePositions(false, mh.position, mh.length); braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length); braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length); } @@ -4228,11 +4398,11 @@ void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lPar * If stuttered = true and not already at first/last row, move to first/last row of window. * If stuttered = true and already at first/last row, scroll as normal. */ -void Editor::PageMove(int direction, selTypes sel, bool stuttered) { +void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) { int topLineNew, newPos; // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem? - int currentLine = pdoc->LineFromPosition(currentPos); + int currentLine = pdoc->LineFromPosition(sel.MainCaret()); int topStutterLine = topLine + caretYSlop; int bottomStutterLine = pdoc->LineFromPosition(PositionFromLocation( @@ -4248,7 +4418,7 @@ void Editor::PageMove(int direction, selTypes sel, bool stuttered) { newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop))); } else { - Point pt = LocationFromPosition(currentPos); + Point pt = LocationFromPosition(sel.MainCaret()); topLineNew = Platform::Clamp( topLine + direction * LinesToScroll(), 0, MaxScrollPos()); @@ -4258,37 +4428,28 @@ void Editor::PageMove(int direction, selTypes sel, bool stuttered) { if (topLineNew != topLine) { SetTopLine(topLineNew); - MovePositionTo(newPos, sel); + MovePositionTo(SelectionPosition(newPos), selt); Redraw(); SetVerticalScrollPos(); } else { - MovePositionTo(newPos, sel); + MovePositionTo(SelectionPosition(newPos), selt); } } void Editor::ChangeCaseOfSelection(bool makeUpperCase) { pdoc->BeginUndoAction(); - int startCurrent = currentPos; - int startAnchor = anchor; - if (selType == selStream) { - pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()), - makeUpperCase); - SetSelection(startCurrent, startAnchor); - } else { - SelectionLineIterator lineIterator(this, false); - while (lineIterator.Iterate()) { - pdoc->ChangeCase( - Range(lineIterator.startPos, lineIterator.endPos), - makeUpperCase); - } - // Would be nicer to keep the rectangular selection but this is complex - SetEmptySelection(startCurrent); + for (size_t r=0; r<sel.Count(); r++) { + SelectionRange current = sel.Range(r); + pdoc->ChangeCase(Range(current.Start().Position(), current.End().Position()), + makeUpperCase); + // Automatic movement cuts off last character so reset to exactly the same as it was. + sel.Range(r) = current; } pdoc->EndUndoAction(); } void Editor::LineTranspose() { - int line = pdoc->LineFromPosition(currentPos); + int line = pdoc->LineFromPosition(sel.MainCaret()); if (line > 0) { pdoc->BeginUndoAction(); int startPrev = pdoc->LineStart(line - 1); @@ -4303,7 +4464,7 @@ void Editor::LineTranspose() { pdoc->DeleteChars(startPrev, len1); pdoc->InsertString(startPrev, line2, len2); pdoc->InsertString(start - len1 + len2, line1, len1); - MovePositionTo(start - len1 + len2); + MovePositionTo(SelectionPosition(start - len1 + len2)); delete []line1; delete []line2; pdoc->EndUndoAction(); @@ -4311,13 +4472,14 @@ void Editor::LineTranspose() { } void Editor::Duplicate(bool forLine) { + // TODO: multiple selection by duplicating each selection int start = SelectionStart(); int end = SelectionEnd(); - if (start == end) { + if (sel.Empty()) { forLine = true; } if (forLine) { - int line = pdoc->LineFromPosition(currentPos); + int line = pdoc->LineFromPosition(sel.MainCaret()); start = pdoc->LineStart(line); end = pdoc->LineEnd(line); } @@ -4333,7 +4495,7 @@ void Editor::Duplicate(bool forLine) { } void Editor::CancelModes() { - moveExtendsSelection = false; + sel.SetMoveExtends(false); } void Editor::NewLine() { @@ -4344,8 +4506,8 @@ void Editor::NewLine() { } else if (pdoc->eolMode == SC_EOL_CR) { eol = "\r"; } // else SC_EOL_LF -> "\n" already set - if (pdoc->InsertCString(currentPos, eol)) { - SetEmptySelection(currentPos + istrlen(eol)); + if (pdoc->InsertCString(sel.MainCaret(), eol)) { + SetEmptySelection(sel.MainCaret() + istrlen(eol)); while (*eol) { NotifyChar(*eol); eol++; @@ -4358,16 +4520,16 @@ void Editor::NewLine() { ShowCaretAtCurrentPosition(); } -void Editor::CursorUpOrDown(int direction, selTypes sel) { - Point pt = LocationFromPosition(currentPos); - int lineDoc = pdoc->LineFromPosition(currentPos); +void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) { + Point pt = LocationFromPosition(sel.MainCaret()); + int lineDoc = pdoc->LineFromPosition(sel.MainCaret()); Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc)); int subLine = (pt.y - ptStartLine.y) / vs.lineHeight; int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0; - int posNew = PositionFromLocation( + SelectionPosition posNew = SPositionFromLocation( Point(lastXChosen, pt.y + direction * vs.lineHeight)); if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) { - posNew = PositionFromLocation( + posNew = SPositionFromLocation( Point(lastXChosen, pt.y + (commentLines + 1) * vs.lineHeight)); } if (direction < 0) { @@ -4375,24 +4537,25 @@ void Editor::CursorUpOrDown(int direction, selTypes sel) { // seek back if that is the case. // There is an equivalent case when moving down which skips // over a line but as that does not trap the user it is fine. - Point ptNew = LocationFromPosition(posNew); - while ((posNew > 0) && (pt.y == ptNew.y)) { - posNew--; - ptNew = LocationFromPosition(posNew); + Point ptNew = LocationFromPosition(posNew.Position()); + while ((posNew.Position() > 0) && (pt.y == ptNew.y)) { + posNew.Add(- 1); + posNew.SetVirtualSpace(0); + ptNew = LocationFromPosition(posNew.Position()); } } - MovePositionTo(posNew, sel); + MovePositionTo(posNew, selt); } -void Editor::ParaUpOrDown(int direction, selTypes sel) { - int lineDoc, savedPos = currentPos; +void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) { + int lineDoc, savedPos = sel.MainCaret(); do { - MovePositionTo(direction > 0 ? pdoc->ParaDown(currentPos) : pdoc->ParaUp(currentPos), sel); - lineDoc = pdoc->LineFromPosition(currentPos); + MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt); + lineDoc = pdoc->LineFromPosition(sel.MainCaret()); if (direction > 0) { - if (currentPos >= pdoc->Length() && !cs.GetVisible(lineDoc)) { - if (sel == noSel) { - MovePositionTo(pdoc->LineEndPosition(savedPos)); + if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) { + if (selt == Selection::noSel) { + MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos))); } break; } @@ -4438,16 +4601,16 @@ int Editor::KeyCommand(unsigned int iMessage) { CursorUpOrDown(1); break; case SCI_LINEDOWNEXTEND: - CursorUpOrDown(1, selStream); + CursorUpOrDown(1, Selection::selStream); break; case SCI_LINEDOWNRECTEXTEND: - CursorUpOrDown(1, selRectangle); + CursorUpOrDown(1, Selection::selRectangle); break; case SCI_PARADOWN: ParaUpOrDown(1); break; case SCI_PARADOWNEXTEND: - ParaUpOrDown(1, selStream); + ParaUpOrDown(1, Selection::selStream); break; case SCI_LINESCROLLDOWN: ScrollTo(topLine + 1); @@ -4457,144 +4620,180 @@ int Editor::KeyCommand(unsigned int iMessage) { CursorUpOrDown(-1); break; case SCI_LINEUPEXTEND: - CursorUpOrDown(-1, selStream); + CursorUpOrDown(-1, Selection::selStream); break; case SCI_LINEUPRECTEXTEND: - CursorUpOrDown(-1, selRectangle); + CursorUpOrDown(-1, Selection::selRectangle); break; case SCI_PARAUP: ParaUpOrDown(-1); break; case SCI_PARAUPEXTEND: - ParaUpOrDown(-1, selStream); + ParaUpOrDown(-1, Selection::selStream); break; case SCI_LINESCROLLUP: ScrollTo(topLine - 1); MoveCaretInsideView(false); break; case SCI_CHARLEFT: - if (SelectionEmpty() || moveExtendsSelection) { - MovePositionTo(MovePositionSoVisible(currentPos - 1, -1)); + if (SelectionEmpty() || sel.MoveExtends()) { + if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) { + SelectionPosition spCaret = sel.RangeMain().caret; + spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1); + MovePositionTo(spCaret); + } else { + MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1)); + } } else { - MovePositionTo(SelectionStart()); + MovePositionTo(sel.RangeMain().Start()); } SetLastXChosen(); break; case SCI_CHARLEFTEXTEND: - MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selStream); + if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) { + SelectionPosition spCaret = sel.RangeMain().caret; + spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1); + MovePositionTo(spCaret, Selection::selStream); + } else { + MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream); + } SetLastXChosen(); break; case SCI_CHARLEFTRECTEXTEND: - MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selRectangle); + if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) { + SelectionPosition spCaret = sel.RangeMain().caret; + spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1); + MovePositionTo(spCaret, Selection::selRectangle); + } else { + MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle); + } SetLastXChosen(); break; case SCI_CHARRIGHT: - if (SelectionEmpty() || moveExtendsSelection) { - MovePositionTo(MovePositionSoVisible(currentPos + 1, 1)); + if (SelectionEmpty() || sel.MoveExtends()) { + if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) { + SelectionPosition spCaret = sel.RangeMain().caret; + spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1); + MovePositionTo(spCaret); + } else { + MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1)); + } } else { - MovePositionTo(SelectionEnd()); + MovePositionTo(sel.RangeMain().End()); } SetLastXChosen(); break; case SCI_CHARRIGHTEXTEND: - MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selStream); + if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) { + SelectionPosition spCaret = sel.RangeMain().caret; + spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1); + MovePositionTo(spCaret, Selection::selStream); + } else { + MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream); + } SetLastXChosen(); break; case SCI_CHARRIGHTRECTEXTEND: - MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selRectangle); + if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) { + SelectionPosition spCaret = sel.RangeMain().caret; + spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1); + MovePositionTo(spCaret, Selection::selRectangle); + } else { + MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle); + } SetLastXChosen(); break; case SCI_WORDLEFT: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1)); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1)); SetLastXChosen(); break; case SCI_WORDLEFTEXTEND: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), selStream); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream); SetLastXChosen(); break; case SCI_WORDRIGHT: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1)); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1)); SetLastXChosen(); break; case SCI_WORDRIGHTEXTEND: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), selStream); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream); SetLastXChosen(); break; case SCI_WORDLEFTEND: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1)); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1)); SetLastXChosen(); break; case SCI_WORDLEFTENDEXTEND: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1), selStream); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream); SetLastXChosen(); break; case SCI_WORDRIGHTEND: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1)); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1)); SetLastXChosen(); break; case SCI_WORDRIGHTENDEXTEND: - MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1), selStream); + MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream); SetLastXChosen(); break; case SCI_HOME: - MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos))); + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()))); SetLastXChosen(); break; case SCI_HOMEEXTEND: - MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selStream); + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream); SetLastXChosen(); break; case SCI_HOMERECTEXTEND: - MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selRectangle); + MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle); SetLastXChosen(); break; case SCI_LINEEND: - MovePositionTo(pdoc->LineEndPosition(currentPos)); + MovePositionTo(pdoc->LineEndPosition(sel.MainCaret())); SetLastXChosen(); break; case SCI_LINEENDEXTEND: - MovePositionTo(pdoc->LineEndPosition(currentPos), selStream); + MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream); SetLastXChosen(); break; case SCI_LINEENDRECTEXTEND: - MovePositionTo(pdoc->LineEndPosition(currentPos), selRectangle); + MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle); SetLastXChosen(); break; case SCI_HOMEWRAP: { - int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); - if (currentPos <= homePos) - homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos)); + SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1); + if (sel.RangeMain().caret <= homePos) + homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()))); MovePositionTo(homePos); SetLastXChosen(); } break; case SCI_HOMEWRAPEXTEND: { - int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); - if (currentPos <= homePos) - homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos)); - MovePositionTo(homePos, selStream); + SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1); + if (sel.RangeMain().caret <= homePos) + homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()))); + MovePositionTo(homePos, Selection::selStream); SetLastXChosen(); } break; case SCI_LINEENDWRAP: { - int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1); - int realEndPos = pdoc->LineEndPosition(currentPos); + SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1); + SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret())); if (endPos > realEndPos // if moved past visible EOLs - || currentPos >= endPos) // if at end of display line already + || sel.RangeMain().caret >= endPos) // if at end of display line already endPos = realEndPos; MovePositionTo(endPos); SetLastXChosen(); } break; case SCI_LINEENDWRAPEXTEND: { - int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1); - int realEndPos = pdoc->LineEndPosition(currentPos); + SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1); + SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret())); if (endPos > realEndPos // if moved past visible EOLs - || currentPos >= endPos) // if at end of display line already + || sel.RangeMain().caret >= endPos) // if at end of display line already endPos = realEndPos; - MovePositionTo(endPos, selStream); + MovePositionTo(endPos, Selection::selStream); SetLastXChosen(); } break; @@ -4603,7 +4802,7 @@ int Editor::KeyCommand(unsigned int iMessage) { SetLastXChosen(); break; case SCI_DOCUMENTSTARTEXTEND: - MovePositionTo(0, selStream); + MovePositionTo(0, Selection::selStream); SetLastXChosen(); break; case SCI_DOCUMENTEND: @@ -4611,38 +4810,38 @@ int Editor::KeyCommand(unsigned int iMessage) { SetLastXChosen(); break; case SCI_DOCUMENTENDEXTEND: - MovePositionTo(pdoc->Length(), selStream); + MovePositionTo(pdoc->Length(), Selection::selStream); SetLastXChosen(); break; case SCI_STUTTEREDPAGEUP: - PageMove(-1, noSel, true); + PageMove(-1, Selection::noSel, true); break; case SCI_STUTTEREDPAGEUPEXTEND: - PageMove(-1, selStream, true); + PageMove(-1, Selection::selStream, true); break; case SCI_STUTTEREDPAGEDOWN: - PageMove(1, noSel, true); + PageMove(1, Selection::noSel, true); break; case SCI_STUTTEREDPAGEDOWNEXTEND: - PageMove(1, selStream, true); + PageMove(1, Selection::selStream, true); break; case SCI_PAGEUP: PageMove(-1); break; case SCI_PAGEUPEXTEND: - PageMove(-1, selStream); + PageMove(-1, Selection::selStream); break; case SCI_PAGEUPRECTEXTEND: - PageMove(-1, selRectangle); + PageMove(-1, Selection::selRectangle); break; case SCI_PAGEDOWN: PageMove(1); break; case SCI_PAGEDOWNEXTEND: - PageMove(1, selStream); + PageMove(1, Selection::selStream); break; case SCI_PAGEDOWNRECTEXTEND: - PageMove(1, selRectangle); + PageMove(1, Selection::selRectangle); break; case SCI_EDITTOGGLEOVERTYPE: inOverstrike = !inOverstrike; @@ -4691,21 +4890,21 @@ int Editor::KeyCommand(unsigned int iMessage) { AddChar('\f'); break; case SCI_VCHOME: - MovePositionTo(pdoc->VCHomePosition(currentPos)); + MovePositionTo(pdoc->VCHomePosition(sel.MainCaret())); SetLastXChosen(); break; case SCI_VCHOMEEXTEND: - MovePositionTo(pdoc->VCHomePosition(currentPos), selStream); + MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream); SetLastXChosen(); break; case SCI_VCHOMERECTEXTEND: - MovePositionTo(pdoc->VCHomePosition(currentPos), selRectangle); + MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle); SetLastXChosen(); break; case SCI_VCHOMEWRAP: { - int homePos = pdoc->VCHomePosition(currentPos); - int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); - if ((viewLineStart < currentPos) && (viewLineStart > homePos)) + SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret())); + SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1); + if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos)) homePos = viewLineStart; MovePositionTo(homePos); @@ -4713,12 +4912,12 @@ int Editor::KeyCommand(unsigned int iMessage) { } break; case SCI_VCHOMEWRAPEXTEND: { - int homePos = pdoc->VCHomePosition(currentPos); - int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1); - if ((viewLineStart < currentPos) && (viewLineStart > homePos)) + SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret())); + SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1); + if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos)) homePos = viewLineStart; - MovePositionTo(homePos, selStream); + MovePositionTo(homePos, Selection::selStream); SetLastXChosen(); } break; @@ -4737,32 +4936,32 @@ int Editor::KeyCommand(unsigned int iMessage) { } break; case SCI_DELWORDLEFT: { - int startWord = pdoc->NextWordStart(currentPos, -1); - pdoc->DeleteChars(startWord, currentPos - startWord); + int startWord = pdoc->NextWordStart(sel.MainCaret(), -1); + pdoc->DeleteChars(startWord, sel.MainCaret() - startWord); SetLastXChosen(); } break; case SCI_DELWORDRIGHT: { - int endWord = pdoc->NextWordStart(currentPos, 1); - pdoc->DeleteChars(currentPos, endWord - currentPos); + int endWord = pdoc->NextWordStart(sel.MainCaret(), 1); + pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret()); } break; case SCI_DELWORDRIGHTEND: { - int endWord = pdoc->NextWordEnd(currentPos, 1); - pdoc->DeleteChars(currentPos, endWord - currentPos); + int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1); + pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret()); } break; case SCI_DELLINELEFT: { - int line = pdoc->LineFromPosition(currentPos); + int line = pdoc->LineFromPosition(sel.MainCaret()); int start = pdoc->LineStart(line); - pdoc->DeleteChars(start, currentPos - start); + pdoc->DeleteChars(start, sel.MainCaret() - start); SetLastXChosen(); } break; case SCI_DELLINERIGHT: { - int line = pdoc->LineFromPosition(currentPos); + int line = pdoc->LineFromPosition(sel.MainCaret()); int end = pdoc->LineEnd(line); - pdoc->DeleteChars(currentPos, end - currentPos); + pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret()); } break; case SCI_LINECOPY: { @@ -4783,7 +4982,7 @@ int Editor::KeyCommand(unsigned int iMessage) { } break; case SCI_LINEDELETE: { - int line = pdoc->LineFromPosition(currentPos); + int line = pdoc->LineFromPosition(sel.MainCaret()); int start = pdoc->LineStart(line); int end = pdoc->LineStart(line + 1); pdoc->DeleteChars(start, end - start); @@ -4805,39 +5004,39 @@ int Editor::KeyCommand(unsigned int iMessage) { ChangeCaseOfSelection(true); break; case SCI_WORDPARTLEFT: - MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1)); + MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1)); SetLastXChosen(); break; case SCI_WORDPARTLEFTEXTEND: - MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1), selStream); + MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream); SetLastXChosen(); break; case SCI_WORDPARTRIGHT: - MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1)); + MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1)); SetLastXChosen(); break; case SCI_WORDPARTRIGHTEXTEND: - MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1), selStream); + MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream); SetLastXChosen(); break; case SCI_HOMEDISPLAY: MovePositionTo(MovePositionSoVisible( - StartEndDisplayLine(currentPos, true), -1)); + StartEndDisplayLine(sel.MainCaret(), true), -1)); SetLastXChosen(); break; case SCI_HOMEDISPLAYEXTEND: MovePositionTo(MovePositionSoVisible( - StartEndDisplayLine(currentPos, true), -1), selStream); + StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream); SetLastXChosen(); break; case SCI_LINEENDDISPLAY: MovePositionTo(MovePositionSoVisible( - StartEndDisplayLine(currentPos, false), 1)); + StartEndDisplayLine(sel.MainCaret(), false), 1)); SetLastXChosen(); break; case SCI_LINEENDDISPLAYEXTEND: MovePositionTo(MovePositionSoVisible( - StartEndDisplayLine(currentPos, false), 1), selStream); + StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream); SetLastXChosen(); break; } @@ -4874,13 +5073,13 @@ int Editor::GetWhitespaceVisible() { void Editor::Indent(bool forwards) { //Platform::DebugPrintf("INdent %d\n", forwards); - int lineOfAnchor = pdoc->LineFromPosition(anchor); - int lineCurrentPos = pdoc->LineFromPosition(currentPos); + int lineOfAnchor = pdoc->LineFromPosition(sel.MainAnchor()); + int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret()); if (lineOfAnchor == lineCurrentPos) { if (forwards) { pdoc->BeginUndoAction(); ClearSelection(); - if (pdoc->GetColumn(currentPos) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) && + if (pdoc->GetColumn(sel.MainCaret()) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) && pdoc->tabIndents) { int indentation = pdoc->GetLineIndentation(lineCurrentPos); int indentationStep = pdoc->IndentSize(); @@ -4888,22 +5087,22 @@ void Editor::Indent(bool forwards) { SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos)); } else { if (pdoc->useTabs) { - pdoc->InsertChar(currentPos, '\t'); - SetEmptySelection(currentPos + 1); + pdoc->InsertChar(sel.MainCaret(), '\t'); + SetEmptySelection(sel.MainCaret() + 1); } else { int numSpaces = (pdoc->tabInChars) - - (pdoc->GetColumn(currentPos) % (pdoc->tabInChars)); + (pdoc->GetColumn(sel.MainCaret()) % (pdoc->tabInChars)); if (numSpaces < 1) numSpaces = pdoc->tabInChars; for (int i = 0; i < numSpaces; i++) { - pdoc->InsertChar(currentPos + i, ' '); + pdoc->InsertChar(sel.MainCaret() + i, ' '); } - SetEmptySelection(currentPos + numSpaces); + SetEmptySelection(sel.MainCaret() + numSpaces); } } pdoc->EndUndoAction(); } else { - if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) && + if (pdoc->GetColumn(sel.MainCaret()) <= pdoc->GetLineIndentation(lineCurrentPos) && pdoc->tabIndents) { pdoc->BeginUndoAction(); int indentation = pdoc->GetLineIndentation(lineCurrentPos); @@ -4912,23 +5111,23 @@ void Editor::Indent(bool forwards) { SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos)); pdoc->EndUndoAction(); } else { - int newColumn = ((pdoc->GetColumn(currentPos) - 1) / pdoc->tabInChars) * + int newColumn = ((pdoc->GetColumn(sel.MainCaret()) - 1) / pdoc->tabInChars) * pdoc->tabInChars; if (newColumn < 0) newColumn = 0; - int newPos = currentPos; + int newPos = sel.MainCaret(); while (pdoc->GetColumn(newPos) > newColumn) newPos--; SetEmptySelection(newPos); } } } else { - int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor); - int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos); + int anchorPosOnLine = sel.MainAnchor() - pdoc->LineStart(lineOfAnchor); + int currentPosPosOnLine = sel.MainCaret() - pdoc->LineStart(lineCurrentPos); // Multiple lines selected so indent / dedent int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos); int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos); - if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos) + if (pdoc->LineStart(lineBottomSel) == sel.MainAnchor() || pdoc->LineStart(lineBottomSel) == sel.MainCaret()) lineBottomSel--; // If not selecting any characters on a line, do not indent pdoc->BeginUndoAction(); pdoc->Indent(forwards, lineBottomSel, lineTopSel); @@ -5079,75 +5278,60 @@ char *Editor::CopyRange(int start, int end) { return text; } -void Editor::CopySelectionFromRange(SelectionText *ss, bool allowLineCopy, int start, int end) { - bool isLine = allowLineCopy && (start == end); - if (isLine) { - int currentLine = pdoc->LineFromPosition(currentPos); - start = pdoc->LineStart(currentLine); - end = pdoc->LineEnd(currentLine); - - char *text = CopyRange(start, end); - int textLen = text ? strlen(text) : 0; - // include room for \r\n\0 - textLen += 3; - char *textWithEndl = new char[textLen]; - textWithEndl[0] = '\0'; - if (text) - strncat(textWithEndl, text, textLen); - if (pdoc->eolMode != SC_EOL_LF) - strncat(textWithEndl, "\r", textLen); - if (pdoc->eolMode != SC_EOL_CR) - strncat(textWithEndl, "\n", textLen); - ss->Set(textWithEndl, strlen(textWithEndl), - pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true); - delete []text; - } else { - ss->Set(CopyRange(start, end), end - start + 1, - pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false); - } -} - void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) { - if (selType == selStream) { - CopySelectionFromRange(ss, allowLineCopy, SelectionStart(), SelectionEnd()); + if (sel.Empty()) { + if (allowLineCopy) { + int currentLine = pdoc->LineFromPosition(sel.MainCaret()); + int start = pdoc->LineStart(currentLine); + int end = pdoc->LineEnd(currentLine); + + char *text = CopyRange(start, end); + int textLen = text ? strlen(text) : 0; + // include room for \r\n\0 + textLen += 3; + char *textWithEndl = new char[textLen]; + textWithEndl[0] = '\0'; + if (text) + strncat(textWithEndl, text, textLen); + if (pdoc->eolMode != SC_EOL_LF) + strncat(textWithEndl, "\r", textLen); + if (pdoc->eolMode != SC_EOL_CR) + strncat(textWithEndl, "\n", textLen); + ss->Set(textWithEndl, strlen(textWithEndl), + pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true); + delete []text; + } } else { - char *text = 0; - int size = 0; - SelectionLineIterator lineIterator(this); - while (lineIterator.Iterate()) { - size += lineIterator.endPos - lineIterator.startPos; - if (selType != selLines) { - size++; - if (pdoc->eolMode == SC_EOL_CRLF) { - size++; - } + int delimiterLength = 0; + if (sel.selType == Selection::selRectangle) { + if (pdoc->eolMode == SC_EOL_CRLF) { + delimiterLength = 2; + } else { + delimiterLength = 1; } } - if (size > 0) { - text = new char[size + 1]; - if (text) { - int j = 0; - lineIterator.Reset(); - while (lineIterator.Iterate()) { - for (int i = lineIterator.startPos; - i < lineIterator.endPos; - i++) { - text[j++] = pdoc->CharAt(i); - } - if (selType != selLines) { - if (pdoc->eolMode != SC_EOL_LF) { - text[j++] = '\r'; - } - if (pdoc->eolMode != SC_EOL_CR) { - text[j++] = '\n'; - } - } + int size = sel.Length() + delimiterLength * sel.Count(); + char *text = new char[size + 1]; + int j = 0; + for (size_t r=0; r<sel.Count(); r++) { + SelectionRange current = sel.Range(r); + for (int i = current.Start().Position(); + i < current.End().Position(); + i++) { + text[j++] = pdoc->CharAt(i); + } + if (sel.selType == Selection::selRectangle) { + if (pdoc->eolMode != SC_EOL_LF) { + text[j++] = '\r'; + } + if (pdoc->eolMode != SC_EOL_CR) { + text[j++] = '\n'; } - text[size] = '\0'; } } + text[size] = '\0'; ss->Set(text, size + 1, pdoc->dbcsCodePage, - vs.styles[STYLE_DEFAULT].characterSet, selType == selRectangle, selType == selLines); + vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines); } } @@ -5206,12 +5390,12 @@ void Editor::DropAt(int position, const char *value, bool moving, bool rectangul if (inDragDrop == ddDragging) dropWentOutside = false; - int positionWasInSelection = PositionInSelection(position); + bool positionWasInSelection = PositionInSelection(position); bool positionOnEdgeOfSelection = (position == SelectionStart()) || (position == SelectionEnd()); - if ((inDragDrop != ddDragging) || !(0 == positionWasInSelection) || + if ((inDragDrop != ddDragging) || !(positionWasInSelection) || (positionOnEdgeOfSelection && !moving)) { int selStart = SelectionStart(); @@ -5222,14 +5406,13 @@ void Editor::DropAt(int position, const char *value, bool moving, bool rectangul int positionAfterDeletion = position; if ((inDragDrop == ddDragging) && moving) { // Remove dragged out text - if (rectangular || selType == selLines) { - SelectionLineIterator lineIterator(this); - while (lineIterator.Iterate()) { - if (position >= lineIterator.startPos) { - if (position > lineIterator.endPos) { - positionAfterDeletion -= lineIterator.endPos - lineIterator.startPos; + if (rectangular || sel.selType == Selection::selLines) { + for (size_t r=0; r<sel.Count(); r++) { + if (position >= sel.Range(r).Start().Position()) { + if (position > sel.Range(r).End().Position()) { + positionAfterDeletion -= sel.Range(r).End().Position() - sel.Range(r).Start().Position(); } else { - positionAfterDeletion -= position - lineIterator.startPos; + positionAfterDeletion -= position - sel.Range(r).Start().Position(); } } } @@ -5243,12 +5426,12 @@ void Editor::DropAt(int position, const char *value, bool moving, bool rectangul position = positionAfterDeletion; if (rectangular) { - PasteRectangular(position, value, istrlen(value)); + PasteRectangular(SelectionPosition(position), value, istrlen(value)); pdoc->EndUndoAction(); // Should try to select new rectangle but it may not be a rectangle now so just select the drop position SetEmptySelection(position); } else { - position = MovePositionOutsideChar(position, currentPos - position); + position = MovePositionOutsideChar(position, sel.MainCaret() - position); if (pdoc->InsertCString(position, value)) { SetSelection(position + istrlen(value), position); } @@ -5260,62 +5443,39 @@ void Editor::DropAt(int position, const char *value, bool moving, bool rectangul } /** - * @return -1 if given position is before the selection, - * 1 if position is after the selection, - * 0 if position is inside the selection, + * @return true if given position is inside the selection, */ -int Editor::PositionInSelection(int pos) { - pos = MovePositionOutsideChar(pos, currentPos - pos); - if (pos < SelectionStart()) { - return -1; - } - if (pos > SelectionEnd()) { - return 1; - } - if (selType == selStream) { - return 0; - } else { - SelectionLineIterator lineIterator(this); - lineIterator.SetAt(pdoc->LineFromPosition(pos)); - if (pos < lineIterator.startPos) { - return -1; - } else if (pos > lineIterator.endPos) { - return 1; - } else { - return 0; - } +bool Editor::PositionInSelection(int pos) { + pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos); + for (size_t r=0; r<sel.Count(); r++) { + if (sel.Range(r).Contains(pos)) + return true; } + return false; } bool Editor::PointInSelection(Point pt) { - int pos = PositionFromLocation(pt); - if (0 == PositionInSelection(pos)) { - // Probably inside, but we must make a finer test - int selStart, selEnd; - if (selType == selStream) { - selStart = SelectionStart(); - selEnd = SelectionEnd(); - } else { - SelectionLineIterator lineIterator(this); - lineIterator.SetAt(pdoc->LineFromPosition(pos)); - selStart = lineIterator.startPos; - selEnd = lineIterator.endPos; - } - if (pos == selStart) { - // see if just before selection - Point locStart = LocationFromPosition(pos); - if (pt.x < locStart.x) { - return false; + SelectionPosition pos = SPositionFromLocation(pt); + int xPos = XFromPosition(pos); + for (size_t r=0; r<sel.Count(); r++) { + SelectionRange range = sel.Range(r); + if (range.Contains(pos)) { + bool hit = true; + if (pos == range.Start()) { + // see if just before selection + if (pt.x < xPos) { + hit = false; + } } - } - if (pos == selEnd) { - // see if just after selection - Point locEnd = LocationFromPosition(pos); - if (pt.x > locEnd.x) { - return false; + if (pos == range.End()) { + // see if just after selection + if (pt.x > xPos) { + hit = false; + } } + if (hit) + return true; } - return true; } return false; } @@ -5358,25 +5518,25 @@ void Editor::DwellEnd(bool mouseMoved) { void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) { //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop); ptMouseLast = pt; - int newPos = PositionFromLocation(pt); - newPos = MovePositionOutsideChar(newPos, currentPos - newPos); + SelectionPosition newPos = SPositionFromLocation(pt); + newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position()); inDragDrop = ddNone; - moveExtendsSelection = false; + sel.SetMoveExtends(false); bool processed = NotifyMarginClick(pt, shift, ctrl, alt); if (processed) return; - NotifyIndicatorClick(true, newPos, shift, ctrl, alt); + NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt); bool inSelMargin = PointInSelMargin(pt); if (shift & !inSelMargin) { - SetSelection(newPos); + SetSelection(newPos.Position()); } if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) { //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime); SetMouseCapture(true); - SetEmptySelection(newPos); + SetEmptySelection(newPos.Position()); bool doubleClick = false; // Stop mouse button bounce changing selection type if (!Platform::MouseButtonBounce() || curTime != lastClickTime) { @@ -5387,16 +5547,16 @@ void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, b selectionType = selLine; } else { selectionType = selChar; - originalAnchorPos = currentPos; + originalAnchorPos = sel.MainCaret(); } } if (selectionType == selWord) { - if (currentPos >= originalAnchorPos) { // Moved forward - SetSelection(pdoc->ExtendWordSelect(currentPos, 1), + if (sel.MainCaret() >= originalAnchorPos) { // Moved forward + SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), 1), pdoc->ExtendWordSelect(originalAnchorPos, -1)); } else { // Moved backward - SetSelection(pdoc->ExtendWordSelect(currentPos, -1), + SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), -1), pdoc->ExtendWordSelect(originalAnchorPos, 1)); } } else if (selectionType == selLine) { @@ -5404,17 +5564,17 @@ void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, b SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor)); //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos); } else { - SetEmptySelection(currentPos); + SetEmptySelection(sel.MainCaret()); } //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos); if (doubleClick) { NotifyDoubleClick(pt, shift, ctrl, alt); - if (PositionIsHotspot(newPos)) - NotifyHotSpotDoubleClicked(newPos, shift, ctrl, alt); + if (PositionIsHotspot(newPos.Position())) + NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt); } } else { // Single click if (inSelMargin) { - selType = selStream; + sel.selType = Selection::selStream; if (ctrl) { SelectAll(); lastClickTime = curTime; @@ -5428,10 +5588,10 @@ void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, b pdoc->LineStart(lineAnchor)); } else { // Single shift+click in margin: select from line anchor to clicked line - if (anchor > currentPos) - lineAnchor = pdoc->LineFromPosition(anchor - 1); + if (sel.MainAnchor() > sel.MainCaret()) + lineAnchor = pdoc->LineFromPosition(sel.MainAnchor() - 1); else - lineAnchor = pdoc->LineFromPosition(anchor); + lineAnchor = pdoc->LineFromPosition(sel.MainAnchor()); int lineStart = LineFromLocation(pt); LineSelection(lineStart, lineAnchor); //lineAnchor = lineStart; // Keep the same anchor for ButtonMove @@ -5442,7 +5602,7 @@ void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, b selectionType = selLine; } else { if (PointIsHotspot(pt)) { - NotifyHotSpotClicked(newPos, shift, ctrl, alt); + NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt); } if (!shift) { if (PointInSelection(pt) && !SelectionEmpty()) @@ -5453,12 +5613,21 @@ void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, b SetMouseCapture(true); if (inDragDrop != ddInitial) { SetDragPosition(invalidPosition); - if (!shift) { - SetEmptySelection(newPos); + if (ctrl) { + InvalidateSelection(SelectionRange(newPos), true); + sel.AddSelection(newPos); + } else if (!shift) { + InvalidateSelection(SelectionRange(newPos), true); + if (sel.Count() > 1) + Redraw(); + sel.Clear(); + sel.selType = alt ? Selection::selRectangle : Selection::selStream; + SetSelection(newPos, newPos); } - selType = alt ? selRectangle : selStream; + sel.selType = alt ? Selection::selRectangle : Selection::selStream; selectionType = selChar; - originalAnchorPos = currentPos; + originalAnchorPos = sel.MainCaret(); + sel.Rectangular() = SelectionRange(newPos, newPos); SetRectangularRange(); } } @@ -5522,13 +5691,13 @@ void Editor::ButtonMove(Point pt) { DwellEnd(true); } - int movePos = PositionFromLocation(pt); - movePos = MovePositionOutsideChar(movePos, currentPos - movePos); + SelectionPosition movePos = SPositionFromLocation(pt); + movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position()); if (inDragDrop == ddInitial) { if (DragThreshold(ptMouseLast, pt)) { SetMouseCapture(false); - SetDragPosition(movePos); + SetDragPosition(movePos.Position()); CopySelectionRange(&drag); StartDrag(); } @@ -5547,13 +5716,22 @@ void Editor::ButtonMove(Point pt) { // Adjust selection if (posDrag >= 0) { - SetDragPosition(movePos); + SetDragPosition(movePos.Position()); } else { if (selectionType == selChar) { - SetSelection(movePos); + if (sel.IsRectangular()) { + sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor); + SetSelection(movePos, sel.RangeMain().anchor); + } else if (sel.Count() > 1) { + sel.TrimSelection(sel.RangeMain().anchor, movePos); + sel.RangeMain() = SelectionRange(movePos, sel.RangeMain().anchor); + InvalidateSelection(SelectionRange(movePos, sel.RangeMain().anchor), true); + } else { + SetSelection(movePos, sel.RangeMain().anchor); + } } else if (selectionType == selWord) { // Continue selecting by word - if (movePos == originalAnchorPos) { // Didn't move + if (movePos.Position() == originalAnchorPos) { // Didn't move // No need to do anything. Previously this case was lumped // in with "Moved forward", but that can be harmful in this // case: a handler for the NotifyDoubleClick re-adjusts @@ -5563,11 +5741,11 @@ void Editor::ButtonMove(Point pt) { // the ButtonMove() called via Tick() for auto-scrolling // could result in the fancier word selection adjustment // being unmade. - } else if (movePos > originalAnchorPos) { // Moved forward - SetSelection(pdoc->ExtendWordSelect(movePos, 1), + } else if (movePos.Position() > originalAnchorPos) { // Moved forward + SetSelection(pdoc->ExtendWordSelect(movePos.Position(), 1), pdoc->ExtendWordSelect(originalAnchorPos, -1)); } else { // Moved backward - SetSelection(pdoc->ExtendWordSelect(movePos, -1), + SetSelection(pdoc->ExtendWordSelect(movePos.Position(), -1), pdoc->ExtendWordSelect(originalAnchorPos, 1)); } } else { @@ -5576,10 +5754,6 @@ void Editor::ButtonMove(Point pt) { LineSelection(lineMove, lineAnchor); } } - // While dragging to make rectangular selection, we don't want the current - // position to jump to the end of smaller or empty lines. - //xEndSelect = pt.x - vs.fixedColumnWidth + xOffset; - xEndSelect = XFromPosition(movePos); // Autoscroll PRectangle rcClient = GetClientRectangle(); @@ -5597,7 +5771,7 @@ void Editor::ButtonMove(Point pt) { } EnsureCaretVisible(false, false, true); - if (hsStart != -1 && !PositionIsHotspot(movePos)) + if (hsStart != -1 && !PositionIsHotspot(movePos.Position())) SetHotSpotRange(NULL); } else { @@ -5622,11 +5796,11 @@ void Editor::ButtonMove(Point pt) { void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) { //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop); - int newPos = PositionFromLocation(pt); - newPos = MovePositionOutsideChar(newPos, currentPos - newPos); + SelectionPosition newPos = SPositionFromLocation(pt); + newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position()); if (inDragDrop == ddInitial) { inDragDrop = ddNone; - SetEmptySelection(newPos); + SetEmptySelection(newPos.Position()); } if (HaveMouseCapture()) { if (PointInSelMargin(pt)) { @@ -5637,31 +5811,29 @@ void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) { } ptMouseLast = pt; SetMouseCapture(false); - int newPos = PositionFromLocation(pt); - newPos = MovePositionOutsideChar(newPos, currentPos - newPos); - NotifyIndicatorClick(false, newPos, false, false, false); + NotifyIndicatorClick(false, newPos.Position(), false, false, false); if (inDragDrop == ddDragging) { int selStart = SelectionStart(); int selEnd = SelectionEnd(); if (selStart < selEnd) { if (drag.len) { if (ctrl) { - if (pdoc->InsertString(newPos, drag.s, drag.len)) { - SetSelection(newPos, newPos + drag.len); + if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) { + SetSelection(newPos.Position(), newPos.Position() + drag.len); } - } else if (newPos < selStart) { + } else if (newPos.Position() < selStart) { pdoc->DeleteChars(selStart, drag.len); - if (pdoc->InsertString(newPos, drag.s, drag.len)) { - SetSelection(newPos, newPos + drag.len); + if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) { + SetSelection(newPos.Position(), newPos.Position() + drag.len); } - } else if (newPos > selEnd) { + } else if (newPos.Position() > selEnd) { pdoc->DeleteChars(selStart, drag.len); - newPos -= drag.len; - if (pdoc->InsertString(newPos, drag.s, drag.len)) { - SetSelection(newPos, newPos + drag.len); + newPos.Add(-drag.len); + if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) { + SetSelection(newPos.Position(), newPos.Position() + drag.len); } } else { - SetEmptySelection(newPos); + SetEmptySelection(newPos.Position()); } drag.Free(); } @@ -5669,14 +5841,20 @@ void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) { } } else { if (selectionType == selChar) { - SetSelection(newPos); + if (sel.Count() > 1) { + sel.RangeMain() = + SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor); + InvalidateSelection(sel.RangeMain(), true); + } else { + SetSelection(newPos, sel.RangeMain().anchor); + } } } SetRectangularRange(); lastClickTime = curTime; lastClick = pt; lastXChosen = pt.x; - if (selType == selStream) { + if (sel.selType == Selection::selStream) { SetLastXChosen(); } inDragDrop = ddNone; @@ -5825,9 +6003,7 @@ void Editor::SetDocPointer(Document *document) { pdoc->AddRef(); // Ensure all positions within document - selType = selStream; - currentPos = 0; - anchor = 0; + sel.Clear(); targetStart = 0; targetEnd = 0; @@ -5898,7 +6074,7 @@ void Editor::ToggleContraction(int line) { if (lineMaxSubord > line) { cs.SetVisible(line + 1, lineMaxSubord, false); - int lineCurrent = pdoc->LineFromPosition(currentPos); + int lineCurrent = pdoc->LineFromPosition(sel.MainCaret()); if (lineCurrent > line && lineCurrent <= lineMaxSubord) { // This does not re-expand the fold EnsureCaretVisible(); @@ -6026,7 +6202,7 @@ void Editor::AddStyledText(char *buffer, int appendLength) { pdoc->SetStyles(textLength, text); delete []text; } - SetEmptySelection(currentPos + textLength); + SetEmptySelection(sel.MainCaret() + textLength); } static bool ValidMargin(unsigned long wParam) { @@ -6236,7 +6412,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { nEnd = pdoc->Length(); if (nStart < 0) nStart = nEnd; // Remove selection - selType = selStream; + sel.selType = Selection::selStream; SetSelection(nEnd, nStart); EnsureCaretVisible(); } @@ -6244,17 +6420,16 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_GETSELTEXT: { if (lParam == 0) { - if (selType == selStream) { + if (sel.selType == Selection::selStream) { return 1 + SelectionEnd() - SelectionStart(); } else { // TODO: why is selLines handled the slow way? int size = 0; int extraCharsPerLine = 0; - if (selType != selLines) + if (sel.selType != Selection::selLines) extraCharsPerLine = (pdoc->eolMode == SC_EOL_CRLF) ? 2 : 1; - SelectionLineIterator lineIterator(this); - while (lineIterator.Iterate()) { - size += lineIterator.endPos + extraCharsPerLine - lineIterator.startPos; + for (size_t r=0; r<sel.Count(); r++) { + size += sel.Range(r).Length() + extraCharsPerLine; } return 1 + size; @@ -6302,9 +6477,9 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { pdoc->BeginUndoAction(); ClearSelection(); char *replacement = CharPtrFromSPtr(lParam); - pdoc->InsertCString(currentPos, replacement); + pdoc->InsertCString(sel.MainCaret(), replacement); pdoc->EndUndoAction(); - SetEmptySelection(currentPos + istrlen(replacement)); + SetEmptySelection(sel.MainCaret() + istrlen(replacement)); EnsureCaretVisible(); } break; @@ -6324,12 +6499,12 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return targetEnd; case SCI_TARGETFROMSELECTION: - if (currentPos < anchor) { - targetStart = currentPos; - targetEnd = anchor; + if (sel.MainCaret() < sel.MainAnchor()) { + targetStart = sel.MainCaret(); + targetEnd = sel.MainAnchor(); } else { - targetStart = anchor; - targetEnd = currentPos; + targetStart = sel.MainAnchor(); + targetEnd = sel.MainCaret(); } break; @@ -6454,7 +6629,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { if (lParam == 0) return 0; pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam); - SetEmptySelection(currentPos + wParam); + SetEmptySelection(sel.MainCaret() + wParam); return 0; } @@ -6542,32 +6717,32 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return pdoc->CharAt(wParam); case SCI_SETCURRENTPOS: - SetSelection(wParam, anchor); + SetSelection(wParam, sel.MainAnchor()); break; case SCI_GETCURRENTPOS: - return currentPos; + return sel.MainCaret(); case SCI_SETANCHOR: - SetSelection(currentPos, wParam); + SetSelection(sel.MainCaret(), wParam); break; case SCI_GETANCHOR: - return anchor; + return sel.MainAnchor(); case SCI_SETSELECTIONSTART: - SetSelection(Platform::Maximum(currentPos, wParam), wParam); + SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam); break; case SCI_GETSELECTIONSTART: - return Platform::Minimum(anchor, currentPos); + return Platform::Minimum(sel.MainAnchor(), sel.MainCaret()); case SCI_SETSELECTIONEND: - SetSelection(wParam, Platform::Minimum(anchor, wParam)); + SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam)); break; case SCI_GETSELECTIONEND: - return Platform::Maximum(anchor, currentPos); + return Platform::Maximum(sel.MainAnchor(), sel.MainCaret()); case SCI_SETPRINTMAGNIFICATION: printMagnification = wParam; @@ -6663,7 +6838,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { break; case SCI_GETCURLINE: { - int lineCurrentPos = pdoc->LineFromPosition(currentPos); + int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret()); int lineStart = pdoc->LineStart(lineCurrentPos); unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1); if (lParam == 0) { @@ -6676,7 +6851,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { ptr[iPlace++] = pdoc->CharAt(iChar); } ptr[iPlace] = '\0'; - return currentPos - lineStart; + return sel.MainCaret() - lineStart; } case SCI_GETENDSTYLED: @@ -7621,7 +7796,7 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { case SCI_CONVERTEOLS: pdoc->ConvertLineEnds(wParam); - SetSelection(currentPos, anchor); // Ensure selection inside document + SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document return 0; case SCI_SETLENGTHFORENCODE: @@ -7629,48 +7804,57 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { return 0; case SCI_SELECTIONISRECTANGLE: - return selType == selRectangle ? 1 : 0; + return sel.selType == Selection::selRectangle ? 1 : 0; case SCI_SETSELECTIONMODE: { switch (wParam) { case SC_SEL_STREAM: - moveExtendsSelection = !moveExtendsSelection || (selType != selStream); - selType = selStream; + sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream)); + sel.selType = Selection::selStream; break; case SC_SEL_RECTANGLE: - moveExtendsSelection = !moveExtendsSelection || (selType != selRectangle); - selType = selRectangle; + sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle)); + sel.selType = Selection::selRectangle; break; case SC_SEL_LINES: - moveExtendsSelection = !moveExtendsSelection || (selType != selLines); - selType = selLines; + sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines)); + sel.selType = Selection::selLines; + break; + case SC_SEL_THIN: + sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin)); + sel.selType = Selection::selThin; break; default: - moveExtendsSelection = !moveExtendsSelection || (selType != selStream); - selType = selStream; + sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream)); + sel.selType = Selection::selStream; } - InvalidateSelection(currentPos, anchor, true); + InvalidateSelection(sel.RangeMain(), true); } case SCI_GETSELECTIONMODE: - switch (selType) { - case selStream: + switch (sel.selType) { + case Selection::selStream: return SC_SEL_STREAM; - case selRectangle: + case Selection::selRectangle: return SC_SEL_RECTANGLE; - case selLines: + case Selection::selLines: return SC_SEL_LINES; + case Selection::selThin: + return SC_SEL_THIN; default: // ?! return SC_SEL_STREAM; } - case SCI_GETLINESELSTARTPOSITION: { - SelectionLineIterator lineIterator(this); - lineIterator.SetAt(wParam); - return lineIterator.startPos; - } + case SCI_GETLINESELSTARTPOSITION: case SCI_GETLINESELENDPOSITION: { - SelectionLineIterator lineIterator(this); - lineIterator.SetAt(wParam); - return lineIterator.endPos; + for (size_t r=0; r<sel.Count(); r++) { + int posLineStart = pdoc->LineStart(wParam); + int posLineEnd = pdoc->LineEnd(wParam); + SelectionPosition spStart; + SelectionPosition spEnd; + if (sel.Range(r).Intersect(posLineStart, posLineEnd, spStart, spEnd)) { + return (iMessage == SCI_GETLINESELSTARTPOSITION) ? spStart.Position() : spEnd.Position(); + } + } + return INVALID_POSITION; } case SCI_SETOVERTYPE: @@ -7916,6 +8100,147 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) { pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE); break; + case SCI_SETMULTILINECARET: + multiLineCaret = wParam != 0; + InvalidateCaret(); + break; + + case SCI_GETMULTILINECARET: + return multiLineCaret; + + case SCI_SETMULTILINECARETBLINKS: + multiLineCaretBlinks = wParam != 0; + InvalidateCaret(); + break; + + case SCI_GETMULTILINECARETBLINKS: + return multiLineCaretBlinks; + + case SCI_GETSELECTIONS: + return sel.Count(); + + case SCI_CLEARSELECTIONS: + sel.Clear(); + Redraw(); + break; + + case SCI_SETSELECTION: + sel.Clear(); + sel.RangeMain() = SelectionRange(wParam, lParam); + Redraw(); + break; + + case SCI_ADDSELECTION: + sel.AddSelection(SelectionPosition(wParam), SelectionPosition(lParam), false); + Redraw(); + break; + + case SCI_SETMAINSELECTION: + sel.SetMain(wParam); + Redraw(); + break; + + case SCI_GETMAINSELECTION: + return sel.Main(); + + case SCI_SETSELECTIONNCARET: + sel.Range(wParam).caret.SetPosition(lParam); + Redraw(); + break; + + case SCI_GETSELECTIONNCARET: + return sel.Range(wParam).caret.Position(); + + case SCI_SETSELECTIONNANCHOR: + sel.Range(wParam).anchor.SetPosition(lParam); + Redraw(); + break; + case SCI_GETSELECTIONNANCHOR: + return sel.Range(wParam).anchor.Position(); + + case SCI_SETSELECTIONNCARETVIRTUALSPACE: + sel.Range(wParam).caret.SetVirtualSpace(lParam); + Redraw(); + break; + + case SCI_GETSELECTIONNCARETVIRTUALSPACE: + return sel.Range(wParam).caret.VirtualSpace(); + + case SCI_SETSELECTIONNANCHORVIRTUALSPACE: + sel.Range(wParam).anchor.SetVirtualSpace(lParam); + Redraw(); + break; + + case SCI_GETSELECTIONNANCHORVIRTUALSPACE: + return sel.Range(wParam).anchor.VirtualSpace(); + + case SCI_SETSELECTIONNSTART: + sel.Range(wParam).anchor.SetPosition(lParam); + Redraw(); + break; + + case SCI_GETSELECTIONNSTART: + return sel.Range(wParam).Start().Position(); + + case SCI_SETSELECTIONNEND: + sel.Range(wParam).caret.SetPosition(lParam); + Redraw(); + break; + + case SCI_GETSELECTIONNEND: + return sel.Range(wParam).End().Position(); + + case SCI_SETRECTANGULARSELECTIONCARET: + if (!sel.IsRectangular()) + sel.Clear(); + sel.selType = Selection::selRectangle; + sel.Rectangular().caret.SetPosition(wParam); + Redraw(); + break; + + case SCI_GETRECTANGULARSELECTIONCARET: + return sel.Rectangular().caret.Position(); + + case SCI_SETRECTANGULARSELECTIONANCHOR: + if (!sel.IsRectangular()) + sel.Clear(); + sel.selType = Selection::selRectangle; + sel.Rectangular().anchor.SetPosition(wParam); + Redraw(); + break; + + case SCI_GETRECTANGULARSELECTIONANCHOR: + return sel.Rectangular().anchor.Position(); + + case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE: + if (!sel.IsRectangular()) + sel.Clear(); + sel.selType = Selection::selRectangle; + sel.Rectangular().caret.SetVirtualSpace(wParam); + Redraw(); + break; + + case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE: + return sel.Rectangular().caret.VirtualSpace(); + + case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE: + if (!sel.IsRectangular()) + sel.Clear(); + sel.selType = Selection::selRectangle; + sel.Rectangular().anchor.SetVirtualSpace(wParam); + Redraw(); + break; + + case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE: + return sel.Rectangular().anchor.VirtualSpace(); + + case SCI_SETVIRTUALSPACEOPTIONS: + virtualSpaceOptions = wParam; + break; + + case SCI_GETVIRTUALSPACEOPTIONS: + return virtualSpaceOptions; + default: return DefWndProc(iMessage, wParam, lParam); } diff --git a/src/Editor.h b/src/Editor.h index a318e6282..ab1e9a1e8 100644 --- a/src/Editor.h +++ b/src/Editor.h @@ -143,6 +143,10 @@ protected: // ScintillaBase subclass needs access to much of Editor bool verticalScrollBarVisible; bool endAtLastLine; bool caretSticky; + bool multiLineCaret; + bool multiLineCaretBlinks; + + int virtualSpaceOptions; Surface *pixmapLine; Surface *pixmapSelMargin; @@ -176,8 +180,6 @@ protected: // ScintillaBase subclass needs access to much of Editor int lastXChosen; int lineAnchor; int originalAnchorPos; - int currentPos; - int anchor; int targetStart; int targetEnd; int searchFlags; @@ -199,11 +201,7 @@ protected: // ScintillaBase subclass needs access to much of Editor int modEventMask; SelectionText drag; - enum selTypes { noSel, selStream, selRectangle, selLines }; - selTypes selType; - bool moveExtendsSelection; - int xStartSelect; ///< x position of start of rectangular selection - int xEndSelect; ///< x position of end of rectangular selection + Selection sel; bool primarySelection; int caretXPolicy; @@ -259,9 +257,13 @@ protected: // ScintillaBase subclass needs access to much of Editor int LinesOnScreen(); int LinesToScroll(); int MaxScrollPos(); + SelectionPosition ClampPositionIntoDocument(SelectionPosition sp) const; Point LocationFromPosition(int pos); int XFromPosition(int pos); + int XFromPosition(SelectionPosition sp); + SelectionPosition SPositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false, bool virtualSpace=true); int PositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false); + SelectionPosition SPositionFromLineX(int lineDoc, int x); int PositionFromLineX(int line, int x); int LineFromLocation(Point pt); void SetTopLine(int topLineNew); @@ -278,15 +280,21 @@ protected: // ScintillaBase subclass needs access to much of Editor int SelectionStart(); int SelectionEnd(); void SetRectangularRange(); - void InvalidateSelection(int currentPos_, int anchor_, bool invalidateWholeSelection); + void InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection=false); + void SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_); void SetSelection(int currentPos_, int anchor_); + void SetSelection(SelectionPosition currentPos_); void SetSelection(int currentPos_); + void SetEmptySelection(SelectionPosition currentPos_); void SetEmptySelection(int currentPos_); bool RangeContainsProtected(int start, int end) const; bool SelectionContainsProtected(); int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true) const; - int MovePositionTo(int newPos, selTypes sel=noSel, bool ensureVisible=true); - int MovePositionSoVisible(int pos, int moveDir); + SelectionPosition MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd=true) const; + int MovePositionTo(SelectionPosition newPos, Selection::selTypes sel=Selection::noSel, bool ensureVisible=true); + int MovePositionTo(int newPos, Selection::selTypes sel=Selection::noSel, bool ensureVisible=true); + SelectionPosition MovePositionSoVisible(SelectionPosition pos, int moveDir); + SelectionPosition MovePositionSoVisible(int pos, int moveDir); void SetLastXChosen(); void ScrollTo(int line, bool moveThumb=true); @@ -324,8 +332,10 @@ protected: // ScintillaBase subclass needs access to much of Editor void DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart, PRectangle rcLine, LineLayout *ll, int subLine); 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); void DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret); + void DrawCarets(Surface *surface, ViewStyle &vsDraw, int line, int xStart, + PRectangle rcLine, LineLayout *ll, int subLine); void RefreshPixMaps(Surface *surfaceWindow); void Paint(Surface *surfaceWindow, PRectangle rcArea); long FormatRange(bool draw, Sci_RangeToFormat *pfr); @@ -344,7 +354,7 @@ protected: // ScintillaBase subclass needs access to much of Editor void ClearAll(); void ClearDocumentStyle(); void Cut(); - void PasteRectangular(int pos, const char *ptr, int len); + void PasteRectangular(SelectionPosition pos, const char *ptr, int len); virtual void Copy() = 0; virtual void CopyAllowLine(); virtual bool CanPaste(); @@ -384,14 +394,14 @@ protected: // ScintillaBase subclass needs access to much of Editor void NotifyStyleNeeded(Document *doc, void *userData, int endPos); void NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam); - void PageMove(int direction, selTypes sel=noSel, bool stuttered = false); + void PageMove(int direction, Selection::selTypes sel=Selection::noSel, bool stuttered = false); void ChangeCaseOfSelection(bool makeUpperCase); void LineTranspose(); void Duplicate(bool forLine); virtual void CancelModes(); void NewLine(); - void CursorUpOrDown(int direction, selTypes sel=noSel); - void ParaUpOrDown(int direction, selTypes sel=noSel); + void CursorUpOrDown(int direction, Selection::selTypes sel=Selection::noSel); + void ParaUpOrDown(int direction, Selection::selTypes sel=Selection::noSel); int StartEndDisplayLine(int pos, bool start); virtual int KeyCommand(unsigned int iMessage); virtual int KeyDefault(int /* key */, int /*modifiers*/); @@ -410,7 +420,6 @@ protected: // ScintillaBase subclass needs access to much of Editor virtual void CopyToClipboard(const SelectionText &selectedText) = 0; char *CopyRange(int start, int end); - void CopySelectionFromRange(SelectionText *ss, bool allowLineCopy, int start, int end); void CopySelectionRange(SelectionText *ss, bool allowLineCopy=false); void CopyRangeToClipboard(int start, int end); void CopyText(int length, const char *text); @@ -419,9 +428,8 @@ protected: // ScintillaBase subclass needs access to much of Editor virtual bool DragThreshold(Point ptStart, Point ptNow); virtual void StartDrag(); void DropAt(int position, const char *value, bool moving, bool rectangular); - /** PositionInSelection returns 0 if position in selection, -1 if position before selection, and 1 if after. - * Before means either before any line of selection or before selection on its line, with a similar meaning to after. */ - int PositionInSelection(int pos); + /** PositionInSelection returns true if position in selection. */ + bool PositionInSelection(int pos); bool PointInSelection(Point pt); bool PointInSelMargin(Point pt); void LineSelection(int lineCurrent_, int lineAnchor_); diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index 71b408236..258a14860 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -28,6 +28,7 @@ #include "CharClassify.h" #include "Decoration.h" #include "Document.h" +#include "Selection.h" #include "PositionCache.h" #ifdef SCI_NAMESPACE @@ -46,11 +47,11 @@ LineLayout::LineLayout(int maxLineLength_) : inCache(false), maxLineLength(-1), numCharsInLine(0), + numCharsBeforeEOL(0), validity(llInvalid), xHighlightGuide(0), highlightColumn(0), - selStart(0), - selEnd(0), + psel(NULL), containsCaret(false), edgeColumn(0), chars(0), @@ -115,12 +116,7 @@ int LineLayout::LineLastVisible(int line) const { if (line < 0) { return 0; } else if ((line >= lines-1) || !lineStarts) { - int startLine = LineStart(line); - int endLine = numCharsInLine; - while ((endLine > startLine) && IsEOLChar(chars[endLine-1])) { - endLine--; - } - return endLine; + return numCharsBeforeEOL; } else { return lineStarts[line+1]; } @@ -412,9 +408,13 @@ BreakFinder::BreakFinder(LineLayout *ll_, int lineStart_, int lineEnd_, int posL nextBreak--; } - if (ll->selStart != ll->selEnd) { - Insert(ll->selStart - posLineStart - 1); - Insert(ll->selEnd - posLineStart - 1); + for (size_t r=0; r<ll->psel->Count(); r++) { + SelectionPosition spStart; + SelectionPosition spEnd; + if (ll->psel->Range(r).Intersect(posLineStart, posLineStart + lineEnd, spStart, spEnd)) { + Insert(spStart.Position() - posLineStart - 1); + Insert(spEnd.Position() - posLineStart - 1); + } } Insert(ll->edgeColumn - 1); diff --git a/src/PositionCache.h b/src/PositionCache.h index 6c4c62efd..bd984d40b 100644 --- a/src/PositionCache.h +++ b/src/PositionCache.h @@ -2,7 +2,7 @@ /** @file PositionCache.h ** Classes for caching layout information. **/ -// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org> +// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org> // The License.txt file describes the conditions under which this software may be distributed. #ifndef POSITIONCACHE_H @@ -30,11 +30,11 @@ public: enum { wrapWidthInfinite = 0x7ffffff }; int maxLineLength; int numCharsInLine; + int numCharsBeforeEOL; enum validLevel { llInvalid, llCheckTextAndStyle, llPositions, llLines } validity; int xHighlightGuide; bool highlightColumn; - int selStart; - int selEnd; + Selection *psel; bool containsCaret; int edgeColumn; char *chars; diff --git a/src/ScintillaBase.cxx b/src/ScintillaBase.cxx index 31e580336..b47986ff0 100644 --- a/src/ScintillaBase.cxx +++ b/src/ScintillaBase.cxx @@ -36,6 +36,7 @@ #include "CharClassify.h" #include "Decoration.h" #include "Document.h" +#include "Selection.h" #include "PositionCache.h" #include "Editor.h" #include "ScintillaBase.h" @@ -190,7 +191,7 @@ int ScintillaBase::KeyCommand(unsigned int iMessage) { ct.CallTipCancel(); } if ((iMessage == SCI_DELETEBACK) || (iMessage == SCI_DELETEBACKNOTLINE)) { - if (currentPos <= ct.posStartCallTip) { + if (sel.MainCaret() <= ct.posStartCallTip) { ct.CallTipCancel(); } } @@ -212,24 +213,24 @@ void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) { const char *typeSep = strchr(list, ac.GetTypesep()); size_t lenInsert = (typeSep) ? (typeSep-list) : strlen(list); if (ac.ignoreCase) { - SetEmptySelection(currentPos - lenEntered); - pdoc->DeleteChars(currentPos, lenEntered); - SetEmptySelection(currentPos); - pdoc->InsertString(currentPos, list, lenInsert); - SetEmptySelection(currentPos + lenInsert); + SetEmptySelection(sel.MainCaret() - lenEntered); + pdoc->DeleteChars(sel.MainCaret(), lenEntered); + SetEmptySelection(sel.MainCaret()); + pdoc->InsertString(sel.MainCaret(), list, lenInsert); + SetEmptySelection(sel.MainCaret() + lenInsert); } else { - SetEmptySelection(currentPos); - pdoc->InsertString(currentPos, list + lenEntered, lenInsert - lenEntered); - SetEmptySelection(currentPos + lenInsert - lenEntered); + SetEmptySelection(sel.MainCaret()); + pdoc->InsertString(sel.MainCaret(), list + lenEntered, lenInsert - lenEntered); + SetEmptySelection(sel.MainCaret() + lenInsert - lenEntered); } return; } } - ac.Start(wMain, idAutoComplete, currentPos, LocationFromPosition(currentPos), + ac.Start(wMain, idAutoComplete, sel.MainCaret(), LocationFromPosition(sel.MainCaret()), lenEntered, vs.lineHeight, IsUnicodeMode()); PRectangle rcClient = GetClientRectangle(); - Point pt = LocationFromPosition(currentPos - lenEntered); + Point pt = LocationFromPosition(sel.MainCaret() - lenEntered); PRectangle rcPopupBounds = wMain.GetMonitorRect(pt); if (rcPopupBounds.Height() == 0) rcPopupBounds = rcClient; @@ -239,7 +240,7 @@ void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) { if (pt.x >= rcClient.right - widthLB) { HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB); Redraw(); - pt = LocationFromPosition(currentPos); + pt = LocationFromPosition(sel.MainCaret()); } PRectangle rcac; rcac.left = pt.x - ac.lb->CaretFromEdge(); @@ -305,7 +306,7 @@ void ScintillaBase::AutoCompleteMoveToCurrentWord() { char wordCurrent[1000]; int i; int startWord = ac.posStart - ac.startLen; - for (i = startWord; i < currentPos && i - startWord < 1000; i++) + for (i = startWord; i < sel.MainCaret() && i - startWord < 1000; i++) wordCurrent[i - startWord] = pdoc->CharAt(i); wordCurrent[Platform::Minimum(i - startWord, 999)] = '\0'; ac.Select(wordCurrent); @@ -322,9 +323,9 @@ void ScintillaBase::AutoCompleteCharacterAdded(char ch) { } void ScintillaBase::AutoCompleteCharacterDeleted() { - if (currentPos < ac.posStart - ac.startLen) { + if (sel.MainCaret() < ac.posStart - ac.startLen) { AutoCompleteCancel(); - } else if (ac.cancelAtStartPos && (currentPos <= ac.posStart)) { + } else if (ac.cancelAtStartPos && (sel.MainCaret() <= ac.posStart)) { AutoCompleteCancel(); } else { AutoCompleteMoveToCurrentWord(); @@ -367,7 +368,7 @@ void ScintillaBase::AutoCompleteCompleted() { if (listType > 0) return; - Position endPos = currentPos; + Position endPos = sel.MainCaret(); if (ac.dropRestOfWord) endPos = pdoc->ExtendWordSelect(endPos, 1, true); if (endPos < firstPos) @@ -401,7 +402,7 @@ void ScintillaBase::CallTipShow(Point pt, const char *defn) { if (ct.UseStyleCallTip()) { ct.SetForeBack(vs.styles[STYLE_CALLTIP].fore, vs.styles[STYLE_CALLTIP].back); } - PRectangle rc = ct.CallTipStart(currentPos, pt, + PRectangle rc = ct.CallTipStart(sel.MainCaret(), pt, defn, vs.styles[ctStyle].fontName, vs.styles[ctStyle].sizeZoomed, @@ -436,10 +437,10 @@ void ScintillaBase::ContextMenu(Point pt) { AddToPopUp("Undo", idcmdUndo, writable && pdoc->CanUndo()); AddToPopUp("Redo", idcmdRedo, writable && pdoc->CanRedo()); AddToPopUp(""); - AddToPopUp("Cut", idcmdCut, writable && currentPos != anchor); - AddToPopUp("Copy", idcmdCopy, currentPos != anchor); + AddToPopUp("Cut", idcmdCut, writable && !sel.Empty()); + AddToPopUp("Copy", idcmdCopy, !sel.Empty()); AddToPopUp("Paste", idcmdPaste, writable && WndProc(SCI_CANPASTE, 0, 0)); - AddToPopUp("Delete", idcmdDelete, writable && currentPos != anchor); + AddToPopUp("Delete", idcmdDelete, writable && !sel.Empty()); AddToPopUp(""); AddToPopUp("Select All", idcmdSelectAll); popup.Show(pt, wMain); diff --git a/src/Selection.cxx b/src/Selection.cxx new file mode 100644 index 000000000..224fafc69 --- /dev/null +++ b/src/Selection.cxx @@ -0,0 +1,345 @@ +// Scintilla source code edit control +/** @file Selection.cxx + ** Classes maintaining the selection. + **/ +// Copyright 2009 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#include <stdlib.h> + +#include "Platform.h" + +#include "Scintilla.h" + +#include "Selection.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +void SelectionPosition::MoveForInsertDelete(bool insertion, int startChange, int length) { + if (insertion) { + if (position > startChange) { + position += length; + } + } else { + if (position > startChange) { + int endDeletion = startChange + length; + if (position > endDeletion) { + position -= length; + } else { + position = startChange; + } + } + } +} + +bool SelectionPosition::operator <(const SelectionPosition &other) const { + if (position == other.position) + return virtualSpace < other.virtualSpace; + else + return position < other.position; +} + +bool SelectionPosition::operator >(const SelectionPosition &other) const { + if (position == other.position) + return virtualSpace > other.virtualSpace; + else + return position > other.position; +} + +bool SelectionPosition::operator <=(const SelectionPosition &other) const { + if (position == other.position && virtualSpace == other.virtualSpace) + return true; + else + return other > *this; +} + +bool SelectionPosition::operator >=(const SelectionPosition &other) const { + if (position == other.position && virtualSpace == other.virtualSpace) + return true; + else + return *this > other; +} + +int SelectionRange::Length() const { + if (anchor > caret) { + return anchor.Position() - caret.Position(); + } else { + return caret.Position() - anchor.Position(); + } +} + +bool SelectionRange::Contains(int pos) const { + if (anchor > caret) + return (pos >= caret.Position()) && (pos <= anchor.Position()); + else + return (pos >= anchor.Position()) && (pos <= caret.Position()); +} + +bool SelectionRange::Contains(SelectionPosition sp) const { + if (anchor > caret) + return (sp >= caret) && (sp <= anchor); + else + return (sp >= anchor) && (sp <= caret); +} + +bool SelectionRange::ContainsCharacter(int posCharacter) const { + if (anchor > caret) + return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position()); + else + return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position()); +} + +bool SelectionRange::Intersect(int start, int end, SelectionPosition &selStart, SelectionPosition &selEnd) const { + SelectionPosition spEnd(end, 100000); // Large amount of virtual space + SelectionPosition first; + SelectionPosition last; + if (anchor > caret) { + first = caret; + last = anchor; + } else { + first = anchor; + last = caret; + } + if ((first < spEnd) && (last.Position() > start)) { + if (start > first.Position()) + selStart = SelectionPosition(start); + else + selStart = first; + if (spEnd < last) + selEnd = SelectionPosition(end); + else + selEnd = last; + return true; + } else { + return false; + } +} + +bool SelectionRange::Trim(SelectionPosition startPos, SelectionPosition endPos) { + SelectionPosition startRange = startPos; + SelectionPosition endRange = endPos; + if (startPos > endPos) { + startRange = endPos; + endRange = startPos; + } + SelectionPosition start = Start(); + SelectionPosition end = End(); + PLATFORM_ASSERT(start <= end); + PLATFORM_ASSERT(startRange <= endRange); + if ((startRange <= end) && (endRange >= start)) { + if ((start > startRange) && (end < endRange)) { + // Completely covered by range -> empty at start + end = start; + } else if ((start < startRange) && (end > endRange)) { + // Completely covers range -> empty at start + end = start; + } else if (start <= startRange) { + // Trim end + end = startRange; + } else { // + PLATFORM_ASSERT(end >= endRange); + // Trim start + start = endRange; + } + if (anchor > caret) { + caret = start; + anchor = end; + } else { + anchor = start; + caret = end; + } + return Empty(); + } else { + return false; + } +} + +// If range is all virtual collapse to start of virtual space +void SelectionRange::MinimizeVirtualSpace() { + if (caret.Position() == anchor.Position()) { + int virtualSpace = caret.VirtualSpace(); + if (virtualSpace > anchor.VirtualSpace()) + virtualSpace = anchor.VirtualSpace(); + caret.SetVirtualSpace(virtualSpace); + anchor.SetVirtualSpace(virtualSpace); + } +} + +void Selection::Allocate() { + if (nRanges >= allocated) { + size_t allocateNew = (allocated + 1) * 2; + SelectionRange *rangesNew = new SelectionRange[allocateNew]; + for (size_t r=0; r<Count(); r++) + rangesNew[r] = ranges[r]; + delete []ranges; + ranges = rangesNew; + allocated = allocateNew; + } +} + +Selection::Selection() : ranges(0), allocated(0), nRanges(0), mainRange(0), moveExtends(false), selType(selStream) { + AddSelection(SelectionPosition(0)); +} + +Selection::~Selection() { + delete []ranges; +} + +bool Selection::IsRectangular() const { + return (selType == selRectangle) || (selType == selThin); +} + +int Selection::MainCaret() const { + return ranges[mainRange].caret.Position(); +} + +int Selection::MainAnchor() const { + return ranges[mainRange].anchor.Position(); +} + +SelectionRange &Selection::Rectangular() { + return rangeRectangular; +} + +size_t Selection::Count() const { + return nRanges; +} + +size_t Selection::Main() const { + return mainRange; +} + +void Selection::SetMain(size_t r) { + PLATFORM_ASSERT(r < nRanges); + mainRange = r; +} + +SelectionRange &Selection::Range(size_t r) { + return ranges[r]; +} + +SelectionRange &Selection::RangeMain() { + return ranges[mainRange]; +} + +void Selection::ClearVirtualSpace(size_t r) { + ranges[r].ClearVirtualSpace(); +} + +bool Selection::MoveExtends() const { + return moveExtends; +} + +void Selection::SetMoveExtends(bool moveExtends_) { + moveExtends = moveExtends_; +} + +bool Selection::Empty() const { + for (size_t i=0; i<nRanges; i++) { + if (!ranges[i].Empty()) + return false; + } + return true; +} + +SelectionPosition Selection::Last() const { + SelectionPosition lastPosition; + for (size_t i=0; i<nRanges; i++) { + if (lastPosition < ranges[i].caret) + lastPosition = ranges[i].caret; + if (lastPosition < ranges[i].anchor) + lastPosition = ranges[i].anchor; + } + return lastPosition; +} + +int Selection::Length() const { + int len = 0; + for (size_t i=0; i<nRanges; i++) { + len += ranges[i].Length(); + } + return len; +} + +void Selection::MovePositions(bool insertion, int startChange, int length) { + for (size_t i=0; i<nRanges; i++) { + ranges[i].caret.MoveForInsertDelete(insertion, startChange, length); + ranges[i].anchor.MoveForInsertDelete(insertion, startChange, length); + } +} + +void Selection::TrimSelection(SelectionPosition startPos, SelectionPosition endPos) { + for (size_t i=0; i<nRanges;) { + if ((i != mainRange) && (ranges[i].Trim(startPos, endPos))) { + // Trimmed to empty so remove + for (size_t j=i;j<nRanges-1;j++) { + ranges[j] = ranges[j+1]; + if (j == mainRange-1) + mainRange--; + } + nRanges--; + } else { + i++; + } + } +} + +void Selection::AddSelection(SelectionPosition spPos) { + Allocate(); + TrimSelection(spPos, spPos); + ranges[nRanges] = SelectionRange(spPos); + mainRange = nRanges; + nRanges++; +} + +void Selection::AddSelection(SelectionPosition spStartPos, SelectionPosition spEndPos, bool anchorLeft) { + Allocate(); + TrimSelection(spStartPos, spEndPos); + ranges[nRanges].caret = anchorLeft ? spEndPos : spStartPos; + ranges[nRanges].anchor = anchorLeft ? spStartPos : spEndPos; + mainRange = nRanges; + nRanges++; +} + +bool Selection::CharacterInSelection(int posCharacter) const { + for (size_t i=0; i<nRanges; i++) { + if (ranges[i].ContainsCharacter(posCharacter)) + return true; + } + return false; +} + +bool Selection::InSelectionForEOL(int pos) const { + for (size_t i=0; i<nRanges; i++) { + if (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position())) + return true; + } + return false; +} + +int Selection::VirtualSpaceFor(int pos) const { + int virtualSpace = 0; + for (size_t i=0; i<nRanges; i++) { + if ((ranges[i].caret.Position() == pos) && (virtualSpace < ranges[i].caret.VirtualSpace())) + virtualSpace = ranges[i].caret.VirtualSpace(); + if ((ranges[i].anchor.Position() == pos) && (virtualSpace < ranges[i].anchor.VirtualSpace())) + virtualSpace = ranges[i].anchor.VirtualSpace(); + } + return virtualSpace; +} + +void Selection::Clear() { + nRanges = 1; + mainRange = 0; + selType = selStream; + moveExtends = false; + ranges[mainRange].Reset(); + rangeRectangular.Reset(); +} + +void Selection::EmptyRanges() { + nRanges = 0; + mainRange = 0; +} diff --git a/src/Selection.h b/src/Selection.h new file mode 100644 index 000000000..fe6a1c464 --- /dev/null +++ b/src/Selection.h @@ -0,0 +1,142 @@ +// Scintilla source code edit control +/** @file Selection.h + ** Classes maintaining the selection. + **/ +// Copyright 2009 by Neil Hodgson <neilh@scintilla.org> +// The License.txt file describes the conditions under which this software may be distributed. + +#ifndef SELECTION_H +#define SELECTION_H + +#ifdef SCI_NAMESPACE +namespace Scintilla { +#endif + +class SelectionPosition { + int position; + int virtualSpace; +public: + explicit SelectionPosition(int position_=INVALID_POSITION, int virtualSpace_=0) : position(position_), virtualSpace(virtualSpace_) { + PLATFORM_ASSERT(virtualSpace < 800000); + if (virtualSpace < 0) + virtualSpace = 0; + } + void Reset() { + position = 0; + virtualSpace = 0; + } + void MoveForInsertDelete(bool insertion, int startChange, int length); + bool operator ==(const SelectionPosition &other) const { + return position == other.position && virtualSpace == other.virtualSpace; + } + bool operator <(const SelectionPosition &other) const; + bool operator >(const SelectionPosition &other) const; + bool operator <=(const SelectionPosition &other) const; + bool operator >=(const SelectionPosition &other) const; + int Position() const { + return position; + } + void SetPosition(int position_) { + position = position_; + virtualSpace = 0; + } + int VirtualSpace() const { + return virtualSpace; + } + void SetVirtualSpace(int virtualSpace_) { + PLATFORM_ASSERT(virtualSpace_ < 800000); + if (virtualSpace_ >= 0) + virtualSpace = virtualSpace_; + } + void Add(int increment) { + position = position + increment; + } +}; + +struct SelectionRange { + SelectionPosition caret; + SelectionPosition anchor; + + SelectionRange() { + } + SelectionRange(SelectionPosition single) : caret(single), anchor(single) { + } + SelectionRange(SelectionPosition caret_, SelectionPosition anchor_) : caret(caret_), anchor(anchor_) { + } + SelectionRange(int caret_, int anchor_) : caret(caret_), anchor(anchor_) { + } + bool Empty() const { + return anchor == caret; + } + int Length() const; + bool operator ==(const SelectionRange &other) const { + return caret == other.caret && anchor == other.anchor; + } + void Reset() { + anchor.Reset(); + caret.Reset(); + } + void ClearVirtualSpace() { + anchor.SetVirtualSpace(0); + caret.SetVirtualSpace(0); + } + bool Contains(int pos) const; + bool Contains(SelectionPosition sp) const; + bool ContainsCharacter(int posCharacter) const; + bool Intersect(int start, int end, SelectionPosition &selStart, SelectionPosition &selEnd) const; + SelectionPosition Start() const { + return (anchor < caret) ? anchor : caret; + } + SelectionPosition End() const { + return (anchor < caret) ? caret : anchor; + } + bool Trim(SelectionPosition startPos, SelectionPosition endPos); + // If range is all virtual collapse to start of virtual space + void MinimizeVirtualSpace(); +}; + +class Selection { + SelectionRange *ranges; + SelectionRange rangeRectangular; + size_t allocated; + size_t nRanges; + size_t mainRange; + bool moveExtends; + void Allocate(); +public: + enum selTypes { noSel, selStream, selRectangle, selLines, selThin }; + selTypes selType; + + Selection(); + ~Selection(); + bool IsRectangular() const; + int MainCaret() const; + int MainAnchor() const; + SelectionRange &Rectangular(); + size_t Count() const; + size_t Main() const; + void SetMain(size_t r); + SelectionRange &Range(size_t r); + SelectionRange &RangeMain(); + void ClearVirtualSpace(size_t r); + bool MoveExtends() const; + void SetMoveExtends(bool moveExtends_); + bool Empty() const; + SelectionPosition Last() const; + int Length() const; + void MovePositions(bool insertion, int startChange, int length); + void TrimSelection(SelectionPosition startPos, SelectionPosition endPos); + void AddSelection(SelectionPosition spPos); + void AddSelection(SelectionPosition spStartPos, SelectionPosition spEndPos, bool anchorLeft); + bool CharacterInSelection(int posCharacter) const; + bool InSelectionForEOL(int pos) const; + int VirtualSpaceFor(int pos) const; + void Clear(); + void EmptyRanges(); +}; + +#ifdef SCI_NAMESPACE +} +#endif + +#endif |