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