Update Scintilla to 3.4.2 pre-release
[geany-mirror.git] / scintilla / src / Selection.cxx
blob4c2ab0508e1addda8b12c1254d3d8fe5fc6b40c1
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.
8 #include <stdlib.h>
10 #include <vector>
11 #include <algorithm>
13 #include "Platform.h"
15 #include "Scintilla.h"
17 #include "Selection.h"
19 #ifdef SCI_NAMESPACE
20 using namespace Scintilla;
21 #endif
23 void SelectionPosition::MoveForInsertDelete(bool insertion, int startChange, int length) {
24 if (insertion) {
25 if (position == startChange) {
26 int virtualLengthRemove = std::min(length, virtualSpace);
27 virtualSpace -= virtualLengthRemove;
28 position += virtualLengthRemove;
29 } else if (position > startChange) {
30 position += length;
32 } else {
33 if (position == startChange) {
34 virtualSpace = 0;
36 if (position > startChange) {
37 int endDeletion = startChange + length;
38 if (position > endDeletion) {
39 position -= length;
40 } else {
41 position = startChange;
42 virtualSpace = 0;
48 bool SelectionPosition::operator <(const SelectionPosition &other) const {
49 if (position == other.position)
50 return virtualSpace < other.virtualSpace;
51 else
52 return position < other.position;
55 bool SelectionPosition::operator >(const SelectionPosition &other) const {
56 if (position == other.position)
57 return virtualSpace > other.virtualSpace;
58 else
59 return position > other.position;
62 bool SelectionPosition::operator <=(const SelectionPosition &other) const {
63 if (position == other.position && virtualSpace == other.virtualSpace)
64 return true;
65 else
66 return other > *this;
69 bool SelectionPosition::operator >=(const SelectionPosition &other) const {
70 if (position == other.position && virtualSpace == other.virtualSpace)
71 return true;
72 else
73 return *this > other;
76 int SelectionRange::Length() const {
77 if (anchor > caret) {
78 return anchor.Position() - caret.Position();
79 } else {
80 return caret.Position() - anchor.Position();
84 bool SelectionRange::Contains(int pos) const {
85 if (anchor > caret)
86 return (pos >= caret.Position()) && (pos <= anchor.Position());
87 else
88 return (pos >= anchor.Position()) && (pos <= caret.Position());
91 bool SelectionRange::Contains(SelectionPosition sp) const {
92 if (anchor > caret)
93 return (sp >= caret) && (sp <= anchor);
94 else
95 return (sp >= anchor) && (sp <= caret);
98 bool SelectionRange::ContainsCharacter(int posCharacter) const {
99 if (anchor > caret)
100 return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());
101 else
102 return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
105 SelectionSegment SelectionRange::Intersect(SelectionSegment check) const {
106 SelectionSegment inOrder(caret, anchor);
107 if ((inOrder.start <= check.end) || (inOrder.end >= check.start)) {
108 SelectionSegment portion = check;
109 if (portion.start < inOrder.start)
110 portion.start = inOrder.start;
111 if (portion.end > inOrder.end)
112 portion.end = inOrder.end;
113 if (portion.start > portion.end)
114 return SelectionSegment();
115 else
116 return portion;
117 } else {
118 return SelectionSegment();
122 bool SelectionRange::Trim(SelectionRange range) {
123 SelectionPosition startRange = range.Start();
124 SelectionPosition endRange = range.End();
125 SelectionPosition start = Start();
126 SelectionPosition end = End();
127 PLATFORM_ASSERT(start <= end);
128 PLATFORM_ASSERT(startRange <= endRange);
129 if ((startRange <= end) && (endRange >= start)) {
130 if ((start > startRange) && (end < endRange)) {
131 // Completely covered by range -> empty at start
132 end = start;
133 } else if ((start < startRange) && (end > endRange)) {
134 // Completely covers range -> empty at start
135 end = start;
136 } else if (start <= startRange) {
137 // Trim end
138 end = startRange;
139 } else { //
140 PLATFORM_ASSERT(end >= endRange);
141 // Trim start
142 start = endRange;
144 if (anchor > caret) {
145 caret = start;
146 anchor = end;
147 } else {
148 anchor = start;
149 caret = end;
151 return Empty();
152 } else {
153 return false;
157 // If range is all virtual collapse to start of virtual space
158 void SelectionRange::MinimizeVirtualSpace() {
159 if (caret.Position() == anchor.Position()) {
160 int virtualSpace = caret.VirtualSpace();
161 if (virtualSpace > anchor.VirtualSpace())
162 virtualSpace = anchor.VirtualSpace();
163 caret.SetVirtualSpace(virtualSpace);
164 anchor.SetVirtualSpace(virtualSpace);
168 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream) {
169 AddSelection(SelectionRange(SelectionPosition(0)));
172 Selection::~Selection() {
175 bool Selection::IsRectangular() const {
176 return (selType == selRectangle) || (selType == selThin);
179 int Selection::MainCaret() const {
180 return ranges[mainRange].caret.Position();
183 int Selection::MainAnchor() const {
184 return ranges[mainRange].anchor.Position();
187 SelectionRange &Selection::Rectangular() {
188 return rangeRectangular;
191 SelectionSegment Selection::Limits() const {
192 if (ranges.empty()) {
193 return SelectionSegment();
194 } else {
195 SelectionSegment sr(ranges[0].anchor, ranges[0].caret);
196 for (size_t i=1; i<ranges.size(); i++) {
197 sr.Extend(ranges[i].anchor);
198 sr.Extend(ranges[i].caret);
200 return sr;
204 SelectionSegment Selection::LimitsForRectangularElseMain() const {
205 if (IsRectangular()) {
206 return Limits();
207 } else {
208 return SelectionSegment(ranges[mainRange].caret, ranges[mainRange].anchor);
212 size_t Selection::Count() const {
213 return ranges.size();
216 size_t Selection::Main() const {
217 return mainRange;
220 void Selection::SetMain(size_t r) {
221 PLATFORM_ASSERT(r < ranges.size());
222 mainRange = r;
225 SelectionRange &Selection::Range(size_t r) {
226 return ranges[r];
229 SelectionRange &Selection::RangeMain() {
230 return ranges[mainRange];
233 SelectionPosition Selection::Start() const {
234 if (IsRectangular()) {
235 return rangeRectangular.Start();
236 } else {
237 return ranges[mainRange].Start();
241 bool Selection::MoveExtends() const {
242 return moveExtends;
245 void Selection::SetMoveExtends(bool moveExtends_) {
246 moveExtends = moveExtends_;
249 bool Selection::Empty() const {
250 for (size_t i=0; i<ranges.size(); i++) {
251 if (!ranges[i].Empty())
252 return false;
254 return true;
257 SelectionPosition Selection::Last() const {
258 SelectionPosition lastPosition;
259 for (size_t i=0; i<ranges.size(); i++) {
260 if (lastPosition < ranges[i].caret)
261 lastPosition = ranges[i].caret;
262 if (lastPosition < ranges[i].anchor)
263 lastPosition = ranges[i].anchor;
265 return lastPosition;
268 int Selection::Length() const {
269 int len = 0;
270 for (size_t i=0; i<ranges.size(); i++) {
271 len += ranges[i].Length();
273 return len;
276 void Selection::MovePositions(bool insertion, int startChange, int length) {
277 for (size_t i=0; i<ranges.size(); i++) {
278 ranges[i].caret.MoveForInsertDelete(insertion, startChange, length);
279 ranges[i].anchor.MoveForInsertDelete(insertion, startChange, length);
283 void Selection::TrimSelection(SelectionRange range) {
284 for (size_t i=0; i<ranges.size();) {
285 if ((i != mainRange) && (ranges[i].Trim(range))) {
286 // Trimmed to empty so remove
287 for (size_t j=i; j<ranges.size()-1; j++) {
288 ranges[j] = ranges[j+1];
289 if (j == mainRange-1)
290 mainRange--;
292 ranges.pop_back();
293 } else {
294 i++;
299 void Selection::SetSelection(SelectionRange range) {
300 ranges.clear();
301 ranges.push_back(range);
302 mainRange = ranges.size() - 1;
305 void Selection::AddSelection(SelectionRange range) {
306 TrimSelection(range);
307 ranges.push_back(range);
308 mainRange = ranges.size() - 1;
311 void Selection::AddSelectionWithoutTrim(SelectionRange range) {
312 ranges.push_back(range);
313 mainRange = ranges.size() - 1;
316 void Selection::DropSelection(size_t r) {
317 if ((ranges.size() > 1) && (r < ranges.size())) {
318 size_t mainNew = mainRange;
319 if (mainNew >= r) {
320 if (mainNew == 0) {
321 mainNew = ranges.size() - 2;
322 } else {
323 mainNew--;
326 ranges.erase(ranges.begin() + r);
327 mainRange = mainNew;
331 void Selection::TentativeSelection(SelectionRange range) {
332 if (!tentativeMain) {
333 rangesSaved = ranges;
335 ranges = rangesSaved;
336 AddSelection(range);
337 TrimSelection(ranges[mainRange]);
338 tentativeMain = true;
341 void Selection::CommitTentative() {
342 rangesSaved.clear();
343 tentativeMain = false;
346 int Selection::CharacterInSelection(int posCharacter) const {
347 for (size_t i=0; i<ranges.size(); i++) {
348 if (ranges[i].ContainsCharacter(posCharacter))
349 return i == mainRange ? 1 : 2;
351 return 0;
354 int Selection::InSelectionForEOL(int pos) const {
355 for (size_t i=0; i<ranges.size(); i++) {
356 if (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position()))
357 return i == mainRange ? 1 : 2;
359 return 0;
362 int Selection::VirtualSpaceFor(int pos) const {
363 int virtualSpace = 0;
364 for (size_t i=0; i<ranges.size(); i++) {
365 if ((ranges[i].caret.Position() == pos) && (virtualSpace < ranges[i].caret.VirtualSpace()))
366 virtualSpace = ranges[i].caret.VirtualSpace();
367 if ((ranges[i].anchor.Position() == pos) && (virtualSpace < ranges[i].anchor.VirtualSpace()))
368 virtualSpace = ranges[i].anchor.VirtualSpace();
370 return virtualSpace;
373 void Selection::Clear() {
374 ranges.clear();
375 ranges.push_back(SelectionRange());
376 mainRange = ranges.size() - 1;
377 selType = selStream;
378 moveExtends = false;
379 ranges[mainRange].Reset();
380 rangeRectangular.Reset();
383 void Selection::RemoveDuplicates() {
384 for (size_t i=0; i<ranges.size()-1; i++) {
385 if (ranges[i].Empty()) {
386 size_t j=i+1;
387 while (j<ranges.size()) {
388 if (ranges[i] == ranges[j]) {
389 ranges.erase(ranges.begin() + j);
390 if (mainRange >= j)
391 mainRange--;
392 } else {
393 j++;
400 void Selection::RotateMain() {
401 mainRange = (mainRange + 1) % ranges.size();