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.
15 #include "Scintilla.h"
17 #include "Selection.h"
20 using namespace Scintilla
;
23 void SelectionPosition::MoveForInsertDelete(bool insertion
, int startChange
, int length
) {
25 if (position
== startChange
) {
26 int virtualLengthRemove
= std::min(length
, virtualSpace
);
27 virtualSpace
-= virtualLengthRemove
;
28 position
+= virtualLengthRemove
;
29 } else if (position
> startChange
) {
33 if (position
== startChange
) {
36 if (position
> startChange
) {
37 int endDeletion
= startChange
+ length
;
38 if (position
> endDeletion
) {
41 position
= startChange
;
48 bool SelectionPosition::operator <(const SelectionPosition
&other
) const {
49 if (position
== other
.position
)
50 return virtualSpace
< other
.virtualSpace
;
52 return position
< other
.position
;
55 bool SelectionPosition::operator >(const SelectionPosition
&other
) const {
56 if (position
== other
.position
)
57 return virtualSpace
> other
.virtualSpace
;
59 return position
> other
.position
;
62 bool SelectionPosition::operator <=(const SelectionPosition
&other
) const {
63 if (position
== other
.position
&& virtualSpace
== other
.virtualSpace
)
69 bool SelectionPosition::operator >=(const SelectionPosition
&other
) const {
70 if (position
== other
.position
&& virtualSpace
== other
.virtualSpace
)
76 int SelectionRange::Length() const {
78 return anchor
.Position() - caret
.Position();
80 return caret
.Position() - anchor
.Position();
84 bool SelectionRange::Contains(int pos
) const {
86 return (pos
>= caret
.Position()) && (pos
<= anchor
.Position());
88 return (pos
>= anchor
.Position()) && (pos
<= caret
.Position());
91 bool SelectionRange::Contains(SelectionPosition sp
) const {
93 return (sp
>= caret
) && (sp
<= anchor
);
95 return (sp
>= anchor
) && (sp
<= caret
);
98 bool SelectionRange::ContainsCharacter(int posCharacter
) const {
100 return (posCharacter
>= caret
.Position()) && (posCharacter
< anchor
.Position());
102 return (posCharacter
>= anchor
.Position()) && (posCharacter
< caret
.Position());
105 SelectionSegment
SelectionRange::Intersect(SelectionSegment check
) const {
106 SelectionSegment
inOrder(caret
, anchor
);
107 if ((inOrder
.start
<= check
.end
) || (inOrder
.end
>= check
.start
)) {
108 SelectionSegment portion
= check
;
109 if (portion
.start
< inOrder
.start
)
110 portion
.start
= inOrder
.start
;
111 if (portion
.end
> inOrder
.end
)
112 portion
.end
= inOrder
.end
;
113 if (portion
.start
> portion
.end
)
114 return SelectionSegment();
118 return SelectionSegment();
122 bool SelectionRange::Trim(SelectionRange range
) {
123 SelectionPosition startRange
= range
.Start();
124 SelectionPosition endRange
= range
.End();
125 SelectionPosition start
= Start();
126 SelectionPosition end
= End();
127 PLATFORM_ASSERT(start
<= end
);
128 PLATFORM_ASSERT(startRange
<= endRange
);
129 if ((startRange
<= end
) && (endRange
>= start
)) {
130 if ((start
> startRange
) && (end
< endRange
)) {
131 // Completely covered by range -> empty at start
133 } else if ((start
< startRange
) && (end
> endRange
)) {
134 // Completely covers range -> empty at start
136 } else if (start
<= startRange
) {
140 PLATFORM_ASSERT(end
>= endRange
);
144 if (anchor
> caret
) {
157 // If range is all virtual collapse to start of virtual space
158 void SelectionRange::MinimizeVirtualSpace() {
159 if (caret
.Position() == anchor
.Position()) {
160 int virtualSpace
= caret
.VirtualSpace();
161 if (virtualSpace
> anchor
.VirtualSpace())
162 virtualSpace
= anchor
.VirtualSpace();
163 caret
.SetVirtualSpace(virtualSpace
);
164 anchor
.SetVirtualSpace(virtualSpace
);
168 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream
) {
169 AddSelection(SelectionRange(SelectionPosition(0)));
172 Selection::~Selection() {
175 bool Selection::IsRectangular() const {
176 return (selType
== selRectangle
) || (selType
== selThin
);
179 int Selection::MainCaret() const {
180 return ranges
[mainRange
].caret
.Position();
183 int Selection::MainAnchor() const {
184 return ranges
[mainRange
].anchor
.Position();
187 SelectionRange
&Selection::Rectangular() {
188 return rangeRectangular
;
191 SelectionSegment
Selection::Limits() const {
192 if (ranges
.empty()) {
193 return SelectionSegment();
195 SelectionSegment
sr(ranges
[0].anchor
, ranges
[0].caret
);
196 for (size_t i
=1; i
<ranges
.size(); i
++) {
197 sr
.Extend(ranges
[i
].anchor
);
198 sr
.Extend(ranges
[i
].caret
);
204 SelectionSegment
Selection::LimitsForRectangularElseMain() const {
205 if (IsRectangular()) {
208 return SelectionSegment(ranges
[mainRange
].caret
, ranges
[mainRange
].anchor
);
212 size_t Selection::Count() const {
213 return ranges
.size();
216 size_t Selection::Main() const {
220 void Selection::SetMain(size_t r
) {
221 PLATFORM_ASSERT(r
< ranges
.size());
225 SelectionRange
&Selection::Range(size_t r
) {
229 SelectionRange
&Selection::RangeMain() {
230 return ranges
[mainRange
];
233 bool Selection::MoveExtends() const {
237 void Selection::SetMoveExtends(bool moveExtends_
) {
238 moveExtends
= moveExtends_
;
241 bool Selection::Empty() const {
242 for (size_t i
=0; i
<ranges
.size(); i
++) {
243 if (!ranges
[i
].Empty())
249 SelectionPosition
Selection::Last() const {
250 SelectionPosition lastPosition
;
251 for (size_t i
=0; i
<ranges
.size(); i
++) {
252 if (lastPosition
< ranges
[i
].caret
)
253 lastPosition
= ranges
[i
].caret
;
254 if (lastPosition
< ranges
[i
].anchor
)
255 lastPosition
= ranges
[i
].anchor
;
260 int Selection::Length() const {
262 for (size_t i
=0; i
<ranges
.size(); i
++) {
263 len
+= ranges
[i
].Length();
268 void Selection::MovePositions(bool insertion
, int startChange
, int length
) {
269 for (size_t i
=0; i
<ranges
.size(); i
++) {
270 ranges
[i
].caret
.MoveForInsertDelete(insertion
, startChange
, length
);
271 ranges
[i
].anchor
.MoveForInsertDelete(insertion
, startChange
, length
);
275 void Selection::TrimSelection(SelectionRange range
) {
276 for (size_t i
=0; i
<ranges
.size();) {
277 if ((i
!= mainRange
) && (ranges
[i
].Trim(range
))) {
278 // Trimmed to empty so remove
279 for (size_t j
=i
; j
<ranges
.size()-1; j
++) {
280 ranges
[j
] = ranges
[j
+1];
281 if (j
== mainRange
-1)
291 void Selection::SetSelection(SelectionRange range
) {
293 ranges
.push_back(range
);
294 mainRange
= ranges
.size() - 1;
297 void Selection::AddSelection(SelectionRange range
) {
298 TrimSelection(range
);
299 ranges
.push_back(range
);
300 mainRange
= ranges
.size() - 1;
303 void Selection::AddSelectionWithoutTrim(SelectionRange range
) {
304 ranges
.push_back(range
);
305 mainRange
= ranges
.size() - 1;
308 void Selection::DropSelection(size_t r
) {
309 if ((ranges
.size() > 1) && (r
< ranges
.size())) {
310 size_t mainNew
= mainRange
;
313 mainNew
= ranges
.size() - 2;
318 ranges
.erase(ranges
.begin() + r
);
323 void Selection::TentativeSelection(SelectionRange range
) {
324 if (!tentativeMain
) {
325 rangesSaved
= ranges
;
327 ranges
= rangesSaved
;
329 TrimSelection(ranges
[mainRange
]);
330 tentativeMain
= true;
333 void Selection::CommitTentative() {
335 tentativeMain
= false;
338 int Selection::CharacterInSelection(int posCharacter
) const {
339 for (size_t i
=0; i
<ranges
.size(); i
++) {
340 if (ranges
[i
].ContainsCharacter(posCharacter
))
341 return i
== mainRange
? 1 : 2;
346 int Selection::InSelectionForEOL(int pos
) const {
347 for (size_t i
=0; i
<ranges
.size(); i
++) {
348 if (!ranges
[i
].Empty() && (pos
> ranges
[i
].Start().Position()) && (pos
<= ranges
[i
].End().Position()))
349 return i
== mainRange
? 1 : 2;
354 int Selection::VirtualSpaceFor(int pos
) const {
355 int virtualSpace
= 0;
356 for (size_t i
=0; i
<ranges
.size(); i
++) {
357 if ((ranges
[i
].caret
.Position() == pos
) && (virtualSpace
< ranges
[i
].caret
.VirtualSpace()))
358 virtualSpace
= ranges
[i
].caret
.VirtualSpace();
359 if ((ranges
[i
].anchor
.Position() == pos
) && (virtualSpace
< ranges
[i
].anchor
.VirtualSpace()))
360 virtualSpace
= ranges
[i
].anchor
.VirtualSpace();
365 void Selection::Clear() {
367 ranges
.push_back(SelectionRange());
368 mainRange
= ranges
.size() - 1;
371 ranges
[mainRange
].Reset();
372 rangeRectangular
.Reset();
375 void Selection::RemoveDuplicates() {
376 for (size_t i
=0; i
<ranges
.size()-1; i
++) {
377 if (ranges
[i
].Empty()) {
379 while (j
<ranges
.size()) {
380 if (ranges
[i
] == ranges
[j
]) {
381 ranges
.erase(ranges
.begin() + j
);
392 void Selection::RotateMain() {
393 mainRange
= (mainRange
+ 1) % ranges
.size();