1 // Scintilla source code edit control
2 /** @file Selection.cxx
3 ** Classes maintaining the selection.
4 **/
5 // Copyright 2009 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <stdlib.h>
9
10 #include <vector>
11
12 #include "Platform.h"
13
14 #include "Scintilla.h"
15
16 #include "Selection.h"
17
18 #ifdef SCI_NAMESPACE
19 using namespace Scintilla;
20 #endif
21
MoveForInsertDelete(bool insertion,int startChange,int length)22 void SelectionPosition::MoveForInsertDelete(bool insertion, int startChange, int length) {
23 if (position == startChange) {
24 virtualSpace = 0;
25 }
26 if (insertion) {
27 if (position > startChange) {
28 position += length;
29 }
30 } else {
31 if (position > startChange) {
32 int endDeletion = startChange + length;
33 if (position > endDeletion) {
34 position -= length;
35 } else {
36 position = startChange;
37 virtualSpace = 0;
38 }
39 }
40 }
41 }
42
operator <(const SelectionPosition & other) const43 bool SelectionPosition::operator <(const SelectionPosition &other) const {
44 if (position == other.position)
45 return virtualSpace < other.virtualSpace;
46 else
47 return position < other.position;
48 }
49
operator >(const SelectionPosition & other) const50 bool SelectionPosition::operator >(const SelectionPosition &other) const {
51 if (position == other.position)
52 return virtualSpace > other.virtualSpace;
53 else
54 return position > other.position;
55 }
56
operator <=(const SelectionPosition & other) const57 bool SelectionPosition::operator <=(const SelectionPosition &other) const {
58 if (position == other.position && virtualSpace == other.virtualSpace)
59 return true;
60 else
61 return other > *this;
62 }
63
operator >=(const SelectionPosition & other) const64 bool SelectionPosition::operator >=(const SelectionPosition &other) const {
65 if (position == other.position && virtualSpace == other.virtualSpace)
66 return true;
67 else
68 return *this > other;
69 }
70
Length() const71 int SelectionRange::Length() const {
72 if (anchor > caret) {
73 return anchor.Position() - caret.Position();
74 } else {
75 return caret.Position() - anchor.Position();
76 }
77 }
78
Contains(int pos) const79 bool SelectionRange::Contains(int pos) const {
80 if (anchor > caret)
81 return (pos >= caret.Position()) && (pos <= anchor.Position());
82 else
83 return (pos >= anchor.Position()) && (pos <= caret.Position());
84 }
85
Contains(SelectionPosition sp) const86 bool SelectionRange::Contains(SelectionPosition sp) const {
87 if (anchor > caret)
88 return (sp >= caret) && (sp <= anchor);
89 else
90 return (sp >= anchor) && (sp <= caret);
91 }
92
ContainsCharacter(int posCharacter) const93 bool SelectionRange::ContainsCharacter(int posCharacter) const {
94 if (anchor > caret)
95 return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());
96 else
97 return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
98 }
99
Intersect(SelectionSegment check) const100 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();
110 else
111 return portion;
112 } else {
113 return SelectionSegment();
114 }
115 }
116
Trim(SelectionRange range)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
127 end = start;
128 } else if ((start < startRange) && (end > endRange)) {
129 // Completely covers range -> empty at start
130 end = start;
131 } else if (start <= startRange) {
132 // Trim end
133 end = startRange;
134 } else { //
135 PLATFORM_ASSERT(end >= endRange);
136 // Trim start
137 start = endRange;
138 }
139 if (anchor > caret) {
140 caret = start;
141 anchor = end;
142 } else {
143 anchor = start;
144 caret = end;
145 }
146 return Empty();
147 } else {
148 return false;
149 }
150 }
151
152 // If range is all virtual collapse to start of virtual space
MinimizeVirtualSpace()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);
160 }
161 }
162
Selection()163 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream) {
164 AddSelection(SelectionPosition(0));
165 }
166
~Selection()167 Selection::~Selection() {
168 }
169
IsRectangular() const170 bool Selection::IsRectangular() const {
171 return (selType == selRectangle) || (selType == selThin);
172 }
173
MainCaret() const174 int Selection::MainCaret() const {
175 return ranges[mainRange].caret.Position();
176 }
177
MainAnchor() const178 int Selection::MainAnchor() const {
179 return ranges[mainRange].anchor.Position();
180 }
181
Rectangular()182 SelectionRange &Selection::Rectangular() {
183 return rangeRectangular;
184 }
185
Limits() const186 SelectionSegment Selection::Limits() const {
187 if (ranges.empty()) {
188 return SelectionSegment();
189 } else {
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);
194 }
195 return sr;
196 }
197 }
198
LimitsForRectangularElseMain() const199 SelectionSegment Selection::LimitsForRectangularElseMain() const {
200 if (IsRectangular()) {
201 return Limits();
202 } else {
203 return SelectionSegment(ranges[mainRange].caret, ranges[mainRange].anchor);
204 }
205 }
206
Count() const207 size_t Selection::Count() const {
208 return ranges.size();
209 }
210
Main() const211 size_t Selection::Main() const {
212 return mainRange;
213 }
214
SetMain(size_t r)215 void Selection::SetMain(size_t r) {
216 PLATFORM_ASSERT(r < ranges.size());
217 mainRange = r;
218 }
219
Range(size_t r)220 SelectionRange &Selection::Range(size_t r) {
221 return ranges[r];
222 }
223
RangeMain()224 SelectionRange &Selection::RangeMain() {
225 return ranges[mainRange];
226 }
227
MoveExtends() const228 bool Selection::MoveExtends() const {
229 return moveExtends;
230 }
231
SetMoveExtends(bool moveExtends_)232 void Selection::SetMoveExtends(bool moveExtends_) {
233 moveExtends = moveExtends_;
234 }
235
Empty() const236 bool Selection::Empty() const {
237 for (size_t i=0; i<ranges.size(); i++) {
238 if (!ranges[i].Empty())
239 return false;
240 }
241 return true;
242 }
243
Last() const244 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;
251 }
252 return lastPosition;
253 }
254
Length() const255 int Selection::Length() const {
256 int len = 0;
257 for (size_t i=0; i<ranges.size(); i++) {
258 len += ranges[i].Length();
259 }
260 return len;
261 }
262
MovePositions(bool insertion,int startChange,int 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);
267 }
268 }
269
TrimSelection(SelectionRange range)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)
277 mainRange--;
278 }
279 ranges.pop_back();
280 } else {
281 i++;
282 }
283 }
284 }
285
SetSelection(SelectionRange range)286 void Selection::SetSelection(SelectionRange range) {
287 ranges.clear();
288 ranges.push_back(range);
289 mainRange = ranges.size() - 1;
290 }
291
AddSelection(SelectionRange range)292 void Selection::AddSelection(SelectionRange range) {
293 TrimSelection(range);
294 ranges.push_back(range);
295 mainRange = ranges.size() - 1;
296 }
297
AddSelectionWithoutTrim(SelectionRange range)298 void Selection::AddSelectionWithoutTrim(SelectionRange range) {
299 ranges.push_back(range);
300 mainRange = ranges.size() - 1;
301 }
302
TentativeSelection(SelectionRange range)303 void Selection::TentativeSelection(SelectionRange range) {
304 if (!tentativeMain) {
305 rangesSaved = ranges;
306 }
307 ranges = rangesSaved;
308 AddSelection(range);
309 TrimSelection(ranges[mainRange]);
310 tentativeMain = true;
311 }
312
CommitTentative()313 void Selection::CommitTentative() {
314 rangesSaved.clear();
315 tentativeMain = false;
316 }
317
CharacterInSelection(int posCharacter) const318 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;
322 }
323 return 0;
324 }
325
InSelectionForEOL(int pos) const326 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;
330 }
331 return 0;
332 }
333
VirtualSpaceFor(int pos) const334 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();
341 }
342 return virtualSpace;
343 }
344
Clear()345 void Selection::Clear() {
346 ranges.clear();
347 ranges.push_back(SelectionRange());
348 mainRange = ranges.size() - 1;
349 selType = selStream;
350 moveExtends = false;
351 ranges[mainRange].Reset();
352 rangeRectangular.Reset();
353 }
354
RemoveDuplicates()355 void Selection::RemoveDuplicates() {
356 for (size_t i=0; i<ranges.size()-1; i++) {
357 if (ranges[i].Empty()) {
358 size_t j=i+1;
359 while (j<ranges.size()) {
360 if (ranges[i] == ranges[j]) {
361 ranges.erase(ranges.begin() + j);
362 if (mainRange >= j)
363 mainRange--;
364 } else {
365 j++;
366 }
367 }
368 }
369 }
370 }
371
RotateMain()372 void Selection::RotateMain() {
373 mainRange = (mainRange + 1) % ranges.size();
374 }
375
376