Fix action icons in the log dialog being clipped on High-DPI displays
[TortoiseGit.git] / ext / scintilla / src / Selection.cxx
blob79ef16cfa8c271b88979b91b7932bb5f18b82a89
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 <stdexcept>
11 #include <vector>
12 #include <algorithm>
14 #include "Platform.h"
16 #include "Scintilla.h"
18 #include "Position.h"
19 #include "Selection.h"
21 #ifdef SCI_NAMESPACE
22 using namespace Scintilla;
23 #endif
25 void SelectionPosition::MoveForInsertDelete(bool insertion, int startChange, int length) {
26 if (insertion) {
27 if (position == startChange) {
28 int virtualLengthRemove = std::min(length, virtualSpace);
29 virtualSpace -= virtualLengthRemove;
30 position += virtualLengthRemove;
31 } else if (position > startChange) {
32 position += length;
34 } else {
35 if (position == startChange) {
36 virtualSpace = 0;
38 if (position > startChange) {
39 int endDeletion = startChange + length;
40 if (position > endDeletion) {
41 position -= length;
42 } else {
43 position = startChange;
44 virtualSpace = 0;
50 bool SelectionPosition::operator <(const SelectionPosition &other) const {
51 if (position == other.position)
52 return virtualSpace < other.virtualSpace;
53 else
54 return position < other.position;
57 bool SelectionPosition::operator >(const SelectionPosition &other) const {
58 if (position == other.position)
59 return virtualSpace > other.virtualSpace;
60 else
61 return position > other.position;
64 bool SelectionPosition::operator <=(const SelectionPosition &other) const {
65 if (position == other.position && virtualSpace == other.virtualSpace)
66 return true;
67 else
68 return other > *this;
71 bool SelectionPosition::operator >=(const SelectionPosition &other) const {
72 if (position == other.position && virtualSpace == other.virtualSpace)
73 return true;
74 else
75 return *this > other;
78 int SelectionRange::Length() const {
79 if (anchor > caret) {
80 return anchor.Position() - caret.Position();
81 } else {
82 return caret.Position() - anchor.Position();
86 void SelectionRange::MoveForInsertDelete(bool insertion, int startChange, int length) {
87 caret.MoveForInsertDelete(insertion, startChange, length);
88 anchor.MoveForInsertDelete(insertion, startChange, length);
91 bool SelectionRange::Contains(int pos) const {
92 if (anchor > caret)
93 return (pos >= caret.Position()) && (pos <= anchor.Position());
94 else
95 return (pos >= anchor.Position()) && (pos <= caret.Position());
98 bool SelectionRange::Contains(SelectionPosition sp) const {
99 if (anchor > caret)
100 return (sp >= caret) && (sp <= anchor);
101 else
102 return (sp >= anchor) && (sp <= caret);
105 bool SelectionRange::ContainsCharacter(int posCharacter) const {
106 if (anchor > caret)
107 return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());
108 else
109 return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
112 SelectionSegment SelectionRange::Intersect(SelectionSegment check) const {
113 SelectionSegment inOrder(caret, anchor);
114 if ((inOrder.start <= check.end) || (inOrder.end >= check.start)) {
115 SelectionSegment portion = check;
116 if (portion.start < inOrder.start)
117 portion.start = inOrder.start;
118 if (portion.end > inOrder.end)
119 portion.end = inOrder.end;
120 if (portion.start > portion.end)
121 return SelectionSegment();
122 else
123 return portion;
124 } else {
125 return SelectionSegment();
129 void SelectionRange::Swap() {
130 std::swap(caret, anchor);
133 bool SelectionRange::Trim(SelectionRange range) {
134 SelectionPosition startRange = range.Start();
135 SelectionPosition endRange = range.End();
136 SelectionPosition start = Start();
137 SelectionPosition end = End();
138 PLATFORM_ASSERT(start <= end);
139 PLATFORM_ASSERT(startRange <= endRange);
140 if ((startRange <= end) && (endRange >= start)) {
141 if ((start > startRange) && (end < endRange)) {
142 // Completely covered by range -> empty at start
143 end = start;
144 } else if ((start < startRange) && (end > endRange)) {
145 // Completely covers range -> empty at start
146 end = start;
147 } else if (start <= startRange) {
148 // Trim end
149 end = startRange;
150 } else { //
151 PLATFORM_ASSERT(end >= endRange);
152 // Trim start
153 start = endRange;
155 if (anchor > caret) {
156 caret = start;
157 anchor = end;
158 } else {
159 anchor = start;
160 caret = end;
162 return Empty();
163 } else {
164 return false;
168 // If range is all virtual collapse to start of virtual space
169 void SelectionRange::MinimizeVirtualSpace() {
170 if (caret.Position() == anchor.Position()) {
171 int virtualSpace = caret.VirtualSpace();
172 if (virtualSpace > anchor.VirtualSpace())
173 virtualSpace = anchor.VirtualSpace();
174 caret.SetVirtualSpace(virtualSpace);
175 anchor.SetVirtualSpace(virtualSpace);
179 Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream) {
180 AddSelection(SelectionRange(SelectionPosition(0)));
183 Selection::~Selection() {
186 bool Selection::IsRectangular() const {
187 return (selType == selRectangle) || (selType == selThin);
190 int Selection::MainCaret() const {
191 return ranges[mainRange].caret.Position();
194 int Selection::MainAnchor() const {
195 return ranges[mainRange].anchor.Position();
198 SelectionRange &Selection::Rectangular() {
199 return rangeRectangular;
202 SelectionSegment Selection::Limits() const {
203 if (ranges.empty()) {
204 return SelectionSegment();
205 } else {
206 SelectionSegment sr(ranges[0].anchor, ranges[0].caret);
207 for (size_t i=1; i<ranges.size(); i++) {
208 sr.Extend(ranges[i].anchor);
209 sr.Extend(ranges[i].caret);
211 return sr;
215 SelectionSegment Selection::LimitsForRectangularElseMain() const {
216 if (IsRectangular()) {
217 return Limits();
218 } else {
219 return SelectionSegment(ranges[mainRange].caret, ranges[mainRange].anchor);
223 size_t Selection::Count() const {
224 return ranges.size();
227 size_t Selection::Main() const {
228 return mainRange;
231 void Selection::SetMain(size_t r) {
232 PLATFORM_ASSERT(r < ranges.size());
233 mainRange = r;
236 SelectionRange &Selection::Range(size_t r) {
237 return ranges[r];
240 const SelectionRange &Selection::Range(size_t r) const {
241 return ranges[r];
244 SelectionRange &Selection::RangeMain() {
245 return ranges[mainRange];
248 const SelectionRange &Selection::RangeMain() const {
249 return ranges[mainRange];
252 SelectionPosition Selection::Start() const {
253 if (IsRectangular()) {
254 return rangeRectangular.Start();
255 } else {
256 return ranges[mainRange].Start();
260 bool Selection::MoveExtends() const {
261 return moveExtends;
264 void Selection::SetMoveExtends(bool moveExtends_) {
265 moveExtends = moveExtends_;
268 bool Selection::Empty() const {
269 for (size_t i=0; i<ranges.size(); i++) {
270 if (!ranges[i].Empty())
271 return false;
273 return true;
276 SelectionPosition Selection::Last() const {
277 SelectionPosition lastPosition;
278 for (size_t i=0; i<ranges.size(); i++) {
279 if (lastPosition < ranges[i].caret)
280 lastPosition = ranges[i].caret;
281 if (lastPosition < ranges[i].anchor)
282 lastPosition = ranges[i].anchor;
284 return lastPosition;
287 int Selection::Length() const {
288 int len = 0;
289 for (size_t i=0; i<ranges.size(); i++) {
290 len += ranges[i].Length();
292 return len;
295 void Selection::MovePositions(bool insertion, int startChange, int length) {
296 for (size_t i=0; i<ranges.size(); i++) {
297 ranges[i].MoveForInsertDelete(insertion, startChange, length);
299 if (selType == selRectangle) {
300 rangeRectangular.MoveForInsertDelete(insertion, startChange, length);
304 void Selection::TrimSelection(SelectionRange range) {
305 for (size_t i=0; i<ranges.size();) {
306 if ((i != mainRange) && (ranges[i].Trim(range))) {
307 // Trimmed to empty so remove
308 for (size_t j=i; j<ranges.size()-1; j++) {
309 ranges[j] = ranges[j+1];
310 if (j == mainRange-1)
311 mainRange--;
313 ranges.pop_back();
314 } else {
315 i++;
320 void Selection::TrimOtherSelections(size_t r, SelectionRange range) {
321 for (size_t i = 0; i<ranges.size(); ++i) {
322 if (i != r) {
323 ranges[i].Trim(range);
328 void Selection::SetSelection(SelectionRange range) {
329 ranges.clear();
330 ranges.push_back(range);
331 mainRange = ranges.size() - 1;
334 void Selection::AddSelection(SelectionRange range) {
335 TrimSelection(range);
336 ranges.push_back(range);
337 mainRange = ranges.size() - 1;
340 void Selection::AddSelectionWithoutTrim(SelectionRange range) {
341 ranges.push_back(range);
342 mainRange = ranges.size() - 1;
345 void Selection::DropSelection(size_t r) {
346 if ((ranges.size() > 1) && (r < ranges.size())) {
347 size_t mainNew = mainRange;
348 if (mainNew >= r) {
349 if (mainNew == 0) {
350 mainNew = ranges.size() - 2;
351 } else {
352 mainNew--;
355 ranges.erase(ranges.begin() + r);
356 mainRange = mainNew;
360 void Selection::DropAdditionalRanges() {
361 SetSelection(RangeMain());
364 void Selection::TentativeSelection(SelectionRange range) {
365 if (!tentativeMain) {
366 rangesSaved = ranges;
368 ranges = rangesSaved;
369 AddSelection(range);
370 TrimSelection(ranges[mainRange]);
371 tentativeMain = true;
374 void Selection::CommitTentative() {
375 rangesSaved.clear();
376 tentativeMain = false;
379 int Selection::CharacterInSelection(int posCharacter) const {
380 for (size_t i=0; i<ranges.size(); i++) {
381 if (ranges[i].ContainsCharacter(posCharacter))
382 return i == mainRange ? 1 : 2;
384 return 0;
387 int Selection::InSelectionForEOL(int pos) const {
388 for (size_t i=0; i<ranges.size(); i++) {
389 if (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position()))
390 return i == mainRange ? 1 : 2;
392 return 0;
395 int Selection::VirtualSpaceFor(int pos) const {
396 int virtualSpace = 0;
397 for (size_t i=0; i<ranges.size(); i++) {
398 if ((ranges[i].caret.Position() == pos) && (virtualSpace < ranges[i].caret.VirtualSpace()))
399 virtualSpace = ranges[i].caret.VirtualSpace();
400 if ((ranges[i].anchor.Position() == pos) && (virtualSpace < ranges[i].anchor.VirtualSpace()))
401 virtualSpace = ranges[i].anchor.VirtualSpace();
403 return virtualSpace;
406 void Selection::Clear() {
407 ranges.clear();
408 ranges.push_back(SelectionRange());
409 mainRange = ranges.size() - 1;
410 selType = selStream;
411 moveExtends = false;
412 ranges[mainRange].Reset();
413 rangeRectangular.Reset();
416 void Selection::RemoveDuplicates() {
417 for (size_t i=0; i<ranges.size()-1; i++) {
418 if (ranges[i].Empty()) {
419 size_t j=i+1;
420 while (j<ranges.size()) {
421 if (ranges[i] == ranges[j]) {
422 ranges.erase(ranges.begin() + j);
423 if (mainRange >= j)
424 mainRange--;
425 } else {
426 j++;
433 void Selection::RotateMain() {
434 mainRange = (mainRange + 1) % ranges.size();