geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Editor.cxx
Go to the documentation of this file.
1// Scintilla source code edit control
2/** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5// Copyright 1998-2011 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#include <cassert>
11#include <cstring>
12#include <cstdio>
13#include <cmath>
14
15#include <stdexcept>
16#include <string>
17#include <vector>
18#include <map>
19#include <forward_list>
20#include <algorithm>
21#include <iterator>
22#include <memory>
23#include <chrono>
24
25#include "Platform.h"
26
27#include "ILoader.h"
28#include "ILexer.h"
29#include "Scintilla.h"
30
31#include "CharacterSet.h"
32#include "CharacterCategory.h"
33#include "Position.h"
34#include "UniqueString.h"
35#include "SplitVector.h"
36#include "Partitioning.h"
37#include "RunStyles.h"
38#include "ContractionState.h"
39#include "CellBuffer.h"
40#include "PerLine.h"
41#include "KeyMap.h"
42#include "Indicator.h"
43#include "LineMarker.h"
44#include "Style.h"
45#include "ViewStyle.h"
46#include "CharClassify.h"
47#include "Decoration.h"
48#include "CaseFolder.h"
49#include "Document.h"
50#include "UniConversion.h"
51#include "Selection.h"
52#include "PositionCache.h"
53#include "EditModel.h"
54#include "MarginView.h"
55#include "EditView.h"
56#include "Editor.h"
57#include "ElapsedPeriod.h"
58
59using namespace Scintilla;
60
61namespace {
62
63/*
64 return whether this modification represents an operation that
65 may reasonably be deferred (not done now OR [possibly] at all)
66*/
67bool CanDeferToLastStep(const DocModification &mh) noexcept {
68 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
69 return true; // CAN skip
70 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
71 return false; // MUST do
72 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
73 return true; // CAN skip
74 return false; // PRESUMABLY must do
75}
76
77constexpr bool CanEliminate(const DocModification &mh) noexcept {
78 return
79 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
80}
81
82/*
83 return whether this modification represents the FINAL step
84 in a [possibly lengthy] multi-step Undo/Redo sequence
85*/
86constexpr bool IsLastStep(const DocModification &mh) noexcept {
87 return
88 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
89 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
90 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
91 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
92}
93
94}
95
96Timer::Timer() noexcept :
97 ticking(false), ticksToWait(0), tickerID{} {}
98
99Idler::Idler() noexcept :
100 state(false), idlerID(0) {}
101
102static bool IsAllSpacesOrTabs(const char *s, unsigned int len) noexcept {
103 for (unsigned int i = 0; i < len; i++) {
104 // This is safe because IsSpaceOrTab() will return false for null terminators
105 if (!IsSpaceOrTab(s[i]))
106 return false;
107 }
108 return true;
109}
110
111Editor::Editor() : durationWrapOneLine(0.00001, 0.000001, 0.0001) {
112 ctrlID = 0;
113
114 stylesValid = false;
116 scaleRGBAImage = 100.0f;
117
119
120 hasFocus = false;
121 errorStatus = 0;
122 mouseDownCaptures = true;
123 mouseWheelCaptures = true;
124
125 lastClickTime = 0;
129 dwelling = false;
130 ptMouseLast.x = 0;
131 ptMouseLast.y = 0;
133 dropWentOutside = false;
137
138 lastXChosen = 0;
139 lineAnchorPos = 0;
144
146 caretPolicies.y = { CARET_EVEN, 0 };
147
148 visiblePolicy = { 0, 0 };
149
150 searchAnchor = 0;
151
152 xCaretMargin = 50;
154 scrollWidth = 2000;
156 endAtLastLine = true;
160 multipleSelection = false;
164
166 searchFlags = 0;
167
168 topLine = 0;
169 posTopLine = 0;
170
171 lengthForEncode = -1;
172
173 needUpdateUI = 0;
175
178 paintingAllText = false;
179 willRedrawAll = false;
181 needIdleStyling = false;
182
184 commandEvents = true;
185
186 pdoc->AddWatcher(this, 0);
187
188 recordingMacro = false;
189 foldAutomatic = 0;
190
191 convertPastes = true;
192
194}
195
197 pdoc->RemoveWatcher(this, 0);
198 DropGraphics(true);
199}
200
202 SetIdle(false);
203 CancelModes();
204}
205
207 reprs.Clear();
208
209 // C0 control set
210 const char *const reps[] = {
211 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
212 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
213 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
214 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
215 };
216 for (size_t j=0; j < Sci::size(reps); j++) {
217 const char c[2] = { static_cast<char>(j), 0 };
218 reprs.SetRepresentation(c, reps[j]);
219 }
220 reprs.SetRepresentation("\x7f", "DEL");
221
222 // C1 control set
223 // As well as Unicode mode, ISO-8859-1 should use these
224 if (IsUnicodeMode()) {
225 const char *const repsC1[] = {
226 "PAD", "HOP", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
227 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
228 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
229 "SOS", "SGCI", "SCI", "CSI", "ST", "OSC", "PM", "APC"
230 };
231 for (size_t j=0; j < Sci::size(repsC1); j++) {
232 const char c1[3] = { '\xc2', static_cast<char>(0x80+j), 0 };
233 reprs.SetRepresentation(c1, repsC1[j]);
234 }
235 reprs.SetRepresentation("\xe2\x80\xa8", "LS");
236 reprs.SetRepresentation("\xe2\x80\xa9", "PS");
237 }
238
239 // UTF-8 invalid bytes
240 if (IsUnicodeMode()) {
241 for (int k=0x80; k < 0x100; k++) {
242 const char hiByte[2] = { static_cast<char>(k), 0 };
243 char hexits[5]; // Really only needs 4 but that causes warning from gcc 7.1
244 sprintf(hexits, "x%2X", k);
245 reprs.SetRepresentation(hiByte, hexits);
246 }
247 } else if (pdoc->dbcsCodePage) {
248 // DBCS invalid single lead bytes
249 for (int k = 0x80; k < 0x100; k++) {
250 const char ch = static_cast<char>(k);
252 const char hiByte[2] = { ch, 0 };
253 char hexits[5]; // Really only needs 4 but that causes warning from gcc 7.1
254 sprintf(hexits, "x%2X", k);
255 reprs.SetRepresentation(hiByte, hexits);
256 }
257 }
258 }
259}
260
261void Editor::DropGraphics(bool freeObjects) {
262 marginView.DropGraphics(freeObjects);
263 view.DropGraphics(freeObjects);
264}
265
269}
270
272 stylesValid = false;
274 DropGraphics(false);
278}
279
281 NeedWrapping();
283 Redraw();
284}
285
287 if (!stylesValid) {
288 stylesValid = true;
289 AutoSurface surface(this);
290 if (surface) {
291 vs.Refresh(*surface, pdoc->tabInChars);
292 }
295 }
296}
297
299 return Point(0, 0);
300}
301
303 PointDocument ptDocument(ptView);
304 if (wMargin.GetID()) {
305 const Point ptOrigin = GetVisibleOriginInMain();
306 ptDocument.x += ptOrigin.x;
307 ptDocument.y += ptOrigin.y;
308 } else {
309 ptDocument.x += xOffset;
310 ptDocument.y += topLine * vs.lineHeight;
311 }
312 return ptDocument;
313}
314
316 if (wMargin.GetID())
317 return 0;
318 else
319 return topLine;
320}
321
323 return wMain.GetClientPosition();
324}
325
327 return GetClientRectangle();
328}
329
332 rc.left += vs.textStart;
334 return rc;
335}
336
338 const PRectangle rcClient = GetClientRectangle();
339 const int htClient = static_cast<int>(rcClient.bottom - rcClient.top);
340 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
341 return htClient / vs.lineHeight;
342}
343
345 const Sci::Line retVal = LinesOnScreen() - 1;
346 if (retVal < 1)
347 return 1;
348 else
349 return retVal;
350}
351
353 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
354 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
355 Sci::Line retVal = pcs->LinesDisplayed();
356 if (endAtLastLine) {
357 retVal -= LinesOnScreen();
358 } else {
359 retVal--;
360 }
361 if (retVal < 0) {
362 return 0;
363 } else {
364 return retVal;
365 }
366}
367
369 if (sp.Position() < 0) {
370 return SelectionPosition(0);
371 } else if (sp.Position() > pdoc->Length()) {
372 return SelectionPosition(pdoc->Length());
373 } else {
374 // If not at end of line then set offset to 0
375 if (!pdoc->IsLineEndPosition(sp.Position()))
376 sp.SetVirtualSpace(0);
377 return sp;
378 }
379}
380
383 AutoSurface surface(this);
384 return view.LocationFromPosition(surface, *this, pos, topLine, vs, pe);
385}
386
389}
390
392 const Point pt = LocationFromPosition(sp);
393 return static_cast<int>(pt.x) - vs.textStart + xOffset;
394}
395
396SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
398 AutoSurface surface(this);
399
400 if (canReturnInvalid) {
401 PRectangle rcClient = GetTextRectangle();
402 // May be in scroll view coordinates so translate back to main view
403 const Point ptOrigin = GetVisibleOriginInMain();
404 rcClient.Move(-ptOrigin.x, -ptOrigin.y);
405 if (!rcClient.Contains(pt))
407 if (pt.x < vs.textStart)
409 if (pt.y < 0)
411 }
412 const PointDocument ptdoc = DocumentPointFromView(pt);
413 return view.SPositionFromLocation(surface, *this, ptdoc, canReturnInvalid, charPosition, virtualSpace, vs);
414}
415
416Sci::Position Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
417 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
418}
419
420/**
421* Find the document position corresponding to an x coordinate on a particular document line.
422* Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
423* This method is used for rectangular selections and does not work on wrapped lines.
424*/
427 if (lineDoc >= pdoc->LinesTotal())
428 return SelectionPosition(pdoc->Length());
429 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
430 AutoSurface surface(this);
431 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
432}
433
435 return SPositionFromLineX(lineDoc, x).Position();
436}
437
439 return pcs->DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
440}
441
443 if ((topLine != topLineNew) && (topLineNew >= 0)) {
444 topLine = topLineNew;
446 }
447 posTopLine = pdoc->LineStart(pcs->DocFromDisplay(topLine));
448}
449
450/**
451 * If painting then abandon the painting because a wider redraw is needed.
452 * @return true if calling code should stop drawing.
453 */
455 if ((paintState == painting) && !paintingAllText) {
457 }
458 return paintState == paintAbandoned;
459}
460
462 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
463
464 // Clip the redraw rectangle into the client area
465 const PRectangle rcClient = GetClientRectangle();
466 if (rc.top < rcClient.top)
467 rc.top = rcClient.top;
468 if (rc.bottom > rcClient.bottom)
469 rc.bottom = rcClient.bottom;
470 if (rc.left < rcClient.left)
471 rc.left = rcClient.left;
472 if (rc.right > rcClient.right)
473 rc.right = rcClient.right;
474
475 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
477 }
478}
479
481 // Overridden on platforms that may draw outside visible area.
482}
483
485 //Platform::DebugPrintf("Redraw all\n");
486 const PRectangle rcClient = GetClientRectangle();
487 wMain.InvalidateRectangle(rcClient);
488 if (wMargin.GetID())
490 //wMain.InvalidateAll();
491}
492
494 const bool markersInText = vs.maskInLine || vs.maskDrawInText;
495 if (!wMargin.GetID() || markersInText) { // May affect text area so may need to abandon and retry
496 if (AbandonPaint()) {
497 return;
498 }
499 }
500 if (wMargin.GetID() && markersInText) {
501 Redraw();
502 return;
503 }
504 PRectangle rcMarkers = GetClientRectangle();
505 if (!markersInText) {
506 // Normal case: just draw the margin
507 rcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;
508 }
509 if (line != -1) {
511
512 // Inflate line rectangle if there are image markers with height larger than line height
514 const int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
515 rcLine.top -= delta;
516 rcLine.bottom += delta;
517 if (rcLine.top < rcMarkers.top)
518 rcLine.top = rcMarkers.top;
519 if (rcLine.bottom > rcMarkers.bottom)
520 rcLine.bottom = rcMarkers.bottom;
521 }
522
523 rcMarkers.top = rcLine.top;
524 if (!allAfter)
525 rcMarkers.bottom = rcLine.bottom;
526 if (rcMarkers.Empty())
527 return;
528 }
529 if (wMargin.GetID()) {
530 const Point ptOrigin = GetVisibleOriginInMain();
531 rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
532 wMargin.InvalidateRectangle(rcMarkers);
533 } else {
534 wMain.InvalidateRectangle(rcMarkers);
535 }
536}
537
539 const Sci::Line minLine = pcs->DisplayFromDoc(
541 const Sci::Line maxLine = pcs->DisplayLastFromDoc(
543 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
544 PRectangle rc;
545 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
546 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
547 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
548 if (rc.top < rcClientDrawing.top)
549 rc.top = rcClientDrawing.top;
550 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
551 rc.right = rcClientDrawing.right;
552 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
553
554 return rc;
555}
556
559}
560
562 return sel.MainCaret();
563}
564
565bool Editor::SelectionEmpty() const noexcept {
566 return sel.Empty();
567}
568
570 return sel.RangeMain().Start();
571}
572
574 return sel.RangeMain().End();
575}
576
578 if (sel.IsRectangular()) {
579 const int xAnchor = XFromPosition(sel.Rectangular().anchor);
580 int xCaret = XFromPosition(sel.Rectangular().caret);
582 xCaret = xAnchor;
583 }
584 const Sci::Line lineAnchorRect =
586 const Sci::Line lineCaret =
588 const int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
589 AutoSurface surface(this);
590 for (Sci::Line line=lineAnchorRect; line != lineCaret+increment; line += increment) {
591 SelectionRange range(
592 view.SPositionFromLineX(surface, *this, line, xCaret, vs),
593 view.SPositionFromLineX(surface, *this, line, xAnchor, vs));
595 range.ClearVirtualSpace();
596 if (line == lineAnchorRect)
597 sel.SetSelection(range);
598 else
600 }
601 }
602}
603
605 if (sel.IsRectangular()) {
609 } else {
611 }
613 }
614}
615
616void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
617 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
618 invalidateWholeSelection = true;
619 }
620 Sci::Position firstAffected = std::min(sel.RangeMain().Start().Position(), newMain.Start().Position());
621 // +1 for lastAffected ensures caret repainted
622 Sci::Position lastAffected = std::max(newMain.caret.Position()+1, newMain.anchor.Position());
623 lastAffected = std::max(lastAffected, sel.RangeMain().End().Position());
624 if (invalidateWholeSelection) {
625 for (size_t r=0; r<sel.Count(); r++) {
626 firstAffected = std::min(firstAffected, sel.Range(r).caret.Position());
627 firstAffected = std::min(firstAffected, sel.Range(r).anchor.Position());
628 lastAffected = std::max(lastAffected, sel.Range(r).caret.Position()+1);
629 lastAffected = std::max(lastAffected, sel.Range(r).anchor.Position());
630 }
631 }
633 InvalidateRange(firstAffected, lastAffected);
634}
635
638}
639
640/* For Line selection - the anchor and caret are always
641 at the beginning and end of the region lines. */
643 if (currentPos_ > anchor_) {
644 anchor_ = SelectionPosition(
646 currentPos_ = SelectionPosition(
647 pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
648 } else {
649 currentPos_ = SelectionPosition(
650 pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
651 anchor_ = SelectionPosition(
653 }
654 return SelectionRange(currentPos_, anchor_);
655}
656
658 currentPos_ = ClampPositionIntoDocument(currentPos_);
659 anchor_ = ClampPositionIntoDocument(anchor_);
660 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
661 SelectionRange rangeNew(currentPos_, anchor_);
663 rangeNew = LineSelectionRange(currentPos_, anchor_);
664 }
665 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
666 InvalidateSelection(rangeNew);
667 }
668 sel.RangeMain() = rangeNew;
672
673 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
675 }
677}
678
680 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
681}
682
683// Just move the caret on the main selection
685 currentPos_ = ClampPositionIntoDocument(currentPos_);
686 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
687 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
689 }
690 if (sel.IsRectangular()) {
691 sel.Rectangular() =
694 } else if (sel.selType == Selection::selLines) {
696 } else {
697 sel.RangeMain() =
699 }
702
703 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
705 }
707}
708
709void Editor::SetSelection(int currentPos_) {
710 SetSelection(SelectionPosition(currentPos_));
711}
712
714 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
715 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
716 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
717 InvalidateSelection(rangeNew);
718 }
719 sel.Clear();
720 sel.RangeMain() = rangeNew;
724
725 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
727 }
729}
730
733}
734
737 // Select word at caret
738 const Sci::Position startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);
739 const Sci::Position endWord = pdoc->ExtendWordSelect(startWord, 1, true);
740 TrimAndSetSelection(endWord, startWord);
741
742 } else {
743
744 if (!pdoc->HasCaseFolder())
746
747 const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());
748 const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);
749
750 const Range rangeTarget(targetRange.start.Position(), targetRange.end.Position());
751 std::vector<Range> searchRanges;
752 // Search should be over the target range excluding the current selection so
753 // may need to search 2 ranges, after the selection then before the selection.
754 if (rangeTarget.Overlaps(rangeMainSelection)) {
755 // Common case is that the selection is completely within the target but
756 // may also have overlap at start or end.
757 if (rangeMainSelection.end < rangeTarget.end)
758 searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end));
759 if (rangeTarget.start < rangeMainSelection.start)
760 searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start));
761 } else {
762 // No overlap
763 searchRanges.push_back(rangeTarget);
764 }
765
766 for (std::vector<Range>::const_iterator it = searchRanges.begin(); it != searchRanges.end(); ++it) {
767 Sci::Position searchStart = it->start;
768 const Sci::Position searchEnd = it->end;
769 for (;;) {
770 Sci::Position lengthFound = selectedText.length();
771 const Sci::Position pos = pdoc->FindText(searchStart, searchEnd,
772 selectedText.c_str(), searchFlags, &lengthFound);
773 if (pos >= 0) {
774 sel.AddSelection(SelectionRange(pos + lengthFound, pos));
777 Redraw();
778 if (addNumber == AddNumber::one)
779 return;
780 searchStart = pos + lengthFound;
781 } else {
782 break;
783 }
784 }
785 }
786 }
787}
788
790 if (vs.ProtectionActive()) {
791 if (start > end) {
792 const Sci::Position t = start;
793 start = end;
794 end = t;
795 }
796 for (Sci::Position pos = start; pos < end; pos++) {
797 if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
798 return true;
799 }
800 }
801 return false;
802}
803
805 for (size_t r=0; r<sel.Count(); r++) {
807 sel.Range(r).End().Position())) {
808 return true;
809 }
810 }
811 return false;
812}
813
814/**
815 * Asks document to find a good position and then moves out of any invisible positions.
816 */
818 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
819}
820
822 const Sci::Position posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
823 if (posMoved != pos.Position())
824 pos.SetPosition(posMoved);
825 if (vs.ProtectionActive()) {
826 if (moveDir > 0) {
827 if ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {
828 while ((pos.Position() < pdoc->Length()) &&
829 (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))
830 pos.Add(1);
831 }
832 } else if (moveDir < 0) {
833 if (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {
834 while ((pos.Position() > 0) &&
835 (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))
836 pos.Add(-1);
837 }
838 }
839 }
840 return pos;
841}
842
844 bool ensureVisible, CaretPolicies policies) {
845 const Sci::Line currentLine = pdoc->SciLineFromPosition(newPos.Position());
846 if (ensureVisible) {
847 // In case in need of wrapping to ensure DisplayFromDoc works.
848 if (currentLine >= wrapPending.start) {
850 Redraw();
851 }
852 }
854 SelectionRange(posDrag.IsValid() ? posDrag : newPos), xysDefault, policies);
855 if (previousPos.IsValid() && (newXY.xOffset == xOffset)) {
856 // simple vertical scroll then invalidate
857 ScrollTo(newXY.topLine);
858 InvalidateSelection(SelectionRange(previousPos), true);
859 } else {
860 SetXYScroll(newXY);
861 }
862 }
863
866
870
871 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
873 }
874}
875
876void Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
877 const SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?
879
880 const Sci::Position delta = newPos.Position() - sel.MainCaret();
881 newPos = ClampPositionIntoDocument(newPos);
882 newPos = MovePositionOutsideChar(newPos, delta);
884 // Can't turn into multiple selection so clear additional selections
885 InvalidateSelection(SelectionRange(newPos), true);
887 }
888 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
889 // Switching to rectangular
891 SelectionRange rangeMain = sel.RangeMain();
892 sel.Clear();
893 sel.Rectangular() = rangeMain;
894 }
895 if (selt != Selection::noSel) {
896 sel.selType = selt;
897 }
898 if (selt != Selection::noSel || sel.MoveExtends()) {
899 SetSelection(newPos);
900 } else {
901 SetEmptySelection(newPos);
902 }
903
904 MovedCaret(newPos, spCaret, ensureVisible, caretPolicies);
905}
906
907void Editor::MovePositionTo(Sci::Position newPos, Selection::selTypes selt, bool ensureVisible) {
908 MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
909}
910
913 pos = MovePositionOutsideChar(pos, moveDir);
914 const Sci::Line lineDoc = pdoc->SciLineFromPosition(pos.Position());
915 if (pcs->GetVisible(lineDoc)) {
916 return pos;
917 } else {
918 Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
919 if (moveDir > 0) {
920 // lineDisplay is already line before fold as lines in fold use display line of line after fold
921 lineDisplay = Sci::clamp(lineDisplay, static_cast<Sci::Line>(0), pcs->LinesDisplayed());
922 return SelectionPosition(
923 pdoc->LineStart(pcs->DocFromDisplay(lineDisplay)));
924 } else {
925 lineDisplay = Sci::clamp(lineDisplay - 1, static_cast<Sci::Line>(0), pcs->LinesDisplayed());
926 return SelectionPosition(
927 pdoc->LineEnd(pcs->DocFromDisplay(lineDisplay)));
928 }
929 }
930}
931
934}
935
938}
939
940/**
941 * Choose the x position that the caret will try to stick to
942 * as it moves up and down.
943 */
945 const Point pt = PointMainCaret();
946 lastXChosen = static_cast<int>(pt.x) + xOffset;
947}
948
949void Editor::ScrollTo(Sci::Line line, bool moveThumb) {
950 const Sci::Line topLineNew = Sci::clamp(line, static_cast<Sci::Line>(0), MaxScrollPos());
951 if (topLineNew != topLine) {
952 // Try to optimise small scrolls
953#ifndef UNDER_CE
954 const Sci::Line linesToMove = topLine - topLineNew;
955 const bool performBlit = (std::abs(linesToMove) <= 10) && (paintState == notPainting);
956 willRedrawAll = !performBlit;
957#endif
958 SetTopLine(topLineNew);
959 // Optimize by styling the view as this will invalidate any needed area
960 // which could abort the initial paint if discovered later.
962#ifndef UNDER_CE
963 // Perform redraw rather than scroll if many lines would be redrawn anyway.
964 if (performBlit) {
965 ScrollText(linesToMove);
966 } else {
967 Redraw();
968 }
969 willRedrawAll = false;
970#else
971 Redraw();
972#endif
973 if (moveThumb) {
975 }
976 }
977}
978
979void Editor::ScrollText(Sci::Line /* linesToMove */) {
980 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
981 Redraw();
982}
983
985 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
986 if (xPos < 0)
987 xPos = 0;
988 if (!Wrapping() && (xOffset != xPos)) {
989 xOffset = xPos;
993 }
994}
995
997 const Sci::Line lineDoc =
999 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
1000 const Sci::Line newTop = lineDisplay - (LinesOnScreen() / 2);
1001 if (topLine != newTop) {
1002 SetTopLine(newTop > 0 ? newTop : 0);
1004 }
1005}
1006
1007void Editor::MoveSelectedLines(int lineDelta) {
1008
1009 if (sel.IsRectangular()) {
1010 return;
1011 }
1012
1013 // if selection doesn't start at the beginning of the line, set the new start
1014 Sci::Position selectionStart = SelectionStart().Position();
1015 const Sci::Line startLine = pdoc->SciLineFromPosition(selectionStart);
1016 const Sci::Position beginningOfStartLine = pdoc->LineStart(startLine);
1017 selectionStart = beginningOfStartLine;
1018
1019 // if selection doesn't end at the beginning of a line greater than that of the start,
1020 // then set it at the beginning of the next one
1021 Sci::Position selectionEnd = SelectionEnd().Position();
1022 const Sci::Line endLine = pdoc->SciLineFromPosition(selectionEnd);
1023 const Sci::Position beginningOfEndLine = pdoc->LineStart(endLine);
1024 bool appendEol = false;
1025 if (selectionEnd > beginningOfEndLine
1026 || selectionStart == selectionEnd) {
1027 selectionEnd = pdoc->LineStart(endLine + 1);
1028 appendEol = (selectionEnd == pdoc->Length() && pdoc->SciLineFromPosition(selectionEnd) == endLine);
1029 }
1030
1031 // if there's nowhere for the selection to move
1032 // (i.e. at the beginning going up or at the end going down),
1033 // stop it right there!
1034 if ((selectionStart == 0 && lineDelta < 0)
1035 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1036 || selectionStart == selectionEnd) {
1037 return;
1038 }
1039
1040 UndoGroup ug(pdoc);
1041
1042 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1043 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1045 selectionEnd = CurrentPosition();
1046 }
1047 SetSelection(selectionStart, selectionEnd);
1048
1049 SelectionText selectedText;
1050 CopySelectionRange(&selectedText);
1051
1052 const Point currentLocation = LocationFromPosition(CurrentPosition());
1053 const Sci::Line currentLine = LineFromLocation(currentLocation);
1054
1055 if (appendEol)
1056 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1058
1059 const char *eol = StringFromEOLMode(pdoc->eolMode);
1060 if (currentLine + lineDelta >= pdoc->LinesTotal())
1061 pdoc->InsertString(pdoc->Length(), eol, strlen(eol));
1062 GoToLine(currentLine + lineDelta);
1063
1064 Sci::Position selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectedText.Length());
1065 if (appendEol) {
1066 const Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, strlen(eol));
1067 selectionLength += lengthInserted;
1068 }
1069 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1070}
1071
1074}
1075
1078}
1079
1080void Editor::MoveCaretInsideView(bool ensureVisible) {
1081 const PRectangle rcClient = GetTextRectangle();
1082 const Point pt = PointMainCaret();
1083 if (pt.y < rcClient.top) {
1085 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1086 false, false, UserVirtualSpace()),
1087 Selection::noSel, ensureVisible);
1088 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1089 const ptrdiff_t yOfLastLineFullyDisplayed = static_cast<ptrdiff_t>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1091 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top + yOfLastLineFullyDisplayed)),
1092 false, false, UserVirtualSpace()),
1093 Selection::noSel, ensureVisible);
1094 }
1095}
1096
1098 AutoSurface surface(this);
1099 return view.DisplayFromPosition(surface, *this, pos, vs);
1100}
1101
1102/**
1103 * Ensure the caret is reasonably visible in context.
1104 *
1105Caret policy in Scintilla
1106
1107If slop is set, we can define a slop value.
1108This value defines an unwanted zone (UZ) where the caret is... unwanted.
1109This zone is defined as a number of pixels near the vertical margins,
1110and as a number of lines near the horizontal margins.
1111By keeping the caret away from the edges, it is seen within its context,
1112so it is likely that the identifier that the caret is on can be completely seen,
1113and that the current line is seen with some of the lines following it which are
1114often dependent on that line.
1115
1116If strict is set, the policy is enforced... strictly.
1117The caret is centred on the display if slop is not set,
1118and cannot go in the UZ if slop is set.
1119
1120If jumps is set, the display is moved more energetically
1121so the caret can move in the same direction longer before the policy is applied again.
1122'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1123
1124If even is not set, instead of having symmetrical UZs,
1125the left and bottom UZs are extended up to right and top UZs respectively.
1126This way, we favour the displaying of useful information: the beginning of lines,
1127where most code reside, and the lines after the caret, eg. the body of a function.
1128
1129 | | | | |
1130slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1131 | | | | | visibility or going into the UZ) display is...
1132-----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1133 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1134 0 | 0 | 0 | 1 | Yes | moved by one position
1135 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1136 0 | 0 | 1 | 1 | Yes | centred on the caret
1137 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1138 0 | 1 | - | 1 | No, caret is always centred | -
1139 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1140 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1141 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1142 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1143 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1144 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1145 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1146*/
1147
1149 const XYScrollOptions options, CaretPolicies policies) {
1150 const PRectangle rcClient = GetTextRectangle();
1151 const Point ptOrigin = GetVisibleOriginInMain();
1152 const Point pt = LocationFromPosition(range.caret) + ptOrigin;
1153 const Point ptAnchor = LocationFromPosition(range.anchor) + ptOrigin;
1154 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1155
1157 if (rcClient.Empty()) {
1158 return newXY;
1159 }
1160
1161 // Vertical positioning
1162 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (policies.y.policy & CARET_STRICT) != 0)) {
1163 const Sci::Line lineCaret = DisplayFromPosition(range.caret.Position());
1164 const Sci::Line linesOnScreen = LinesOnScreen();
1165 const Sci::Line halfScreen = std::max(linesOnScreen - 1, static_cast<Sci::Line>(2)) / 2;
1166 const bool bSlop = (policies.y.policy & CARET_SLOP) != 0;
1167 const bool bStrict = (policies.y.policy & CARET_STRICT) != 0;
1168 const bool bJump = (policies.y.policy & CARET_JUMPS) != 0;
1169 const bool bEven = (policies.y.policy & CARET_EVEN) != 0;
1170
1171 // It should be possible to scroll the window to show the caret,
1172 // but this fails to remove the caret on GTK+
1173 if (bSlop) { // A margin is defined
1174 Sci::Line yMoveT, yMoveB;
1175 if (bStrict) {
1176 Sci::Line yMarginT, yMarginB;
1177 if (!(options & xysUseMargin)) {
1178 // In drag mode, avoid moves
1179 // otherwise, a double click will select several lines.
1180 yMarginT = yMarginB = 0;
1181 } else {
1182 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1183 // a maximum of slightly less than half the height of the text area.
1184 yMarginT = Sci::clamp(static_cast<Sci::Line>(policies.y.slop), static_cast<Sci::Line>(1), halfScreen);
1185 if (bEven) {
1186 yMarginB = yMarginT;
1187 } else {
1188 yMarginB = linesOnScreen - yMarginT - 1;
1189 }
1190 }
1191 yMoveT = yMarginT;
1192 if (bEven) {
1193 if (bJump) {
1194 yMoveT = Sci::clamp(static_cast<Sci::Line>(policies.y.slop * 3), static_cast<Sci::Line>(1), halfScreen);
1195 }
1196 yMoveB = yMoveT;
1197 } else {
1198 yMoveB = linesOnScreen - yMoveT - 1;
1199 }
1200 if (lineCaret < topLine + yMarginT) {
1201 // Caret goes too high
1202 newXY.topLine = lineCaret - yMoveT;
1203 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1204 // Caret goes too low
1205 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1206 }
1207 } else { // Not strict
1208 yMoveT = bJump ? policies.y.slop * 3 : policies.y.slop;
1209 yMoveT = Sci::clamp(yMoveT, static_cast<Sci::Line>(1), halfScreen);
1210 if (bEven) {
1211 yMoveB = yMoveT;
1212 } else {
1213 yMoveB = linesOnScreen - yMoveT - 1;
1214 }
1215 if (lineCaret < topLine) {
1216 // Caret goes too high
1217 newXY.topLine = lineCaret - yMoveT;
1218 } else if (lineCaret > topLine + linesOnScreen - 1) {
1219 // Caret goes too low
1220 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1221 }
1222 }
1223 } else { // No slop
1224 if (!bStrict && !bJump) {
1225 // Minimal move
1226 if (lineCaret < topLine) {
1227 // Caret goes too high
1228 newXY.topLine = lineCaret;
1229 } else if (lineCaret > topLine + linesOnScreen - 1) {
1230 // Caret goes too low
1231 if (bEven) {
1232 newXY.topLine = lineCaret - linesOnScreen + 1;
1233 } else {
1234 newXY.topLine = lineCaret;
1235 }
1236 }
1237 } else { // Strict or going out of display
1238 if (bEven) {
1239 // Always centre caret
1240 newXY.topLine = lineCaret - halfScreen;
1241 } else {
1242 // Always put caret on top of display
1243 newXY.topLine = lineCaret;
1244 }
1245 }
1246 }
1247 if (!(range.caret == range.anchor)) {
1248 const Sci::Line lineAnchor = DisplayFromPosition(range.anchor.Position());
1249 if (lineAnchor < lineCaret) {
1250 // Shift up to show anchor or as much of range as possible
1251 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1252 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1253 } else {
1254 // Shift down to show anchor or as much of range as possible
1255 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1256 newXY.topLine = std::min(newXY.topLine, lineCaret);
1257 }
1258 }
1259 newXY.topLine = Sci::clamp(newXY.topLine, static_cast<Sci::Line>(0), MaxScrollPos());
1260 }
1261
1262 // Horizontal positioning
1263 if ((options & xysHorizontal) && !Wrapping()) {
1264 const int halfScreen = std::max(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1265 const bool bSlop = (policies.x.policy & CARET_SLOP) != 0;
1266 const bool bStrict = (policies.x.policy & CARET_STRICT) != 0;
1267 const bool bJump = (policies.x.policy & CARET_JUMPS) != 0;
1268 const bool bEven = (policies.x.policy & CARET_EVEN) != 0;
1269
1270 if (bSlop) { // A margin is defined
1271 int xMoveL, xMoveR;
1272 if (bStrict) {
1273 int xMarginL, xMarginR;
1274 if (!(options & xysUseMargin)) {
1275 // In drag mode, avoid moves unless very near of the margin
1276 // otherwise, a simple click will select text.
1277 xMarginL = xMarginR = 2;
1278 } else {
1279 // xMargin must equal to caretXSlop, with a minimum of 2 and
1280 // a maximum of slightly less than half the width of the text area.
1281 xMarginR = Sci::clamp(policies.x.slop, 2, halfScreen);
1282 if (bEven) {
1283 xMarginL = xMarginR;
1284 } else {
1285 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1286 }
1287 }
1288 if (bJump && bEven) {
1289 // Jump is used only in even mode
1290 xMoveL = xMoveR = Sci::clamp(policies.x.slop * 3, 1, halfScreen);
1291 } else {
1292 xMoveL = xMoveR = 0; // Not used, avoid a warning
1293 }
1294 if (pt.x < rcClient.left + xMarginL) {
1295 // Caret is on the left of the display
1296 if (bJump && bEven) {
1297 newXY.xOffset -= xMoveL;
1298 } else {
1299 // Move just enough to allow to display the caret
1300 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1301 }
1302 } else if (pt.x >= rcClient.right - xMarginR) {
1303 // Caret is on the right of the display
1304 if (bJump && bEven) {
1305 newXY.xOffset += xMoveR;
1306 } else {
1307 // Move just enough to allow to display the caret
1308 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1309 }
1310 }
1311 } else { // Not strict
1312 xMoveR = bJump ? policies.x.slop * 3 : policies.x.slop;
1313 xMoveR = Sci::clamp(xMoveR, 1, halfScreen);
1314 if (bEven) {
1315 xMoveL = xMoveR;
1316 } else {
1317 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1318 }
1319 if (pt.x < rcClient.left) {
1320 // Caret is on the left of the display
1321 newXY.xOffset -= xMoveL;
1322 } else if (pt.x >= rcClient.right) {
1323 // Caret is on the right of the display
1324 newXY.xOffset += xMoveR;
1325 }
1326 }
1327 } else { // No slop
1328 if (bStrict ||
1329 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1330 // Strict or going out of display
1331 if (bEven) {
1332 // Centre caret
1333 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1334 } else {
1335 // Put caret on right
1336 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1337 }
1338 } else {
1339 // Move just enough to allow to display the caret
1340 if (pt.x < rcClient.left) {
1341 // Caret is on the left of the display
1342 if (bEven) {
1343 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1344 } else {
1345 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1346 }
1347 } else if (pt.x >= rcClient.right) {
1348 // Caret is on the right of the display
1349 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1350 }
1351 }
1352 }
1353 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1354 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1355 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1356 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1357 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1359 // Ensure we can see a good portion of the block caret
1360 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1361 }
1362 }
1363 if (!(range.caret == range.anchor)) {
1364 if (ptAnchor.x < pt.x) {
1365 // Shift to left to show anchor or as much of range as possible
1366 const int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1367 const int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1368 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1369 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1370 } else {
1371 // Shift to right to show anchor or as much of range as possible
1372 const int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1373 const int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1374 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1375 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1376 }
1377 }
1378 if (newXY.xOffset < 0) {
1379 newXY.xOffset = 0;
1380 }
1381 }
1382
1383 return newXY;
1384}
1385
1387 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1388 if (newXY.topLine != topLine) {
1389 SetTopLine(newXY.topLine);
1391 }
1392 if (newXY.xOffset != xOffset) {
1393 xOffset = newXY.xOffset;
1395 if (newXY.xOffset > 0) {
1396 const PRectangle rcText = GetTextRectangle();
1398 rcText.Width() + xOffset > scrollWidth) {
1399 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1400 SetScrollBars();
1401 }
1402 }
1404 }
1405 Redraw();
1407 }
1408}
1409
1412}
1413
1414void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1416 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0)),
1417 caretPolicies));
1418}
1419
1421 if (hasFocus) {
1422 caret.active = true;
1423 caret.on = true;
1425 if (caret.period > 0)
1427 } else {
1428 caret.active = false;
1429 caret.on = false;
1431 }
1433}
1434
1436 caret.active = false;
1439}
1440
1441void Editor::CaretSetPeriod(int period) {
1442 if (caret.period != period) {
1443 caret.period = period;
1444 caret.on = true;
1446 if ((caret.active) && (caret.period > 0))
1449 }
1450}
1451
1453 if (posDrag.IsValid()) {
1455 } else {
1456 for (size_t r=0; r<sel.Count(); r++) {
1458 }
1459 }
1461}
1462
1464}
1465
1467}
1468
1469bool Editor::Wrapping() const noexcept {
1470 return vs.wrapState != WrapMode::none;
1471}
1472
1473void Editor::NeedWrapping(Sci::Line docLineStart, Sci::Line docLineEnd) {
1474//Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1475 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1477 }
1478 // Wrap lines during idle.
1479 if (Wrapping() && wrapPending.NeedsWrap()) {
1480 SetIdle(true);
1481 }
1482}
1483
1484bool Editor::WrapOneLine(Surface *surface, Sci::Line lineToWrap) {
1485 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1486 int linesWrapped = 1;
1487 if (ll) {
1488 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1489 linesWrapped = ll->lines;
1490 }
1491 return pcs->SetHeight(lineToWrap, linesWrapped +
1492 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1493}
1494
1495// Perform wrapping for a subset of the lines needing wrapping.
1496// wsAll: wrap all lines which need wrapping in this single call
1497// wsVisible: wrap currently visible lines
1498// wsIdle: wrap one page + 100 lines
1499// Return true if wrapping occurred.
1501 Sci::Line goodTopLine = topLine;
1502 bool wrapOccurred = false;
1503 if (!Wrapping()) {
1506 for (Sci::Line lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1507 pcs->SetHeight(lineDoc, 1 +
1508 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1509 }
1510 wrapOccurred = true;
1511 }
1513
1514 } else if (wrapPending.NeedsWrap()) {
1516 if (!SetIdle(true)) {
1517 // Idle processing not supported so full wrap required.
1518 ws = WrapScope::wsAll;
1519 }
1520 // Decide where to start wrapping
1521 Sci::Line lineToWrap = wrapPending.start;
1522 Sci::Line lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1523 const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
1524 const Sci::Line subLineTop = topLine - pcs->DisplayFromDoc(lineDocTop);
1525 if (ws == WrapScope::wsVisible) {
1526 lineToWrap = Sci::clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1527 // Priority wrap to just after visible area.
1528 // Since wrapping could reduce display lines, treat each
1529 // as taking only one display line.
1530 lineToWrapEnd = lineDocTop;
1532 while ((lineToWrapEnd < pcs->LinesInDoc()) && (lines>0)) {
1533 if (pcs->GetVisible(lineToWrapEnd))
1534 lines--;
1535 lineToWrapEnd++;
1536 }
1537 // .. and if the paint window is outside pending wraps
1538 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1539 // Currently visible text does not need wrapping
1540 return false;
1541 }
1542 } else if (ws == WrapScope::wsIdle) {
1543 // Try to keep time taken by wrapping reasonable so interaction remains smooth.
1544 constexpr double secondsAllowed = 0.01;
1545 const Sci::Line linesInAllowedTime = Sci::clamp(
1546 static_cast<Sci::Line>(secondsAllowed / durationWrapOneLine.Duration()),
1547 LinesOnScreen() + 50, static_cast<Sci::Line>(0x10000));
1548 lineToWrapEnd = lineToWrap + linesInAllowedTime;
1549 }
1550 const Sci::Line lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1551 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1552
1553 // Ensure all lines being wrapped are styled.
1554 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1555
1556 if (lineToWrap < lineToWrapEnd) {
1557
1558 PRectangle rcTextArea = GetClientRectangle();
1559 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1560 rcTextArea.right -= vs.rightMarginWidth;
1561 wrapWidth = static_cast<int>(rcTextArea.Width());
1563 AutoSurface surface(this);
1564 if (surface) {
1565//Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1566
1567 const Sci::Line linesBeingWrapped = lineToWrapEnd - lineToWrap;
1568 ElapsedPeriod epWrapping;
1569 while (lineToWrap < lineToWrapEnd) {
1570 if (WrapOneLine(surface, lineToWrap)) {
1571 wrapOccurred = true;
1572 }
1573 wrapPending.Wrapped(lineToWrap);
1574 lineToWrap++;
1575 }
1576 durationWrapOneLine.AddSample(linesBeingWrapped, epWrapping.Duration());
1577
1578 goodTopLine = pcs->DisplayFromDoc(lineDocTop) + std::min(
1579 subLineTop, static_cast<Sci::Line>(pcs->GetHeight(lineDocTop)-1));
1580 }
1581 }
1582
1583 // If wrapping is done, bring it to resting position
1584 if (wrapPending.start >= lineEndNeedWrap) {
1586 }
1587 }
1588
1589 if (wrapOccurred) {
1590 SetScrollBars();
1591 SetTopLine(Sci::clamp(goodTopLine, static_cast<Sci::Line>(0), MaxScrollPos()));
1593 }
1594
1595 return wrapOccurred;
1596}
1597
1600 UndoGroup ug(pdoc);
1601 bool prevNonWS = true;
1605 pdoc->DelChar(pos);
1606 if (prevNonWS) {
1607 // Ensure at least one space separating previous lines
1608 const Sci::Position lengthInserted = pdoc->InsertString(pos, " ", 1);
1609 targetRange.end.Add(lengthInserted);
1610 }
1611 } else {
1612 prevNonWS = pdoc->CharAt(pos) != ' ';
1613 }
1614 }
1615 }
1616}
1617
1618const char *Editor::StringFromEOLMode(int eolMode) noexcept {
1619 if (eolMode == SC_EOL_CRLF) {
1620 return "\r\n";
1621 } else if (eolMode == SC_EOL_CR) {
1622 return "\r";
1623 } else {
1624 return "\n";
1625 }
1626}
1627
1628void Editor::LinesSplit(int pixelWidth) {
1630 if (pixelWidth == 0) {
1631 const PRectangle rcText = GetTextRectangle();
1632 pixelWidth = static_cast<int>(rcText.Width());
1633 }
1636 const char *eol = StringFromEOLMode(pdoc->eolMode);
1637 UndoGroup ug(pdoc);
1638 for (Sci::Line line = lineStart; line <= lineEnd; line++) {
1639 AutoSurface surface(this);
1641 if (surface && ll) {
1642 const Sci::Position posLineStart = pdoc->LineStart(line);
1643 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1644 Sci::Position lengthInsertedTotal = 0;
1645 for (int subLine = 1; subLine < ll->lines; subLine++) {
1646 const Sci::Position lengthInserted = pdoc->InsertString(
1647 posLineStart + lengthInsertedTotal + ll->LineStart(subLine),
1648 eol, strlen(eol));
1649 targetRange.end.Add(lengthInserted);
1650 lengthInsertedTotal += lengthInserted;
1651 }
1652 }
1654 }
1655 }
1656}
1657
1658void Editor::PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc) {
1659 if (vs.fixedColumnWidth == 0)
1660 return;
1661
1664 RefreshPixMaps(surfaceWindow);
1665
1666 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1667 // at this point. The Initialised call checks for this case and sets the status
1668 // to be bad which avoids crashes in following calls.
1669 if (!surfaceWindow->Initialised()) {
1670 return;
1671 }
1672
1673 PRectangle rcMargin = GetClientRectangle();
1674 const Point ptOrigin = GetVisibleOriginInMain();
1675 rcMargin.Move(0, -ptOrigin.y);
1676 rcMargin.left = 0;
1677 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1678
1679 if (!rc.Intersects(rcMargin))
1680 return;
1681
1682 Surface *surface;
1683 if (view.bufferedDraw) {
1684 surface = marginView.pixmapSelMargin.get();
1685 } else {
1686 surface = surfaceWindow;
1687 }
1688
1689 // Clip vertically to paint area to avoid drawing line numbers
1690 if (rcMargin.bottom > rc.bottom)
1691 rcMargin.bottom = rc.bottom;
1692 if (rcMargin.top < rc.top)
1693 rcMargin.top = rc.top;
1694
1695 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1696
1697 if (view.bufferedDraw) {
1698 surfaceWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1699 }
1700}
1701
1702void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1703 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1704 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1705 if (view.bufferedDraw) {
1706 const PRectangle rcClient = GetClientRectangle();
1707 if (!view.pixmapLine->Initialised()) {
1708
1709 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1710 surfaceWindow, wMain.GetID());
1711 }
1712 if (!marginView.pixmapSelMargin->Initialised()) {
1714 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1715 }
1716 }
1717}
1718
1719void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1720 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1721 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1723
1726 return; // Scroll bars may have changed so need redraw
1727 RefreshPixMaps(surfaceWindow);
1728
1730
1731 StyleAreaBounded(rcArea, false);
1732
1733 const PRectangle rcClient = GetClientRectangle();
1734 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1735 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1736
1737 if (NotifyUpdateUI()) {
1739 RefreshPixMaps(surfaceWindow);
1740 }
1741
1742 // Wrap the visible lines if needed.
1744 // The wrapping process has changed the height of some lines so
1745 // abandon this paint for a complete repaint.
1746 if (AbandonPaint()) {
1747 return;
1748 }
1749 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1750 }
1751
1752 if (!marginView.pixmapSelPattern->Initialised()) {
1753 // When Direct2D is used, pixmap creation may fail with D2DERR_RECREATE_TARGET so
1754 // abandon this paint to avoid further failures.
1755 // Main drawing surface and pixmaps should be recreated by next paint.
1756 return;
1757 }
1758
1759 if (!view.bufferedDraw)
1760 surfaceWindow->SetClip(rcArea);
1761
1762 if (paintState != paintAbandoned) {
1763 if (vs.marginInside) {
1764 PaintSelMargin(surfaceWindow, rcArea);
1765 PRectangle rcRightMargin = rcClient;
1766 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1767 if (rcArea.Intersects(rcRightMargin)) {
1768 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1769 }
1770 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1771 PRectangle rcLeftMargin = rcArea;
1772 rcLeftMargin.left = 0;
1773 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1774 if (rcArea.Intersects(rcLeftMargin)) {
1775 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1776 }
1777 }
1778 }
1779
1780 if (paintState == paintAbandoned) {
1781 // Either styling or NotifyUpdateUI noticed that painting is needed
1782 // outside the current painting rectangle
1783 //Platform::DebugPrintf("Abandoning paint\n");
1784 if (Wrapping()) {
1786 // Styling has spilled over a line end, such as occurs by starting a multiline
1787 // comment. The width of subsequent text may have changed, so rewrap.
1788 NeedWrapping(pcs->DocFromDisplay(topLine));
1789 }
1790 }
1791 return;
1792 }
1793
1794 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1795
1799 FineTickerStart(tickWiden, 50, 5);
1800 }
1801 }
1802
1803 NotifyPainted();
1804}
1805
1806// This is mostly copied from the Paint method but with some things omitted
1807// such as the margin markers, line numbers, selection and caret
1808// Should be merged back into a combined Draw method.
1810 if (!pfr)
1811 return 0;
1812
1813 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1814 if (!surface)
1815 return 0;
1816 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1817 if (!surfaceMeasure) {
1818 return 0;
1819 }
1820 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1821}
1822
1823long Editor::TextWidth(uptr_t style, const char *text) {
1825 AutoSurface surface(this);
1826 if (surface) {
1827 return Sci::lround(surface->WidthText(vs.styles[style].font, text, static_cast<int>(strlen(text))));
1828 } else {
1829 return 1;
1830 }
1831}
1832
1833// Empty method is overridden on GTK+ to show / hide scrollbars
1835
1838
1839 const Sci::Line nMax = MaxScrollPos();
1840 const Sci::Line nPage = LinesOnScreen();
1841 const bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1842 if (modified) {
1843 DwellEnd(true);
1844 }
1845
1846 // TODO: ensure always showing as many lines as possible
1847 // May not be, if, for example, window made larger
1848 if (topLine > MaxScrollPos()) {
1849 SetTopLine(Sci::clamp(topLine, static_cast<Sci::Line>(0), MaxScrollPos()));
1851 Redraw();
1852 }
1853 if (modified) {
1854 if (!AbandonPaint())
1855 Redraw();
1856 }
1857 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1858}
1859
1861 DropGraphics(false);
1862 SetScrollBars();
1863 if (Wrapping()) {
1864 PRectangle rcTextArea = GetClientRectangle();
1865 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1866 rcTextArea.right -= vs.rightMarginWidth;
1867 if (wrapWidth != rcTextArea.Width()) {
1868 NeedWrapping();
1869 Redraw();
1870 }
1871 }
1872}
1873
1875 if (virtualSpace > 0) {
1878 if (indent == position) {
1879 return pdoc->SetLineIndentation(line, pdoc->GetLineIndentation(line) + virtualSpace);
1880 } else {
1881 std::string spaceText(virtualSpace, ' ');
1882 const Sci::Position lengthInserted = pdoc->InsertString(position, spaceText.c_str(), virtualSpace);
1883 position += lengthInserted;
1884 }
1885 }
1886 return position;
1887}
1888
1890 // Return the new position with no virtual space
1891 return SelectionPosition(RealizeVirtualSpace(position.Position(), position.VirtualSpace()));
1892}
1893
1894void Editor::AddChar(char ch) {
1895 char s[2];
1896 s[0] = ch;
1897 s[1] = '\0';
1899}
1900
1902 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1905 }
1906}
1907
1908// InsertCharacter inserts a character encoded in document code page.
1909void Editor::InsertCharacter(const char *s, unsigned int len, CharacterSource charSource) {
1910 if (len == 0) {
1911 return;
1912 }
1914 {
1915 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1916
1917 // Vector elements point into selection in order to change selection.
1918 std::vector<SelectionRange *> selPtrs;
1919 for (size_t r = 0; r < sel.Count(); r++) {
1920 selPtrs.push_back(&sel.Range(r));
1921 }
1922 // Order selections by position in document.
1923 std::sort(selPtrs.begin(), selPtrs.end(),
1924 [](const SelectionRange *a, const SelectionRange *b) noexcept {return *a < *b;});
1925
1926 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1927 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1928 rit != selPtrs.rend(); ++rit) {
1929 SelectionRange *currentSel = *rit;
1930 if (!RangeContainsProtected(currentSel->Start().Position(),
1931 currentSel->End().Position())) {
1932 Sci::Position positionInsert = currentSel->Start().Position();
1933 if (!currentSel->Empty()) {
1934 if (currentSel->Length()) {
1935 pdoc->DeleteChars(positionInsert, currentSel->Length());
1936 currentSel->ClearVirtualSpace();
1937 } else {
1938 // Range is all virtual so collapse to start of virtual space
1939 currentSel->MinimizeVirtualSpace();
1940 }
1941 } else if (inOverstrike) {
1942 if (positionInsert < pdoc->Length()) {
1943 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1944 pdoc->DelChar(positionInsert);
1945 currentSel->ClearVirtualSpace();
1946 }
1947 }
1948 }
1949 positionInsert = RealizeVirtualSpace(positionInsert, currentSel->caret.VirtualSpace());
1950 const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, s, len);
1951 if (lengthInserted > 0) {
1952 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1953 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1954 }
1955 currentSel->ClearVirtualSpace();
1956 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1957 if (Wrapping()) {
1958 AutoSurface surface(this);
1959 if (surface) {
1960 if (WrapOneLine(surface, pdoc->SciLineFromPosition(positionInsert))) {
1961 SetScrollBars();
1963 Redraw();
1964 }
1965 }
1966 }
1967 }
1968 }
1969 }
1970 if (Wrapping()) {
1971 SetScrollBars();
1972 }
1974 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1976 // Avoid blinking during rapid typing:
1981 }
1982
1983 int ch = static_cast<unsigned char>(s[0]);
1984 if (pdoc->dbcsCodePage != SC_CP_UTF8) {
1985 if (len > 1) {
1986 // DBCS code page or DBCS font character set.
1987 ch = (ch << 8) | static_cast<unsigned char>(s[1]);
1988 }
1989 } else {
1990 if ((ch < 0xC0) || (1 == len)) {
1991 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1992 // characters when not in UTF-8 mode.
1993 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1994 // characters representing themselves.
1995 } else {
1996 unsigned int utf32[1] = { 0 };
1997 UTF32FromUTF8(s, len, utf32, Sci::size(utf32));
1998 ch = utf32[0];
1999 }
2000 }
2001 NotifyChar(ch, charSource);
2002
2003 if (recordingMacro && charSource != CharacterSource::tentativeInput) {
2004 std::string copy(s, len); // ensure NUL-terminated
2005 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(copy.data()));
2006 }
2007}
2008
2010 // Make positions for the first composition string.
2012 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
2013 for (size_t r = 0; r<sel.Count(); r++) {
2015 sel.Range(r).End().Position())) {
2016 const Sci::Position positionInsert = sel.Range(r).Start().Position();
2017 if (!sel.Range(r).Empty()) {
2018 if (sel.Range(r).Length()) {
2019 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2021 } else {
2022 // Range is all virtual so collapse to start of virtual space
2024 }
2025 }
2026 RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2028 }
2029 }
2030}
2031
2034 SelectionPosition selStart = sel.Start();
2035 selStart = RealizeVirtualSpace(selStart);
2036 const Sci::Position lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
2037 if (lengthInserted > 0) {
2038 SetEmptySelection(selStart.Position() + lengthInserted);
2039 }
2040 } else {
2041 // SC_MULTIPASTE_EACH
2042 for (size_t r=0; r<sel.Count(); r++) {
2044 sel.Range(r).End().Position())) {
2045 Sci::Position positionInsert = sel.Range(r).Start().Position();
2046 if (!sel.Range(r).Empty()) {
2047 if (sel.Range(r).Length()) {
2048 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2050 } else {
2051 // Range is all virtual so collapse to start of virtual space
2053 }
2054 }
2055 positionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2056 const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, text, len);
2057 if (lengthInserted > 0) {
2058 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
2059 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
2060 }
2062 }
2063 }
2064 }
2065}
2066
2068 std::string convertedText;
2069 if (convertPastes) {
2070 // Convert line endings of the paste into our local line-endings mode
2071 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
2072 len = convertedText.length();
2073 text = convertedText.c_str();
2074 }
2075 if (shape == pasteRectangular) {
2076 PasteRectangular(sel.Start(), text, len);
2077 } else {
2078 if (shape == pasteLine) {
2079 const Sci::Position insertPos =
2081 Sci::Position lengthInserted = pdoc->InsertString(insertPos, text, len);
2082 // add the newline if necessary
2083 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
2084 const char *endline = StringFromEOLMode(pdoc->eolMode);
2085 const Sci::Position length = strlen(endline);
2086 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
2087 }
2088 if (sel.MainCaret() == insertPos) {
2089 SetEmptySelection(sel.MainCaret() + lengthInserted);
2090 }
2091 } else {
2092 InsertPaste(text, len);
2093 }
2094 }
2095}
2096
2097void Editor::ClearSelection(bool retainMultipleSelections) {
2098 if (!sel.IsRectangular() && !retainMultipleSelections)
2100 UndoGroup ug(pdoc);
2101 for (size_t r=0; r<sel.Count(); r++) {
2102 if (!sel.Range(r).Empty()) {
2104 sel.Range(r).End().Position())) {
2106 sel.Range(r).Length());
2108 }
2109 }
2110 }
2115}
2116
2118 {
2119 UndoGroup ug(pdoc);
2120 if (0 != pdoc->Length()) {
2121 pdoc->DeleteChars(0, pdoc->Length());
2122 }
2123 if (!pdoc->IsReadOnly()) {
2124 pcs->Clear();
2128 }
2129 }
2130
2132
2133 sel.Clear();
2134 SetTopLine(0);
2137}
2138
2140 pdoc->decorations->DeleteLexerDecorations();
2141 pdoc->StartStyling(0, '\377');
2142 pdoc->SetStyleFor(pdoc->Length(), 0);
2143 pcs->ShowAll();
2145 pdoc->ClearLevels();
2146}
2147
2149 SelectionText selectedText;
2150 CopySelectionRange(&selectedText, true);
2151 CopyToClipboard(selectedText);
2152}
2153
2157 Copy();
2159 }
2160}
2161
2164 return;
2165 }
2166 sel.Clear();
2169 UndoGroup ug(pdoc);
2171 const int xInsert = XFromPosition(sel.RangeMain().caret);
2172 bool prevCr = false;
2173 while ((len > 0) && IsEOLChar(ptr[len-1]))
2174 len--;
2175 for (Sci::Position i = 0; i < len; i++) {
2176 if (IsEOLChar(ptr[i])) {
2177 if ((ptr[i] == '\r') || (!prevCr))
2178 line++;
2179 if (line >= pdoc->LinesTotal()) {
2180 if (pdoc->eolMode != SC_EOL_LF)
2181 pdoc->InsertString(pdoc->Length(), "\r", 1);
2182 if (pdoc->eolMode != SC_EOL_CR)
2183 pdoc->InsertString(pdoc->Length(), "\n", 1);
2184 }
2185 // Pad the end of lines with spaces if required
2187 if ((XFromPosition(sel.RangeMain().caret) < xInsert) && (i + 1 < len)) {
2188 while (XFromPosition(sel.RangeMain().caret) < xInsert) {
2189 assert(pdoc);
2190 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2191 sel.RangeMain().caret.Add(lengthInserted);
2192 }
2193 }
2194 prevCr = ptr[i] == '\r';
2195 } else {
2196 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2197 sel.RangeMain().caret.Add(lengthInserted);
2198 prevCr = false;
2199 }
2200 }
2202}
2203
2206}
2207
2209 // If multiple selections, don't delete EOLS
2210 if (sel.Empty()) {
2211 bool singleVirtual = false;
2212 if ((sel.Count() == 1) &&
2215 singleVirtual = true;
2216 }
2217 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2218 for (size_t r=0; r<sel.Count(); r++) {
2220 if (sel.Range(r).Start().VirtualSpace()) {
2221 if (sel.Range(r).anchor < sel.Range(r).caret)
2223 else
2225 }
2226 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2229 } // else multiple selection so don't eat line ends
2230 } else {
2232 }
2233 }
2234 } else {
2236 }
2238 ShowCaretAtCurrentPosition(); // Avoid blinking
2239}
2240
2242 sel.Clear();
2243 SetSelection(0, pdoc->Length());
2244 Redraw();
2245}
2246
2248 if (pdoc->CanUndo()) {
2250 const Sci::Position newPos = pdoc->Undo();
2251 if (newPos >= 0)
2252 SetEmptySelection(newPos);
2254 }
2255}
2256
2258 if (pdoc->CanRedo()) {
2259 const Sci::Position newPos = pdoc->Redo();
2260 if (newPos >= 0)
2261 SetEmptySelection(newPos);
2263 }
2264}
2265
2266void Editor::DelCharBack(bool allowLineStartDeletion) {
2268 if (!sel.IsRectangular())
2270 if (sel.IsRectangular())
2271 allowLineStartDeletion = false;
2272 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2273 if (sel.Empty()) {
2274 for (size_t r=0; r<sel.Count(); r++) {
2276 if (sel.Range(r).caret.VirtualSpace()) {
2279 } else {
2280 const Sci::Line lineCurrentPos =
2282 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2283 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2285 UndoGroup ugInner(pdoc, !ug.Needed());
2286 const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2287 const int indentationStep = pdoc->IndentSize();
2288 int indentationChange = indentation % indentationStep;
2289 if (indentationChange == 0)
2290 indentationChange = indentationStep;
2291 const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2292 // SetEmptySelection
2293 sel.Range(r) = SelectionRange(posSelect);
2294 } else {
2296 }
2297 }
2298 }
2299 } else {
2301 }
2302 }
2304 } else {
2306 }
2309 // Avoid blinking during rapid typing:
2311}
2312
2313int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta, bool super) noexcept {
2314 return
2315 (shift ? SCI_SHIFT : 0) |
2316 (ctrl ? SCI_CTRL : 0) |
2317 (alt ? SCI_ALT : 0) |
2318 (meta ? SCI_META : 0) |
2319 (super ? SCI_SUPER : 0);
2320}
2321
2322void Editor::NotifyFocus(bool focus) {
2323 SCNotification scn = {};
2324 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2325 NotifyParent(scn);
2326}
2327
2328void Editor::SetCtrlID(int identifier) {
2329 ctrlID = identifier;
2330}
2331
2333 SCNotification scn = {};
2335 scn.position = endStyleNeeded;
2336 NotifyParent(scn);
2337}
2338
2339void Editor::NotifyStyleNeeded(Document *, void *, Sci::Position endStyleNeeded) {
2340 NotifyStyleToNeeded(endStyleNeeded);
2341}
2342
2344}
2345
2346void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2347 errorStatus = status;
2348}
2349
2350void Editor::NotifyChar(int ch, CharacterSource charSource) {
2351 SCNotification scn = {};
2352 scn.nmhdr.code = SCN_CHARADDED;
2353 scn.ch = ch;
2354 scn.characterSource = static_cast<int>(charSource);
2355 NotifyParent(scn);
2356}
2357
2358void Editor::NotifySavePoint(bool isSavePoint) {
2359 SCNotification scn = {};
2360 if (isSavePoint) {
2362 } else {
2364 }
2365 NotifyParent(scn);
2366}
2367
2369 SCNotification scn = {};
2371 NotifyParent(scn);
2372}
2373
2374void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2375 SCNotification scn = {};
2377 scn.line = LineFromLocation(pt);
2378 scn.position = PositionFromLocation(pt, true);
2379 scn.modifiers = modifiers;
2380 NotifyParent(scn);
2381}
2382
2384 SCNotification scn = {};
2386 scn.position = position;
2387 scn.modifiers = modifiers;
2388 NotifyParent(scn);
2389}
2390
2392 SCNotification scn = {};
2394 scn.position = position;
2395 scn.modifiers = modifiers;
2396 NotifyParent(scn);
2397}
2398
2400 SCNotification scn = {};
2402 scn.position = position;
2403 scn.modifiers = modifiers;
2404 NotifyParent(scn);
2405}
2406
2408 if (needUpdateUI) {
2409 SCNotification scn = {};
2410 scn.nmhdr.code = SCN_UPDATEUI;
2411 scn.updated = needUpdateUI;
2412 NotifyParent(scn);
2413 needUpdateUI = 0;
2414 return true;
2415 }
2416 return false;
2417}
2418
2420 SCNotification scn = {};
2421 scn.nmhdr.code = SCN_PAINTED;
2422 NotifyParent(scn);
2423}
2424
2425void Editor::NotifyIndicatorClick(bool click, Sci::Position position, int modifiers) {
2426 const int mask = pdoc->decorations->AllOnFor(position);
2427 if ((click && mask) || pdoc->decorations->ClickNotified()) {
2428 SCNotification scn = {};
2429 pdoc->decorations->SetClickNotified(click);
2431 scn.modifiers = modifiers;
2432 scn.position = position;
2433 NotifyParent(scn);
2434 }
2435}
2436
2437bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2438 const int marginClicked = vs.MarginFromLocation(pt);
2439 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2441 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2442 const bool ctrl = (modifiers & SCI_CTRL) != 0;
2443 const bool shift = (modifiers & SCI_SHIFT) != 0;
2444 const Sci::Line lineClick = pdoc->SciLineFromPosition(position);
2445 if (shift && ctrl) {
2447 } else {
2448 const int levelClick = pdoc->GetLevel(lineClick);
2449 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2450 if (shift) {
2451 // Ensure all children visible
2452 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2453 } else if (ctrl) {
2454 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2455 } else {
2456 // Toggle this line
2457 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2458 }
2459 }
2460 }
2461 return true;
2462 }
2463 SCNotification scn = {};
2465 scn.modifiers = modifiers;
2466 scn.position = position;
2467 scn.margin = marginClicked;
2468 NotifyParent(scn);
2469 return true;
2470 } else {
2471 return false;
2472 }
2473}
2474
2475bool Editor::NotifyMarginRightClick(Point pt, int modifiers) {
2476 const int marginRightClicked = vs.MarginFromLocation(pt);
2477 if ((marginRightClicked >= 0) && vs.ms[marginRightClicked].sensitive) {
2479 SCNotification scn = {};
2481 scn.modifiers = modifiers;
2482 scn.position = position;
2483 scn.margin = marginRightClicked;
2484 NotifyParent(scn);
2485 return true;
2486 } else {
2487 return false;
2488 }
2489}
2490
2492 SCNotification scn = {};
2493 scn.nmhdr.code = SCN_NEEDSHOWN;
2494 scn.position = pos;
2495 scn.length = len;
2496 NotifyParent(scn);
2497}
2498
2499void Editor::NotifyDwelling(Point pt, bool state) {
2500 SCNotification scn = {};
2501 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2502 scn.position = PositionFromLocation(pt, true);
2503 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2504 scn.y = static_cast<int>(pt.y);
2505 NotifyParent(scn);
2506}
2507
2509 SCNotification scn = {};
2510 scn.nmhdr.code = SCN_ZOOM;
2511 NotifyParent(scn);
2512}
2513
2514// Notifications from document
2516 //Platform::DebugPrintf("** Modify Attempt\n");
2518}
2519
2520void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2521 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2522 NotifySavePoint(atSavePoint);
2523}
2524
2528 const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2529 const Sci::Line lines = std::max(static_cast<Sci::Line>(0), mh.linesAdded);
2530 if (Wrapping()) {
2531 NeedWrapping(lineDoc, lineDoc + lines + 1);
2532 }
2534 // Fix up annotation heights
2535 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2536 }
2537}
2538
2539namespace {
2540
2541// Move a position so it is still after the same character as before the insertion.
2542Sci::Position MovePositionForInsertion(Sci::Position position, Sci::Position startInsertion, Sci::Position length) noexcept {
2543 if (position > startInsertion) {
2544 return position + length;
2545 }
2546 return position;
2547}
2548
2549// Move a position so it is still after the same character as before the deletion if that
2550// character is still present else after the previous surviving character.
2551Sci::Position MovePositionForDeletion(Sci::Position position, Sci::Position startDeletion, Sci::Position length) noexcept {
2552 if (position > startDeletion) {
2553 const Sci::Position endDeletion = startDeletion + length;
2554 if (position > endDeletion) {
2555 return position - length;
2556 } else {
2557 return startDeletion;
2558 }
2559 } else {
2560 return position;
2561 }
2562}
2563
2564}
2565
2568 if (paintState == painting) {
2570 }
2572 if (paintState == painting) {
2574 Range(pdoc->LineStart(mh.line),
2575 pdoc->LineStart(mh.line + 1)));
2576 } else {
2577 // Could check that change is before last visible line.
2578 Redraw();
2579 }
2580 }
2582 Redraw();
2583 }
2585 if (paintState == painting) {
2587 Range(mh.position, mh.position + mh.length));
2588 } else {
2589 Redraw();
2590 }
2591 }
2595 }
2596 if (paintState == notPainting) {
2597 const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
2598 if (mh.position < pdoc->LineStart(lineDocTop)) {
2599 // Styling performed before this view
2600 Redraw();
2601 } else {
2603 }
2604 }
2607 }
2608 } else {
2609 // Move selection and brace highlights
2611 sel.MovePositions(true, mh.position, mh.length);
2612 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2613 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2614 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
2615 sel.MovePositions(false, mh.position, mh.length);
2616 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2617 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2618 }
2619 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && pcs->HiddenLines()) {
2620 // Some lines are hidden so may need shown.
2621 const Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2622 Sci::Position endNeedShown = mh.position;
2624 if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
2625 endNeedShown = pdoc->LineStart(lineOfPos+1);
2626 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2627 // If the deletion includes any EOL then we extend the need shown area.
2628 endNeedShown = mh.position + mh.length;
2630 for (Sci::Line line = lineOfPos + 1; line <= lineLast; line++) {
2631 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, -1, -1);
2632 if (lineLast < lineMaxSubord) {
2633 lineLast = lineMaxSubord;
2634 endNeedShown = pdoc->LineEnd(lineLast);
2635 }
2636 }
2637 }
2638 NeedShown(mh.position, endNeedShown - mh.position);
2639 }
2640 if (mh.linesAdded != 0) {
2641 // Update contraction state for inserted and removed lines
2642 // lineOfPos should be calculated in context of state before modification, shouldn't it
2643 Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2644 if (mh.position > pdoc->LineStart(lineOfPos))
2645 lineOfPos++; // Affecting subsequent lines
2646 if (mh.linesAdded > 0) {
2647 pcs->InsertLines(lineOfPos, mh.linesAdded);
2648 } else {
2649 pcs->DeleteLines(lineOfPos, -mh.linesAdded);
2650 }
2651 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2652 }
2654 const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2655 if (vs.annotationVisible) {
2656 if (pcs->SetHeight(lineDoc, pcs->GetHeight(lineDoc) + static_cast<int>(mh.annotationLinesAdded))) {
2657 SetScrollBars();
2658 }
2659 Redraw();
2660 }
2661 }
2664 Redraw();
2665 }
2666 }
2668 if (mh.linesAdded != 0) {
2669 // Avoid scrolling of display if change before current display
2670 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2671 const Sci::Line newTop = Sci::clamp(topLine + mh.linesAdded, static_cast<Sci::Line>(0), MaxScrollPos());
2672 if (newTop != topLine) {
2673 SetTopLine(newTop);
2675 }
2676 }
2677
2678 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2681 }
2682 Redraw();
2683 }
2684 } else {
2685 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2688 }
2690 }
2691 }
2692 }
2693
2694 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2695 SetScrollBars();
2696 }
2697
2701 // Fold changes can affect the drawing of following lines so redraw whole margin
2703 } else {
2705 }
2706 }
2707 }
2710 }
2711
2712 // NOW pay the piper WRT "deferred" visual updates
2713 if (IsLastStep(mh)) {
2714 SetScrollBars();
2715 Redraw();
2716 }
2717
2718 // If client wants to see this modification
2719 if (mh.modificationType & modEventMask) {
2720 if (commandEvents) {
2722 // Real modification made to text of document.
2723 NotifyChange(); // Send EN_CHANGE
2724 }
2725 }
2726
2727 SCNotification scn = {};
2728 scn.nmhdr.code = SCN_MODIFIED;
2729 scn.position = mh.position;
2731 scn.text = mh.text;
2732 scn.length = mh.length;
2733 scn.linesAdded = mh.linesAdded;
2734 scn.line = mh.line;
2735 scn.foldLevelNow = mh.foldLevelNow;
2737 scn.token = static_cast<int>(mh.token);
2739 NotifyParent(scn);
2740 }
2741}
2742
2743void Editor::NotifyDeleted(Document *, void *) noexcept {
2744 /* Do nothing */
2745}
2746
2747void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2748
2749 // Enumerates all macroable messages
2750 switch (iMessage) {
2751 case SCI_CUT:
2752 case SCI_COPY:
2753 case SCI_PASTE:
2754 case SCI_CLEAR:
2755 case SCI_REPLACESEL:
2756 case SCI_ADDTEXT:
2757 case SCI_INSERTTEXT:
2758 case SCI_APPENDTEXT:
2759 case SCI_CLEARALL:
2760 case SCI_SELECTALL:
2761 case SCI_GOTOLINE:
2762 case SCI_GOTOPOS:
2763 case SCI_SEARCHANCHOR:
2764 case SCI_SEARCHNEXT:
2765 case SCI_SEARCHPREV:
2766 case SCI_LINEDOWN:
2767 case SCI_LINEDOWNEXTEND:
2768 case SCI_PARADOWN:
2769 case SCI_PARADOWNEXTEND:
2770 case SCI_LINEUP:
2771 case SCI_LINEUPEXTEND:
2772 case SCI_PARAUP:
2773 case SCI_PARAUPEXTEND:
2774 case SCI_CHARLEFT:
2775 case SCI_CHARLEFTEXTEND:
2776 case SCI_CHARRIGHT:
2778 case SCI_WORDLEFT:
2779 case SCI_WORDLEFTEXTEND:
2780 case SCI_WORDRIGHT:
2782 case SCI_WORDPARTLEFT:
2784 case SCI_WORDPARTRIGHT:
2786 case SCI_WORDLEFTEND:
2788 case SCI_WORDRIGHTEND:
2790 case SCI_HOME:
2791 case SCI_HOMEEXTEND:
2792 case SCI_LINEEND:
2793 case SCI_LINEENDEXTEND:
2794 case SCI_HOMEWRAP:
2795 case SCI_HOMEWRAPEXTEND:
2796 case SCI_LINEENDWRAP:
2798 case SCI_DOCUMENTSTART:
2800 case SCI_DOCUMENTEND:
2806 case SCI_PAGEUP:
2807 case SCI_PAGEUPEXTEND:
2808 case SCI_PAGEDOWN:
2809 case SCI_PAGEDOWNEXTEND:
2811 case SCI_CANCEL:
2812 case SCI_DELETEBACK:
2813 case SCI_TAB:
2814 case SCI_BACKTAB:
2815 case SCI_FORMFEED:
2816 case SCI_VCHOME:
2817 case SCI_VCHOMEEXTEND:
2818 case SCI_VCHOMEWRAP:
2820 case SCI_VCHOMEDISPLAY:
2822 case SCI_DELWORDLEFT:
2823 case SCI_DELWORDRIGHT:
2825 case SCI_DELLINELEFT:
2826 case SCI_DELLINERIGHT:
2827 case SCI_LINECOPY:
2828 case SCI_LINECUT:
2829 case SCI_LINEDELETE:
2830 case SCI_LINETRANSPOSE:
2831 case SCI_LINEREVERSE:
2832 case SCI_LINEDUPLICATE:
2833 case SCI_LOWERCASE:
2834 case SCI_UPPERCASE:
2835 case SCI_LINESCROLLDOWN:
2836 case SCI_LINESCROLLUP:
2838 case SCI_HOMEDISPLAY:
2840 case SCI_LINEENDDISPLAY:
2847 case SCI_HOMERECTEXTEND:
2853 case SCI_COPYALLOWLINE:
2857 case SCI_SCROLLTOSTART:
2858 case SCI_SCROLLTOEND:
2859 break;
2860
2861 // Filter out all others like display changes. Also, newlines are redundant
2862 // with char insert messages.
2863 case SCI_NEWLINE:
2864 default:
2865 // printf("Filtered out %ld of macro recording\n", iMessage);
2866 return;
2867 }
2868
2869 // Send notification
2870 SCNotification scn = {};
2872 scn.message = iMessage;
2873 scn.wParam = wParam;
2874 scn.lParam = lParam;
2875 NotifyParent(scn);
2876}
2877
2878// Something has changed that the container should know about
2879void Editor::ContainerNeedsUpdate(int flags) noexcept {
2880 needUpdateUI |= flags;
2881}
2882
2883/**
2884 * Force scroll and keep position relative to top of window.
2885 *
2886 * If stuttered = true and not already at first/last row, move to first/last row of window.
2887 * If stuttered = true and already at first/last row, scroll as normal.
2888 */
2889void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2890 Sci::Line topLineNew;
2891 SelectionPosition newPos;
2892
2893 const Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());
2894 const Sci::Line topStutterLine = topLine + caretPolicies.y.slop;
2895 const Sci::Line bottomStutterLine =
2897 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * static_cast<int>(LinesToScroll()))))
2898 - caretPolicies.y.slop - 1;
2899
2900 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2901 topLineNew = topLine;
2903 false, false, UserVirtualSpace());
2904
2905 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2906 topLineNew = topLine;
2908 false, false, UserVirtualSpace());
2909
2910 } else {
2912
2913 topLineNew = Sci::clamp(
2914 topLine + direction * LinesToScroll(), static_cast<Sci::Line>(0), MaxScrollPos());
2915 newPos = SPositionFromLocation(
2916 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) +
2917 direction * (vs.lineHeight * static_cast<int>(LinesToScroll()))),
2918 false, false, UserVirtualSpace());
2919 }
2920
2921 if (topLineNew != topLine) {
2922 SetTopLine(topLineNew);
2923 MovePositionTo(newPos, selt);
2924 Redraw();
2926 } else {
2927 MovePositionTo(newPos, selt);
2928 }
2929}
2930
2931void Editor::ChangeCaseOfSelection(int caseMapping) {
2932 UndoGroup ug(pdoc);
2933 for (size_t r=0; r<sel.Count(); r++) {
2934 SelectionRange current = sel.Range(r);
2935 SelectionRange currentNoVS = current;
2936 currentNoVS.ClearVirtualSpace();
2937 const size_t rangeBytes = currentNoVS.Length();
2938 if (rangeBytes > 0) {
2939 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2940
2941 std::string sMapped = CaseMapString(sText, caseMapping);
2942
2943 if (sMapped != sText) {
2944 size_t firstDifference = 0;
2945 while (sMapped[firstDifference] == sText[firstDifference])
2946 firstDifference++;
2947 size_t lastDifferenceText = sText.size() - 1;
2948 size_t lastDifferenceMapped = sMapped.size() - 1;
2949 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2950 lastDifferenceText--;
2951 lastDifferenceMapped--;
2952 }
2953 const size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2955 currentNoVS.Start().Position() + firstDifference,
2956 rangeBytes - firstDifference - endDifferenceText);
2957 const Sci::Position lengthChange = lastDifferenceMapped - firstDifference + 1;
2958 const Sci::Position lengthInserted = pdoc->InsertString(
2959 currentNoVS.Start().Position() + firstDifference,
2960 sMapped.c_str() + firstDifference,
2961 lengthChange);
2962 // Automatic movement changes selection so reset to exactly the same as it was.
2963 const Sci::Position diffSizes = sMapped.size() - sText.size() + lengthInserted - lengthChange;
2964 if (diffSizes != 0) {
2965 if (current.anchor > current.caret)
2966 current.anchor.Add(diffSizes);
2967 else
2968 current.caret.Add(diffSizes);
2969 }
2970 sel.Range(r) = current;
2971 }
2972 }
2973 }
2974}
2975
2978 if (line > 0) {
2979 UndoGroup ug(pdoc);
2980
2981 const Sci::Position startPrevious = pdoc->LineStart(line - 1);
2982 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2983
2984 Sci::Position startCurrent = pdoc->LineStart(line);
2985 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2986
2987 pdoc->DeleteChars(startCurrent, lineCurrent.length());
2988 pdoc->DeleteChars(startPrevious, linePrevious.length());
2989 startCurrent -= linePrevious.length();
2990
2991 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2992 lineCurrent.length());
2993 pdoc->InsertString(startCurrent, linePrevious.c_str(),
2994 linePrevious.length());
2995 // Move caret to start of current line
2996 MovePositionTo(SelectionPosition(startCurrent));
2997 }
2998}
2999
3001 const Sci::Line lineStart =
3003 const Sci::Line lineEnd =
3005 const Sci::Line lineDiff = lineEnd - lineStart;
3006 if (lineDiff <= 0)
3007 return;
3008 UndoGroup ug(pdoc);
3009 for (Sci::Line i=(lineDiff+1)/2-1; i>=0; --i) {
3010 const Sci::Line lineNum2 = lineEnd - i;
3011 const Sci::Line lineNum1 = lineStart + i;
3012 Sci::Position lineStart2 = pdoc->LineStart(lineNum2);
3013 const Sci::Position lineStart1 = pdoc->LineStart(lineNum1);
3014 const std::string line2 = RangeText(lineStart2, pdoc->LineEnd(lineNum2));
3015 const std::string line1 = RangeText(lineStart1, pdoc->LineEnd(lineNum1));
3016 const Sci::Position lineLen2 = line2.length();
3017 const Sci::Position lineLen1 = line1.length();
3018 pdoc->DeleteChars(lineStart2, lineLen2);
3019 pdoc->DeleteChars(lineStart1, lineLen1);
3020 lineStart2 -= lineLen1;
3021 pdoc->InsertString(lineStart2, line1.c_str(), lineLen1);
3022 pdoc->InsertString(lineStart1, line2.c_str(), lineLen2);
3023 }
3024 // Wholly select all affected lines
3025 sel.RangeMain() = SelectionRange(pdoc->LineStart(lineStart),
3026 pdoc->LineStart(lineEnd+1));
3027}
3028
3029void Editor::Duplicate(bool forLine) {
3030 if (sel.Empty()) {
3031 forLine = true;
3032 }
3033 UndoGroup ug(pdoc);
3034 const char *eol = "";
3035 Sci::Position eolLen = 0;
3036 if (forLine) {
3038 eolLen = strlen(eol);
3039 }
3040 for (size_t r=0; r<sel.Count(); r++) {
3041 SelectionPosition start = sel.Range(r).Start();
3042 SelectionPosition end = sel.Range(r).End();
3043 if (forLine) {
3047 }
3048 std::string text = RangeText(start.Position(), end.Position());
3049 Sci::Position lengthInserted = eolLen;
3050 if (forLine)
3051 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
3052 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), text.length());
3053 }
3054 if (sel.Count() && sel.IsRectangular()) {
3055 SelectionPosition last = sel.Last();
3056 if (forLine) {
3058 last = SelectionPosition(last.Position() +
3060 }
3062 sel.Rectangular().anchor = last;
3063 else
3064 sel.Rectangular().caret = last;
3066 }
3067}
3068
3070 sel.SetMoveExtends(false);
3071}
3072
3076 // Remove non-main ranges
3078 }
3079
3080 UndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));
3081
3082 // Clear each range
3083 if (!sel.Empty()) {
3085 }
3086
3087 // Insert each line end
3088 size_t countInsertions = 0;
3089 for (size_t r = 0; r < sel.Count(); r++) {
3091 const char *eol = StringFromEOLMode(pdoc->eolMode);
3092 const Sci::Position positionInsert = sel.Range(r).caret.Position();
3093 const Sci::Position insertLength = pdoc->InsertString(positionInsert, eol, strlen(eol));
3094 if (insertLength > 0) {
3095 sel.Range(r) = SelectionRange(positionInsert + insertLength);
3096 countInsertions++;
3097 }
3098 }
3099
3100 // Perform notifications after all the changes as the application may change the
3101 // selections in response to the characters.
3102 for (size_t i = 0; i < countInsertions; i++) {
3103 const char *eol = StringFromEOLMode(pdoc->eolMode);
3104 while (*eol) {
3106 if (recordingMacro) {
3107 char txt[2];
3108 txt[0] = *eol;
3109 txt[1] = '\0';
3110 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3111 }
3112 eol++;
3113 }
3114 }
3115
3117 SetScrollBars();
3119 // Avoid blinking during rapid typing:
3121}
3122
3124 const Point pt = LocationFromPosition(spStart);
3125 int skipLines = 0;
3126
3127 if (vs.annotationVisible) {
3128 const Sci::Line lineDoc = pdoc->SciLineFromPosition(spStart.Position());
3129 const Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
3130 const int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
3131
3132 if (direction < 0 && subLine == 0) {
3133 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
3134 if (lineDisplay > 0) {
3135 skipLines = pdoc->AnnotationLines(pcs->DocFromDisplay(lineDisplay - 1));
3136 }
3137 } else if (direction > 0 && subLine >= (pcs->GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
3138 skipLines = pdoc->AnnotationLines(lineDoc);
3139 }
3140 }
3141
3142 const Sci::Line newY = static_cast<Sci::Line>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
3143 if (lastX < 0) {
3144 lastX = static_cast<int>(pt.x) + xOffset;
3145 }
3147 Point::FromInts(lastX - xOffset, static_cast<int>(newY)), false, false, UserVirtualSpace());
3148
3149 if (direction < 0) {
3150 // Line wrapping may lead to a location on the same line, so
3151 // seek back if that is the case.
3152 Point ptNew = LocationFromPosition(posNew.Position());
3153 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
3154 posNew.Add(-1);
3155 posNew.SetVirtualSpace(0);
3156 ptNew = LocationFromPosition(posNew.Position());
3157 }
3158 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
3159 // There is an equivalent case when moving down which skips
3160 // over a line.
3161 Point ptNew = LocationFromPosition(posNew.Position());
3162 while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3163 posNew.Add(-1);
3164 posNew.SetVirtualSpace(0);
3165 ptNew = LocationFromPosition(posNew.Position());
3166 }
3167 }
3168 return posNew;
3169}
3170
3172 if ((selt == Selection::noSel) && sel.MoveExtends()) {
3174 }
3175 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
3176 if (sel.IsRectangular()) {
3177 if (selt == Selection::noSel) {
3178 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
3179 } else {
3180 caretToUse = sel.Rectangular().caret;
3181 }
3182 }
3183 if (selt == Selection::selRectangle) {
3184 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3185 if (!sel.IsRectangular()) {
3188 }
3190 PositionUpOrDown(caretToUse, direction, lastXChosen), direction);
3192 sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
3194 MovedCaret(posNew, caretToUse, true, caretPolicies);
3195 } else if (sel.selType == Selection::selLines && sel.MoveExtends()) {
3196 // Calculate new caret position and call SetSelection(), which will ensure whole lines are selected.
3198 PositionUpOrDown(caretToUse, direction, -1), direction);
3199 SetSelection(posNew, sel.Range(sel.Main()).anchor);
3200 } else {
3204 }
3206 for (size_t r = 0; r < sel.Count(); r++) {
3207 const int lastX = (r == sel.Main()) ? lastXChosen : -1;
3208 const SelectionPosition spCaretNow = sel.Range(r).caret;
3210 PositionUpOrDown(spCaretNow, direction, lastX), direction);
3211 sel.Range(r) = selt == Selection::selStream ?
3212 SelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);
3213 }
3215 MovedCaret(sel.RangeMain().caret, caretToUse, true, caretPolicies);
3216 }
3217}
3218
3220 Sci::Line lineDoc;
3221 const Sci::Position savedPos = sel.MainCaret();
3222 do {
3224 lineDoc = pdoc->SciLineFromPosition(sel.MainCaret());
3225 if (direction > 0) {
3226 if (sel.MainCaret() >= pdoc->Length() && !pcs->GetVisible(lineDoc)) {
3227 if (selt == Selection::noSel) {
3229 }
3230 break;
3231 }
3232 }
3233 } while (!pcs->GetVisible(lineDoc));
3234}
3235
3238 AutoSurface surface(this);
3239 return view.RangeDisplayLine(surface, *this, lineVisible, vs);
3240}
3241
3244 AutoSurface surface(this);
3245 const Sci::Position posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3246 if (posRet == INVALID_POSITION) {
3247 return pos;
3248 } else {
3249 return posRet;
3250 }
3251}
3252
3253namespace {
3254
3255constexpr short HighShortFromWParam(uptr_t x) {
3256 return static_cast<short>(x >> 16);
3257}
3258
3259constexpr short LowShortFromWParam(uptr_t x) {
3260 return static_cast<short>(x & 0xffff);
3261}
3262
3263unsigned int WithExtends(unsigned int iMessage) noexcept {
3264 switch (iMessage) {
3265 case SCI_CHARLEFT: return SCI_CHARLEFTEXTEND;
3267
3268 case SCI_WORDLEFT: return SCI_WORDLEFTEXTEND;
3274
3275 case SCI_HOME: return SCI_HOMEEXTEND;
3277 case SCI_HOMEWRAP: return SCI_HOMEWRAPEXTEND;
3278 case SCI_VCHOME: return SCI_VCHOMEEXTEND;
3281
3282 case SCI_LINEEND: return SCI_LINEENDEXTEND;
3285
3286 default: return iMessage;
3287 }
3288}
3289
3290int NaturalDirection(unsigned int iMessage) noexcept {
3291 switch (iMessage) {
3292 case SCI_CHARLEFT:
3293 case SCI_CHARLEFTEXTEND:
3295 case SCI_WORDLEFT:
3296 case SCI_WORDLEFTEXTEND:
3297 case SCI_WORDLEFTEND:
3299 case SCI_WORDPARTLEFT:
3301 case SCI_HOME:
3302 case SCI_HOMEEXTEND:
3303 case SCI_HOMEDISPLAY:
3305 case SCI_HOMEWRAP:
3306 case SCI_HOMEWRAPEXTEND:
3307 // VC_HOME* mostly goes back
3308 case SCI_VCHOME:
3309 case SCI_VCHOMEEXTEND:
3310 case SCI_VCHOMEDISPLAY:
3312 case SCI_VCHOMEWRAP:
3314 return -1;
3315
3316 default:
3317 return 1;
3318 }
3319}
3320
3321bool IsRectExtend(unsigned int iMessage, bool isRectMoveExtends) noexcept {
3322 switch (iMessage) {
3325 case SCI_HOMERECTEXTEND:
3328 return true;
3329 default:
3330 if (isRectMoveExtends) {
3331 // Handle SCI_SETSELECTIONMODE(SC_SEL_RECTANGLE) and subsequent movements.
3332 switch (iMessage) {
3333 case SCI_CHARLEFTEXTEND:
3335 case SCI_HOMEEXTEND:
3336 case SCI_VCHOMEEXTEND:
3337 case SCI_LINEENDEXTEND:
3338 return true;
3339 default:
3340 return false;
3341 }
3342 }
3343 return false;
3344 }
3345}
3346
3347}
3348
3350 const Sci::Position homePos = pdoc->VCHomePosition(position);
3351 const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3352 if (viewLineStart > homePos)
3353 return viewLineStart;
3354 else
3355 return homePos;
3356}
3357
3359 const Sci::Position homePos = pdoc->VCHomePosition(position);
3360 const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3361 if ((viewLineStart < position) && (viewLineStart > homePos))
3362 return viewLineStart;
3363 else
3364 return homePos;
3365}
3366
3368 const Sci::Position endPos = StartEndDisplayLine(position, false);
3369 const Sci::Position realEndPos = pdoc->LineEndPosition(position);
3370 if (endPos > realEndPos // if moved past visible EOLs
3371 || position >= endPos) // if at end of display line already
3372 return realEndPos;
3373 else
3374 return endPos;
3375}
3376
3377int Editor::HorizontalMove(unsigned int iMessage) {
3379 return 0; // horizontal moves with line selection have no effect
3380 }
3381 if (sel.MoveExtends()) {
3382 iMessage = WithExtends(iMessage);
3383 }
3384
3385 if (!multipleSelection && !sel.IsRectangular()) {
3386 // Simplify selection down to 1
3388 }
3389
3390 // Invalidate each of the current selections
3392
3393 if (IsRectExtend(iMessage, sel.IsRectangular() && sel.MoveExtends())) {
3394 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3395 if (!sel.IsRectangular()) {
3397 }
3398 // Will change to rectangular if not currently rectangular
3399 SelectionPosition spCaret = rangeBase.caret;
3400 switch (iMessage) {
3402 case SCI_CHARLEFTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3403 if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
3404 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3405 } else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3406 spCaret = SelectionPosition(spCaret.Position() - 1);
3407 }
3408 break;
3410 case SCI_CHARRIGHTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3412 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3413 } else {
3414 spCaret = SelectionPosition(spCaret.Position() + 1);
3415 }
3416 break;
3417 case SCI_HOMERECTEXTEND:
3418 case SCI_HOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3419 spCaret = SelectionPosition(
3421 break;
3423 case SCI_VCHOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3424 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3425 break;
3427 case SCI_LINEENDEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3428 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3429 break;
3430 }
3431 const int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;
3432 spCaret = MovePositionSoVisible(spCaret, directionMove);
3434 sel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);
3436 } else if (sel.IsRectangular()) {
3437 // Not a rectangular extension so switch to stream.
3438 SelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;
3439 switch (iMessage) {
3440 case SCI_HOME:
3441 selAtLimit = SelectionPosition(
3442 pdoc->LineStart(pdoc->LineFromPosition(selAtLimit.Position())));
3443 break;
3444 case SCI_VCHOME:
3445 selAtLimit = SelectionPosition(pdoc->VCHomePosition(selAtLimit.Position()));
3446 break;
3447 case SCI_LINEEND:
3448 selAtLimit = SelectionPosition(pdoc->LineEndPosition(selAtLimit.Position()));
3449 break;
3450 }
3452 sel.SetSelection(SelectionRange(selAtLimit));
3453 } else {
3457 }
3458 for (size_t r = 0; r < sel.Count(); r++) {
3459 const SelectionPosition spCaretNow = sel.Range(r).caret;
3460 SelectionPosition spCaret = spCaretNow;
3461 switch (iMessage) {
3462 case SCI_CHARLEFT:
3463 case SCI_CHARLEFTEXTEND:
3464 if (spCaret.VirtualSpace()) {
3465 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3466 } else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3467 spCaret = SelectionPosition(spCaret.Position() - 1);
3468 }
3469 break;
3470 case SCI_CHARRIGHT:
3473 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3474 } else {
3475 spCaret = SelectionPosition(spCaret.Position() + 1);
3476 }
3477 break;
3478 case SCI_WORDLEFT:
3479 case SCI_WORDLEFTEXTEND:
3480 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3481 break;
3482 case SCI_WORDRIGHT:
3484 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3485 break;
3486 case SCI_WORDLEFTEND:
3488 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3489 break;
3490 case SCI_WORDRIGHTEND:
3492 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3493 break;
3494 case SCI_WORDPARTLEFT:
3496 spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3497 break;
3498 case SCI_WORDPARTRIGHT:
3500 spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3501 break;
3502 case SCI_HOME:
3503 case SCI_HOMEEXTEND:
3504 spCaret = SelectionPosition(
3506 break;
3507 case SCI_HOMEDISPLAY:
3509 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3510 break;
3511 case SCI_HOMEWRAP:
3512 case SCI_HOMEWRAPEXTEND:
3513 spCaret = MovePositionSoVisible(StartEndDisplayLine(spCaret.Position(), true), -1);
3514 if (spCaretNow <= spCaret)
3515 spCaret = SelectionPosition(
3517 break;
3518 case SCI_VCHOME:
3519 case SCI_VCHOMEEXTEND:
3520 // VCHome alternates between beginning of line and beginning of text so may move back or forwards
3521 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3522 break;
3523 case SCI_VCHOMEDISPLAY:
3525 spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3526 break;
3527 case SCI_VCHOMEWRAP:
3529 spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3530 break;
3531 case SCI_LINEEND:
3532 case SCI_LINEENDEXTEND:
3533 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3534 break;
3535 case SCI_LINEENDDISPLAY:
3537 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3538 break;
3539 case SCI_LINEENDWRAP:
3541 spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3542 break;
3543
3544 default:
3545 PLATFORM_ASSERT(false);
3546 }
3547
3548 const int directionMove = (spCaret < spCaretNow) ? -1 : 1;
3549 spCaret = MovePositionSoVisible(spCaret, directionMove);
3550
3551 // Handle move versus extend, and special behaviour for non-empty left/right
3552 switch (iMessage) {
3553 case SCI_CHARLEFT:
3554 case SCI_CHARRIGHT:
3555 if (sel.Range(r).Empty()) {
3556 sel.Range(r) = SelectionRange(spCaret);
3557 } else {
3559 (iMessage == SCI_CHARLEFT) ? sel.Range(r).Start() : sel.Range(r).End());
3560 }
3561 break;
3562
3563 case SCI_WORDLEFT:
3564 case SCI_WORDRIGHT:
3565 case SCI_WORDLEFTEND:
3566 case SCI_WORDRIGHTEND:
3567 case SCI_WORDPARTLEFT:
3568 case SCI_WORDPARTRIGHT:
3569 case SCI_HOME:
3570 case SCI_HOMEDISPLAY:
3571 case SCI_HOMEWRAP:
3572 case SCI_VCHOME:
3573 case SCI_VCHOMEDISPLAY:
3574 case SCI_VCHOMEWRAP:
3575 case SCI_LINEEND:
3576 case SCI_LINEENDDISPLAY:
3577 case SCI_LINEENDWRAP:
3578 sel.Range(r) = SelectionRange(spCaret);
3579 break;
3580
3581 case SCI_CHARLEFTEXTEND:
3583 case SCI_WORDLEFTEXTEND:
3589 case SCI_HOMEEXTEND:
3591 case SCI_HOMEWRAPEXTEND:
3592 case SCI_VCHOMEEXTEND:
3595 case SCI_LINEENDEXTEND:
3597 case SCI_LINEENDWRAPEXTEND: {
3598 SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);
3600 sel.Range(r) = rangeNew;
3601 }
3602 break;
3603
3604 default:
3605 PLATFORM_ASSERT(false);
3606 }
3607 }
3608 }
3609
3611
3613
3614 // Invalidate the new state of the selection
3616
3618 // Need the line moving and so forth from MovePositionTo
3619 return 0;
3620}
3621
3622int Editor::DelWordOrLine(unsigned int iMessage) {
3623 // Virtual space may be realised for SCI_DELWORDRIGHT or SCI_DELWORDRIGHTEND
3624 // which means 2 actions so wrap in an undo group.
3625
3626 // Rightwards and leftwards deletions differ in treatment of virtual space.
3627 // Clear virtual space for leftwards, realise for rightwards.
3628 const bool leftwards = (iMessage == SCI_DELWORDLEFT) || (iMessage == SCI_DELLINELEFT);
3629
3633 }
3634
3635 UndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);
3636
3637 for (size_t r = 0; r < sel.Count(); r++) {
3638 if (leftwards) {
3639 // Delete to the left so first clear the virtual space.
3641 } else {
3642 // Delete to the right so first realise the virtual space.
3645 }
3646
3647 Range rangeDelete;
3648 switch (iMessage) {
3649 case SCI_DELWORDLEFT:
3650 rangeDelete = Range(
3652 sel.Range(r).caret.Position());
3653 break;
3654 case SCI_DELWORDRIGHT:
3655 rangeDelete = Range(
3656 sel.Range(r).caret.Position(),
3658 break;
3660 rangeDelete = Range(
3661 sel.Range(r).caret.Position(),
3663 break;
3664 case SCI_DELLINELEFT:
3665 rangeDelete = Range(
3667 sel.Range(r).caret.Position());
3668 break;
3669 case SCI_DELLINERIGHT:
3670 rangeDelete = Range(
3671 sel.Range(r).caret.Position(),
3673 break;
3674 }
3675 if (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {
3676 pdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);
3677 }
3678 }
3679
3680 // May need something stronger here: can selections overlap at this point?
3682
3684
3685 // Invalidate the new state of the selection
3687
3689 return 0;
3690}
3691
3692int Editor::KeyCommand(unsigned int iMessage) {
3693 switch (iMessage) {
3694 case SCI_LINEDOWN:
3696 break;
3697 case SCI_LINEDOWNEXTEND:
3699 break;
3702 break;
3703 case SCI_PARADOWN:
3705 break;
3706 case SCI_PARADOWNEXTEND:
3708 break;
3709 case SCI_LINESCROLLDOWN:
3710 ScrollTo(topLine + 1);
3711 MoveCaretInsideView(false);
3712 break;
3713 case SCI_LINEUP:
3715 break;
3716 case SCI_LINEUPEXTEND:
3718 break;
3721 break;
3722 case SCI_PARAUP:
3724 break;
3725 case SCI_PARAUPEXTEND:
3727 break;
3728 case SCI_LINESCROLLUP:
3729 ScrollTo(topLine - 1);
3730 MoveCaretInsideView(false);
3731 break;
3732
3733 case SCI_CHARLEFT:
3734 case SCI_CHARLEFTEXTEND:
3736 case SCI_CHARRIGHT:
3739 case SCI_WORDLEFT:
3740 case SCI_WORDLEFTEXTEND:
3741 case SCI_WORDRIGHT:
3743 case SCI_WORDLEFTEND:
3745 case SCI_WORDRIGHTEND:
3747 case SCI_WORDPARTLEFT:
3749 case SCI_WORDPARTRIGHT:
3751 case SCI_HOME:
3752 case SCI_HOMEEXTEND:
3753 case SCI_HOMERECTEXTEND:
3754 case SCI_HOMEDISPLAY:
3756 case SCI_HOMEWRAP:
3757 case