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();