1 // Scintilla source code edit control
2 /** @file Selection.cxx
3 ** Classes maintaining the selection.
5 // Copyright 2009 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
14 #include "Scintilla.h"
16 #include "Selection.h"
19 using namespace Scintilla
;
22 void SelectionPosition::MoveForInsertDelete(bool insertion
, int startChange
, int length
) {
23 if (position
== startChange
) {
27 if (position
> startChange
) {
31 if (position
> startChange
) {
32 int endDeletion
= startChange
+ length
;
33 if (position
> endDeletion
) {
36 position
= startChange
;
43 bool SelectionPosition::operator <(const SelectionPosition
&other
) const {
44 if (position
== other
.position
)
45 return virtualSpace
< other
.virtualSpace
;
47 return position
< other
.position
;
50 bool SelectionPosition::operator >(const SelectionPosition
&other
) const {
51 if (position
== other
.position
)
52 return virtualSpace
> other
.virtualSpace
;
54 return position
> other
.position
;
57 bool SelectionPosition::operator <=(const SelectionPosition
&other
) const {
58 if (position
== other
.position
&& virtualSpace
== other
.virtualSpace
)
64 bool SelectionPosition::operator >=(const SelectionPosition
&other
) const {
65 if (position
== other
.position
&& virtualSpace
== other
.virtualSpace
)
71 int SelectionRange::Length() const {
73 return anchor
.Position() - caret
.Position();
75 return caret
.Position() - anchor
.Position();
79 bool SelectionRange::Contains(int pos
) const {
81 return (pos
>= caret
.Position()) && (pos
<= anchor
.Position());
83 return (pos
>= anchor
.Position()) && (pos
<= caret
.Position());
86 bool SelectionRange::Contains(SelectionPosition sp
) const {
88 return (sp
>= caret
) && (sp
<= anchor
);
90 return (sp
>= anchor
) && (sp
<= caret
);
93 bool SelectionRange::ContainsCharacter(int posCharacter
) const {
95 return (posCharacter
>= caret
.Position()) && (posCharacter
< anchor
.Position());
97 return (posCharacter
>= anchor
.Position()) && (posCharacter
< caret
.Position());
100 SelectionSegment
SelectionRange::Intersect(SelectionSegment check
) const {
101 SelectionSegment
inOrder(caret
, anchor
);
102 if ((inOrder
.start
<= check
.end
) || (inOrder
.end
>= check
.start
)) {
103 SelectionSegment portion
= check
;
104 if (portion
.start
< inOrder
.start
)
105 portion
.start
= inOrder
.start
;
106 if (portion
.end
> inOrder
.end
)
107 portion
.end
= inOrder
.end
;
108 if (portion
.start
> portion
.end
)
109 return SelectionSegment();
113 return SelectionSegment();
117 bool SelectionRange::Trim(SelectionRange range
) {
118 SelectionPosition startRange
= range
.Start();
119 SelectionPosition endRange
= range
.End();
120 SelectionPosition start
= Start();
121 SelectionPosition end
= End();
122 PLATFORM_ASSERT(start
<= end
);
123 PLATFORM_ASSERT(startRange
<= endRange
);
124 if ((startRange
<= end
) && (endRange
>= start
)) {
125 if ((start
> startRange
) && (end
< endRange
)) {
126 // Completely covered by range -> empty at start
128 } else if ((start
< startRange
) && (end
> endRange
)) {
129 // Completely covers range -> empty at start
131 } else if (start
<= startRange
) {
135 PLATFORM_ASSERT(end
>= endRange
);
139 if (anchor
> caret
) {
152 // If range is all virtual collapse to start of virtual space
153 void SelectionRange::MinimizeVirtualSpace() {
154 if (caret
.Position() == anchor
.Position()) {
155 int virtualSpace
= caret
.VirtualSpace();
156 if (virtualSpace
> anchor
.VirtualSpace())
157 virtualSpace
= anchor
.VirtualSpace();
158 caret
.SetVirtualSpace(virtualSpace
);
159 anchor
.SetVirtualSpace(virtualSpace
);
163 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream
) {
164 AddSelection(SelectionPosition(0));
167 Selection::~Selection() {
170 bool Selection::IsRectangular() const {
171 return (selType
== selRectangle
) || (selType
== selThin
);
174 int Selection::MainCaret() const {
175 return ranges
[mainRange
].caret
.Position();
178 int Selection::MainAnchor() const {
179 return ranges
[mainRange
].anchor
.Position();
182 SelectionRange
&Selection::Rectangular() {
183 return rangeRectangular
;
186 SelectionSegment
Selection::Limits() const {
187 if (ranges
.empty()) {
188 return SelectionSegment();
190 SelectionSegment
sr(ranges
[0].anchor
, ranges
[0].caret
);
191 for (size_t i
=1; i
<ranges
.size(); i
++) {
192 sr
.Extend(ranges
[i
].anchor
);
193 sr
.Extend(ranges
[i
].caret
);
199 SelectionSegment
Selection::LimitsForRectangularElseMain() const {
200 if (IsRectangular()) {
203 return SelectionSegment(ranges
[mainRange
].caret
, ranges
[mainRange
].anchor
);
207 size_t Selection::Count() const {
208 return ranges
.size();
211 size_t Selection::Main() const {
215 void Selection::SetMain(size_t r
) {
216 PLATFORM_ASSERT(r
< ranges
.size());
220 SelectionRange
&Selection::Range(size_t r
) {
224 SelectionRange
&Selection::RangeMain() {
225 return ranges
[mainRange
];
228 bool Selection::MoveExtends() const {
232 void Selection::SetMoveExtends(bool moveExtends_
) {
233 moveExtends
= moveExtends_
;
236 bool Selection::Empty() const {
237 for (size_t i
=0; i
<ranges
.size(); i
++) {
238 if (!ranges
[i
].Empty())
244 SelectionPosition
Selection::Last() const {
245 SelectionPosition lastPosition
;
246 for (size_t i
=0; i
<ranges
.size(); i
++) {
247 if (lastPosition
< ranges
[i
].caret
)
248 lastPosition
= ranges
[i
].caret
;
249 if (lastPosition
< ranges
[i
].anchor
)
250 lastPosition
= ranges
[i
].anchor
;
255 int Selection::Length() const {
257 for (size_t i
=0; i
<ranges
.size(); i
++) {
258 len
+= ranges
[i
].Length();
263 void Selection::MovePositions(bool insertion
, int startChange
, int length
) {
264 for (size_t i
=0; i
<ranges
.size(); i
++) {
265 ranges
[i
].caret
.MoveForInsertDelete(insertion
, startChange
, length
);
266 ranges
[i
].anchor
.MoveForInsertDelete(insertion
, startChange
, length
);
270 void Selection::TrimSelection(SelectionRange range
) {
271 for (size_t i
=0; i
<ranges
.size();) {
272 if ((i
!= mainRange
) && (ranges
[i
].Trim(range
))) {
273 // Trimmed to empty so remove
274 for (size_t j
=i
; j
<ranges
.size()-1; j
++) {
275 ranges
[j
] = ranges
[j
+1];
276 if (j
== mainRange
-1)
286 void Selection::SetSelection(SelectionRange range
) {
288 ranges
.push_back(range
);
289 mainRange
= ranges
.size() - 1;
292 void Selection::AddSelection(SelectionRange range
) {
293 TrimSelection(range
);
294 ranges
.push_back(range
);
295 mainRange
= ranges
.size() - 1;
298 void Selection::AddSelectionWithoutTrim(SelectionRange range
) {
299 ranges
.push_back(range
);
300 mainRange
= ranges
.size() - 1;
303 void Selection::TentativeSelection(SelectionRange range
) {
304 if (!tentativeMain
) {
305 rangesSaved
= ranges
;
307 ranges
= rangesSaved
;
309 TrimSelection(ranges
[mainRange
]);
310 tentativeMain
= true;
313 void Selection::CommitTentative() {
315 tentativeMain
= false;
318 int Selection::CharacterInSelection(int posCharacter
) const {
319 for (size_t i
=0; i
<ranges
.size(); i
++) {
320 if (ranges
[i
].ContainsCharacter(posCharacter
))
321 return i
== mainRange
? 1 : 2;
326 int Selection::InSelectionForEOL(int pos
) const {
327 for (size_t i
=0; i
<ranges
.size(); i
++) {
328 if (!ranges
[i
].Empty() && (pos
> ranges
[i
].Start().Position()) && (pos
<= ranges
[i
].End().Position()))
329 return i
== mainRange
? 1 : 2;
334 int Selection::VirtualSpaceFor(int pos
) const {
335 int virtualSpace
= 0;
336 for (size_t i
=0; i
<ranges
.size(); i
++) {
337 if ((ranges
[i
].caret
.Position() == pos
) && (virtualSpace
< ranges
[i
].caret
.VirtualSpace()))
338 virtualSpace
= ranges
[i
].caret
.VirtualSpace();
339 if ((ranges
[i
].anchor
.Position() == pos
) && (virtualSpace
< ranges
[i
].anchor
.VirtualSpace()))
340 virtualSpace
= ranges
[i
].anchor
.VirtualSpace();
345 void Selection::Clear() {
347 ranges
.push_back(SelectionRange());
348 mainRange
= ranges
.size() - 1;
351 ranges
[mainRange
].Reset();
352 rangeRectangular
.Reset();
355 void Selection::RemoveDuplicates() {
356 for (size_t i
=0; i
<ranges
.size()-1; i
++) {
357 if (ranges
[i
].Empty()) {
359 while (j
<ranges
.size()) {
360 if (ranges
[i
] == ranges
[j
]) {
361 ranges
.erase(ranges
.begin() + j
);
372 void Selection::RotateMain() {
373 mainRange
= (mainRange
+ 1) % ranges
.size();