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 void SelectionRange::MoveForInsertDelete(bool insertion
, int startChange
, int length
) {
85 caret
.MoveForInsertDelete(insertion
, startChange
, length
);
86 anchor
.MoveForInsertDelete(insertion
, startChange
, length
);
89 bool SelectionRange::Contains(int pos
) const {
91 return (pos
>= caret
.Position()) && (pos
<= anchor
.Position());
93 return (pos
>= anchor
.Position()) && (pos
<= caret
.Position());
96 bool SelectionRange::Contains(SelectionPosition sp
) const {
98 return (sp
>= caret
) && (sp
<= anchor
);
100 return (sp
>= anchor
) && (sp
<= caret
);
103 bool SelectionRange::ContainsCharacter(int posCharacter
) const {
105 return (posCharacter
>= caret
.Position()) && (posCharacter
< anchor
.Position());
107 return (posCharacter
>= anchor
.Position()) && (posCharacter
< caret
.Position());
110 SelectionSegment
SelectionRange::Intersect(SelectionSegment check
) const {
111 SelectionSegment
inOrder(caret
, anchor
);
112 if ((inOrder
.start
<= check
.end
) || (inOrder
.end
>= check
.start
)) {
113 SelectionSegment portion
= check
;
114 if (portion
.start
< inOrder
.start
)
115 portion
.start
= inOrder
.start
;
116 if (portion
.end
> inOrder
.end
)
117 portion
.end
= inOrder
.end
;
118 if (portion
.start
> portion
.end
)
119 return SelectionSegment();
123 return SelectionSegment();
127 bool SelectionRange::Trim(SelectionRange range
) {
128 SelectionPosition startRange
= range
.Start();
129 SelectionPosition endRange
= range
.End();
130 SelectionPosition start
= Start();
131 SelectionPosition end
= End();
132 PLATFORM_ASSERT(start
<= end
);
133 PLATFORM_ASSERT(startRange
<= endRange
);
134 if ((startRange
<= end
) && (endRange
>= start
)) {
135 if ((start
> startRange
) && (end
< endRange
)) {
136 // Completely covered by range -> empty at start
138 } else if ((start
< startRange
) && (end
> endRange
)) {
139 // Completely covers range -> empty at start
141 } else if (start
<= startRange
) {
145 PLATFORM_ASSERT(end
>= endRange
);
149 if (anchor
> caret
) {
162 // If range is all virtual collapse to start of virtual space
163 void SelectionRange::MinimizeVirtualSpace() {
164 if (caret
.Position() == anchor
.Position()) {
165 int virtualSpace
= caret
.VirtualSpace();
166 if (virtualSpace
> anchor
.VirtualSpace())
167 virtualSpace
= anchor
.VirtualSpace();
168 caret
.SetVirtualSpace(virtualSpace
);
169 anchor
.SetVirtualSpace(virtualSpace
);
173 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream
) {
174 AddSelection(SelectionRange(SelectionPosition(0)));
177 Selection::~Selection() {
180 bool Selection::IsRectangular() const {
181 return (selType
== selRectangle
) || (selType
== selThin
);
184 int Selection::MainCaret() const {
185 return ranges
[mainRange
].caret
.Position();
188 int Selection::MainAnchor() const {
189 return ranges
[mainRange
].anchor
.Position();
192 SelectionRange
&Selection::Rectangular() {
193 return rangeRectangular
;
196 SelectionSegment
Selection::Limits() const {
197 if (ranges
.empty()) {
198 return SelectionSegment();
200 SelectionSegment
sr(ranges
[0].anchor
, ranges
[0].caret
);
201 for (size_t i
=1; i
<ranges
.size(); i
++) {
202 sr
.Extend(ranges
[i
].anchor
);
203 sr
.Extend(ranges
[i
].caret
);
209 SelectionSegment
Selection::LimitsForRectangularElseMain() const {
210 if (IsRectangular()) {
213 return SelectionSegment(ranges
[mainRange
].caret
, ranges
[mainRange
].anchor
);
217 size_t Selection::Count() const {
218 return ranges
.size();
221 size_t Selection::Main() const {
225 void Selection::SetMain(size_t r
) {
226 PLATFORM_ASSERT(r
< ranges
.size());
230 SelectionRange
&Selection::Range(size_t r
) {
234 const SelectionRange
&Selection::Range(size_t r
) const {
238 SelectionRange
&Selection::RangeMain() {
239 return ranges
[mainRange
];
242 const SelectionRange
&Selection::RangeMain() const {
243 return ranges
[mainRange
];
246 SelectionPosition
Selection::Start() const {
247 if (IsRectangular()) {
248 return rangeRectangular
.Start();
250 return ranges
[mainRange
].Start();
254 bool Selection::MoveExtends() const {
258 void Selection::SetMoveExtends(bool moveExtends_
) {
259 moveExtends
= moveExtends_
;
262 bool Selection::Empty() const {
263 for (size_t i
=0; i
<ranges
.size(); i
++) {
264 if (!ranges
[i
].Empty())
270 SelectionPosition
Selection::Last() const {
271 SelectionPosition lastPosition
;
272 for (size_t i
=0; i
<ranges
.size(); i
++) {
273 if (lastPosition
< ranges
[i
].caret
)
274 lastPosition
= ranges
[i
].caret
;
275 if (lastPosition
< ranges
[i
].anchor
)
276 lastPosition
= ranges
[i
].anchor
;
281 int Selection::Length() const {
283 for (size_t i
=0; i
<ranges
.size(); i
++) {
284 len
+= ranges
[i
].Length();
289 void Selection::MovePositions(bool insertion
, int startChange
, int length
) {
290 for (size_t i
=0; i
<ranges
.size(); i
++) {
291 ranges
[i
].MoveForInsertDelete(insertion
, startChange
, length
);
293 if (selType
== selRectangle
) {
294 rangeRectangular
.MoveForInsertDelete(insertion
, startChange
, length
);
298 void Selection::TrimSelection(SelectionRange range
) {
299 for (size_t i
=0; i
<ranges
.size();) {
300 if ((i
!= mainRange
) && (ranges
[i
].Trim(range
))) {
301 // Trimmed to empty so remove
302 for (size_t j
=i
; j
<ranges
.size()-1; j
++) {
303 ranges
[j
] = ranges
[j
+1];
304 if (j
== mainRange
-1)
314 void Selection::SetSelection(SelectionRange range
) {
316 ranges
.push_back(range
);
317 mainRange
= ranges
.size() - 1;
320 void Selection::AddSelection(SelectionRange range
) {
321 TrimSelection(range
);
322 ranges
.push_back(range
);
323 mainRange
= ranges
.size() - 1;
326 void Selection::AddSelectionWithoutTrim(SelectionRange range
) {
327 ranges
.push_back(range
);
328 mainRange
= ranges
.size() - 1;
331 void Selection::DropSelection(size_t r
) {
332 if ((ranges
.size() > 1) && (r
< ranges
.size())) {
333 size_t mainNew
= mainRange
;
336 mainNew
= ranges
.size() - 2;
341 ranges
.erase(ranges
.begin() + r
);
346 void Selection::TentativeSelection(SelectionRange range
) {
347 if (!tentativeMain
) {
348 rangesSaved
= ranges
;
350 ranges
= rangesSaved
;
352 TrimSelection(ranges
[mainRange
]);
353 tentativeMain
= true;
356 void Selection::CommitTentative() {
358 tentativeMain
= false;
361 int Selection::CharacterInSelection(int posCharacter
) const {
362 for (size_t i
=0; i
<ranges
.size(); i
++) {
363 if (ranges
[i
].ContainsCharacter(posCharacter
))
364 return i
== mainRange
? 1 : 2;
369 int Selection::InSelectionForEOL(int pos
) const {
370 for (size_t i
=0; i
<ranges
.size(); i
++) {
371 if (!ranges
[i
].Empty() && (pos
> ranges
[i
].Start().Position()) && (pos
<= ranges
[i
].End().Position()))
372 return i
== mainRange
? 1 : 2;
377 int Selection::VirtualSpaceFor(int pos
) const {
378 int virtualSpace
= 0;
379 for (size_t i
=0; i
<ranges
.size(); i
++) {
380 if ((ranges
[i
].caret
.Position() == pos
) && (virtualSpace
< ranges
[i
].caret
.VirtualSpace()))
381 virtualSpace
= ranges
[i
].caret
.VirtualSpace();
382 if ((ranges
[i
].anchor
.Position() == pos
) && (virtualSpace
< ranges
[i
].anchor
.VirtualSpace()))
383 virtualSpace
= ranges
[i
].anchor
.VirtualSpace();
388 void Selection::Clear() {
390 ranges
.push_back(SelectionRange());
391 mainRange
= ranges
.size() - 1;
394 ranges
[mainRange
].Reset();
395 rangeRectangular
.Reset();
398 void Selection::RemoveDuplicates() {
399 for (size_t i
=0; i
<ranges
.size()-1; i
++) {
400 if (ranges
[i
].Empty()) {
402 while (j
<ranges
.size()) {
403 if (ranges
[i
] == ranges
[j
]) {
404 ranges
.erase(ranges
.begin() + j
);
415 void Selection::RotateMain() {
416 mainRange
= (mainRange
+ 1) % ranges
.size();