aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--call/ScintillaCall.cxx4
-rw-r--r--doc/ScintillaDoc.html6
-rw-r--r--doc/ScintillaHistory.html3
-rw-r--r--include/Scintilla.h1
-rw-r--r--include/Scintilla.iface3
-rw-r--r--include/ScintillaCall.h1
-rw-r--r--include/ScintillaMessages.h1
-rw-r--r--src/Editor.cxx25
-rw-r--r--src/Editor.h1
-rw-r--r--src/Selection.cxx7
-rw-r--r--src/Selection.h1
-rw-r--r--test/simpleTests.py39
12 files changed, 89 insertions, 3 deletions
diff --git a/call/ScintillaCall.cxx b/call/ScintillaCall.cxx
index 6935eacbd..99e400e28 100644
--- a/call/ScintillaCall.cxx
+++ b/call/ScintillaCall.cxx
@@ -2887,6 +2887,10 @@ void ScintillaCall::AddSelection(Position caret, Position anchor) {
Call(Message::AddSelection, caret, anchor);
}
+int ScintillaCall::SelectionFromPoint(int x, int y) {
+ return static_cast<int>(Call(Message::SelectionFromPoint, x, y));
+}
+
void ScintillaCall::DropSelectionN(int selection) {
Call(Message::DropSelectionN, selection);
}
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html
index dcabdd9ee..27a542e90 100644
--- a/doc/ScintillaDoc.html
+++ b/doc/ScintillaDoc.html
@@ -1199,6 +1199,7 @@ struct Sci_TextRangeFull {
<a class="message" href="#SCI_CLEARSELECTIONS">SCI_CLEARSELECTIONS</a><br />
<a class="message" href="#SCI_SETSELECTION">SCI_SETSELECTION(position caret, position anchor)</a><br />
<a class="message" href="#SCI_ADDSELECTION">SCI_ADDSELECTION(position caret, position anchor)</a><br />
+ <a class="message" href="#SCI_SELECTIONFROMPOINT">SCI_SELECTIONFROMPOINT(int x, int y) &rarr; int</a><br />
<a class="message" href="#SCI_DROPSELECTIONN">SCI_DROPSELECTIONN(int selection)</a><br />
<a class="message" href="#SCI_SETMAINSELECTION">SCI_SETMAINSELECTION(int selection)</a><br />
<a class="message" href="#SCI_GETMAINSELECTION">SCI_GETMAINSELECTION &rarr; int</a><br />
@@ -1344,6 +1345,11 @@ struct Sci_TextRangeFull {
added with <code>SCI_SETSELECTION</code> and later selections added with <code>SCI_ADDSELECTION</code></p>
<p>
+ <b id="SCI_SELECTIONFROMPOINT">SCI_SELECTIONFROMPOINT(int x, int y) &rarr; int</b><br />
+ Return the index of the selection at the point. If there is no selection at the point, return -1.
+ This can be used to drop a selection or make it the main selection.</p>
+
+ <p>
<b id="SCI_DROPSELECTIONN">SCI_DROPSELECTIONN(int selection)</b><br />
If there are multiple selections, remove the indicated selection.
If this was the main selection then make the previous selection the main and if it was the first then the last selection becomes main.
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html
index b808bd4b8..2e26d8900 100644
--- a/doc/ScintillaHistory.html
+++ b/doc/ScintillaHistory.html
@@ -591,6 +591,9 @@
Released 18 November 2023.
</li>
<li>
+ Add SCI_SELECTIONFROMPOINT for modifying multiple selections.
+ </li>
+ <li>
Add SCI_SETMOVEEXTENDSSELECTION and SCI_CHANGESELECTIONMODE to
simplify selection mode manipulation.
</li>
diff --git a/include/Scintilla.h b/include/Scintilla.h
index 64238a5a3..8c555352c 100644
--- a/include/Scintilla.h
+++ b/include/Scintilla.h
@@ -1022,6 +1022,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP
#define SCI_CLEARSELECTIONS 2571
#define SCI_SETSELECTION 2572
#define SCI_ADDSELECTION 2573
+#define SCI_SELECTIONFROMPOINT 2474
#define SCI_DROPSELECTIONN 2671
#define SCI_SETMAINSELECTION 2574
#define SCI_GETMAINSELECTION 2575
diff --git a/include/Scintilla.iface b/include/Scintilla.iface
index c393d6179..918121596 100644
--- a/include/Scintilla.iface
+++ b/include/Scintilla.iface
@@ -2797,6 +2797,9 @@ fun void SetSelection=2572(position caret, position anchor)
# Add a selection
fun void AddSelection=2573(position caret, position anchor)
+# Find the selection index for a point. -1 when not at a selection.
+fun int SelectionFromPoint=2474(int x, int y)
+
# Drop one selection
fun void DropSelectionN=2671(int selection,)
diff --git a/include/ScintillaCall.h b/include/ScintillaCall.h
index 7f8088748..1a1a1e0f2 100644
--- a/include/ScintillaCall.h
+++ b/include/ScintillaCall.h
@@ -765,6 +765,7 @@ public:
void ClearSelections();
void SetSelection(Position caret, Position anchor);
void AddSelection(Position caret, Position anchor);
+ int SelectionFromPoint(int x, int y);
void DropSelectionN(int selection);
void SetMainSelection(int selection);
int MainSelection();
diff --git a/include/ScintillaMessages.h b/include/ScintillaMessages.h
index eaf162ae5..4b9421242 100644
--- a/include/ScintillaMessages.h
+++ b/include/ScintillaMessages.h
@@ -678,6 +678,7 @@ enum class Message {
ClearSelections = 2571,
SetSelection = 2572,
AddSelection = 2573,
+ SelectionFromPoint = 2474,
DropSelectionN = 2671,
SetMainSelection = 2574,
GetMainSelection = 2575,
diff --git a/src/Editor.cxx b/src/Editor.cxx
index 627179876..527c9b47c 100644
--- a/src/Editor.cxx
+++ b/src/Editor.cxx
@@ -4511,6 +4511,28 @@ bool Editor::PointInSelection(Point pt) {
return false;
}
+ptrdiff_t Editor::SelectionFromPoint(Point pt) {
+ // Prioritize checking inside non-empty selections since each character will be inside only 1
+ const SelectionPosition posChar = SPositionFromLocation(pt, true, true);
+ for (size_t r = 0; r < sel.Count(); r++) {
+ if (sel.Range(r).ContainsCharacter(posChar)) {
+ return r;
+ }
+ }
+
+ // Then check if near empty selections as may be near more than 1
+ const SelectionPosition pos = SPositionFromLocation(pt, true, false);
+ for (size_t r = 0; r < sel.Count(); r++) {
+ const SelectionRange &range = sel.Range(r);
+ if ((range.Empty()) && (pos == range.caret)) {
+ return r;
+ }
+ }
+
+ // No selection at point
+ return -1;
+}
+
bool Editor::PointInSelMargin(Point pt) const {
// Really means: "Point in a margin"
if (vs.fixedColumnWidth > 0) { // There is a margin
@@ -8676,6 +8698,9 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {
Redraw();
break;
+ case Message::SelectionFromPoint:
+ return SelectionFromPoint(PointFromParameters(wParam, lParam));
+
case Message::DropSelectionN:
sel.DropSelection(wParam);
ContainerNeedsUpdate(Update::Selection);
diff --git a/src/Editor.h b/src/Editor.h
index e7e198aec..19b0c730c 100644
--- a/src/Editor.h
+++ b/src/Editor.h
@@ -527,6 +527,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
/** PositionInSelection returns true if position in selection. */
bool PositionInSelection(Sci::Position pos);
bool PointInSelection(Point pt);
+ ptrdiff_t SelectionFromPoint(Point pt);
bool PointInSelMargin(Point pt) const;
Window::Cursor GetMarginCursor(Point pt) const noexcept;
void TrimAndSetSelection(Sci::Position currentPos_, Sci::Position anchor_);
diff --git a/src/Selection.cxx b/src/Selection.cxx
index fbdc474eb..53559e329 100644
--- a/src/Selection.cxx
+++ b/src/Selection.cxx
@@ -123,6 +123,13 @@ bool SelectionRange::ContainsCharacter(Sci::Position posCharacter) const noexcep
return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
}
+bool SelectionRange::ContainsCharacter(SelectionPosition spCharacter) const noexcept {
+ if (anchor > caret)
+ return (spCharacter >= caret) && (spCharacter < anchor);
+ else
+ return (spCharacter >= anchor) && (spCharacter < caret);
+}
+
SelectionSegment SelectionRange::Intersect(SelectionSegment check) const noexcept {
const SelectionSegment inOrder(caret, anchor);
if ((inOrder.start <= check.end) || (inOrder.end >= check.start)) {
diff --git a/src/Selection.h b/src/Selection.h
index 30e1e27ad..03ce5083c 100644
--- a/src/Selection.h
+++ b/src/Selection.h
@@ -129,6 +129,7 @@ struct SelectionRange {
bool Contains(Sci::Position pos) const noexcept;
bool Contains(SelectionPosition sp) const noexcept;
bool ContainsCharacter(Sci::Position posCharacter) const noexcept;
+ bool ContainsCharacter(SelectionPosition spCharacter) const noexcept;
SelectionSegment Intersect(SelectionSegment check) const noexcept;
SelectionPosition Start() const noexcept {
return (anchor < caret) ? anchor : caret;
diff --git a/test/simpleTests.py b/test/simpleTests.py
index fd1f7423a..4263ad036 100644
--- a/test/simpleTests.py
+++ b/test/simpleTests.py
@@ -809,11 +809,44 @@ class TestSimple(unittest.TestCase):
self.assertEqual(self.ed.TargetEndVirtualSpace, 0)
def testPointsAndPositions(self):
- self.ed.AddText(1, b"x")
+ self.ed.SetContents(b"xyz")
+
+ # Inter-character positions
# Start of text
- self.assertEqual(self.ed.PositionFromPoint(0,0), 0)
+ self.assertEqual(self.ed.PositionFromPoint(1,1), 0)
# End of text
- self.assertEqual(self.ed.PositionFromPoint(0,100), 1)
+ self.assertEqual(self.ed.PositionFromPoint(100, 1), 3)
+ self.assertEqual(self.ed.PositionFromPointClose(100, 1), -1)
+
+ # Character positions
+ # Start of text
+ self.assertEqual(self.ed.CharPositionFromPoint(0,0), 0)
+ # End of text
+ self.assertEqual(self.ed.CharPositionFromPoint(100, 0), 3)
+ self.assertEqual(self.ed.CharPositionFromPointClose(100, 0), -1)
+
+ def testSelectionFromPoint(self):
+ self.ed.SetContents(b"xxxxxx")
+ self.ed.SetSelection(3, 2)
+ self.ed.AddSelection(0, 1)
+ self.ed.AddSelection(5, 5) # Empty
+ xStart = self.ed.PointXFromPosition(0, 0)
+ xEnd = self.ed.PointXFromPosition(0, 5)
+ width = xEnd-xStart
+ charWidth = width // 5
+ widthMid = charWidth // 2
+
+ posMid1 = xStart+widthMid
+ self.assertEqual(self.ed.SelectionFromPoint(posMid1, 1), 1)
+ posMid2 = xStart+charWidth+widthMid
+ self.assertEqual(self.ed.SelectionFromPoint(posMid2, 1), -1)
+ posMid3 = xStart+charWidth*2+widthMid
+ self.assertEqual(self.ed.SelectionFromPoint(posMid3, 1), 0)
+ # Empty selection at 5. Exact and then a few pixels either side
+ self.assertEqual(self.ed.SelectionFromPoint(xEnd, 1), 2)
+ self.assertEqual(self.ed.SelectionFromPoint(xEnd-2, 1), 2)
+ self.assertEqual(self.ed.SelectionFromPoint(xEnd+2, 1), 2)
+ self.assertEqual(self.ed.SelectionFromPoint(100, 0), -1)
def testLinePositions(self):
text = b"ab\ncd\nef"