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 SelectionPosition
Selection::Start() const {
234 if (IsRectangular()) {
235 return rangeRectangular
.Start();
237 return ranges
[mainRange
].Start();
241 bool Selection::MoveExtends() const {
245 void Selection::SetMoveExtends(bool moveExtends_
) {
246 moveExtends
= moveExtends_
;
249 bool Selection::Empty() const {
250 for (size_t i
=0; i
<ranges
.size(); i
++) {
251 if (!ranges
[i
].Empty())
257 SelectionPosition
Selection::Last() const {
258 SelectionPosition lastPosition
;
259 for (size_t i
=0; i
<ranges
.size(); i
++) {
260 if (lastPosition
< ranges
[i
].caret
)
261 lastPosition
= ranges
[i
].caret
;
262 if (lastPosition
< ranges
[i
].anchor
)
263 lastPosition
= ranges
[i
].anchor
;
268 int Selection::Length() const {
270 for (size_t i
=0; i
<ranges
.size(); i
++) {
271 len
+= ranges
[i
].Length();
276 void Selection::MovePositions(bool insertion
, int startChange
, int length
) {
277 for (size_t i
=0; i
<ranges
.size(); i
++) {
278 ranges
[i
].caret
.MoveForInsertDelete(insertion
, startChange
, length
);
279 ranges
[i
].anchor
.MoveForInsertDelete(insertion
, startChange
, length
);
283 void Selection::TrimSelection(SelectionRange range
) {
284 for (size_t i
=0; i
<ranges
.size();) {
285 if ((i
!= mainRange
) && (ranges
[i
].Trim(range
))) {
286 // Trimmed to empty so remove
287 for (size_t j
=i
; j
<ranges
.size()-1; j
++) {
288 ranges
[j
] = ranges
[j
+1];
289 if (j
== mainRange
-1)
299 void Selection::SetSelection(SelectionRange range
) {
301 ranges
.push_back(range
);
302 mainRange
= ranges
.size() - 1;
305 void Selection::AddSelection(SelectionRange range
) {
306 TrimSelection(range
);
307 ranges
.push_back(range
);
308 mainRange
= ranges
.size() - 1;
311 void Selection::AddSelectionWithoutTrim(SelectionRange range
) {
312 ranges
.push_back(range
);
313 mainRange
= ranges
.size() - 1;
316 void Selection::DropSelection(size_t r
) {
317 if ((ranges
.size() > 1) && (r
< ranges
.size())) {
318 size_t mainNew
= mainRange
;
321 mainNew
= ranges
.size() - 2;
326 ranges
.erase(ranges
.begin() + r
);
331 void Selection::TentativeSelection(SelectionRange range
) {
332 if (!tentativeMain
) {
333 rangesSaved
= ranges
;
335 ranges
= rangesSaved
;
337 TrimSelection(ranges
[mainRange
]);
338 tentativeMain
= true;
341 void Selection::CommitTentative() {
343 tentativeMain
= false;
346 int Selection::CharacterInSelection(int posCharacter
) const {
347 for (size_t i
=0; i
<ranges
.size(); i
++) {
348 if (ranges
[i
].ContainsCharacter(posCharacter
))
349 return i
== mainRange
? 1 : 2;
354 int Selection::InSelectionForEOL(int pos
) const {
355 for (size_t i
=0; i
<ranges
.size(); i
++) {
356 if (!ranges
[i
].Empty() && (pos
> ranges
[i
].Start().Position()) && (pos
<= ranges
[i
].End().Position()))
357 return i
== mainRange
? 1 : 2;
362 int Selection::VirtualSpaceFor(int pos
) const {
363 int virtualSpace
= 0;
364 for (size_t i
=0; i
<ranges
.size(); i
++) {
365 if ((ranges
[i
].caret
.Position() == pos
) && (virtualSpace
< ranges
[i
].caret
.VirtualSpace()))
366 virtualSpace
= ranges
[i
].caret
.VirtualSpace();
367 if ((ranges
[i
].anchor
.Position() == pos
) && (virtualSpace
< ranges
[i
].anchor
.VirtualSpace()))
368 virtualSpace
= ranges
[i
].anchor
.VirtualSpace();
373 void Selection::Clear() {
375 ranges
.push_back(SelectionRange());
376 mainRange
= ranges
.size() - 1;
379 ranges
[mainRange
].Reset();
380 rangeRectangular
.Reset();
383 void Selection::RemoveDuplicates() {
384 for (size_t i
=0; i
<ranges
.size()-1; i
++) {
385 if (ranges
[i
].Empty()) {
387 while (j
<ranges
.size()) {
388 if (ranges
[i
] == ranges
[j
]) {
389 ranges
.erase(ranges
.begin() + j
);
400 void Selection::RotateMain() {
401 mainRange
= (mainRange
+ 1) % ranges
.size();