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
) {
24 if (position
> startChange
) {
28 if (position
> startChange
) {
29 int endDeletion
= startChange
+ length
;
30 if (position
> endDeletion
) {
33 position
= startChange
;
39 bool SelectionPosition::operator <(const SelectionPosition
&other
) const {
40 if (position
== other
.position
)
41 return virtualSpace
< other
.virtualSpace
;
43 return position
< other
.position
;
46 bool SelectionPosition::operator >(const SelectionPosition
&other
) const {
47 if (position
== other
.position
)
48 return virtualSpace
> other
.virtualSpace
;
50 return position
> other
.position
;
53 bool SelectionPosition::operator <=(const SelectionPosition
&other
) const {
54 if (position
== other
.position
&& virtualSpace
== other
.virtualSpace
)
60 bool SelectionPosition::operator >=(const SelectionPosition
&other
) const {
61 if (position
== other
.position
&& virtualSpace
== other
.virtualSpace
)
67 int SelectionRange::Length() const {
69 return anchor
.Position() - caret
.Position();
71 return caret
.Position() - anchor
.Position();
75 bool SelectionRange::Contains(int pos
) const {
77 return (pos
>= caret
.Position()) && (pos
<= anchor
.Position());
79 return (pos
>= anchor
.Position()) && (pos
<= caret
.Position());
82 bool SelectionRange::Contains(SelectionPosition sp
) const {
84 return (sp
>= caret
) && (sp
<= anchor
);
86 return (sp
>= anchor
) && (sp
<= caret
);
89 bool SelectionRange::ContainsCharacter(int posCharacter
) const {
91 return (posCharacter
>= caret
.Position()) && (posCharacter
< anchor
.Position());
93 return (posCharacter
>= anchor
.Position()) && (posCharacter
< caret
.Position());
96 SelectionSegment
SelectionRange::Intersect(SelectionSegment check
) const {
97 SelectionSegment
inOrder(caret
, anchor
);
98 if ((inOrder
.start
<= check
.end
) || (inOrder
.end
>= check
.start
)) {
99 SelectionSegment portion
= check
;
100 if (portion
.start
< inOrder
.start
)
101 portion
.start
= inOrder
.start
;
102 if (portion
.end
> inOrder
.end
)
103 portion
.end
= inOrder
.end
;
104 if (portion
.start
> portion
.end
)
105 return SelectionSegment();
109 return SelectionSegment();
113 bool SelectionRange::Trim(SelectionRange range
) {
114 SelectionPosition startRange
= range
.Start();
115 SelectionPosition endRange
= range
.End();
116 SelectionPosition start
= Start();
117 SelectionPosition end
= End();
118 PLATFORM_ASSERT(start
<= end
);
119 PLATFORM_ASSERT(startRange
<= endRange
);
120 if ((startRange
<= end
) && (endRange
>= start
)) {
121 if ((start
> startRange
) && (end
< endRange
)) {
122 // Completely covered by range -> empty at start
124 } else if ((start
< startRange
) && (end
> endRange
)) {
125 // Completely covers range -> empty at start
127 } else if (start
<= startRange
) {
131 PLATFORM_ASSERT(end
>= endRange
);
135 if (anchor
> caret
) {
148 // If range is all virtual collapse to start of virtual space
149 void SelectionRange::MinimizeVirtualSpace() {
150 if (caret
.Position() == anchor
.Position()) {
151 int virtualSpace
= caret
.VirtualSpace();
152 if (virtualSpace
> anchor
.VirtualSpace())
153 virtualSpace
= anchor
.VirtualSpace();
154 caret
.SetVirtualSpace(virtualSpace
);
155 anchor
.SetVirtualSpace(virtualSpace
);
159 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream
) {
160 AddSelection(SelectionPosition(0));
163 Selection::~Selection() {
166 bool Selection::IsRectangular() const {
167 return (selType
== selRectangle
) || (selType
== selThin
);
170 int Selection::MainCaret() const {
171 return ranges
[mainRange
].caret
.Position();
174 int Selection::MainAnchor() const {
175 return ranges
[mainRange
].anchor
.Position();
178 SelectionRange
&Selection::Rectangular() {
179 return rangeRectangular
;
182 SelectionSegment
Selection::Limits() const {
183 if (ranges
.empty()) {
184 return SelectionSegment();
186 SelectionSegment
sr(ranges
[0].anchor
, ranges
[0].caret
);
187 for (size_t i
=1; i
<ranges
.size(); i
++) {
188 sr
.Extend(ranges
[i
].anchor
);
189 sr
.Extend(ranges
[i
].caret
);
195 SelectionSegment
Selection::LimitsForRectangularElseMain() const {
196 if (IsRectangular()) {
199 return SelectionSegment(ranges
[mainRange
].caret
, ranges
[mainRange
].anchor
);
203 size_t Selection::Count() const {
204 return ranges
.size();
207 size_t Selection::Main() const {
211 void Selection::SetMain(size_t r
) {
212 PLATFORM_ASSERT(r
< ranges
.size());
216 SelectionRange
&Selection::Range(size_t r
) {
220 SelectionRange
&Selection::RangeMain() {
221 return ranges
[mainRange
];
224 bool Selection::MoveExtends() const {
228 void Selection::SetMoveExtends(bool moveExtends_
) {
229 moveExtends
= moveExtends_
;
232 bool Selection::Empty() const {
233 for (size_t i
=0; i
<ranges
.size(); i
++) {
234 if (!ranges
[i
].Empty())
240 SelectionPosition
Selection::Last() const {
241 SelectionPosition lastPosition
;
242 for (size_t i
=0; i
<ranges
.size(); i
++) {
243 if (lastPosition
< ranges
[i
].caret
)
244 lastPosition
= ranges
[i
].caret
;
245 if (lastPosition
< ranges
[i
].anchor
)
246 lastPosition
= ranges
[i
].anchor
;
251 int Selection::Length() const {
253 for (size_t i
=0; i
<ranges
.size(); i
++) {
254 len
+= ranges
[i
].Length();
259 void Selection::MovePositions(bool insertion
, int startChange
, int length
) {
260 for (size_t i
=0; i
<ranges
.size(); i
++) {
261 ranges
[i
].caret
.MoveForInsertDelete(insertion
, startChange
, length
);
262 ranges
[i
].anchor
.MoveForInsertDelete(insertion
, startChange
, length
);
266 void Selection::TrimSelection(SelectionRange range
) {
267 for (size_t i
=0; i
<ranges
.size();) {
268 if ((i
!= mainRange
) && (ranges
[i
].Trim(range
))) {
269 // Trimmed to empty so remove
270 for (size_t j
=i
; j
<ranges
.size()-1; j
++) {
271 ranges
[j
] = ranges
[j
+1];
272 if (j
== mainRange
-1)
282 void Selection::SetSelection(SelectionRange range
) {
284 ranges
.push_back(range
);
285 mainRange
= ranges
.size() - 1;
288 void Selection::AddSelection(SelectionRange range
) {
289 TrimSelection(range
);
290 ranges
.push_back(range
);
291 mainRange
= ranges
.size() - 1;
294 void Selection::TentativeSelection(SelectionRange range
) {
295 if (!tentativeMain
) {
296 rangesSaved
= ranges
;
298 ranges
= rangesSaved
;
300 TrimSelection(ranges
[mainRange
]);
301 tentativeMain
= true;
304 void Selection::CommitTentative() {
306 tentativeMain
= false;
309 int Selection::CharacterInSelection(int posCharacter
) const {
310 for (size_t i
=0; i
<ranges
.size(); i
++) {
311 if (ranges
[i
].ContainsCharacter(posCharacter
))
312 return i
== mainRange
? 1 : 2;
317 int Selection::InSelectionForEOL(int pos
) const {
318 for (size_t i
=0; i
<ranges
.size(); i
++) {
319 if (!ranges
[i
].Empty() && (pos
> ranges
[i
].Start().Position()) && (pos
<= ranges
[i
].End().Position()))
320 return i
== mainRange
? 1 : 2;
325 int Selection::VirtualSpaceFor(int pos
) const {
326 int virtualSpace
= 0;
327 for (size_t i
=0; i
<ranges
.size(); i
++) {
328 if ((ranges
[i
].caret
.Position() == pos
) && (virtualSpace
< ranges
[i
].caret
.VirtualSpace()))
329 virtualSpace
= ranges
[i
].caret
.VirtualSpace();
330 if ((ranges
[i
].anchor
.Position() == pos
) && (virtualSpace
< ranges
[i
].anchor
.VirtualSpace()))
331 virtualSpace
= ranges
[i
].anchor
.VirtualSpace();
336 void Selection::Clear() {
338 ranges
.push_back(SelectionRange());
339 mainRange
= ranges
.size() - 1;
342 ranges
[mainRange
].Reset();
343 rangeRectangular
.Reset();
346 void Selection::RemoveDuplicates() {
347 for (size_t i
=0; i
<ranges
.size()-1; i
++) {
348 if (ranges
[i
].Empty()) {
350 while (j
<ranges
.size()) {
351 if (ranges
[i
] == ranges
[j
]) {
352 ranges
.erase(ranges
.begin() + j
);
363 void Selection::RotateMain() {
364 mainRange
= (mainRange
+ 1) % ranges
.size();