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 <cstddef>
9 #include <cstdlib>
10 
11 #include <stdexcept>
12 #include <string_view>
13 #include <vector>
14 #include <algorithm>
15 #include <memory>
16 
17 #include "Platform.h"
18 
19 #include "Scintilla.h"
20 
21 #include "Position.h"
22 #include "Selection.h"
23 
24 using namespace Scintilla;
25 
MoveForInsertDelete(bool insertion,Sci::Position startChange,Sci::Position length,bool moveForEqual)26 void SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length, bool moveForEqual) noexcept {
27 	if (insertion) {
28 		if (position == startChange) {
29 			// Always consume virtual space
30 			const Sci::Position virtualLengthRemove = std::min(length, virtualSpace);
31 			virtualSpace -= virtualLengthRemove;
32 			position += virtualLengthRemove;
33 			if (moveForEqual) {
34 				const Sci::Position lengthAfterVirtualRemove = length - virtualLengthRemove;
35 				position += lengthAfterVirtualRemove;
36 			}
37 		} else if (position > startChange) {
38 			position += length;
39 		}
40 	} else {
41 		if (position == startChange) {
42 			virtualSpace = 0;
43 		}
44 		if (position > startChange) {
45 			const Sci::Position endDeletion = startChange + length;
46 			if (position > endDeletion) {
47 				position -= length;
48 			} else {
49 				position = startChange;
50 				virtualSpace = 0;
51 			}
52 		}
53 	}
54 }
55 
operator <(const SelectionPosition & other) const56 bool SelectionPosition::operator <(const SelectionPosition &other) const noexcept {
57 	if (position == other.position)
58 		return virtualSpace < other.virtualSpace;
59 	else
60 		return position < other.position;
61 }
62 
operator >(const SelectionPosition & other) const63 bool SelectionPosition::operator >(const SelectionPosition &other) const noexcept {
64 	if (position == other.position)
65 		return virtualSpace > other.virtualSpace;
66 	else
67 		return position > other.position;
68 }
69 
operator <=(const SelectionPosition & other) const70 bool SelectionPosition::operator <=(const SelectionPosition &other) const noexcept {
71 	if (position == other.position && virtualSpace == other.virtualSpace)
72 		return true;
73 	else
74 		return other > *this;
75 }
76 
operator >=(const SelectionPosition & other) const77 bool SelectionPosition::operator >=(const SelectionPosition &other) const noexcept {
78 	if (position == other.position && virtualSpace == other.virtualSpace)
79 		return true;
80 	else
81 		return *this > other;
82 }
83 
Length() const84 Sci::Position SelectionRange::Length() const noexcept {
85 	if (anchor > caret) {
86 		return anchor.Position() - caret.Position();
87 	} else {
88 		return caret.Position() - anchor.Position();
89 	}
90 }
91 
MoveForInsertDelete(bool insertion,Sci::Position startChange,Sci::Position length)92 void SelectionRange::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) noexcept {
93 	// For insertions that occur at the start of the selection move both the start
94 	// and end of the selection to preserve the selected length.
95 	// The end will automatically move since it is after the insertion, so determine
96 	// which position is the start and pass this into
97 	// SelectionPosition::MoveForInsertDelete.
98 	// There isn't any reason to move an empty selection so don't move it.
99 	const bool caretStart = caret.Position() < anchor.Position();
100 	const bool anchorStart = anchor.Position() < caret.Position();
101 
102 	caret.MoveForInsertDelete(insertion, startChange, length, caretStart);
103 	anchor.MoveForInsertDelete(insertion, startChange, length, anchorStart);
104 }
105 
Contains(Sci::Position pos) const106 bool SelectionRange::Contains(Sci::Position pos) const noexcept {
107 	if (anchor > caret)
108 		return (pos >= caret.Position()) && (pos <= anchor.Position());
109 	else
110 		return (pos >= anchor.Position()) && (pos <= caret.Position());
111 }
112 
Contains(SelectionPosition sp) const113 bool SelectionRange::Contains(SelectionPosition sp) const noexcept {
114 	if (anchor > caret)
115 		return (sp >= caret) && (sp <= anchor);
116 	else
117 		return (sp >= anchor) && (sp <= caret);
118 }
119 
ContainsCharacter(Sci::Position posCharacter) const120 bool SelectionRange::ContainsCharacter(Sci::Position posCharacter) const noexcept {
121 	if (anchor > caret)
122 		return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());
123 	else
124 		return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
125 }
126 
Intersect(SelectionSegment check) const127 SelectionSegment SelectionRange::Intersect(SelectionSegment check) const noexcept {
128 	const SelectionSegment inOrder(caret, anchor);
129 	if ((inOrder.start <= check.end) || (inOrder.end >= check.start)) {
130 		SelectionSegment portion = check;
131 		if (portion.start < inOrder.start)
132 			portion.start = inOrder.start;
133 		if (portion.end > inOrder.end)
134 			portion.end = inOrder.end;
135 		if (portion.start > portion.end)
136 			return SelectionSegment();
137 		else
138 			return portion;
139 	} else {
140 		return SelectionSegment();
141 	}
142 }
143 
Swap()144 void SelectionRange::Swap() noexcept {
145 	std::swap(caret, anchor);
146 }
147 
Trim(SelectionRange range)148 bool SelectionRange::Trim(SelectionRange range) noexcept {
149 	const SelectionPosition startRange = range.Start();
150 	const SelectionPosition endRange = range.End();
151 	SelectionPosition start = Start();
152 	SelectionPosition end = End();
153 	PLATFORM_ASSERT(start <= end);
154 	PLATFORM_ASSERT(startRange <= endRange);
155 	if ((startRange <= end) && (endRange >= start)) {
156 		if ((start > startRange) && (end < endRange)) {
157 			// Completely covered by range -> empty at start
158 			end = start;
159 		} else if ((start < startRange) && (end > endRange)) {
160 			// Completely covers range -> empty at start
161 			end = start;
162 		} else if (start <= startRange) {
163 			// Trim end
164 			end = startRange;
165 		} else { //
166 			PLATFORM_ASSERT(end >= endRange);
167 			// Trim start
168 			start = endRange;
169 		}
170 		if (anchor > caret) {
171 			caret = start;
172 			anchor = end;
173 		} else {
174 			anchor = start;
175 			caret = end;
176 		}
177 		return Empty();
178 	} else {
179 		return false;
180 	}
181 }
182 
183 // If range is all virtual collapse to start of virtual space
MinimizeVirtualSpace()184 void SelectionRange::MinimizeVirtualSpace() noexcept {
185 	if (caret.Position() == anchor.Position()) {
186 		Sci::Position virtualSpace = caret.VirtualSpace();
187 		if (virtualSpace > anchor.VirtualSpace())
188 			virtualSpace = anchor.VirtualSpace();
189 		caret.SetVirtualSpace(virtualSpace);
190 		anchor.SetVirtualSpace(virtualSpace);
191 	}
192 }
193 
Selection()194 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream) {
195 	AddSelection(SelectionRange(SelectionPosition(0)));
196 }
197 
~Selection()198 Selection::~Selection() {
199 }
200 
IsRectangular() const201 bool Selection::IsRectangular() const noexcept {
202 	return (selType == selRectangle) || (selType == selThin);
203 }
204 
MainCaret() const205 Sci::Position Selection::MainCaret() const noexcept {
206 	return ranges[mainRange].caret.Position();
207 }
208 
MainAnchor() const209 Sci::Position Selection::MainAnchor() const noexcept {
210 	return ranges[mainRange].anchor.Position();
211 }
212 
Rectangular()213 SelectionRange &Selection::Rectangular() noexcept {
214 	return rangeRectangular;
215 }
216 
Limits() const217 SelectionSegment Selection::Limits() const noexcept {
218 	if (ranges.empty()) {
219 		return SelectionSegment();
220 	} else {
221 		SelectionSegment sr(ranges[0].anchor, ranges[0].caret);
222 		for (size_t i=1; i<ranges.size(); i++) {
223 			sr.Extend(ranges[i].anchor);
224 			sr.Extend(ranges[i].caret);
225 		}
226 		return sr;
227 	}
228 }
229 
LimitsForRectangularElseMain() const230 SelectionSegment Selection::LimitsForRectangularElseMain() const {
231 	if (IsRectangular()) {
232 		return Limits();
233 	} else {
234 		return SelectionSegment(ranges[mainRange].caret, ranges[mainRange].anchor);
235 	}
236 }
237 
Count() const238 size_t Selection::Count() const noexcept {
239 	return ranges.size();
240 }
241 
Main() const242 size_t Selection::Main() const noexcept {
243 	return mainRange;
244 }
245 
SetMain(size_t r)246 void Selection::SetMain(size_t r) noexcept {
247 	PLATFORM_ASSERT(r < ranges.size());
248 	mainRange = r;
249 }
250 
Range(size_t r)251 SelectionRange &Selection::Range(size_t r) noexcept {
252 	return ranges[r];
253 }
254 
Range(size_t r) const255 const SelectionRange &Selection::Range(size_t r) const noexcept {
256 	return ranges[r];
257 }
258 
RangeMain()259 SelectionRange &Selection::RangeMain() noexcept {
260 	return ranges[mainRange];
261 }
262 
RangeMain() const263 const SelectionRange &Selection::RangeMain() const noexcept {
264 	return ranges[mainRange];
265 }
266 
Start() const267 SelectionPosition Selection::Start() const noexcept {
268 	if (IsRectangular()) {
269 		return rangeRectangular.Start();
270 	} else {
271 		return ranges[mainRange].Start();
272 	}
273 }
274 
MoveExtends() const275 bool Selection::MoveExtends() const noexcept {
276 	return moveExtends;
277 }
278 
SetMoveExtends(bool moveExtends_)279 void Selection::SetMoveExtends(bool moveExtends_) noexcept {
280 	moveExtends = moveExtends_;
281 }
282 
Empty() const283 bool Selection::Empty() const noexcept {
284 	for (const SelectionRange &range : ranges) {
285 		if (!range.Empty())
286 			return false;
287 	}
288 	return true;
289 }
290 
Last() const291 SelectionPosition Selection::Last() const noexcept {
292 	SelectionPosition lastPosition;
293 	for (const SelectionRange &range : ranges) {
294 		if (lastPosition < range.caret)
295 			lastPosition = range.caret;
296 		if (lastPosition < range.anchor)
297 			lastPosition = range.anchor;
298 	}
299 	return lastPosition;
300 }
301 
Length() const302 Sci::Position Selection::Length() const noexcept {
303 	Sci::Position len = 0;
304 	for (const SelectionRange &range : ranges) {
305 		len += range.Length();
306 	}
307 	return len;
308 }
309 
MovePositions(bool insertion,Sci::Position startChange,Sci::Position length)310 void Selection::MovePositions(bool insertion, Sci::Position startChange, Sci::Position length) noexcept {
311 	for (SelectionRange &range : ranges) {
312 		range.MoveForInsertDelete(insertion, startChange, length);
313 	}
314 	if (selType == selRectangle) {
315 		rangeRectangular.MoveForInsertDelete(insertion, startChange, length);
316 	}
317 }
318 
TrimSelection(SelectionRange range)319 void Selection::TrimSelection(SelectionRange range) noexcept {
320 	for (size_t i=0; i<ranges.size();) {
321 		if ((i != mainRange) && (ranges[i].Trim(range))) {
322 			// Trimmed to empty so remove
323 			for (size_t j=i; j<ranges.size()-1; j++) {
324 				ranges[j] = ranges[j+1];
325 				if (j == mainRange-1)
326 					mainRange--;
327 			}
328 			ranges.pop_back();
329 		} else {
330 			i++;
331 		}
332 	}
333 }
334 
TrimOtherSelections(size_t r,SelectionRange range)335 void Selection::TrimOtherSelections(size_t r, SelectionRange range) noexcept {
336 	for (size_t i = 0; i<ranges.size(); ++i) {
337 		if (i != r) {
338 			ranges[i].Trim(range);
339 		}
340 	}
341 }
342 
SetSelection(SelectionRange range)343 void Selection::SetSelection(SelectionRange range) {
344 	ranges.clear();
345 	ranges.push_back(range);
346 	mainRange = ranges.size() - 1;
347 }
348 
AddSelection(SelectionRange range)349 void Selection::AddSelection(SelectionRange range) {
350 	TrimSelection(range);
351 	ranges.push_back(range);
352 	mainRange = ranges.size() - 1;
353 }
354 
AddSelectionWithoutTrim(SelectionRange range)355 void Selection::AddSelectionWithoutTrim(SelectionRange range) {
356 	ranges.push_back(range);
357 	mainRange = ranges.size() - 1;
358 }
359 
DropSelection(size_t r)360 void Selection::DropSelection(size_t r) {
361 	if ((ranges.size() > 1) && (r < ranges.size())) {
362 		size_t mainNew = mainRange;
363 		if (mainNew >= r) {
364 			if (mainNew == 0) {
365 				mainNew = ranges.size() - 2;
366 			} else {
367 				mainNew--;
368 			}
369 		}
370 		ranges.erase(ranges.begin() + r);
371 		mainRange = mainNew;
372 	}
373 }
374 
DropAdditionalRanges()375 void Selection::DropAdditionalRanges() {
376 	SetSelection(RangeMain());
377 }
378 
TentativeSelection(SelectionRange range)379 void Selection::TentativeSelection(SelectionRange range) {
380 	if (!tentativeMain) {
381 		rangesSaved = ranges;
382 	}
383 	ranges = rangesSaved;
384 	AddSelection(range);
385 	TrimSelection(ranges[mainRange]);
386 	tentativeMain = true;
387 }
388 
CommitTentative()389 void Selection::CommitTentative() noexcept {
390 	rangesSaved.clear();
391 	tentativeMain = false;
392 }
393 
CharacterInSelection(Sci::Position posCharacter) const394 int Selection::CharacterInSelection(Sci::Position posCharacter) const noexcept {
395 	for (size_t i=0; i<ranges.size(); i++) {
396 		if (ranges[i].ContainsCharacter(posCharacter))
397 			return i == mainRange ? 1 : 2;
398 	}
399 	return 0;
400 }
401 
InSelectionForEOL(Sci::Position pos) const402 int Selection::InSelectionForEOL(Sci::Position pos) const noexcept {
403 	for (size_t i=0; i<ranges.size(); i++) {
404 		if (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position()))
405 			return i == mainRange ? 1 : 2;
406 	}
407 	return 0;
408 }
409 
VirtualSpaceFor(Sci::Position pos) const410 Sci::Position Selection::VirtualSpaceFor(Sci::Position pos) const noexcept {
411 	Sci::Position virtualSpace = 0;
412 	for (const SelectionRange &range : ranges) {
413 		if ((range.caret.Position() == pos) && (virtualSpace < range.caret.VirtualSpace()))
414 			virtualSpace = range.caret.VirtualSpace();
415 		if ((range.anchor.Position() == pos) && (virtualSpace < range.anchor.VirtualSpace()))
416 			virtualSpace = range.anchor.VirtualSpace();
417 	}
418 	return virtualSpace;
419 }
420 
Clear()421 void Selection::Clear() {
422 	ranges.clear();
423 	ranges.emplace_back();
424 	mainRange = ranges.size() - 1;
425 	selType = selStream;
426 	moveExtends = false;
427 	ranges[mainRange].Reset();
428 	rangeRectangular.Reset();
429 }
430 
RemoveDuplicates()431 void Selection::RemoveDuplicates() {
432 	for (size_t i=0; i<ranges.size()-1; i++) {
433 		if (ranges[i].Empty()) {
434 			size_t j=i+1;
435 			while (j<ranges.size()) {
436 				if (ranges[i] == ranges[j]) {
437 					ranges.erase(ranges.begin() + j);
438 					if (mainRange >= j)
439 						mainRange--;
440 				} else {
441 					j++;
442 				}
443 			}
444 		}
445 	}
446 }
447 
RotateMain()448 void Selection::RotateMain() noexcept {
449 	mainRange = (mainRange + 1) % ranges.size();
450 }
451 
452