# HG changeset patch
# User Mitchell
# Date 1519447387 -39600
# Node ID 0a8a766722c0bc91e1e2ecd958927a504cea5cac
# Parent 26e7749ba67ae8fc994e2f17c760a65c5d0ca479
Fix move-extends-selection mode for rectangular and line selections.
diff -r 26e7749ba67a -r 0a8a766722c0 doc/ScintillaHistory.html
--- a/doc/ScintillaHistory.html Thu Feb 22 08:13:37 2018 +1100
+++ b/doc/ScintillaHistory.html Sat Feb 24 15:43:07 2018 +1100
@@ -548,6 +548,9 @@
Bug #1993.
+ Fix move-extends-selection mode for rectangular and line selections.
+
+
SciTE on Windows can execute Python scripts directly by name when on path.
Feature #1209.
diff -r 26e7749ba67a -r 0a8a766722c0 src/Editor.cxx
--- a/src/Editor.cxx Thu Feb 22 08:13:37 2018 +1100
+++ b/src/Editor.cxx Sat Feb 24 15:43:07 2018 +1100
@@ -626,26 +626,31 @@
InvalidateSelection(sel.RangeMain(), true);
}
+/* For Line selection - the anchor and caret are always
+ at the beginning and end of the region lines. */
+SelectionRange Editor::LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const {
+ if (currentPos_ > anchor_) {
+ anchor_ = SelectionPosition(static_cast(
+ pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position()))));
+ currentPos_ = SelectionPosition(static_cast(
+ pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position()))));
+ } else {
+ currentPos_ = SelectionPosition(static_cast(
+ pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position()))));
+ anchor_ = SelectionPosition(static_cast(
+ pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position()))));
+ }
+ return SelectionRange(currentPos_, anchor_);
+}
+
void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
currentPos_ = ClampPositionIntoDocument(currentPos_);
anchor_ = ClampPositionIntoDocument(anchor_);
Sci::Line currentLine = static_cast(pdoc->LineFromPosition(currentPos_.Position()));
- /* For Line selection - ensure the anchor and caret are always
- at the beginning and end of the region lines. */
+ SelectionRange rangeNew(currentPos_, anchor_);
if (sel.selType == Selection::selLines) {
- if (currentPos_ > anchor_) {
- anchor_ = SelectionPosition(static_cast(
- pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position()))));
- currentPos_ = SelectionPosition(static_cast(
- pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position()))));
- } else {
- currentPos_ = SelectionPosition(static_cast(
- pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position()))));
- anchor_ = SelectionPosition(static_cast(
- pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position()))));
- }
- }
- SelectionRange rangeNew(currentPos_, anchor_);
+ rangeNew = LineSelectionRange(currentPos_, anchor_);
+ }
if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
InvalidateSelection(rangeNew);
}
@@ -675,6 +680,8 @@
sel.Rectangular() =
SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
SetRectangularRange();
+ } else if (sel.selType == Selection::selLines) {
+ sel.RangeMain() = LineSelectionRange(currentPos_, sel.RangeMain().anchor);
} else {
sel.RangeMain() =
SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
@@ -3120,7 +3127,7 @@
void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
if ((selt == Selection::noSel) && sel.MoveExtends()) {
- selt = Selection::selStream;
+ selt = !sel.IsRectangular() ? Selection::selStream : Selection::selRectangle;
}
SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
if (sel.IsRectangular()) {
@@ -3142,6 +3149,11 @@
sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
SetRectangularRange();
MovedCaret(posNew, caretToUse, true);
+ } else if (sel.selType == Selection::selLines && sel.MoveExtends()) {
+ // Calculate new caret position and call SetSelection(), which will ensure whole lines are selected.
+ const SelectionPosition posNew = MovePositionSoVisible(
+ PositionUpOrDown(caretToUse, direction, -1), direction);
+ SetSelection(posNew, sel.Range(sel.Main()).anchor);
} else {
InvalidateWholeSelection();
if (!additionalSelectionTyping || (sel.IsRectangular())) {
@@ -3263,7 +3275,7 @@
}
}
-bool IsRectExtend(unsigned int iMessage) {
+bool IsRectExtend(unsigned int iMessage, bool isRectMoveExtends) {
switch (iMessage) {
case SCI_CHARLEFTRECTEXTEND:
case SCI_CHARRIGHTRECTEXTEND:
@@ -3272,6 +3284,19 @@
case SCI_LINEENDRECTEXTEND:
return true;
default:
+ if (isRectMoveExtends) {
+ // Handle SCI_SETSELECTIONMODE(SC_SEL_RECTANGLE) and subsequent movements.
+ switch (iMessage) {
+ case SCI_CHARLEFTEXTEND:
+ case SCI_CHARRIGHTEXTEND:
+ case SCI_HOMEEXTEND:
+ case SCI_VCHOMEEXTEND:
+ case SCI_LINEENDEXTEND:
+ return true;
+ default:
+ return false;
+ }
+ }
return false;
}
}
@@ -3307,6 +3332,9 @@
}
int Editor::HorizontalMove(unsigned int iMessage) {
+ if (sel.selType == Selection::selLines) {
+ return 0; // horizontal moves with line selection have no effect
+ }
if (sel.MoveExtends()) {
iMessage = WithExtends(iMessage);
}
@@ -3319,7 +3347,7 @@
// Invalidate each of the current selections
InvalidateWholeSelection();
- if (IsRectExtend(iMessage)) {
+ if (IsRectExtend(iMessage, sel.IsRectangular() && sel.MoveExtends())) {
const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
if (!sel.IsRectangular()) {
sel.DropAdditionalRanges();
@@ -3328,6 +3356,7 @@
SelectionPosition spCaret = rangeBase.caret;
switch (iMessage) {
case SCI_CHARLEFTRECTEXTEND:
+ case SCI_CHARLEFTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
} else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
@@ -3335,6 +3364,7 @@
}
break;
case SCI_CHARRIGHTRECTEXTEND:
+ case SCI_CHARRIGHTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
} else {
@@ -3342,13 +3372,16 @@
}
break;
case SCI_HOMERECTEXTEND:
+ case SCI_HOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
spCaret = SelectionPosition(
static_cast(pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position()))));
break;
case SCI_VCHOMERECTEXTEND:
+ case SCI_VCHOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
break;
case SCI_LINEENDRECTEXTEND:
+ case SCI_LINEENDEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
break;
}
@@ -7615,10 +7648,12 @@
case SC_SEL_RECTANGLE:
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
sel.selType = Selection::selRectangle;
+ sel.Rectangular() = sel.RangeMain(); // adjust current selection
break;
case SC_SEL_LINES:
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
sel.selType = Selection::selLines;
+ SetSelection(sel.RangeMain().caret, sel.RangeMain().anchor); // adjust current selection
break;
case SC_SEL_THIN:
sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
diff -r 26e7749ba67a -r 0a8a766722c0 src/Editor.h
--- a/src/Editor.h Thu Feb 22 08:13:37 2018 +1100
+++ b/src/Editor.h Sat Feb 24 15:43:07 2018 +1100
@@ -312,6 +312,7 @@
void ThinRectangularRange();
void InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection=false);
void InvalidateWholeSelection();
+ SelectionRange LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const;
void SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_);
void SetSelection(Sci::Position currentPos_, Sci::Position anchor_);
void SetSelection(SelectionPosition currentPos_);
diff -r 26e7749ba67a -r 0a8a766722c0 test/simpleTests.py
--- a/test/simpleTests.py Thu Feb 22 08:13:37 2018 +1100
+++ b/test/simpleTests.py Sat Feb 24 15:43:07 2018 +1100
@@ -1466,6 +1466,88 @@
self.ed.DropSelectionN(0)
self.assertEquals(self.ed.MainSelection, 2)
+class TestModalSelection(unittest.TestCase):
+
+ def setUp(self):
+ self.xite = Xite.xiteFrame
+ self.ed = self.xite.ed
+ self.ed.ClearAll()
+ self.ed.EmptyUndoBuffer()
+ # 3 lines of 3 characters
+ t = b"xxx\nxxx\nxxx"
+ self.ed.AddText(len(t), t)
+
+ def testCharacterSelection(self):
+ self.ed.SetSelection(1, 1)
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 1)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.SelectionMode = self.ed.SC_SEL_STREAM
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 1)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.CharRight()
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 2)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.LineDown()
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 6)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.ClearSelections()
+
+ def testRectangleSelection(self):
+ self.ed.SetSelection(1, 1)
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 1)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.SelectionMode = self.ed.SC_SEL_RECTANGLE
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 1)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.CharRight()
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 2)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.LineDown()
+ self.assertEquals(self.ed.Selections, 2)
+ self.assertEquals(self.ed.MainSelection, 1)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 2)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.assertEquals(self.ed.GetSelectionNCaret(1), 6)
+ self.assertEquals(self.ed.GetSelectionNAnchor(1), 5)
+ self.ed.ClearSelections()
+
+ def testLinesSelection(self):
+ self.ed.SetSelection(1, 1)
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 1)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 1)
+ self.ed.SelectionMode = self.ed.SC_SEL_LINES
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 0)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 3)
+ self.ed.CharRight()
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 0)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 3)
+ self.ed.LineDown()
+ self.assertEquals(self.ed.Selections, 1)
+ self.assertEquals(self.ed.MainSelection, 0)
+ self.assertEquals(self.ed.GetSelectionNCaret(0), 7)
+ self.assertEquals(self.ed.GetSelectionNAnchor(0), 0)
+ self.ed.ClearSelections()
+
class TestStyleAttributes(unittest.TestCase):
""" These tests are just to ensure that the calls set and retrieve values.
They do not check the visual appearance of the style attributes.