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 <string_view>
18 #include <vector>
19 #include <map>
20 #include <forward_list>
21 #include <algorithm>
22 #include <iterator>
23 #include <memory>
24 #include <chrono>
25
26 #include "Platform.h"
27
28 #include "ILoader.h"
29 #include "ILexer.h"
30 #include "Scintilla.h"
31
32 #include "CharacterSet.h"
33 #include "CharacterCategory.h"
34 #include "Position.h"
35 #include "UniqueString.h"
36 #include "SplitVector.h"
37 #include "Partitioning.h"
38 #include "RunStyles.h"
39 #include "ContractionState.h"
40 #include "CellBuffer.h"
41 #include "PerLine.h"
42 #include "KeyMap.h"
43 #include "Indicator.h"
44 #include "LineMarker.h"
45 #include "Style.h"
46 #include "ViewStyle.h"
47 #include "CharClassify.h"
48 #include "Decoration.h"
49 #include "CaseFolder.h"
50 #include "Document.h"
51 #include "UniConversion.h"
52 #include "Selection.h"
53 #include "PositionCache.h"
54 #include "EditModel.h"
55 #include "MarginView.h"
56 #include "EditView.h"
57 #include "Editor.h"
58 #include "ElapsedPeriod.h"
59
60 using namespace Scintilla;
61
62 namespace {
63
64 /*
65 return whether this modification represents an operation that
66 may reasonably be deferred (not done now OR [possibly] at all)
67 */
CanDeferToLastStep(const DocModification & mh)68 constexpr bool CanDeferToLastStep(const DocModification &mh) noexcept {
69 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
70 return true; // CAN skip
71 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
72 return false; // MUST do
73 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
74 return true; // CAN skip
75 return false; // PRESUMABLY must do
76 }
77
CanEliminate(const DocModification & mh)78 constexpr bool CanEliminate(const DocModification &mh) noexcept {
79 return
80 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
81 }
82
83 /*
84 return whether this modification represents the FINAL step
85 in a [possibly lengthy] multi-step Undo/Redo sequence
86 */
IsLastStep(const DocModification & mh)87 constexpr bool IsLastStep(const DocModification &mh) noexcept {
88 return
89 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
90 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
91 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
92 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
93 }
94
95 }
96
Timer()97 Timer::Timer() noexcept :
98 ticking(false), ticksToWait(0), tickerID{} {}
99
Idler()100 Idler::Idler() noexcept :
101 state(false), idlerID(0) {}
102
IsAllSpacesOrTabs(std::string_view sv)103 static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {
104 for (const char ch : sv) {
105 // This is safe because IsSpaceOrTab() will return false for null terminators
106 if (!IsSpaceOrTab(ch))
107 return false;
108 }
109 return true;
110 }
111
Editor()112 Editor::Editor() : durationWrapOneLine(0.00001, 0.000001, 0.0001) {
113 ctrlID = 0;
114
115 stylesValid = false;
116 technology = SC_TECHNOLOGY_DEFAULT;
117 scaleRGBAImage = 100.0f;
118
119 cursorMode = SC_CURSORNORMAL;
120
121 hasFocus = false;
122 errorStatus = 0;
123 mouseDownCaptures = true;
124 mouseWheelCaptures = true;
125
126 lastClickTime = 0;
127 doubleClickCloseThreshold = Point(3, 3);
128 dwellDelay = SC_TIME_FOREVER;
129 ticksToDwell = SC_TIME_FOREVER;
130 dwelling = false;
131 ptMouseLast.x = 0;
132 ptMouseLast.y = 0;
133 inDragDrop = ddNone;
134 dropWentOutside = false;
135 posDrop = SelectionPosition(Sci::invalidPosition);
136 hotSpotClickPos = INVALID_POSITION;
137 selectionType = selChar;
138
139 lastXChosen = 0;
140 lineAnchorPos = 0;
141 originalAnchorPos = 0;
142 wordSelectAnchorStartPos = 0;
143 wordSelectAnchorEndPos = 0;
144 wordSelectInitialCaretPos = -1;
145
146 caretPolicies.x = { CARET_SLOP | CARET_EVEN, 50 };
147 caretPolicies.y = { CARET_EVEN, 0 };
148
149 visiblePolicy = { 0, 0 };
150
151 searchAnchor = 0;
152
153 xCaretMargin = 50;
154 horizontalScrollBarVisible = true;
155 scrollWidth = 2000;
156 verticalScrollBarVisible = true;
157 endAtLastLine = true;
158 caretSticky = SC_CARETSTICKY_OFF;
159 marginOptions = SC_MARGINOPTION_NONE;
160 mouseSelectionRectangularSwitch = false;
161 multipleSelection = false;
162 additionalSelectionTyping = false;
163 multiPasteMode = SC_MULTIPASTE_ONCE;
164 virtualSpaceOptions = SCVS_NONE;
165
166 targetRange = SelectionSegment();
167 searchFlags = 0;
168
169 topLine = 0;
170 posTopLine = 0;
171
172 lengthForEncode = -1;
173
174 needUpdateUI = 0;
175 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
176
177 paintState = notPainting;
178 paintAbandonedByStyling = false;
179 paintingAllText = false;
180 willRedrawAll = false;
181 idleStyling = SC_IDLESTYLING_NONE;
182 needIdleStyling = false;
183
184 modEventMask = SC_MODEVENTMASKALL;
185 commandEvents = true;
186
187 pdoc->AddWatcher(this, 0);
188
189 recordingMacro = false;
190 foldAutomatic = 0;
191
192 convertPastes = true;
193
194 SetRepresentations();
195 }
196
~Editor()197 Editor::~Editor() {
198 pdoc->RemoveWatcher(this, 0);
199 DropGraphics(true);
200 }
201
Finalise()202 void Editor::Finalise() {
203 SetIdle(false);
204 CancelModes();
205 }
206
SetRepresentations()207 void Editor::SetRepresentations() {
208 reprs.Clear();
209
210 // C0 control set
211 const char *const reps[] = {
212 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
213 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
214 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
215 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
216 };
217 for (size_t j=0; j < std::size(reps); j++) {
218 const char c[2] = { static_cast<char>(j), 0 };
219 reprs.SetRepresentation(c, reps[j]);
220 }
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 < std::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);
251 if (pdoc->IsDBCSLeadByteNoExcept(ch) || pdoc->IsDBCSLeadByteInvalid(ch)) {
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
DropGraphics(bool freeObjects)261 void Editor::DropGraphics(bool freeObjects) {
262 marginView.DropGraphics(freeObjects);
263 view.DropGraphics(freeObjects);
264 }
265
AllocateGraphics()266 void Editor::AllocateGraphics() {
267 marginView.AllocateGraphics(vs);
268 view.AllocateGraphics(vs);
269 }
270
InvalidateStyleData()271 void Editor::InvalidateStyleData() {
272 stylesValid = false;
273 vs.technology = technology;
274 DropGraphics(false);
275 AllocateGraphics();
276 view.llc.Invalidate(LineLayout::llInvalid);
277 view.posCache.Clear();
278 }
279
InvalidateStyleRedraw()280 void Editor::InvalidateStyleRedraw() {
281 NeedWrapping();
282 InvalidateStyleData();
283 Redraw();
284 }
285
RefreshStyleData()286 void Editor::RefreshStyleData() {
287 if (!stylesValid) {
288 stylesValid = true;
289 AutoSurface surface(this);
290 if (surface) {
291 vs.Refresh(*surface, pdoc->tabInChars);
292 }
293 SetScrollBars();
294 SetRectangularRange();
295 }
296 }
297
GetVisibleOriginInMain() const298 Point Editor::GetVisibleOriginInMain() const {
299 return Point(0, 0);
300 }
301
DocumentPointFromView(Point ptView) const302 PointDocument Editor::DocumentPointFromView(Point ptView) const {
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
TopLineOfMain() const315 Sci::Line Editor::TopLineOfMain() const {
316 if (wMargin.GetID())
317 return 0;
318 else
319 return topLine;
320 }
321
GetClientRectangle() const322 PRectangle Editor::GetClientRectangle() const {
323 return wMain.GetClientPosition();
324 }
325
GetClientDrawingRectangle()326 PRectangle Editor::GetClientDrawingRectangle() {
327 return GetClientRectangle();
328 }
329
GetTextRectangle() const330 PRectangle Editor::GetTextRectangle() const {
331 PRectangle rc = GetClientRectangle();
332 rc.left += vs.textStart;
333 rc.right -= vs.rightMarginWidth;
334 return rc;
335 }
336
LinesOnScreen() const337 Sci::Line Editor::LinesOnScreen() const {
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
LinesToScroll() const344 Sci::Line Editor::LinesToScroll() const {
345 const Sci::Line retVal = LinesOnScreen() - 1;
346 if (retVal < 1)
347 return 1;
348 else
349 return retVal;
350 }
351
MaxScrollPos() const352 Sci::Line Editor::MaxScrollPos() const {
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
ClampPositionIntoDocument(SelectionPosition sp) const368 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
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
LocationFromPosition(SelectionPosition pos,PointEnd pe)381 Point Editor::LocationFromPosition(SelectionPosition pos, PointEnd pe) {
382 const PRectangle rcClient = GetTextRectangle();
383 RefreshStyleData();
384 AutoSurface surface(this);
385 return view.LocationFromPosition(surface, *this, pos, topLine, vs, pe, rcClient);
386 }
387
LocationFromPosition(Sci::Position pos,PointEnd pe)388 Point Editor::LocationFromPosition(Sci::Position pos, PointEnd pe) {
389 return LocationFromPosition(SelectionPosition(pos), pe);
390 }
391
XFromPosition(SelectionPosition sp)392 int Editor::XFromPosition(SelectionPosition sp) {
393 const Point pt = LocationFromPosition(sp);
394 return static_cast<int>(pt.x) - vs.textStart + xOffset;
395 }
396
SPositionFromLocation(Point pt,bool canReturnInvalid,bool charPosition,bool virtualSpace)397 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
398 RefreshStyleData();
399 AutoSurface surface(this);
400
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
406 if (canReturnInvalid) {
407 if (!rcClient.Contains(pt))
408 return SelectionPosition(INVALID_POSITION);
409 if (pt.x < vs.textStart)
410 return SelectionPosition(INVALID_POSITION);
411 if (pt.y < 0)
412 return SelectionPosition(INVALID_POSITION);
413 }
414 const PointDocument ptdoc = DocumentPointFromView(pt);
415 return view.SPositionFromLocation(surface, *this, ptdoc, canReturnInvalid,
416 charPosition, virtualSpace, vs, rcClient);
417 }
418
PositionFromLocation(Point pt,bool canReturnInvalid,bool charPosition)419 Sci::Position Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
420 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
421 }
422
423 /**
424 * Find the document position corresponding to an x coordinate on a particular document line.
425 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
426 * This method is used for rectangular selections and does not work on wrapped lines.
427 */
SPositionFromLineX(Sci::Line lineDoc,int x)428 SelectionPosition Editor::SPositionFromLineX(Sci::Line lineDoc, int x) {
429 RefreshStyleData();
430 if (lineDoc >= pdoc->LinesTotal())
431 return SelectionPosition(pdoc->Length());
432 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
433 AutoSurface surface(this);
434 return view.SPositionFromLineX(surface, *this, lineDoc, x, vs);
435 }
436
PositionFromLineX(Sci::Line lineDoc,int x)437 Sci::Position Editor::PositionFromLineX(Sci::Line lineDoc, int x) {
438 return SPositionFromLineX(lineDoc, x).Position();
439 }
440
LineFromLocation(Point pt) const441 Sci::Line Editor::LineFromLocation(Point pt) const {
442 return pcs->DocFromDisplay(static_cast<int>(pt.y) / vs.lineHeight + topLine);
443 }
444
SetTopLine(Sci::Line topLineNew)445 void Editor::SetTopLine(Sci::Line topLineNew) {
446 if ((topLine != topLineNew) && (topLineNew >= 0)) {
447 topLine = topLineNew;
448 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
449 }
450 posTopLine = pdoc->LineStart(pcs->DocFromDisplay(topLine));
451 }
452
453 /**
454 * If painting then abandon the painting because a wider redraw is needed.
455 * @return true if calling code should stop drawing.
456 */
AbandonPaint()457 bool Editor::AbandonPaint() {
458 if ((paintState == painting) && !paintingAllText) {
459 paintState = paintAbandoned;
460 }
461 return paintState == paintAbandoned;
462 }
463
RedrawRect(PRectangle rc)464 void Editor::RedrawRect(PRectangle rc) {
465 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
466
467 // Clip the redraw rectangle into the client area
468 const PRectangle rcClient = GetClientRectangle();
469 if (rc.top < rcClient.top)
470 rc.top = rcClient.top;
471 if (rc.bottom > rcClient.bottom)
472 rc.bottom = rcClient.bottom;
473 if (rc.left < rcClient.left)
474 rc.left = rcClient.left;
475 if (rc.right > rcClient.right)
476 rc.right = rcClient.right;
477
478 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
479 wMain.InvalidateRectangle(rc);
480 }
481 }
482
DiscardOverdraw()483 void Editor::DiscardOverdraw() {
484 // Overridden on platforms that may draw outside visible area.
485 }
486
Redraw()487 void Editor::Redraw() {
488 //Platform::DebugPrintf("Redraw all\n");
489 const PRectangle rcClient = GetClientRectangle();
490 wMain.InvalidateRectangle(rcClient);
491 if (wMargin.GetID())
492 wMargin.InvalidateAll();
493 //wMain.InvalidateAll();
494 }
495
RedrawSelMargin(Sci::Line line,bool allAfter)496 void Editor::RedrawSelMargin(Sci::Line line, bool allAfter) {
497 const bool markersInText = vs.maskInLine || vs.maskDrawInText;
498 if (!wMargin.GetID() || markersInText) { // May affect text area so may need to abandon and retry
499 if (AbandonPaint()) {
500 return;
501 }
502 }
503 if (wMargin.GetID() && markersInText) {
504 Redraw();
505 return;
506 }
507 PRectangle rcMarkers = GetClientRectangle();
508 if (!markersInText) {
509 // Normal case: just draw the margin
510 rcMarkers.right = rcMarkers.left + vs.fixedColumnWidth;
511 }
512 if (line != -1) {
513 PRectangle rcLine = RectangleFromRange(Range(pdoc->LineStart(line)), 0);
514
515 // Inflate line rectangle if there are image markers with height larger than line height
516 if (vs.largestMarkerHeight > vs.lineHeight) {
517 const int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
518 rcLine.top -= delta;
519 rcLine.bottom += delta;
520 if (rcLine.top < rcMarkers.top)
521 rcLine.top = rcMarkers.top;
522 if (rcLine.bottom > rcMarkers.bottom)
523 rcLine.bottom = rcMarkers.bottom;
524 }
525
526 rcMarkers.top = rcLine.top;
527 if (!allAfter)
528 rcMarkers.bottom = rcLine.bottom;
529 if (rcMarkers.Empty())
530 return;
531 }
532 if (wMargin.GetID()) {
533 const Point ptOrigin = GetVisibleOriginInMain();
534 rcMarkers.Move(-ptOrigin.x, -ptOrigin.y);
535 wMargin.InvalidateRectangle(rcMarkers);
536 } else {
537 wMain.InvalidateRectangle(rcMarkers);
538 }
539 }
540
RectangleFromRange(Range r,int overlap)541 PRectangle Editor::RectangleFromRange(Range r, int overlap) {
542 const Sci::Line minLine = pcs->DisplayFromDoc(
543 pdoc->SciLineFromPosition(r.First()));
544 const Sci::Line maxLine = pcs->DisplayLastFromDoc(
545 pdoc->SciLineFromPosition(r.Last()));
546 const PRectangle rcClientDrawing = GetClientDrawingRectangle();
547 PRectangle rc;
548 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
549 rc.left = static_cast<XYPOSITION>(vs.textStart - leftTextOverlap);
550 rc.top = static_cast<XYPOSITION>((minLine - TopLineOfMain()) * vs.lineHeight - overlap);
551 if (rc.top < rcClientDrawing.top)
552 rc.top = rcClientDrawing.top;
553 // Extend to right of prepared area if any to prevent artifacts from caret line highlight
554 rc.right = rcClientDrawing.right;
555 rc.bottom = static_cast<XYPOSITION>((maxLine - TopLineOfMain() + 1) * vs.lineHeight + overlap);
556
557 return rc;
558 }
559
InvalidateRange(Sci::Position start,Sci::Position end)560 void Editor::InvalidateRange(Sci::Position start, Sci::Position end) {
561 RedrawRect(RectangleFromRange(Range(start, end), view.LinesOverlap() ? vs.lineOverlap : 0));
562 }
563
CurrentPosition() const564 Sci::Position Editor::CurrentPosition() const {
565 return sel.MainCaret();
566 }
567
SelectionEmpty() const568 bool Editor::SelectionEmpty() const noexcept {
569 return sel.Empty();
570 }
571
SelectionStart()572 SelectionPosition Editor::SelectionStart() {
573 return sel.RangeMain().Start();
574 }
575
SelectionEnd()576 SelectionPosition Editor::SelectionEnd() {
577 return sel.RangeMain().End();
578 }
579
SetRectangularRange()580 void Editor::SetRectangularRange() {
581 if (sel.IsRectangular()) {
582 const int xAnchor = XFromPosition(sel.Rectangular().anchor);
583 int xCaret = XFromPosition(sel.Rectangular().caret);
584 if (sel.selType == Selection::selThin) {
585 xCaret = xAnchor;
586 }
587 const Sci::Line lineAnchorRect =
588 pdoc->SciLineFromPosition(sel.Rectangular().anchor.Position());
589 const Sci::Line lineCaret =
590 pdoc->SciLineFromPosition(sel.Rectangular().caret.Position());
591 const int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
592 AutoSurface surface(this);
593 for (Sci::Line line=lineAnchorRect; line != lineCaret+increment; line += increment) {
594 SelectionRange range(
595 view.SPositionFromLineX(surface, *this, line, xCaret, vs),
596 view.SPositionFromLineX(surface, *this, line, xAnchor, vs));
597 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
598 range.ClearVirtualSpace();
599 if (line == lineAnchorRect)
600 sel.SetSelection(range);
601 else
602 sel.AddSelectionWithoutTrim(range);
603 }
604 }
605 }
606
ThinRectangularRange()607 void Editor::ThinRectangularRange() {
608 if (sel.IsRectangular()) {
609 sel.selType = Selection::selThin;
610 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
611 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
612 } else {
613 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
614 }
615 SetRectangularRange();
616 }
617 }
618
InvalidateSelection(SelectionRange newMain,bool invalidateWholeSelection)619 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
620 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
621 invalidateWholeSelection = true;
622 }
623 Sci::Position firstAffected = std::min(sel.RangeMain().Start().Position(), newMain.Start().Position());
624 // +1 for lastAffected ensures caret repainted
625 Sci::Position lastAffected = std::max(newMain.caret.Position()+1, newMain.anchor.Position());
626 lastAffected = std::max(lastAffected, sel.RangeMain().End().Position());
627 if (invalidateWholeSelection) {
628 for (size_t r=0; r<sel.Count(); r++) {
629 firstAffected = std::min(firstAffected, sel.Range(r).caret.Position());
630 firstAffected = std::min(firstAffected, sel.Range(r).anchor.Position());
631 lastAffected = std::max(lastAffected, sel.Range(r).caret.Position()+1);
632 lastAffected = std::max(lastAffected, sel.Range(r).anchor.Position());
633 }
634 }
635 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
636 InvalidateRange(firstAffected, lastAffected);
637 }
638
InvalidateWholeSelection()639 void Editor::InvalidateWholeSelection() {
640 InvalidateSelection(sel.RangeMain(), true);
641 }
642
643 /* For Line selection - the anchor and caret are always
644 at the beginning and end of the region lines. */
LineSelectionRange(SelectionPosition currentPos_,SelectionPosition anchor_) const645 SelectionRange Editor::LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const {
646 if (currentPos_ > anchor_) {
647 anchor_ = SelectionPosition(
648 pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
649 currentPos_ = SelectionPosition(
650 pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
651 } else {
652 currentPos_ = SelectionPosition(
653 pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
654 anchor_ = SelectionPosition(
655 pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
656 }
657 return SelectionRange(currentPos_, anchor_);
658 }
659
SetSelection(SelectionPosition currentPos_,SelectionPosition anchor_)660 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
661 currentPos_ = ClampPositionIntoDocument(currentPos_);
662 anchor_ = ClampPositionIntoDocument(anchor_);
663 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
664 SelectionRange rangeNew(currentPos_, anchor_);
665 if (sel.selType == Selection::selLines) {
666 rangeNew = LineSelectionRange(currentPos_, anchor_);
667 }
668 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
669 InvalidateSelection(rangeNew);
670 }
671 sel.RangeMain() = rangeNew;
672 SetRectangularRange();
673 ClaimSelection();
674 SetHoverIndicatorPosition(sel.MainCaret());
675
676 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
677 RedrawSelMargin();
678 }
679 QueueIdleWork(WorkNeeded::workUpdateUI);
680 }
681
SetSelection(Sci::Position currentPos_,Sci::Position anchor_)682 void Editor::SetSelection(Sci::Position currentPos_, Sci::Position anchor_) {
683 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
684 }
685
686 // Just move the caret on the main selection
SetSelection(SelectionPosition currentPos_)687 void Editor::SetSelection(SelectionPosition currentPos_) {
688 currentPos_ = ClampPositionIntoDocument(currentPos_);
689 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
690 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
691 InvalidateSelection(SelectionRange(currentPos_));
692 }
693 if (sel.IsRectangular()) {
694 sel.Rectangular() =
695 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
696 SetRectangularRange();
697 } else if (sel.selType == Selection::selLines) {
698 sel.RangeMain() = LineSelectionRange(currentPos_, sel.RangeMain().anchor);
699 } else {
700 sel.RangeMain() =
701 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
702 }
703 ClaimSelection();
704 SetHoverIndicatorPosition(sel.MainCaret());
705
706 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
707 RedrawSelMargin();
708 }
709 QueueIdleWork(WorkNeeded::workUpdateUI);
710 }
711
SetSelection(int currentPos_)712 void Editor::SetSelection(int currentPos_) {
713 SetSelection(SelectionPosition(currentPos_));
714 }
715
SetEmptySelection(SelectionPosition currentPos_)716 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
717 const Sci::Line currentLine = pdoc->SciLineFromPosition(currentPos_.Position());
718 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
719 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
720 InvalidateSelection(rangeNew);
721 }
722 sel.Clear();
723 sel.RangeMain() = rangeNew;
724 SetRectangularRange();
725 ClaimSelection();
726 SetHoverIndicatorPosition(sel.MainCaret());
727
728 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
729 RedrawSelMargin();
730 }
731 QueueIdleWork(WorkNeeded::workUpdateUI);
732 }
733
SetEmptySelection(Sci::Position currentPos_)734 void Editor::SetEmptySelection(Sci::Position currentPos_) {
735 SetEmptySelection(SelectionPosition(currentPos_));
736 }
737
MultipleSelectAdd(AddNumber addNumber)738 void Editor::MultipleSelectAdd(AddNumber addNumber) {
739 if (SelectionEmpty() || !multipleSelection) {
740 // Select word at caret
741 const Sci::Position startWord = pdoc->ExtendWordSelect(sel.MainCaret(), -1, true);
742 const Sci::Position endWord = pdoc->ExtendWordSelect(startWord, 1, true);
743 TrimAndSetSelection(endWord, startWord);
744
745 } else {
746
747 if (!pdoc->HasCaseFolder())
748 pdoc->SetCaseFolder(CaseFolderForEncoding());
749
750 const Range rangeMainSelection(sel.RangeMain().Start().Position(), sel.RangeMain().End().Position());
751 const std::string selectedText = RangeText(rangeMainSelection.start, rangeMainSelection.end);
752
753 const Range rangeTarget(targetRange.start.Position(), targetRange.end.Position());
754 std::vector<Range> searchRanges;
755 // Search should be over the target range excluding the current selection so
756 // may need to search 2 ranges, after the selection then before the selection.
757 if (rangeTarget.Overlaps(rangeMainSelection)) {
758 // Common case is that the selection is completely within the target but
759 // may also have overlap at start or end.
760 if (rangeMainSelection.end < rangeTarget.end)
761 searchRanges.push_back(Range(rangeMainSelection.end, rangeTarget.end));
762 if (rangeTarget.start < rangeMainSelection.start)
763 searchRanges.push_back(Range(rangeTarget.start, rangeMainSelection.start));
764 } else {
765 // No overlap
766 searchRanges.push_back(rangeTarget);
767 }
768
769 for (std::vector<Range>::const_iterator it = searchRanges.begin(); it != searchRanges.end(); ++it) {
770 Sci::Position searchStart = it->start;
771 const Sci::Position searchEnd = it->end;
772 for (;;) {
773 Sci::Position lengthFound = selectedText.length();
774 const Sci::Position pos = pdoc->FindText(searchStart, searchEnd,
775 selectedText.c_str(), searchFlags, &lengthFound);
776 if (pos >= 0) {
777 sel.AddSelection(SelectionRange(pos + lengthFound, pos));
778 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
779 ScrollRange(sel.RangeMain());
780 Redraw();
781 if (addNumber == addOne)
782 return;
783 searchStart = pos + lengthFound;
784 } else {
785 break;
786 }
787 }
788 }
789 }
790 }
791
RangeContainsProtected(Sci::Position start,Sci::Position end) const792 bool Editor::RangeContainsProtected(Sci::Position start, Sci::Position end) const noexcept {
793 if (vs.ProtectionActive()) {
794 if (start > end) {
795 const Sci::Position t = start;
796 start = end;
797 end = t;
798 }
799 for (Sci::Position pos = start; pos < end; pos++) {
800 if (vs.styles[pdoc->StyleIndexAt(pos)].IsProtected())
801 return true;
802 }
803 }
804 return false;
805 }
806
SelectionContainsProtected() const807 bool Editor::SelectionContainsProtected() const {
808 for (size_t r=0; r<sel.Count(); r++) {
809 if (RangeContainsProtected(sel.Range(r).Start().Position(),
810 sel.Range(r).End().Position())) {
811 return true;
812 }
813 }
814 return false;
815 }
816
817 /**
818 * Asks document to find a good position and then moves out of any invisible positions.
819 */
MovePositionOutsideChar(Sci::Position pos,Sci::Position moveDir,bool checkLineEnd) const820 Sci::Position Editor::MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd) const {
821 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
822 }
823
MovePositionOutsideChar(SelectionPosition pos,Sci::Position moveDir,bool checkLineEnd) const824 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, Sci::Position moveDir, bool checkLineEnd) const {
825 const Sci::Position posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
826 if (posMoved != pos.Position())
827 pos.SetPosition(posMoved);
828 if (vs.ProtectionActive()) {
829 if (moveDir > 0) {
830 if ((pos.Position() > 0) && vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()) {
831 while ((pos.Position() < pdoc->Length()) &&
832 (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()))
833 pos.Add(1);
834 }
835 } else if (moveDir < 0) {
836 if (vs.styles[pdoc->StyleIndexAt(pos.Position())].IsProtected()) {
837 while ((pos.Position() > 0) &&
838 (vs.styles[pdoc->StyleIndexAt(pos.Position() - 1)].IsProtected()))
839 pos.Add(-1);
840 }
841 }
842 }
843 return pos;
844 }
845
MovedCaret(SelectionPosition newPos,SelectionPosition previousPos,bool ensureVisible,CaretPolicies policies)846 void Editor::MovedCaret(SelectionPosition newPos, SelectionPosition previousPos,
847 bool ensureVisible, CaretPolicies policies) {
848 const Sci::Line currentLine = pdoc->SciLineFromPosition(newPos.Position());
849 if (ensureVisible) {
850 // In case in need of wrapping to ensure DisplayFromDoc works.
851 if (currentLine >= wrapPending.start) {
852 if (WrapLines(WrapScope::wsAll)) {
853 Redraw();
854 }
855 }
856 const XYScrollPosition newXY = XYScrollToMakeVisible(
857 SelectionRange(posDrag.IsValid() ? posDrag : newPos), xysDefault, policies);
858 if (previousPos.IsValid() && (newXY.xOffset == xOffset)) {
859 // simple vertical scroll then invalidate
860 ScrollTo(newXY.topLine);
861 InvalidateSelection(SelectionRange(previousPos), true);
862 } else {
863 SetXYScroll(newXY);
864 }
865 }
866
867 ShowCaretAtCurrentPosition();
868 NotifyCaretMove();
869
870 ClaimSelection();
871 SetHoverIndicatorPosition(sel.MainCaret());
872 QueueIdleWork(WorkNeeded::workUpdateUI);
873
874 if (marginView.highlightDelimiter.NeedsDrawing(currentLine)) {
875 RedrawSelMargin();
876 }
877 }
878
MovePositionTo(SelectionPosition newPos,Selection::selTypes selt,bool ensureVisible)879 void Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
880 const SelectionPosition spCaret = ((sel.Count() == 1) && sel.Empty()) ?
881 sel.Last() : SelectionPosition(INVALID_POSITION);
882
883 const Sci::Position delta = newPos.Position() - sel.MainCaret();
884 newPos = ClampPositionIntoDocument(newPos);
885 newPos = MovePositionOutsideChar(newPos, delta);
886 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
887 // Can't turn into multiple selection so clear additional selections
888 InvalidateSelection(SelectionRange(newPos), true);
889 sel.DropAdditionalRanges();
890 }
891 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
892 // Switching to rectangular
893 InvalidateSelection(sel.RangeMain(), false);
894 SelectionRange rangeMain = sel.RangeMain();
895 sel.Clear();
896 sel.Rectangular() = rangeMain;
897 }
898 if (selt != Selection::noSel) {
899 sel.selType = selt;
900 }
901 if (selt != Selection::noSel || sel.MoveExtends()) {
902 SetSelection(newPos);
903 } else {
904 SetEmptySelection(newPos);
905 }
906
907 MovedCaret(newPos, spCaret, ensureVisible, caretPolicies);
908 }
909
MovePositionTo(Sci::Position newPos,Selection::selTypes selt,bool ensureVisible)910 void Editor::MovePositionTo(Sci::Position newPos, Selection::selTypes selt, bool ensureVisible) {
911 MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
912 }
913
MovePositionSoVisible(SelectionPosition pos,int moveDir)914 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
915 pos = ClampPositionIntoDocument(pos);
916 pos = MovePositionOutsideChar(pos, moveDir);
917 const Sci::Line lineDoc = pdoc->SciLineFromPosition(pos.Position());
918 if (pcs->GetVisible(lineDoc)) {
919 return pos;
920 } else {
921 Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
922 if (moveDir > 0) {
923 // lineDisplay is already line before fold as lines in fold use display line of line after fold
924 lineDisplay = std::clamp<Sci::Line>(lineDisplay, 0, pcs->LinesDisplayed());
925 return SelectionPosition(
926 pdoc->LineStart(pcs->DocFromDisplay(lineDisplay)));
927 } else {
928 lineDisplay = std::clamp<Sci::Line>(lineDisplay - 1, 0, pcs->LinesDisplayed());
929 return SelectionPosition(
930 pdoc->LineEnd(pcs->DocFromDisplay(lineDisplay)));
931 }
932 }
933 }
934
MovePositionSoVisible(Sci::Position pos,int moveDir)935 SelectionPosition Editor::MovePositionSoVisible(Sci::Position pos, int moveDir) {
936 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
937 }
938
PointMainCaret()939 Point Editor::PointMainCaret() {
940 return LocationFromPosition(sel.Range(sel.Main()).caret);
941 }
942
943 /**
944 * Choose the x position that the caret will try to stick to
945 * as it moves up and down.
946 */
SetLastXChosen()947 void Editor::SetLastXChosen() {
948 const Point pt = PointMainCaret();
949 lastXChosen = static_cast<int>(pt.x) + xOffset;
950 }
951
ScrollTo(Sci::Line line,bool moveThumb)952 void Editor::ScrollTo(Sci::Line line, bool moveThumb) {
953 const Sci::Line topLineNew = std::clamp<Sci::Line>(line, 0, MaxScrollPos());
954 if (topLineNew != topLine) {
955 // Try to optimise small scrolls
956 #ifndef UNDER_CE
957 const Sci::Line linesToMove = topLine - topLineNew;
958 const bool performBlit = (std::abs(linesToMove) <= 10) && (paintState == notPainting);
959 willRedrawAll = !performBlit;
960 #endif
961 SetTopLine(topLineNew);
962 // Optimize by styling the view as this will invalidate any needed area
963 // which could abort the initial paint if discovered later.
964 StyleAreaBounded(GetClientRectangle(), true);
965 #ifndef UNDER_CE
966 // Perform redraw rather than scroll if many lines would be redrawn anyway.
967 if (performBlit) {
968 ScrollText(linesToMove);
969 } else {
970 Redraw();
971 }
972 willRedrawAll = false;
973 #else
974 Redraw();
975 #endif
976 if (moveThumb) {
977 SetVerticalScrollPos();
978 }
979 }
980 }
981
ScrollText(Sci::Line)982 void Editor::ScrollText(Sci::Line /* linesToMove */) {
983 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
984 Redraw();
985 }
986
HorizontalScrollTo(int xPos)987 void Editor::HorizontalScrollTo(int xPos) {
988 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
989 if (xPos < 0)
990 xPos = 0;
991 if (!Wrapping() && (xOffset != xPos)) {
992 xOffset = xPos;
993 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
994 SetHorizontalScrollPos();
995 RedrawRect(GetClientRectangle());
996 }
997 }
998
VerticalCentreCaret()999 void Editor::VerticalCentreCaret() {
1000 const Sci::Line lineDoc =
1001 pdoc->SciLineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1002 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
1003 const Sci::Line newTop = lineDisplay - (LinesOnScreen() / 2);
1004 if (topLine != newTop) {
1005 SetTopLine(newTop > 0 ? newTop : 0);
1006 RedrawRect(GetClientRectangle());
1007 }
1008 }
1009
MoveSelectedLines(int lineDelta)1010 void Editor::MoveSelectedLines(int lineDelta) {
1011
1012 if (sel.IsRectangular()) {
1013 return;
1014 }
1015
1016 // if selection doesn't start at the beginning of the line, set the new start
1017 Sci::Position selectionStart = SelectionStart().Position();
1018 const Sci::Line startLine = pdoc->SciLineFromPosition(selectionStart);
1019 const Sci::Position beginningOfStartLine = pdoc->LineStart(startLine);
1020 selectionStart = beginningOfStartLine;
1021
1022 // if selection doesn't end at the beginning of a line greater than that of the start,
1023 // then set it at the beginning of the next one
1024 Sci::Position selectionEnd = SelectionEnd().Position();
1025 const Sci::Line endLine = pdoc->SciLineFromPosition(selectionEnd);
1026 const Sci::Position beginningOfEndLine = pdoc->LineStart(endLine);
1027 bool appendEol = false;
1028 if (selectionEnd > beginningOfEndLine
1029 || selectionStart == selectionEnd) {
1030 selectionEnd = pdoc->LineStart(endLine + 1);
1031 appendEol = (selectionEnd == pdoc->Length() && pdoc->SciLineFromPosition(selectionEnd) == endLine);
1032 }
1033
1034 // if there's nowhere for the selection to move
1035 // (i.e. at the beginning going up or at the end going down),
1036 // stop it right there!
1037 if ((selectionStart == 0 && lineDelta < 0)
1038 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1039 || selectionStart == selectionEnd) {
1040 return;
1041 }
1042
1043 UndoGroup ug(pdoc);
1044
1045 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1046 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1047 ClearSelection();
1048 selectionEnd = CurrentPosition();
1049 }
1050 SetSelection(selectionStart, selectionEnd);
1051
1052 SelectionText selectedText;
1053 CopySelectionRange(&selectedText);
1054
1055 const Point currentLocation = LocationFromPosition(CurrentPosition());
1056 const Sci::Line currentLine = LineFromLocation(currentLocation);
1057
1058 if (appendEol)
1059 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1060 ClearSelection();
1061
1062 const char *eol = StringFromEOLMode(pdoc->eolMode);
1063 if (currentLine + lineDelta >= pdoc->LinesTotal())
1064 pdoc->InsertString(pdoc->Length(), eol, strlen(eol));
1065 GoToLine(currentLine + lineDelta);
1066
1067 Sci::Position selectionLength = pdoc->InsertString(CurrentPosition(), selectedText.Data(), selectedText.Length());
1068 if (appendEol) {
1069 const Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition() + selectionLength, eol, strlen(eol));
1070 selectionLength += lengthInserted;
1071 }
1072 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1073 }
1074
MoveSelectedLinesUp()1075 void Editor::MoveSelectedLinesUp() {
1076 MoveSelectedLines(-1);
1077 }
1078
MoveSelectedLinesDown()1079 void Editor::MoveSelectedLinesDown() {
1080 MoveSelectedLines(1);
1081 }
1082
MoveCaretInsideView(bool ensureVisible)1083 void Editor::MoveCaretInsideView(bool ensureVisible) {
1084 const PRectangle rcClient = GetTextRectangle();
1085 const Point pt = PointMainCaret();
1086 if (pt.y < rcClient.top) {
1087 MovePositionTo(SPositionFromLocation(
1088 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top)),
1089 false, false, UserVirtualSpace()),
1090 Selection::noSel, ensureVisible);
1091 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1092 const ptrdiff_t yOfLastLineFullyDisplayed = static_cast<ptrdiff_t>(rcClient.top) + (LinesOnScreen() - 1) * vs.lineHeight;
1093 MovePositionTo(SPositionFromLocation(
1094 Point::FromInts(lastXChosen - xOffset, static_cast<int>(rcClient.top + yOfLastLineFullyDisplayed)),
1095 false, false, UserVirtualSpace()),
1096 Selection::noSel, ensureVisible);
1097 }
1098 }
1099
DisplayFromPosition(Sci::Position pos)1100 Sci::Line Editor::DisplayFromPosition(Sci::Position pos) {
1101 AutoSurface surface(this);
1102 return view.DisplayFromPosition(surface, *this, pos, vs);
1103 }
1104
1105 /**
1106 * Ensure the caret is reasonably visible in context.
1107 *
1108 Caret policy in Scintilla
1109
1110 If slop is set, we can define a slop value.
1111 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1112 This zone is defined as a number of pixels near the vertical margins,
1113 and as a number of lines near the horizontal margins.
1114 By keeping the caret away from the edges, it is seen within its context,
1115 so it is likely that the identifier that the caret is on can be completely seen,
1116 and that the current line is seen with some of the lines following it which are
1117 often dependent on that line.
1118
1119 If strict is set, the policy is enforced... strictly.
1120 The caret is centred on the display if slop is not set,
1121 and cannot go in the UZ if slop is set.
1122
1123 If jumps is set, the display is moved more energetically
1124 so the caret can move in the same direction longer before the policy is applied again.
1125 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1126
1127 If even is not set, instead of having symmetrical UZs,
1128 the left and bottom UZs are extended up to right and top UZs respectively.
1129 This way, we favour the displaying of useful information: the beginning of lines,
1130 where most code reside, and the lines after the caret, eg. the body of a function.
1131
1132 | | | | |
1133 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1134 | | | | | visibility or going into the UZ) display is...
1135 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1136 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1137 0 | 0 | 0 | 1 | Yes | moved by one position
1138 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1139 0 | 0 | 1 | 1 | Yes | centred on the caret
1140 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1141 0 | 1 | - | 1 | No, caret is always centred | -
1142 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1143 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1144 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1145 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1146 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1147 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1148 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1149 */
1150
XYScrollToMakeVisible(const SelectionRange & range,const XYScrollOptions options,CaretPolicies policies)1151 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange &range,
1152 const XYScrollOptions options, CaretPolicies policies) {
1153 const PRectangle rcClient = GetTextRectangle();
1154 const Point ptOrigin = GetVisibleOriginInMain();
1155 const Point pt = LocationFromPosition(range.caret) + ptOrigin;
1156 const Point ptAnchor = LocationFromPosition(range.anchor) + ptOrigin;
1157 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1158
1159 XYScrollPosition newXY(xOffset, topLine);
1160 if (rcClient.Empty()) {
1161 return newXY;
1162 }
1163
1164 // Vertical positioning
1165 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (policies.y.policy & CARET_STRICT) != 0)) {
1166 const Sci::Line lineCaret = DisplayFromPosition(range.caret.Position());
1167 const Sci::Line linesOnScreen = LinesOnScreen();
1168 const Sci::Line halfScreen = std::max(linesOnScreen - 1, static_cast<Sci::Line>(2)) / 2;
1169 const bool bSlop = (policies.y.policy & CARET_SLOP) != 0;
1170 const bool bStrict = (policies.y.policy & CARET_STRICT) != 0;
1171 const bool bJump = (policies.y.policy & CARET_JUMPS) != 0;
1172 const bool bEven = (policies.y.policy & CARET_EVEN) != 0;
1173
1174 // It should be possible to scroll the window to show the caret,
1175 // but this fails to remove the caret on GTK+
1176 if (bSlop) { // A margin is defined
1177 Sci::Line yMoveT, yMoveB;
1178 if (bStrict) {
1179 Sci::Line yMarginT, yMarginB;
1180 if (!(options & xysUseMargin)) {
1181 // In drag mode, avoid moves
1182 // otherwise, a double click will select several lines.
1183 yMarginT = yMarginB = 0;
1184 } else {
1185 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1186 // a maximum of slightly less than half the heigth of the text area.
1187 yMarginT = std::clamp<Sci::Line>(policies.y.slop, 1, halfScreen);
1188 if (bEven) {
1189 yMarginB = yMarginT;
1190 } else {
1191 yMarginB = linesOnScreen - yMarginT - 1;
1192 }
1193 }
1194 yMoveT = yMarginT;
1195 if (bEven) {
1196 if (bJump) {
1197 yMoveT = std::clamp<Sci::Line>(policies.y.slop * 3, 1, halfScreen);
1198 }
1199 yMoveB = yMoveT;
1200 } else {
1201 yMoveB = linesOnScreen - yMoveT - 1;
1202 }
1203 if (lineCaret < topLine + yMarginT) {
1204 // Caret goes too high
1205 newXY.topLine = lineCaret - yMoveT;
1206 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1207 // Caret goes too low
1208 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1209 }
1210 } else { // Not strict
1211 yMoveT = bJump ? policies.y.slop * 3 : policies.y.slop;
1212 yMoveT = std::clamp<Sci::Line>(yMoveT, 1, halfScreen);
1213 if (bEven) {
1214 yMoveB = yMoveT;
1215 } else {
1216 yMoveB = linesOnScreen - yMoveT - 1;
1217 }
1218 if (lineCaret < topLine) {
1219 // Caret goes too high
1220 newXY.topLine = lineCaret - yMoveT;
1221 } else if (lineCaret > topLine + linesOnScreen - 1) {
1222 // Caret goes too low
1223 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1224 }
1225 }
1226 } else { // No slop
1227 if (!bStrict && !bJump) {
1228 // Minimal move
1229 if (lineCaret < topLine) {
1230 // Caret goes too high
1231 newXY.topLine = lineCaret;
1232 } else if (lineCaret > topLine + linesOnScreen - 1) {
1233 // Caret goes too low
1234 if (bEven) {
1235 newXY.topLine = lineCaret - linesOnScreen + 1;
1236 } else {
1237 newXY.topLine = lineCaret;
1238 }
1239 }
1240 } else { // Strict or going out of display
1241 if (bEven) {
1242 // Always center caret
1243 newXY.topLine = lineCaret - halfScreen;
1244 } else {
1245 // Always put caret on top of display
1246 newXY.topLine = lineCaret;
1247 }
1248 }
1249 }
1250 if (!(range.caret == range.anchor)) {
1251 const Sci::Line lineAnchor = DisplayFromPosition(range.anchor.Position());
1252 if (lineAnchor < lineCaret) {
1253 // Shift up to show anchor or as much of range as possible
1254 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1255 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1256 } else {
1257 // Shift down to show anchor or as much of range as possible
1258 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1259 newXY.topLine = std::min(newXY.topLine, lineCaret);
1260 }
1261 }
1262 newXY.topLine = std::clamp<Sci::Line>(newXY.topLine, 0, MaxScrollPos());
1263 }
1264
1265 // Horizontal positioning
1266 if ((options & xysHorizontal) && !Wrapping()) {
1267 const int halfScreen = std::max(static_cast<int>(rcClient.Width()) - 4, 4) / 2;
1268 const bool bSlop = (policies.x.policy & CARET_SLOP) != 0;
1269 const bool bStrict = (policies.x.policy & CARET_STRICT) != 0;
1270 const bool bJump = (policies.x.policy & CARET_JUMPS) != 0;
1271 const bool bEven = (policies.x.policy & CARET_EVEN) != 0;
1272
1273 if (bSlop) { // A margin is defined
1274 int xMoveL, xMoveR;
1275 if (bStrict) {
1276 int xMarginL, xMarginR;
1277 if (!(options & xysUseMargin)) {
1278 // In drag mode, avoid moves unless very near of the margin
1279 // otherwise, a simple click will select text.
1280 xMarginL = xMarginR = 2;
1281 } else {
1282 // xMargin must equal to caretXSlop, with a minimum of 2 and
1283 // a maximum of slightly less than half the width of the text area.
1284 xMarginR = std::clamp(policies.x.slop, 2, halfScreen);
1285 if (bEven) {
1286 xMarginL = xMarginR;
1287 } else {
1288 xMarginL = static_cast<int>(rcClient.Width()) - xMarginR - 4;
1289 }
1290 }
1291 if (bJump && bEven) {
1292 // Jump is used only in even mode
1293 xMoveL = xMoveR = std::clamp(policies.x.slop * 3, 1, halfScreen);
1294 } else {
1295 xMoveL = xMoveR = 0; // Not used, avoid a warning
1296 }
1297 if (pt.x < rcClient.left + xMarginL) {
1298 // Caret is on the left of the display
1299 if (bJump && bEven) {
1300 newXY.xOffset -= xMoveL;
1301 } else {
1302 // Move just enough to allow to display the caret
1303 newXY.xOffset -= static_cast<int>((rcClient.left + xMarginL) - pt.x);
1304 }
1305 } else if (pt.x >= rcClient.right - xMarginR) {
1306 // Caret is on the right of the display
1307 if (bJump && bEven) {
1308 newXY.xOffset += xMoveR;
1309 } else {
1310 // Move just enough to allow to display the caret
1311 newXY.xOffset += static_cast<int>(pt.x - (rcClient.right - xMarginR) + 1);
1312 }
1313 }
1314 } else { // Not strict
1315 xMoveR = bJump ? policies.x.slop * 3 : policies.x.slop;
1316 xMoveR = std::clamp(xMoveR, 1, halfScreen);
1317 if (bEven) {
1318 xMoveL = xMoveR;
1319 } else {
1320 xMoveL = static_cast<int>(rcClient.Width()) - xMoveR - 4;
1321 }
1322 if (pt.x < rcClient.left) {
1323 // Caret is on the left of the display
1324 newXY.xOffset -= xMoveL;
1325 } else if (pt.x >= rcClient.right) {
1326 // Caret is on the right of the display
1327 newXY.xOffset += xMoveR;
1328 }
1329 }
1330 } else { // No slop
1331 if (bStrict ||
1332 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1333 // Strict or going out of display
1334 if (bEven) {
1335 // Center caret
1336 newXY.xOffset += static_cast<int>(pt.x - rcClient.left - halfScreen);
1337 } else {
1338 // Put caret on right
1339 newXY.xOffset += static_cast<int>(pt.x - rcClient.right + 1);
1340 }
1341 } else {
1342 // Move just enough to allow to display the caret
1343 if (pt.x < rcClient.left) {
1344 // Caret is on the left of the display
1345 if (bEven) {
1346 newXY.xOffset -= static_cast<int>(rcClient.left - pt.x);
1347 } else {
1348 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1349 }
1350 } else if (pt.x >= rcClient.right) {
1351 // Caret is on the right of the display
1352 newXY.xOffset += static_cast<int>(pt.x - rcClient.right) + 1;
1353 }
1354 }
1355 }
1356 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1357 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1358 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 2;
1359 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1360 newXY.xOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 2;
1361 if (vs.IsBlockCaretStyle() || view.imeCaretBlockOverride) {
1362 // Ensure we can see a good portion of the block caret
1363 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1364 }
1365 }
1366 if (!(range.caret == range.anchor)) {
1367 if (ptAnchor.x < pt.x) {
1368 // Shift to left to show anchor or as much of range as possible
1369 const int maxOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.left) - 1;
1370 const int minOffset = static_cast<int>(pt.x + xOffset - rcClient.right) + 1;
1371 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1372 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1373 } else {
1374 // Shift to right to show anchor or as much of range as possible
1375 const int minOffset = static_cast<int>(ptAnchor.x + xOffset - rcClient.right) + 1;
1376 const int maxOffset = static_cast<int>(pt.x + xOffset - rcClient.left) - 1;
1377 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1378 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1379 }
1380 }
1381 if (newXY.xOffset < 0) {
1382 newXY.xOffset = 0;
1383 }
1384 }
1385
1386 return newXY;
1387 }
1388
SetXYScroll(XYScrollPosition newXY)1389 void Editor::SetXYScroll(XYScrollPosition newXY) {
1390 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1391 if (newXY.topLine != topLine) {
1392 SetTopLine(newXY.topLine);
1393 SetVerticalScrollPos();
1394 }
1395 if (newXY.xOffset != xOffset) {
1396 xOffset = newXY.xOffset;
1397 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1398 if (newXY.xOffset > 0) {
1399 const PRectangle rcText = GetTextRectangle();
1400 if (horizontalScrollBarVisible &&
1401 rcText.Width() + xOffset > scrollWidth) {
1402 scrollWidth = xOffset + static_cast<int>(rcText.Width());
1403 SetScrollBars();
1404 }
1405 }
1406 SetHorizontalScrollPos();
1407 }
1408 Redraw();
1409 UpdateSystemCaret();
1410 }
1411 }
1412
ScrollRange(SelectionRange range)1413 void Editor::ScrollRange(SelectionRange range) {
1414 SetXYScroll(XYScrollToMakeVisible(range, xysDefault, caretPolicies));
1415 }
1416
EnsureCaretVisible(bool useMargin,bool vert,bool horiz)1417 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1418 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1419 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0)),
1420 caretPolicies));
1421 }
1422
ShowCaretAtCurrentPosition()1423 void Editor::ShowCaretAtCurrentPosition() {
1424 if (hasFocus) {
1425 caret.active = true;
1426 caret.on = true;
1427 FineTickerCancel(tickCaret);
1428 if (caret.period > 0)
1429 FineTickerStart(tickCaret, caret.period, caret.period/10);
1430 } else {
1431 caret.active = false;
1432 caret.on = false;
1433 FineTickerCancel(tickCaret);
1434 }
1435 InvalidateCaret();
1436 }
1437
DropCaret()1438 void Editor::DropCaret() {
1439 caret.active = false;
1440 FineTickerCancel(tickCaret);
1441 InvalidateCaret();
1442 }
1443
CaretSetPeriod(int period)1444 void Editor::CaretSetPeriod(int period) {
1445 if (caret.period != period) {
1446 caret.period = period;
1447 caret.on = true;
1448 FineTickerCancel(tickCaret);
1449 if ((caret.active) && (caret.period > 0))
1450 FineTickerStart(tickCaret, caret.period, caret.period/10);
1451 InvalidateCaret();
1452 }
1453 }
1454
InvalidateCaret()1455 void Editor::InvalidateCaret() {
1456 if (posDrag.IsValid()) {
1457 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1458 } else {
1459 for (size_t r=0; r<sel.Count(); r++) {
1460 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1461 }
1462 }
1463 UpdateSystemCaret();
1464 }
1465
NotifyCaretMove()1466 void Editor::NotifyCaretMove() {
1467 }
1468
UpdateSystemCaret()1469 void Editor::UpdateSystemCaret() {
1470 }
1471
Wrapping() const1472 bool Editor::Wrapping() const noexcept {
1473 return vs.wrapState != eWrapNone;
1474 }
1475
NeedWrapping(Sci::Line docLineStart,Sci::Line docLineEnd)1476 void Editor::NeedWrapping(Sci::Line docLineStart, Sci::Line docLineEnd) {
1477 //Platform::DebugPrintf("\nNeedWrapping: %0d..%0d\n", docLineStart, docLineEnd);
1478 if (wrapPending.AddRange(docLineStart, docLineEnd)) {
1479 view.llc.Invalidate(LineLayout::llPositions);
1480 }
1481 // Wrap lines during idle.
1482 if (Wrapping() && wrapPending.NeedsWrap()) {
1483 SetIdle(true);
1484 }
1485 }
1486
WrapOneLine(Surface * surface,Sci::Line lineToWrap)1487 bool Editor::WrapOneLine(Surface *surface, Sci::Line lineToWrap) {
1488 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(lineToWrap, *this));
1489 int linesWrapped = 1;
1490 if (ll) {
1491 view.LayoutLine(*this, lineToWrap, surface, vs, ll, wrapWidth);
1492 linesWrapped = ll->lines;
1493 }
1494 return pcs->SetHeight(lineToWrap, linesWrapped +
1495 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1496 }
1497
1498 // Perform wrapping for a subset of the lines needing wrapping.
1499 // wsAll: wrap all lines which need wrapping in this single call
1500 // wsVisible: wrap currently visible lines
1501 // wsIdle: wrap one page + 100 lines
1502 // Return true if wrapping occurred.
WrapLines(WrapScope ws)1503 bool Editor::WrapLines(WrapScope ws) {
1504 Sci::Line goodTopLine = topLine;
1505 bool wrapOccurred = false;
1506 if (!Wrapping()) {
1507 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1508 wrapWidth = LineLayout::wrapWidthInfinite;
1509 for (Sci::Line lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1510 pcs->SetHeight(lineDoc, 1 +
1511 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1512 }
1513 wrapOccurred = true;
1514 }
1515 wrapPending.Reset();
1516
1517 } else if (wrapPending.NeedsWrap()) {
1518 wrapPending.start = std::min(wrapPending.start, pdoc->LinesTotal());
1519 if (!SetIdle(true)) {
1520 // Idle processing not supported so full wrap required.
1521 ws = WrapScope::wsAll;
1522 }
1523 // Decide where to start wrapping
1524 Sci::Line lineToWrap = wrapPending.start;
1525 Sci::Line lineToWrapEnd = std::min(wrapPending.end, pdoc->LinesTotal());
1526 const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
1527 const Sci::Line subLineTop = topLine - pcs->DisplayFromDoc(lineDocTop);
1528 if (ws == WrapScope::wsVisible) {
1529 lineToWrap = std::clamp(lineDocTop-5, wrapPending.start, pdoc->LinesTotal());
1530 // Priority wrap to just after visible area.
1531 // Since wrapping could reduce display lines, treat each
1532 // as taking only one display line.
1533 lineToWrapEnd = lineDocTop;
1534 Sci::Line lines = LinesOnScreen() + 1;
1535 while ((lineToWrapEnd < pcs->LinesInDoc()) && (lines>0)) {
1536 if (pcs->GetVisible(lineToWrapEnd))
1537 lines--;
1538 lineToWrapEnd++;
1539 }
1540 // .. and if the paint window is outside pending wraps
1541 if ((lineToWrap > wrapPending.end) || (lineToWrapEnd < wrapPending.start)) {
1542 // Currently visible text does not need wrapping
1543 return false;
1544 }
1545 } else if (ws == WrapScope::wsIdle) {
1546 // Try to keep time taken by wrapping reasonable so interaction remains smooth.
1547 const double secondsAllowed = 0.01;
1548 const Sci::Line linesInAllowedTime = std::clamp<Sci::Line>(
1549 static_cast<Sci::Line>(secondsAllowed / durationWrapOneLine.Duration()),
1550 LinesOnScreen() + 50, 0x10000);
1551 lineToWrapEnd = lineToWrap + linesInAllowedTime;
1552 }
1553 const Sci::Line lineEndNeedWrap = std::min(wrapPending.end, pdoc->LinesTotal());
1554 lineToWrapEnd = std::min(lineToWrapEnd, lineEndNeedWrap);
1555
1556 // Ensure all lines being wrapped are styled.
1557 pdoc->EnsureStyledTo(pdoc->LineStart(lineToWrapEnd));
1558
1559 if (lineToWrap < lineToWrapEnd) {
1560
1561 PRectangle rcTextArea = GetClientRectangle();
1562 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1563 rcTextArea.right -= vs.rightMarginWidth;
1564 wrapWidth = static_cast<int>(rcTextArea.Width());
1565 RefreshStyleData();
1566 AutoSurface surface(this);
1567 if (surface) {
1568 //Platform::DebugPrintf("Wraplines: scope=%0d need=%0d..%0d perform=%0d..%0d\n", ws, wrapPending.start, wrapPending.end, lineToWrap, lineToWrapEnd);
1569
1570 const Sci::Line linesBeingWrapped = lineToWrapEnd - lineToWrap;
1571 ElapsedPeriod epWrapping;
1572 while (lineToWrap < lineToWrapEnd) {
1573 if (WrapOneLine(surface, lineToWrap)) {
1574 wrapOccurred = true;
1575 }
1576 wrapPending.Wrapped(lineToWrap);
1577 lineToWrap++;
1578 }
1579 durationWrapOneLine.AddSample(linesBeingWrapped, epWrapping.Duration());
1580
1581 goodTopLine = pcs->DisplayFromDoc(lineDocTop) + std::min(
1582 subLineTop, static_cast<Sci::Line>(pcs->GetHeight(lineDocTop)-1));
1583 }
1584 }
1585
1586 // If wrapping is done, bring it to resting position
1587 if (wrapPending.start >= lineEndNeedWrap) {
1588 wrapPending.Reset();
1589 }
1590 }
1591
1592 if (wrapOccurred) {
1593 SetScrollBars();
1594 SetTopLine(std::clamp<Sci::Line>(goodTopLine, 0, MaxScrollPos()));
1595 SetVerticalScrollPos();
1596 }
1597
1598 return wrapOccurred;
1599 }
1600
LinesJoin()1601 void Editor::LinesJoin() {
1602 if (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {
1603 UndoGroup ug(pdoc);
1604 bool prevNonWS = true;
1605 for (Sci::Position pos = targetRange.start.Position(); pos < targetRange.end.Position(); pos++) {
1606 if (pdoc->IsPositionInLineEnd(pos)) {
1607 targetRange.end.Add(-pdoc->LenChar(pos));
1608 pdoc->DelChar(pos);
1609 if (prevNonWS) {
1610 // Ensure at least one space separating previous lines
1611 const Sci::Position lengthInserted = pdoc->InsertString(pos, " ", 1);
1612 targetRange.end.Add(lengthInserted);
1613 }
1614 } else {
1615 prevNonWS = pdoc->CharAt(pos) != ' ';
1616 }
1617 }
1618 }
1619 }
1620
StringFromEOLMode(int eolMode)1621 const char *Editor::StringFromEOLMode(int eolMode) noexcept {
1622 if (eolMode == SC_EOL_CRLF) {
1623 return "\r\n";
1624 } else if (eolMode == SC_EOL_CR) {
1625 return "\r";
1626 } else {
1627 return "\n";
1628 }
1629 }
1630
LinesSplit(int pixelWidth)1631 void Editor::LinesSplit(int pixelWidth) {
1632 if (!RangeContainsProtected(targetRange.start.Position(), targetRange.end.Position())) {
1633 if (pixelWidth == 0) {
1634 const PRectangle rcText = GetTextRectangle();
1635 pixelWidth = static_cast<int>(rcText.Width());
1636 }
1637 const Sci::Line lineStart = pdoc->SciLineFromPosition(targetRange.start.Position());
1638 Sci::Line lineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());
1639 const char *eol = StringFromEOLMode(pdoc->eolMode);
1640 UndoGroup ug(pdoc);
1641 for (Sci::Line line = lineStart; line <= lineEnd; line++) {
1642 AutoSurface surface(this);
1643 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
1644 if (surface && ll) {
1645 const Sci::Position posLineStart = pdoc->LineStart(line);
1646 view.LayoutLine(*this, line, surface, vs, ll, pixelWidth);
1647 Sci::Position lengthInsertedTotal = 0;
1648 for (int subLine = 1; subLine < ll->lines; subLine++) {
1649 const Sci::Position lengthInserted = pdoc->InsertString(
1650 posLineStart + lengthInsertedTotal + ll->LineStart(subLine),
1651 eol, strlen(eol));
1652 targetRange.end.Add(lengthInserted);
1653 lengthInsertedTotal += lengthInserted;
1654 }
1655 }
1656 lineEnd = pdoc->SciLineFromPosition(targetRange.end.Position());
1657 }
1658 }
1659 }
1660
PaintSelMargin(Surface * surfaceWindow,const PRectangle & rc)1661 void Editor::PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc) {
1662 if (vs.fixedColumnWidth == 0)
1663 return;
1664
1665 AllocateGraphics();
1666 RefreshStyleData();
1667 RefreshPixMaps(surfaceWindow);
1668
1669 // On GTK+ with Ubuntu overlay scroll bars, the surface may have been finished
1670 // at this point. The Initialised call checks for this case and sets the status
1671 // to be bad which avoids crashes in following calls.
1672 if (!surfaceWindow->Initialised()) {
1673 return;
1674 }
1675
1676 PRectangle rcMargin = GetClientRectangle();
1677 const Point ptOrigin = GetVisibleOriginInMain();
1678 rcMargin.Move(0, -ptOrigin.y);
1679 rcMargin.left = 0;
1680 rcMargin.right = static_cast<XYPOSITION>(vs.fixedColumnWidth);
1681
1682 if (!rc.Intersects(rcMargin))
1683 return;
1684
1685 Surface *surface;
1686 if (view.bufferedDraw) {
1687 surface = marginView.pixmapSelMargin.get();
1688 } else {
1689 surface = surfaceWindow;
1690 }
1691
1692 // Clip vertically to paint area to avoid drawing line numbers
1693 if (rcMargin.bottom > rc.bottom)
1694 rcMargin.bottom = rc.bottom;
1695 if (rcMargin.top < rc.top)
1696 rcMargin.top = rc.top;
1697
1698 marginView.PaintMargin(surface, topLine, rc, rcMargin, *this, vs);
1699
1700 if (view.bufferedDraw) {
1701 surfaceWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *marginView.pixmapSelMargin);
1702 }
1703 }
1704
RefreshPixMaps(Surface * surfaceWindow)1705 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
1706 view.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1707 marginView.RefreshPixMaps(surfaceWindow, wMain.GetID(), vs);
1708 if (view.bufferedDraw) {
1709 const PRectangle rcClient = GetClientRectangle();
1710 if (!view.pixmapLine->Initialised()) {
1711
1712 view.pixmapLine->InitPixMap(static_cast<int>(rcClient.Width()), vs.lineHeight,
1713 surfaceWindow, wMain.GetID());
1714 }
1715 if (!marginView.pixmapSelMargin->Initialised()) {
1716 marginView.pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
1717 static_cast<int>(rcClient.Height()), surfaceWindow, wMain.GetID());
1718 }
1719 }
1720 }
1721
Paint(Surface * surfaceWindow,PRectangle rcArea)1722 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
1723 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
1724 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
1725 AllocateGraphics();
1726
1727 RefreshStyleData();
1728 if (paintState == paintAbandoned)
1729 return; // Scroll bars may have changed so need redraw
1730 RefreshPixMaps(surfaceWindow);
1731
1732 paintAbandonedByStyling = false;
1733
1734 StyleAreaBounded(rcArea, false);
1735
1736 const PRectangle rcClient = GetClientRectangle();
1737 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
1738 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
1739
1740 if (NotifyUpdateUI()) {
1741 RefreshStyleData();
1742 RefreshPixMaps(surfaceWindow);
1743 }
1744
1745 // Wrap the visible lines if needed.
1746 if (WrapLines(WrapScope::wsVisible)) {
1747 // The wrapping process has changed the height of some lines so
1748 // abandon this paint for a complete repaint.
1749 if (AbandonPaint()) {
1750 return;
1751 }
1752 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
1753 }
1754
1755 if (!marginView.pixmapSelPattern->Initialised()) {
1756 // When Direct2D is used, pixmap creation may fail with D2DERR_RECREATE_TARGET so
1757 // abandon this paint to avoid further failures.
1758 // Main drawing surface and pixmaps should be recreated by next paint.
1759 return;
1760 }
1761
1762 if (!view.bufferedDraw)
1763 surfaceWindow->SetClip(rcArea);
1764
1765 if (paintState != paintAbandoned) {
1766 if (vs.marginInside) {
1767 PaintSelMargin(surfaceWindow, rcArea);
1768 PRectangle rcRightMargin = rcClient;
1769 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
1770 if (rcArea.Intersects(rcRightMargin)) {
1771 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
1772 }
1773 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
1774 PRectangle rcLeftMargin = rcArea;
1775 rcLeftMargin.left = 0;
1776 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
1777 if (rcArea.Intersects(rcLeftMargin)) {
1778 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
1779 }
1780 }
1781 }
1782
1783 if (paintState == paintAbandoned) {
1784 // Either styling or NotifyUpdateUI noticed that painting is needed
1785 // outside the current painting rectangle
1786 //Platform::DebugPrintf("Abandoning paint\n");
1787 if (Wrapping()) {
1788 if (paintAbandonedByStyling) {
1789 // Styling has spilled over a line end, such as occurs by starting a multiline
1790 // comment. The width of subsequent text may have changed, so rewrap.
1791 NeedWrapping(pcs->DocFromDisplay(topLine));
1792 }
1793 }
1794 return;
1795 }
1796
1797 view.PaintText(surfaceWindow, *this, rcArea, rcClient, vs);
1798
1799 if (horizontalScrollBarVisible && trackLineWidth && (view.lineWidthMaxSeen > scrollWidth)) {
1800 scrollWidth = view.lineWidthMaxSeen;
1801 if (!FineTickerRunning(tickWiden)) {
1802 FineTickerStart(tickWiden, 50, 5);
1803 }
1804 }
1805
1806 NotifyPainted();
1807 }
1808
1809 // This is mostly copied from the Paint method but with some things omitted
1810 // such as the margin markers, line numbers, selection and caret
1811 // Should be merged back into a combined Draw method.
FormatRange(bool draw,const Sci_RangeToFormat * pfr)1812 Sci::Position Editor::FormatRange(bool draw, const Sci_RangeToFormat *pfr) {
1813 if (!pfr)
1814 return 0;
1815
1816 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
1817 if (!surface)
1818 return 0;
1819 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
1820 if (!surfaceMeasure) {
1821 return 0;
1822 }
1823 return view.FormatRange(draw, pfr, surface, surfaceMeasure, *this, vs);
1824 }
1825
TextWidth(int style,const char * text)1826 int Editor::TextWidth(int style, const char *text) {
1827 RefreshStyleData();
1828 AutoSurface surface(this);
1829 if (surface) {
1830 return static_cast<int>(surface->WidthText(vs.styles[style].font, text));
1831 } else {
1832 return 1;
1833 }
1834 }
1835
1836 // Empty method is overridden on GTK+ to show / hide scrollbars
ReconfigureScrollBars()1837 void Editor::ReconfigureScrollBars() {}
1838
SetScrollBars()1839 void Editor::SetScrollBars() {
1840 RefreshStyleData();
1841
1842 const Sci::Line nMax = MaxScrollPos();
1843 const Sci::Line nPage = LinesOnScreen();
1844 const bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
1845 if (modified) {
1846 DwellEnd(true);
1847 }
1848
1849 // TODO: ensure always showing as many lines as possible
1850 // May not be, if, for example, window made larger
1851 if (topLine > MaxScrollPos()) {
1852 SetTopLine(std::clamp<Sci::Line>(topLine, 0, MaxScrollPos()));
1853 SetVerticalScrollPos();
1854 Redraw();
1855 }
1856 if (modified) {
1857 if (!AbandonPaint())
1858 Redraw();
1859 }
1860 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
1861 }
1862
ChangeSize()1863 void Editor::ChangeSize() {
1864 DropGraphics(false);
1865 SetScrollBars();
1866 if (Wrapping()) {
1867 PRectangle rcTextArea = GetClientRectangle();
1868 rcTextArea.left = static_cast<XYPOSITION>(vs.textStart);
1869 rcTextArea.right -= vs.rightMarginWidth;
1870 if (wrapWidth != rcTextArea.Width()) {
1871 NeedWrapping();
1872 Redraw();
1873 }
1874 }
1875 }
1876
RealizeVirtualSpace(Sci::Position position,Sci::Position virtualSpace)1877 Sci::Position Editor::RealizeVirtualSpace(Sci::Position position, Sci::Position virtualSpace) {
1878 if (virtualSpace > 0) {
1879 const Sci::Line line = pdoc->SciLineFromPosition(position);
1880 const Sci::Position indent = pdoc->GetLineIndentPosition(line);
1881 if (indent == position) {
1882 return pdoc->SetLineIndentation(line, pdoc->GetLineIndentation(line) + virtualSpace);
1883 } else {
1884 std::string spaceText(virtualSpace, ' ');
1885 const Sci::Position lengthInserted = pdoc->InsertString(position, spaceText.c_str(), virtualSpace);
1886 position += lengthInserted;
1887 }
1888 }
1889 return position;
1890 }
1891
RealizeVirtualSpace(const SelectionPosition & position)1892 SelectionPosition Editor::RealizeVirtualSpace(const SelectionPosition &position) {
1893 // Return the new position with no virtual space
1894 return SelectionPosition(RealizeVirtualSpace(position.Position(), position.VirtualSpace()));
1895 }
1896
AddChar(char ch)1897 void Editor::AddChar(char ch) {
1898 char s[2];
1899 s[0] = ch;
1900 s[1] = '\0';
1901 InsertCharacter(std::string_view(s, 1), CharacterSource::directInput);
1902 }
1903
FilterSelections()1904 void Editor::FilterSelections() {
1905 if (!additionalSelectionTyping && (sel.Count() > 1)) {
1906 InvalidateWholeSelection();
1907 sel.DropAdditionalRanges();
1908 }
1909 }
1910
1911 // InsertCharacter inserts a character encoded in document code page.
InsertCharacter(std::string_view sv,CharacterSource charSource)1912 void Editor::InsertCharacter(std::string_view sv, CharacterSource charSource) {
1913 if (sv.empty()) {
1914 return;
1915 }
1916 FilterSelections();
1917 {
1918 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
1919
1920 // Vector elements point into selection in order to change selection.
1921 std::vector<SelectionRange *> selPtrs;
1922 for (size_t r = 0; r < sel.Count(); r++) {
1923 selPtrs.push_back(&sel.Range(r));
1924 }
1925 // Order selections by position in document.
1926 std::sort(selPtrs.begin(), selPtrs.end(),
1927 [](const SelectionRange *a, const SelectionRange *b) noexcept {return *a < *b;});
1928
1929 // Loop in reverse to avoid disturbing positions of selections yet to be processed.
1930 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
1931 rit != selPtrs.rend(); ++rit) {
1932 SelectionRange *currentSel = *rit;
1933 if (!RangeContainsProtected(currentSel->Start().Position(),
1934 currentSel->End().Position())) {
1935 Sci::Position positionInsert = currentSel->Start().Position();
1936 if (!currentSel->Empty()) {
1937 if (currentSel->Length()) {
1938 pdoc->DeleteChars(positionInsert, currentSel->Length());
1939 currentSel->ClearVirtualSpace();
1940 } else {
1941 // Range is all virtual so collapse to start of virtual space
1942 currentSel->MinimizeVirtualSpace();
1943 }
1944 } else if (inOverstrike) {
1945 if (positionInsert < pdoc->Length()) {
1946 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
1947 pdoc->DelChar(positionInsert);
1948 currentSel->ClearVirtualSpace();
1949 }
1950 }
1951 }
1952 positionInsert = RealizeVirtualSpace(positionInsert, currentSel->caret.VirtualSpace());
1953 const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, sv.data(), sv.length());
1954 if (lengthInserted > 0) {
1955 currentSel->caret.SetPosition(positionInsert + lengthInserted);
1956 currentSel->anchor.SetPosition(positionInsert + lengthInserted);
1957 }
1958 currentSel->ClearVirtualSpace();
1959 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1960 if (Wrapping()) {
1961 AutoSurface surface(this);
1962 if (surface) {
1963 if (WrapOneLine(surface, pdoc->SciLineFromPosition(positionInsert))) {
1964 SetScrollBars();
1965 SetVerticalScrollPos();
1966 Redraw();
1967 }
1968 }
1969 }
1970 }
1971 }
1972 }
1973 if (Wrapping()) {
1974 SetScrollBars();
1975 }
1976 ThinRectangularRange();
1977 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
1978 EnsureCaretVisible();
1979 // Avoid blinking during rapid typing:
1980 ShowCaretAtCurrentPosition();
1981 if ((caretSticky == SC_CARETSTICKY_OFF) ||
1982 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(sv))) {
1983 SetLastXChosen();
1984 }
1985
1986 int ch = static_cast<unsigned char>(sv[0]);
1987 if (pdoc->dbcsCodePage != SC_CP_UTF8) {
1988 if (sv.length() > 1) {
1989 // DBCS code page or DBCS font character set.
1990 ch = (ch << 8) | static_cast<unsigned char>(sv[1]);
1991 }
1992 } else {
1993 if ((ch < 0xC0) || (1 == sv.length())) {
1994 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
1995 // characters when not in UTF-8 mode.
1996 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
1997 // characters representing themselves.
1998 } else {
1999 unsigned int utf32[1] = { 0 };
2000 UTF32FromUTF8(sv, utf32, std::size(utf32));
2001 ch = utf32[0];
2002 }
2003 }
2004 NotifyChar(ch, charSource);
2005
2006 if (recordingMacro && charSource != CharacterSource::tentativeInput) {
2007 std::string copy(sv); // ensure NUL-terminated
2008 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(copy.data()));
2009 }
2010 }
2011
ClearBeforeTentativeStart()2012 void Editor::ClearBeforeTentativeStart() {
2013 // Make positions for the first composition string.
2014 FilterSelections();
2015 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
2016 for (size_t r = 0; r<sel.Count(); r++) {
2017 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2018 sel.Range(r).End().Position())) {
2019 const Sci::Position positionInsert = sel.Range(r).Start().Position();
2020 if (!sel.Range(r).Empty()) {
2021 if (sel.Range(r).Length()) {
2022 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2023 sel.Range(r).ClearVirtualSpace();
2024 } else {
2025 // Range is all virtual so collapse to start of virtual space
2026 sel.Range(r).MinimizeVirtualSpace();
2027 }
2028 }
2029 RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2030 sel.Range(r).ClearVirtualSpace();
2031 }
2032 }
2033 }
2034
InsertPaste(const char * text,Sci::Position len)2035 void Editor::InsertPaste(const char *text, Sci::Position len) {
2036 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
2037 SelectionPosition selStart = sel.Start();
2038 selStart = RealizeVirtualSpace(selStart);
2039 const Sci::Position lengthInserted = pdoc->InsertString(selStart.Position(), text, len);
2040 if (lengthInserted > 0) {
2041 SetEmptySelection(selStart.Position() + lengthInserted);
2042 }
2043 } else {
2044 // SC_MULTIPASTE_EACH
2045 for (size_t r=0; r<sel.Count(); r++) {
2046 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2047 sel.Range(r).End().Position())) {
2048 Sci::Position positionInsert = sel.Range(r).Start().Position();
2049 if (!sel.Range(r).Empty()) {
2050 if (sel.Range(r).Length()) {
2051 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
2052 sel.Range(r).ClearVirtualSpace();
2053 } else {
2054 // Range is all virtual so collapse to start of virtual space
2055 sel.Range(r).MinimizeVirtualSpace();
2056 }
2057 }
2058 positionInsert = RealizeVirtualSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
2059 const Sci::Position lengthInserted = pdoc->InsertString(positionInsert, text, len);
2060 if (lengthInserted > 0) {
2061 sel.Range(r).caret.SetPosition(positionInsert + lengthInserted);
2062 sel.Range(r).anchor.SetPosition(positionInsert + lengthInserted);
2063 }
2064 sel.Range(r).ClearVirtualSpace();
2065 }
2066 }
2067 }
2068 }
2069
InsertPasteShape(const char * text,Sci::Position len,PasteShape shape)2070 void Editor::InsertPasteShape(const char *text, Sci::Position len, PasteShape shape) {
2071 std::string convertedText;
2072 if (convertPastes) {
2073 // Convert line endings of the paste into our local line-endings mode
2074 convertedText = Document::TransformLineEnds(text, len, pdoc->eolMode);
2075 len = convertedText.length();
2076 text = convertedText.c_str();
2077 }
2078 if (shape == pasteRectangular) {
2079 PasteRectangular(sel.Start(), text, len);
2080 } else {
2081 if (shape == pasteLine) {
2082 const Sci::Position insertPos =
2083 pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret()));
2084 Sci::Position lengthInserted = pdoc->InsertString(insertPos, text, len);
2085 // add the newline if necessary
2086 if ((len > 0) && (text[len - 1] != '\n' && text[len - 1] != '\r')) {
2087 const char *endline = StringFromEOLMode(pdoc->eolMode);
2088 const Sci::Position length = strlen(endline);
2089 lengthInserted += pdoc->InsertString(insertPos + lengthInserted, endline, length);
2090 }
2091 if (sel.MainCaret() == insertPos) {
2092 SetEmptySelection(sel.MainCaret() + lengthInserted);
2093 }
2094 } else {
2095 InsertPaste(text, len);
2096 }
2097 }
2098 }
2099
ClearSelection(bool retainMultipleSelections)2100 void Editor::ClearSelection(bool retainMultipleSelections) {
2101 if (!sel.IsRectangular() && !retainMultipleSelections)
2102 FilterSelections();
2103 UndoGroup ug(pdoc);
2104 for (size_t r=0; r<sel.Count(); r++) {
2105 if (!sel.Range(r).Empty()) {
2106 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
2107 sel.Range(r).End().Position())) {
2108 pdoc->DeleteChars(sel.Range(r).Start().Position(),
2109 sel.Range(r).Length());
2110 sel.Range(r) = SelectionRange(sel.Range(r).Start());
2111 }
2112 }
2113 }
2114 ThinRectangularRange();
2115 sel.RemoveDuplicates();
2116 ClaimSelection();
2117 SetHoverIndicatorPosition(sel.MainCaret());
2118 }
2119
ClearAll()2120 void Editor::ClearAll() {
2121 {
2122 UndoGroup ug(pdoc);
2123 if (0 != pdoc->Length()) {
2124 pdoc->DeleteChars(0, pdoc->Length());
2125 }
2126 if (!pdoc->IsReadOnly()) {
2127 pcs->Clear();
2128 pdoc->AnnotationClearAll();
2129 pdoc->MarginClearAll();
2130 }
2131 }
2132
2133 view.ClearAllTabstops();
2134
2135 sel.Clear();
2136 SetTopLine(0);
2137 SetVerticalScrollPos();
2138 InvalidateStyleRedraw();
2139 }
2140
ClearDocumentStyle()2141 void Editor::ClearDocumentStyle() {
2142 pdoc->decorations->DeleteLexerDecorations();
2143 pdoc->StartStyling(0);
2144 pdoc->SetStyleFor(pdoc->Length(), 0);
2145 pcs->ShowAll();
2146 SetAnnotationHeights(0, pdoc->LinesTotal());
2147 pdoc->ClearLevels();
2148 }
2149
CopyAllowLine()2150 void Editor::CopyAllowLine() {
2151 SelectionText selectedText;
2152 CopySelectionRange(&selectedText, true);
2153 CopyToClipboard(selectedText);
2154 }
2155
Cut()2156 void Editor::Cut() {
2157 pdoc->CheckReadOnly();
2158 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
2159 Copy();
2160 ClearSelection();
2161 }
2162 }
2163
PasteRectangular(SelectionPosition pos,const char * ptr,Sci::Position len)2164 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, Sci::Position len) {
2165 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
2166 return;
2167 }
2168 sel.Clear();
2169 sel.RangeMain() = SelectionRange(pos);
2170 Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
2171 UndoGroup ug(pdoc);
2172 sel.RangeMain().caret = RealizeVirtualSpace(sel.RangeMain().caret);
2173 const int xInsert = XFromPosition(sel.RangeMain().caret);
2174 bool prevCr = false;
2175 while ((len > 0) && IsEOLChar(ptr[len-1]))
2176 len--;
2177 for (Sci::Position i = 0; i < len; i++) {
2178 if (IsEOLChar(ptr[i])) {
2179 if ((ptr[i] == '\r') || (!prevCr))
2180 line++;
2181 if (line >= pdoc->LinesTotal()) {
2182 if (pdoc->eolMode != SC_EOL_LF)
2183 pdoc->InsertString(pdoc->Length(), "\r", 1);
2184 if (pdoc->eolMode != SC_EOL_CR)
2185 pdoc->InsertString(pdoc->Length(), "\n", 1);
2186 }
2187 // Pad the end of lines with spaces if required
2188 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
2189 if ((XFromPosition(sel.RangeMain().caret) < xInsert) && (i + 1 < len)) {
2190 while (XFromPosition(sel.RangeMain().caret) < xInsert) {
2191 assert(pdoc);
2192 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), " ", 1);
2193 sel.RangeMain().caret.Add(lengthInserted);
2194 }
2195 }
2196 prevCr = ptr[i] == '\r';
2197 } else {
2198 const Sci::Position lengthInserted = pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
2199 sel.RangeMain().caret.Add(lengthInserted);
2200 prevCr = false;
2201 }
2202 }
2203 SetEmptySelection(pos);
2204 }
2205
CanPaste()2206 bool Editor::CanPaste() {
2207 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
2208 }
2209
Clear()2210 void Editor::Clear() {
2211 // If multiple selections, don't delete EOLS
2212 if (sel.Empty()) {
2213 bool singleVirtual = false;
2214 if ((sel.Count() == 1) &&
2215 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
2216 sel.RangeMain().Start().VirtualSpace()) {
2217 singleVirtual = true;
2218 }
2219 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
2220 for (size_t r=0; r<sel.Count(); r++) {
2221 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
2222 if (sel.Range(r).Start().VirtualSpace()) {
2223 if (sel.Range(r).anchor < sel.Range(r).caret)
2224 sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
2225 else
2226 sel.Range(r) = SelectionRange(RealizeVirtualSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
2227 }
2228 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
2229 pdoc->DelChar(sel.Range(r).caret.Position());
2230 sel.Range(r).ClearVirtualSpace();
2231 } // else multiple selection so don't eat line ends
2232 } else {
2233 sel.Range(r).ClearVirtualSpace();
2234 }
2235 }
2236 } else {
2237 ClearSelection();
2238 }
2239 sel.RemoveDuplicates();
2240 ShowCaretAtCurrentPosition(); // Avoid blinking
2241 }
2242
SelectAll()2243 void Editor::SelectAll() {
2244 sel.Clear();
2245 SetSelection(0, pdoc->Length());
2246 Redraw();
2247 }
2248
Undo()2249 void Editor::Undo() {
2250 if (pdoc->CanUndo()) {
2251 InvalidateCaret();
2252 const Sci::Position newPos = pdoc->Undo();
2253 if (newPos >= 0)
2254 SetEmptySelection(newPos);
2255 EnsureCaretVisible();
2256 }
2257 }
2258
Redo()2259 void Editor::Redo() {
2260 if (pdoc->CanRedo()) {
2261 const Sci::Position newPos = pdoc->Redo();
2262 if (newPos >= 0)
2263 SetEmptySelection(newPos);
2264 EnsureCaretVisible();
2265 }
2266 }
2267
DelCharBack(bool allowLineStartDeletion)2268 void Editor::DelCharBack(bool allowLineStartDeletion) {
2269 RefreshStyleData();
2270 if (!sel.IsRectangular())
2271 FilterSelections();
2272 if (sel.IsRectangular())
2273 allowLineStartDeletion = false;
2274 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
2275 if (sel.Empty()) {
2276 for (size_t r=0; r<sel.Count(); r++) {
2277 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
2278 if (sel.Range(r).caret.VirtualSpace()) {
2279 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
2280 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
2281 } else {
2282 const Sci::Line lineCurrentPos =
2283 pdoc->SciLineFromPosition(sel.Range(r).caret.Position());
2284 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
2285 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
2286 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
2287 UndoGroup ugInner(pdoc, !ug.Needed());
2288 const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
2289 const int indentationStep = pdoc->IndentSize();
2290 int indentationChange = indentation % indentationStep;
2291 if (indentationChange == 0)
2292 indentationChange = indentationStep;
2293 const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationChange);
2294 // SetEmptySelection
2295 sel.Range(r) = SelectionRange(posSelect);
2296 } else {
2297 pdoc->DelCharBack(sel.Range(r).caret.Position());
2298 }
2299 }
2300 }
2301 } else {
2302 sel.Range(r).ClearVirtualSpace();
2303 }
2304 }
2305 ThinRectangularRange();
2306 } else {
2307 ClearSelection();
2308 }
2309 sel.RemoveDuplicates();
2310 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
2311 // Avoid blinking during rapid typing:
2312 ShowCaretAtCurrentPosition();
2313 }
2314
ModifierFlags(bool shift,bool ctrl,bool alt,bool meta,bool super)2315 int Editor::ModifierFlags(bool shift, bool ctrl, bool alt, bool meta, bool super) noexcept {
2316 return
2317 (shift ? SCI_SHIFT : 0) |
2318 (ctrl ? SCI_CTRL : 0) |
2319 (alt ? SCI_ALT : 0) |
2320 (meta ? SCI_META : 0) |
2321 (super ? SCI_SUPER : 0);
2322 }
2323
NotifyFocus(bool focus)2324 void Editor::NotifyFocus(bool focus) {
2325 SCNotification scn = {};
2326 scn.nmhdr.code = focus ? SCN_FOCUSIN : SCN_FOCUSOUT;
2327 NotifyParent(scn);
2328 }
2329
SetCtrlID(int identifier)2330 void Editor::SetCtrlID(int identifier) {
2331 ctrlID = identifier;
2332 }
2333
NotifyStyleToNeeded(Sci::Position endStyleNeeded)2334 void Editor::NotifyStyleToNeeded(Sci::Position endStyleNeeded) {
2335 SCNotification scn = {};
2336 scn.nmhdr.code = SCN_STYLENEEDED;
2337 scn.position = endStyleNeeded;
2338 NotifyParent(scn);
2339 }
2340
NotifyStyleNeeded(Document *,void *,Sci::Position endStyleNeeded)2341 void Editor::NotifyStyleNeeded(Document *, void *, Sci::Position endStyleNeeded) {
2342 NotifyStyleToNeeded(endStyleNeeded);
2343 }
2344
NotifyLexerChanged(Document *,void *)2345 void Editor::NotifyLexerChanged(Document *, void *) {
2346 }
2347
NotifyErrorOccurred(Document *,void *,int status)2348 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
2349 errorStatus = status;
2350 }
2351
NotifyChar(int ch,CharacterSource charSource)2352 void Editor::NotifyChar(int ch, CharacterSource charSource) {
2353 SCNotification scn = {};
2354 scn.nmhdr.code = SCN_CHARADDED;
2355 scn.ch = ch;
2356 scn.characterSource = static_cast<int>(charSource);
2357 NotifyParent(scn);
2358 }
2359
NotifySavePoint(bool isSavePoint)2360 void Editor::NotifySavePoint(bool isSavePoint) {
2361 SCNotification scn = {};
2362 if (isSavePoint) {
2363 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
2364 } else {
2365 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
2366 }
2367 NotifyParent(scn);
2368 }
2369
NotifyModifyAttempt()2370 void Editor::NotifyModifyAttempt() {
2371 SCNotification scn = {};
2372 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
2373 NotifyParent(scn);
2374 }
2375
NotifyDoubleClick(Point pt,int modifiers)2376 void Editor::NotifyDoubleClick(Point pt, int modifiers) {
2377 SCNotification scn = {};
2378 scn.nmhdr.code = SCN_DOUBLECLICK;
2379 scn.line = LineFromLocation(pt);
2380 scn.position = PositionFromLocation(pt, true);
2381 scn.modifiers = modifiers;
2382 NotifyParent(scn);
2383 }
2384
NotifyHotSpotDoubleClicked(Sci::Position position,int modifiers)2385 void Editor::NotifyHotSpotDoubleClicked(Sci::Position position, int modifiers) {
2386 SCNotification scn = {};
2387 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
2388 scn.position = position;
2389 scn.modifiers = modifiers;
2390 NotifyParent(scn);
2391 }
2392
NotifyHotSpotClicked(Sci::Position position,int modifiers)2393 void Editor::NotifyHotSpotClicked(Sci::Position position, int modifiers) {
2394 SCNotification scn = {};
2395 scn.nmhdr.code = SCN_HOTSPOTCLICK;
2396 scn.position = position;
2397 scn.modifiers = modifiers;
2398 NotifyParent(scn);
2399 }
2400
NotifyHotSpotReleaseClick(Sci::Position position,int modifiers)2401 void Editor::NotifyHotSpotReleaseClick(Sci::Position position, int modifiers) {
2402 SCNotification scn = {};
2403 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
2404 scn.position = position;
2405 scn.modifiers = modifiers;
2406 NotifyParent(scn);
2407 }
2408
NotifyUpdateUI()2409 bool Editor::NotifyUpdateUI() {
2410 if (needUpdateUI) {
2411 SCNotification scn = {};
2412 scn.nmhdr.code = SCN_UPDATEUI;
2413 scn.updated = needUpdateUI;
2414 NotifyParent(scn);
2415 needUpdateUI = 0;
2416 return true;
2417 }
2418 return false;
2419 }
2420
NotifyPainted()2421 void Editor::NotifyPainted() {
2422 SCNotification scn = {};
2423 scn.nmhdr.code = SCN_PAINTED;
2424 NotifyParent(scn);
2425 }
2426
NotifyIndicatorClick(bool click,Sci::Position position,int modifiers)2427 void Editor::NotifyIndicatorClick(bool click, Sci::Position position, int modifiers) {
2428 const int mask = pdoc->decorations->AllOnFor(position);
2429 if ((click && mask) || pdoc->decorations->ClickNotified()) {
2430 SCNotification scn = {};
2431 pdoc->decorations->SetClickNotified(click);
2432 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
2433 scn.modifiers = modifiers;
2434 scn.position = position;
2435 NotifyParent(scn);
2436 }
2437 }
2438
NotifyMarginClick(Point pt,int modifiers)2439 bool Editor::NotifyMarginClick(Point pt, int modifiers) {
2440 const int marginClicked = vs.MarginFromLocation(pt);
2441 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
2442 const Sci::Position position = pdoc->LineStart(LineFromLocation(pt));
2443 if ((vs.ms[marginClicked].mask & SC_MASK_FOLDERS) && (foldAutomatic & SC_AUTOMATICFOLD_CLICK)) {
2444 const bool ctrl = (modifiers & SCI_CTRL) != 0;
2445 const bool shift = (modifiers & SCI_SHIFT) != 0;
2446 const Sci::Line lineClick = pdoc->SciLineFromPosition(position);
2447 if (shift && ctrl) {
2448 FoldAll(SC_FOLDACTION_TOGGLE);
2449 } else {
2450 const int levelClick = pdoc->GetLevel(lineClick);
2451 if (levelClick & SC_FOLDLEVELHEADERFLAG) {
2452 if (shift) {
2453 // Ensure all children visible
2454 FoldExpand(lineClick, SC_FOLDACTION_EXPAND, levelClick);
2455 } else if (ctrl) {
2456 FoldExpand(lineClick, SC_FOLDACTION_TOGGLE, levelClick);
2457 } else {
2458 // Toggle this line
2459 FoldLine(lineClick, SC_FOLDACTION_TOGGLE);
2460 }
2461 }
2462 }
2463 return true;
2464 }
2465 SCNotification scn = {};
2466 scn.nmhdr.code = SCN_MARGINCLICK;
2467 scn.modifiers = modifiers;
2468 scn.position = position;
2469 scn.margin = marginClicked;
2470 NotifyParent(scn);
2471 return true;
2472 } else {
2473 return false;
2474 }
2475 }
2476
NotifyMarginRightClick(Point pt,int modifiers)2477 bool Editor::NotifyMarginRightClick(Point pt, int modifiers) {
2478 const int marginRightClicked = vs.MarginFromLocation(pt);
2479 if ((marginRightClicked >= 0) && vs.ms[marginRightClicked].sensitive) {
2480 const Sci::Position position = pdoc->LineStart(LineFromLocation(pt));
2481 SCNotification scn = {};
2482 scn.nmhdr.code = SCN_MARGINRIGHTCLICK;
2483 scn.modifiers = modifiers;
2484 scn.position = position;
2485 scn.margin = marginRightClicked;
2486 NotifyParent(scn);
2487 return true;
2488 } else {
2489 return false;
2490 }
2491 }
2492
NotifyNeedShown(Sci::Position pos,Sci::Position len)2493 void Editor::NotifyNeedShown(Sci::Position pos, Sci::Position len) {
2494 SCNotification scn = {};
2495 scn.nmhdr.code = SCN_NEEDSHOWN;
2496 scn.position = pos;
2497 scn.length = len;
2498 NotifyParent(scn);
2499 }
2500
NotifyDwelling(Point pt,bool state)2501 void Editor::NotifyDwelling(Point pt, bool state) {
2502 SCNotification scn = {};
2503 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
2504 scn.position = PositionFromLocation(pt, true);
2505 scn.x = static_cast<int>(pt.x + vs.ExternalMarginWidth());
2506 scn.y = static_cast<int>(pt.y);
2507 NotifyParent(scn);
2508 }
2509
NotifyZoom()2510 void Editor::NotifyZoom() {
2511 SCNotification scn = {};
2512 scn.nmhdr.code = SCN_ZOOM;
2513 NotifyParent(scn);
2514 }
2515
2516 // Notifications from document
NotifyModifyAttempt(Document *,void *)2517 void Editor::NotifyModifyAttempt(Document *, void *) {
2518 //Platform::DebugPrintf("** Modify Attempt\n");
2519 NotifyModifyAttempt();
2520 }
2521
NotifySavePoint(Document *,void *,bool atSavePoint)2522 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
2523 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
2524 NotifySavePoint(atSavePoint);
2525 }
2526
CheckModificationForWrap(DocModification mh)2527 void Editor::CheckModificationForWrap(DocModification mh) {
2528 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
2529 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2530 const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2531 const Sci::Line lines = std::max(static_cast<Sci::Line>(0), mh.linesAdded);
2532 if (Wrapping()) {
2533 NeedWrapping(lineDoc, lineDoc + lines + 1);
2534 }
2535 RefreshStyleData();
2536 // Fix up annotation heights
2537 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
2538 }
2539 }
2540
2541 namespace {
2542
2543 // Move a position so it is still after the same character as before the insertion.
MovePositionForInsertion(Sci::Position position,Sci::Position startInsertion,Sci::Position length)2544 constexpr Sci::Position MovePositionForInsertion(Sci::Position position, Sci::Position startInsertion, Sci::Position length) noexcept {
2545 if (position > startInsertion) {
2546 return position + length;
2547 }
2548 return position;
2549 }
2550
2551 // Move a position so it is still after the same character as before the deletion if that
2552 // character is still present else after the previous surviving character.
MovePositionForDeletion(Sci::Position position,Sci::Position startDeletion,Sci::Position length)2553 constexpr Sci::Position MovePositionForDeletion(Sci::Position position, Sci::Position startDeletion, Sci::Position length) noexcept {
2554 if (position > startDeletion) {
2555 const Sci::Position endDeletion = startDeletion + length;
2556 if (position > endDeletion) {
2557 return position - length;
2558 } else {
2559 return startDeletion;
2560 }
2561 } else {
2562 return position;
2563 }
2564 }
2565
2566 }
2567
NotifyModified(Document *,DocModification mh,void *)2568 void Editor::NotifyModified(Document *, DocModification mh, void *) {
2569 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
2570 if (paintState == painting) {
2571 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
2572 }
2573 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
2574 if (paintState == painting) {
2575 CheckForChangeOutsidePaint(
2576 Range(pdoc->LineStart(mh.line),
2577 pdoc->LineStart(mh.line + 1)));
2578 } else {
2579 // Could check that change is before last visible line.
2580 Redraw();
2581 }
2582 }
2583 if (mh.modificationType & SC_MOD_CHANGETABSTOPS) {
2584 Redraw();
2585 }
2586 if (mh.modificationType & SC_MOD_LEXERSTATE) {
2587 if (paintState == painting) {
2588 CheckForChangeOutsidePaint(
2589 Range(mh.position, mh.position + mh.length));
2590 } else {
2591 Redraw();
2592 }
2593 }
2594 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
2595 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2596 pdoc->IncrementStyleClock();
2597 }
2598 if (paintState == notPainting) {
2599 const Sci::Line lineDocTop = pcs->DocFromDisplay(topLine);
2600 if (mh.position < pdoc->LineStart(lineDocTop)) {
2601 // Styling performed before this view
2602 Redraw();
2603 } else {
2604 InvalidateRange(mh.position, mh.position + mh.length);
2605 }
2606 }
2607 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
2608 view.llc.Invalidate(LineLayout::llCheckTextAndStyle);
2609 }
2610 } else {
2611 // Move selection and brace highlights
2612 if (mh.modificationType & SC_MOD_INSERTTEXT) {
2613 sel.MovePositions(true, mh.position, mh.length);
2614 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
2615 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
2616 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
2617 sel.MovePositions(false, mh.position, mh.length);
2618 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
2619 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
2620 }
2621 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && pcs->HiddenLines()) {
2622 // Some lines are hidden so may need shown.
2623 const Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2624 Sci::Position endNeedShown = mh.position;
2625 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
2626 if (pdoc->ContainsLineEnd(mh.text, mh.length) && (mh.position != pdoc->LineStart(lineOfPos)))
2627 endNeedShown = pdoc->LineStart(lineOfPos+1);
2628 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
2629 // If the deletion includes any EOL then we extend the need shown area.
2630 endNeedShown = mh.position + mh.length;
2631 Sci::Line lineLast = pdoc->SciLineFromPosition(mh.position+mh.length);
2632 for (Sci::Line line = lineOfPos + 1; line <= lineLast; line++) {
2633 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, -1, -1);
2634 if (lineLast < lineMaxSubord) {
2635 lineLast = lineMaxSubord;
2636 endNeedShown = pdoc->LineEnd(lineLast);
2637 }
2638 }
2639 }
2640 NeedShown(mh.position, endNeedShown - mh.position);
2641 }
2642 if (mh.linesAdded != 0) {
2643 // Update contraction state for inserted and removed lines
2644 // lineOfPos should be calculated in context of state before modification, shouldn't it
2645 Sci::Line lineOfPos = pdoc->SciLineFromPosition(mh.position);
2646 if (mh.position > pdoc->LineStart(lineOfPos))
2647 lineOfPos++; // Affecting subsequent lines
2648 if (mh.linesAdded > 0) {
2649 pcs->InsertLines(lineOfPos, mh.linesAdded);
2650 } else {
2651 pcs->DeleteLines(lineOfPos, -mh.linesAdded);
2652 }
2653 view.LinesAddedOrRemoved(lineOfPos, mh.linesAdded);
2654 }
2655 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
2656 const Sci::Line lineDoc = pdoc->SciLineFromPosition(mh.position);
2657 if (vs.annotationVisible) {
2658 if (pcs->SetHeight(lineDoc, pcs->GetHeight(lineDoc) + static_cast<int>(mh.annotationLinesAdded))) {
2659 SetScrollBars();
2660 }
2661 Redraw();
2662 }
2663 }
2664 CheckModificationForWrap(mh);
2665 if (mh.linesAdded != 0) {
2666 // Avoid scrolling of display if change before current display
2667 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
2668 const Sci::Line newTop = std::clamp<Sci::Line>(topLine + mh.linesAdded, 0, MaxScrollPos());
2669 if (newTop != topLine) {
2670 SetTopLine(newTop);
2671 SetVerticalScrollPos();
2672 }
2673 }
2674
2675 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
2676 if (SynchronousStylingToVisible()) {
2677 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
2678 }
2679 Redraw();
2680 }
2681 } else {
2682 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
2683 if (SynchronousStylingToVisible()) {
2684 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
2685 }
2686 InvalidateRange(mh.position, mh.position + mh.length);
2687 }
2688 }
2689 }
2690
2691 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
2692 SetScrollBars();
2693 }
2694
2695 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
2696 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
2697 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
2698 // Fold changes can affect the drawing of following lines so redraw whole margin
2699 RedrawSelMargin(marginView.highlightDelimiter.isEnabled ? -1 : mh.line - 1, true);
2700 } else {
2701 RedrawSelMargin(mh.line);
2702 }
2703 }
2704 }
2705 if ((mh.modificationType & SC_MOD_CHANGEFOLD) && (foldAutomatic & SC_AUTOMATICFOLD_CHANGE)) {
2706 FoldChanged(mh.line, mh.foldLevelNow, mh.foldLevelPrev);
2707 }
2708
2709 // NOW pay the piper WRT "deferred" visual updates
2710 if (IsLastStep(mh)) {
2711 SetScrollBars();
2712 Redraw();
2713 }
2714
2715 // If client wants to see this modification
2716 if (mh.modificationType & modEventMask) {
2717 if (commandEvents) {
2718 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
2719 // Real modification made to text of document.
2720 NotifyChange(); // Send EN_CHANGE
2721 }
2722 }
2723
2724 SCNotification scn = {};
2725 scn.nmhdr.code = SCN_MODIFIED;
2726 scn.position = mh.position;
2727 scn.modificationType = mh.modificationType;
2728 scn.text = mh.text;
2729 scn.length = mh.length;
2730 scn.linesAdded = mh.linesAdded;
2731 scn.line = mh.line;
2732 scn.foldLevelNow = mh.foldLevelNow;
2733 scn.foldLevelPrev = mh.foldLevelPrev;
2734 scn.token = static_cast<int>(mh.token);
2735 scn.annotationLinesAdded = mh.annotationLinesAdded;
2736 NotifyParent(scn);
2737 }
2738 }
2739
NotifyDeleted(Document *,void *)2740 void Editor::NotifyDeleted(Document *, void *) noexcept {
2741 /* Do nothing */
2742 }
2743
NotifyMacroRecord(unsigned int iMessage,uptr_t wParam,sptr_t lParam)2744 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
2745
2746 // Enumerates all macroable messages
2747 switch (iMessage) {
2748 case SCI_CUT:
2749 case SCI_COPY:
2750 case SCI_PASTE:
2751 case SCI_CLEAR:
2752 case SCI_REPLACESEL:
2753 case SCI_ADDTEXT:
2754 case SCI_INSERTTEXT:
2755 case SCI_APPENDTEXT:
2756 case SCI_CLEARALL:
2757 case SCI_SELECTALL:
2758 case SCI_GOTOLINE:
2759 case SCI_GOTOPOS:
2760 case SCI_SEARCHANCHOR:
2761 case SCI_SEARCHNEXT:
2762 case SCI_SEARCHPREV:
2763 case SCI_LINEDOWN:
2764 case SCI_LINEDOWNEXTEND:
2765 case SCI_PARADOWN:
2766 case SCI_PARADOWNEXTEND:
2767 case SCI_LINEUP:
2768 case SCI_LINEUPEXTEND:
2769 case SCI_PARAUP:
2770 case SCI_PARAUPEXTEND:
2771 case SCI_CHARLEFT:
2772 case SCI_CHARLEFTEXTEND:
2773 case SCI_CHARRIGHT:
2774 case SCI_CHARRIGHTEXTEND:
2775 case SCI_WORDLEFT:
2776 case SCI_WORDLEFTEXTEND:
2777 case SCI_WORDRIGHT:
2778 case SCI_WORDRIGHTEXTEND:
2779 case SCI_WORDPARTLEFT:
2780 case SCI_WORDPARTLEFTEXTEND:
2781 case SCI_WORDPARTRIGHT:
2782 case SCI_WORDPARTRIGHTEXTEND:
2783 case SCI_WORDLEFTEND:
2784 case SCI_WORDLEFTENDEXTEND:
2785 case SCI_WORDRIGHTEND:
2786 case SCI_WORDRIGHTENDEXTEND:
2787 case SCI_HOME:
2788 case SCI_HOMEEXTEND:
2789 case SCI_LINEEND:
2790 case SCI_LINEENDEXTEND:
2791 case SCI_HOMEWRAP:
2792 case SCI_HOMEWRAPEXTEND:
2793 case SCI_LINEENDWRAP:
2794 case SCI_LINEENDWRAPEXTEND:
2795 case SCI_DOCUMENTSTART:
2796 case SCI_DOCUMENTSTARTEXTEND:
2797 case SCI_DOCUMENTEND:
2798 case SCI_DOCUMENTENDEXTEND:
2799 case SCI_STUTTEREDPAGEUP:
2800 case SCI_STUTTEREDPAGEUPEXTEND:
2801 case SCI_STUTTEREDPAGEDOWN:
2802 case SCI_STUTTEREDPAGEDOWNEXTEND:
2803 case SCI_PAGEUP:
2804 case SCI_PAGEUPEXTEND:
2805 case SCI_PAGEDOWN:
2806 case SCI_PAGEDOWNEXTEND:
2807 case SCI_EDITTOGGLEOVERTYPE:
2808 case SCI_CANCEL:
2809 case SCI_DELETEBACK:
2810 case SCI_TAB:
2811 case SCI_BACKTAB:
2812 case SCI_FORMFEED:
2813 case SCI_VCHOME:
2814 case SCI_VCHOMEEXTEND:
2815 case SCI_VCHOMEWRAP:
2816 case SCI_VCHOMEWRAPEXTEND:
2817 case SCI_VCHOMEDISPLAY:
2818 case SCI_VCHOMEDISPLAYEXTEND:
2819 case SCI_DELWORDLEFT:
2820 case SCI_DELWORDRIGHT:
2821 case SCI_DELWORDRIGHTEND:
2822 case SCI_DELLINELEFT:
2823 case SCI_DELLINERIGHT:
2824 case SCI_LINECOPY:
2825 case SCI_LINECUT:
2826 case SCI_LINEDELETE:
2827 case SCI_LINETRANSPOSE:
2828 case SCI_LINEREVERSE:
2829 case SCI_LINEDUPLICATE:
2830 case SCI_LOWERCASE:
2831 case SCI_UPPERCASE:
2832 case SCI_LINESCROLLDOWN:
2833 case SCI_LINESCROLLUP:
2834 case SCI_DELETEBACKNOTLINE:
2835 case SCI_HOMEDISPLAY:
2836 case SCI_HOMEDISPLAYEXTEND:
2837 case SCI_LINEENDDISPLAY:
2838 case SCI_LINEENDDISPLAYEXTEND:
2839 case SCI_SETSELECTIONMODE:
2840 case SCI_LINEDOWNRECTEXTEND:
2841 case SCI_LINEUPRECTEXTEND:
2842 case SCI_CHARLEFTRECTEXTEND:
2843 case SCI_CHARRIGHTRECTEXTEND:
2844 case SCI_HOMERECTEXTEND:
2845 case SCI_VCHOMERECTEXTEND:
2846 case SCI_LINEENDRECTEXTEND:
2847 case SCI_PAGEUPRECTEXTEND:
2848 case SCI_PAGEDOWNRECTEXTEND:
2849 case SCI_SELECTIONDUPLICATE:
2850 case SCI_COPYALLOWLINE:
2851 case SCI_VERTICALCENTRECARET:
2852 case SCI_MOVESELECTEDLINESUP:
2853 case SCI_MOVESELECTEDLINESDOWN:
2854 case SCI_SCROLLTOSTART:
2855 case SCI_SCROLLTOEND:
2856 break;
2857
2858 // Filter out all others like display changes. Also, newlines are redundant
2859 // with char insert messages.
2860 case SCI_NEWLINE:
2861 default:
2862 // printf("Filtered out %ld of macro recording\n", iMessage);
2863 return;
2864 }
2865
2866 // Send notification
2867 SCNotification scn = {};
2868 scn.nmhdr.code = SCN_MACRORECORD;
2869 scn.message = iMessage;
2870 scn.wParam = wParam;
2871 scn.lParam = lParam;
2872 NotifyParent(scn);
2873 }
2874
2875 // Something has changed that the container should know about
ContainerNeedsUpdate(int flags)2876 void Editor::ContainerNeedsUpdate(int flags) noexcept {
2877 needUpdateUI |= flags;
2878 }
2879
2880 /**
2881 * Force scroll and keep position relative to top of window.
2882 *
2883 * If stuttered = true and not already at first/last row, move to first/last row of window.
2884 * If stuttered = true and already at first/last row, scroll as normal.
2885 */
PageMove(int direction,Selection::selTypes selt,bool stuttered)2886 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
2887 Sci::Line topLineNew;
2888 SelectionPosition newPos;
2889
2890 const Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());
2891 const Sci::Line topStutterLine = topLine + caretPolicies.y.slop;
2892 const Sci::Line bottomStutterLine =
2893 pdoc->SciLineFromPosition(PositionFromLocation(
2894 Point::FromInts(lastXChosen - xOffset, direction * vs.lineHeight * static_cast<int>(LinesToScroll()))))
2895 - caretPolicies.y.slop - 1;
2896
2897 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
2898 topLineNew = topLine;
2899 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * caretPolicies.y.slop),
2900 false, false, UserVirtualSpace());
2901
2902 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
2903 topLineNew = topLine;
2904 newPos = SPositionFromLocation(Point::FromInts(lastXChosen - xOffset, vs.lineHeight * static_cast<int>(LinesToScroll() - caretPolicies.y.slop)),
2905 false, false, UserVirtualSpace());
2906
2907 } else {
2908 const Point pt = LocationFromPosition(sel.MainCaret());
2909
2910 topLineNew = std::clamp<Sci::Line>(
2911 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
2912 newPos = SPositionFromLocation(
2913 Point::FromInts(lastXChosen - xOffset, static_cast<int>(pt.y) +
2914 direction * (vs.lineHeight * static_cast<int>(LinesToScroll()))),
2915 false, false, UserVirtualSpace());
2916 }
2917
2918 if (topLineNew != topLine) {
2919 SetTopLine(topLineNew);
2920 MovePositionTo(newPos, selt);
2921 Redraw();
2922 SetVerticalScrollPos();
2923 } else {
2924 MovePositionTo(newPos, selt);
2925 }
2926 }
2927
ChangeCaseOfSelection(int caseMapping)2928 void Editor::ChangeCaseOfSelection(int caseMapping) {
2929 UndoGroup ug(pdoc);
2930 for (size_t r=0; r<sel.Count(); r++) {
2931 SelectionRange current = sel.Range(r);
2932 SelectionRange currentNoVS = current;
2933 currentNoVS.ClearVirtualSpace();
2934 const size_t rangeBytes = currentNoVS.Length();
2935 if (rangeBytes > 0) {
2936 std::string sText = RangeText(currentNoVS.Start().Position(), currentNoVS.End().Position());
2937
2938 std::string sMapped = CaseMapString(sText, caseMapping);
2939
2940 if (sMapped != sText) {
2941 size_t firstDifference = 0;
2942 while (sMapped[firstDifference] == sText[firstDifference])
2943 firstDifference++;
2944 size_t lastDifferenceText = sText.size() - 1;
2945 size_t lastDifferenceMapped = sMapped.size() - 1;
2946 while (sMapped[lastDifferenceMapped] == sText[lastDifferenceText]) {
2947 lastDifferenceText--;
2948 lastDifferenceMapped--;
2949 }
2950 const size_t endDifferenceText = sText.size() - 1 - lastDifferenceText;
2951 pdoc->DeleteChars(
2952 currentNoVS.Start().Position() + firstDifference,
2953 rangeBytes - firstDifference - endDifferenceText);
2954 const Sci::Position lengthChange = lastDifferenceMapped - firstDifference + 1;
2955 const Sci::Position lengthInserted = pdoc->InsertString(
2956 currentNoVS.Start().Position() + firstDifference,
2957 sMapped.c_str() + firstDifference,
2958 lengthChange);
2959 // Automatic movement changes selection so reset to exactly the same as it was.
2960 const Sci::Position diffSizes = sMapped.size() - sText.size() + lengthInserted - lengthChange;
2961 if (diffSizes != 0) {
2962 if (current.anchor > current.caret)
2963 current.anchor.Add(diffSizes);
2964 else
2965 current.caret.Add(diffSizes);
2966 }
2967 sel.Range(r) = current;
2968 }
2969 }
2970 }
2971 }
2972
LineTranspose()2973 void Editor::LineTranspose() {
2974 const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
2975 if (line > 0) {
2976 UndoGroup ug(pdoc);
2977
2978 const Sci::Position startPrevious = pdoc->LineStart(line - 1);
2979 const std::string linePrevious = RangeText(startPrevious, pdoc->LineEnd(line - 1));
2980
2981 Sci::Position startCurrent = pdoc->LineStart(line);
2982 const std::string lineCurrent = RangeText(startCurrent, pdoc->LineEnd(line));
2983
2984 pdoc->DeleteChars(startCurrent, lineCurrent.length());
2985 pdoc->DeleteChars(startPrevious, linePrevious.length());
2986 startCurrent -= linePrevious.length();
2987
2988 startCurrent += pdoc->InsertString(startPrevious, lineCurrent.c_str(),
2989 lineCurrent.length());
2990 pdoc->InsertString(startCurrent, linePrevious.c_str(),
2991 linePrevious.length());
2992 // Move caret to start of current line
2993 MovePositionTo(SelectionPosition(startCurrent));
2994 }
2995 }
2996
LineReverse()2997 void Editor::LineReverse() {
2998 const Sci::Line lineStart =
2999 pdoc->SciLineFromPosition(sel.RangeMain().Start().Position());
3000 const Sci::Line lineEnd =
3001 pdoc->SciLineFromPosition(sel.RangeMain().End().Position()-1);
3002 const Sci::Line lineDiff = lineEnd - lineStart;
3003 if (lineDiff <= 0)
3004 return;
3005 UndoGroup ug(pdoc);
3006 for (Sci::Line i=(lineDiff+1)/2-1; i>=0; --i) {
3007 const Sci::Line lineNum2 = lineEnd - i;
3008 const Sci::Line lineNum1 = lineStart + i;
3009 Sci::Position lineStart2 = pdoc->LineStart(lineNum2);
3010 const Sci::Position lineStart1 = pdoc->LineStart(lineNum1);
3011 const std::string line2 = RangeText(lineStart2, pdoc->LineEnd(lineNum2));
3012 const std::string line1 = RangeText(lineStart1, pdoc->LineEnd(lineNum1));
3013 const Sci::Position lineLen2 = line2.length();
3014 const Sci::Position lineLen1 = line1.length();
3015 pdoc->DeleteChars(lineStart2, lineLen2);
3016 pdoc->DeleteChars(lineStart1, lineLen1);
3017 lineStart2 -= lineLen1;
3018 pdoc->InsertString(lineStart2, line1.c_str(), lineLen1);
3019 pdoc->InsertString(lineStart1, line2.c_str(), lineLen2);
3020 }
3021 // Wholly select all affected lines
3022 sel.RangeMain() = SelectionRange(pdoc->LineStart(lineStart),
3023 pdoc->LineStart(lineEnd+1));
3024 }
3025
Duplicate(bool forLine)3026 void Editor::Duplicate(bool forLine) {
3027 if (sel.Empty()) {
3028 forLine = true;
3029 }
3030 UndoGroup ug(pdoc);
3031 const char *eol = "";
3032 Sci::Position eolLen = 0;
3033 if (forLine) {
3034 eol = StringFromEOLMode(pdoc->eolMode);
3035 eolLen = strlen(eol);
3036 }
3037 for (size_t r=0; r<sel.Count(); r++) {
3038 SelectionPosition start = sel.Range(r).Start();
3039 SelectionPosition end = sel.Range(r).End();
3040 if (forLine) {
3041 const Sci::Line line = pdoc->SciLineFromPosition(sel.Range(r).caret.Position());
3042 start = SelectionPosition(pdoc->LineStart(line));
3043 end = SelectionPosition(pdoc->LineEnd(line));
3044 }
3045 std::string text = RangeText(start.Position(), end.Position());
3046 Sci::Position lengthInserted = eolLen;
3047 if (forLine)
3048 lengthInserted = pdoc->InsertString(end.Position(), eol, eolLen);
3049 pdoc->InsertString(end.Position() + lengthInserted, text.c_str(), text.length());
3050 }
3051 if (sel.Count() && sel.IsRectangular()) {
3052 SelectionPosition last = sel.Last();
3053 if (forLine) {
3054 const Sci::Line line = pdoc->SciLineFromPosition(last.Position());
3055 last = SelectionPosition(last.Position() +
3056 pdoc->LineStart(line+1) - pdoc->LineStart(line));
3057 }
3058 if (sel.Rectangular().anchor > sel.Rectangular().caret)
3059 sel.Rectangular().anchor = last;
3060 else
3061 sel.Rectangular().caret = last;
3062 SetRectangularRange();
3063 }
3064 }
3065
CancelModes()3066 void Editor::CancelModes() {
3067 sel.SetMoveExtends(false);
3068 }
3069
NewLine()3070 void Editor::NewLine() {
3071 InvalidateWholeSelection();
3072 if (sel.IsRectangular() || !additionalSelectionTyping) {
3073 // Remove non-main ranges
3074 sel.DropAdditionalRanges();
3075 }
3076
3077 UndoGroup ug(pdoc, !sel.Empty() || (sel.Count() > 1));
3078
3079 // Clear each range
3080 if (!sel.Empty()) {
3081 ClearSelection();
3082 }
3083
3084 // Insert each line end
3085 size_t countInsertions = 0;
3086 for (size_t r = 0; r < sel.Count(); r++) {
3087 sel.Range(r).ClearVirtualSpace();
3088 const char *eol = StringFromEOLMode(pdoc->eolMode);
3089 const Sci::Position positionInsert = sel.Range(r).caret.Position();
3090 const Sci::Position insertLength = pdoc->InsertString(positionInsert, eol, strlen(eol));
3091 if (insertLength > 0) {
3092 sel.Range(r) = SelectionRange(positionInsert + insertLength);
3093 countInsertions++;
3094 }
3095 }
3096
3097 // Perform notifications after all the changes as the application may change the
3098 // selections in response to the characters.
3099 for (size_t i = 0; i < countInsertions; i++) {
3100 const char *eol = StringFromEOLMode(pdoc->eolMode);
3101 while (*eol) {
3102 NotifyChar(*eol, CharacterSource::directInput);
3103 if (recordingMacro) {
3104 char txt[2];
3105 txt[0] = *eol;
3106 txt[1] = '\0';
3107 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3108 }
3109 eol++;
3110 }
3111 }
3112
3113 SetLastXChosen();
3114 SetScrollBars();
3115 EnsureCaretVisible();
3116 // Avoid blinking during rapid typing:
3117 ShowCaretAtCurrentPosition();
3118 }
3119
PositionUpOrDown(SelectionPosition spStart,int direction,int lastX)3120 SelectionPosition Editor::PositionUpOrDown(SelectionPosition spStart, int direction, int lastX) {
3121 const Point pt = LocationFromPosition(spStart);
3122 int skipLines = 0;
3123
3124 if (vs.annotationVisible) {
3125 const Sci::Line lineDoc = pdoc->SciLineFromPosition(spStart.Position());
3126 const Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
3127 const int subLine = static_cast<int>(pt.y - ptStartLine.y) / vs.lineHeight;
3128
3129 if (direction < 0 && subLine == 0) {
3130 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
3131 if (lineDisplay > 0) {
3132 skipLines = pdoc->AnnotationLines(pcs->DocFromDisplay(lineDisplay - 1));
3133 }
3134 } else if (direction > 0 && subLine >= (pcs->GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
3135 skipLines = pdoc->AnnotationLines(lineDoc);
3136 }
3137 }
3138
3139 const Sci::Line newY = static_cast<Sci::Line>(pt.y) + (1 + skipLines) * direction * vs.lineHeight;
3140 if (lastX < 0) {
3141 lastX = static_cast<int>(pt.x) + xOffset;
3142 }
3143 SelectionPosition posNew = SPositionFromLocation(
3144 Point::FromInts(lastX - xOffset, static_cast<int>(newY)), false, false, UserVirtualSpace());
3145
3146 if (direction < 0) {
3147 // Line wrapping may lead to a location on the same line, so
3148 // seek back if that is the case.
3149 Point ptNew = LocationFromPosition(posNew.Position());
3150 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
3151 posNew.Add(-1);
3152 posNew.SetVirtualSpace(0);
3153 ptNew = LocationFromPosition(posNew.Position());
3154 }
3155 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
3156 // There is an equivalent case when moving down which skips
3157 // over a line.
3158 Point ptNew = LocationFromPosition(posNew.Position());
3159 while ((posNew.Position() > spStart.Position()) && (ptNew.y > newY)) {
3160 posNew.Add(-1);
3161 posNew.SetVirtualSpace(0);
3162 ptNew = LocationFromPosition(posNew.Position());
3163 }
3164 }
3165 return posNew;
3166 }
3167
CursorUpOrDown(int direction,Selection::selTypes selt)3168 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
3169 if ((selt == Selection::noSel) && sel.MoveExtends()) {
3170 selt = !sel.IsRectangular() ? Selection::selStream : Selection::selRectangle;
3171 }
3172 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
3173 if (sel.IsRectangular()) {
3174 if (selt == Selection::noSel) {
3175 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
3176 } else {
3177 caretToUse = sel.Rectangular().caret;
3178 }
3179 }
3180 if (selt == Selection::selRectangle) {
3181 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3182 if (!sel.IsRectangular()) {
3183 InvalidateWholeSelection();
3184 sel.DropAdditionalRanges();
3185 }
3186 const SelectionPosition posNew = MovePositionSoVisible(
3187 PositionUpOrDown(caretToUse, direction, lastXChosen), direction);
3188 sel.selType = Selection::selRectangle;
3189 sel.Rectangular() = SelectionRange(posNew, rangeBase.anchor);
3190 SetRectangularRange();
3191 MovedCaret(posNew, caretToUse, true, caretPolicies);
3192 } else if (sel.selType == Selection::selLines && sel.MoveExtends()) {
3193 // Calculate new caret position and call SetSelection(), which will ensure whole lines are selected.
3194 const SelectionPosition posNew = MovePositionSoVisible(
3195 PositionUpOrDown(caretToUse, direction, -1), direction);
3196 SetSelection(posNew, sel.Range(sel.Main()).anchor);
3197 } else {
3198 InvalidateWholeSelection();
3199 if (!additionalSelectionTyping || (sel.IsRectangular())) {
3200 sel.DropAdditionalRanges();
3201 }
3202 sel.selType = Selection::selStream;
3203 for (size_t r = 0; r < sel.Count(); r++) {
3204 const int lastX = (r == sel.Main()) ? lastXChosen : -1;
3205 const SelectionPosition spCaretNow = sel.Range(r).caret;
3206 const SelectionPosition posNew = MovePositionSoVisible(
3207 PositionUpOrDown(spCaretNow, direction, lastX), direction);
3208 sel.Range(r) = selt == Selection::selStream ?
3209 SelectionRange(posNew, sel.Range(r).anchor) : SelectionRange(posNew);
3210 }
3211 sel.RemoveDuplicates();
3212 MovedCaret(sel.RangeMain().caret, caretToUse, true, caretPolicies);
3213 }
3214 }
3215
ParaUpOrDown(int direction,Selection::selTypes selt)3216 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
3217 Sci::Line lineDoc;
3218 const Sci::Position savedPos = sel.MainCaret();
3219 do {
3220 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
3221 lineDoc = pdoc->SciLineFromPosition(sel.MainCaret());
3222 if (direction > 0) {
3223 if (sel.MainCaret() >= pdoc->Length() && !pcs->GetVisible(lineDoc)) {
3224 if (selt == Selection::noSel) {
3225 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
3226 }
3227 break;
3228 }
3229 }
3230 } while (!pcs->GetVisible(lineDoc));
3231 }
3232
RangeDisplayLine(Sci::Line lineVisible)3233 Range Editor::RangeDisplayLine(Sci::Line lineVisible) {
3234 RefreshStyleData();
3235 AutoSurface surface(this);
3236 return view.RangeDisplayLine(surface, *this, lineVisible, vs);
3237 }
3238
StartEndDisplayLine(Sci::Position pos,bool start)3239 Sci::Position Editor::StartEndDisplayLine(Sci::Position pos, bool start) {
3240 RefreshStyleData();
3241 AutoSurface surface(this);
3242 const Sci::Position posRet = view.StartEndDisplayLine(surface, *this, pos, start, vs);
3243 if (posRet == INVALID_POSITION) {
3244 return pos;
3245 } else {
3246 return posRet;
3247 }
3248 }
3249
3250 namespace {
3251
HighShortFromWParam(uptr_t x)3252 constexpr short HighShortFromWParam(uptr_t x) {
3253 return static_cast<short>(x >> 16);
3254 }
3255
LowShortFromWParam(uptr_t x)3256 constexpr short LowShortFromWParam(uptr_t x) {
3257 return static_cast<short>(x & 0xffff);
3258 }
3259
WithExtends(unsigned int iMessage)3260 constexpr unsigned int WithExtends(unsigned int iMessage) noexcept {
3261 switch (iMessage) {
3262 case SCI_CHARLEFT: return SCI_CHARLEFTEXTEND;
3263 case SCI_CHARRIGHT: return SCI_CHARRIGHTEXTEND;
3264
3265 case SCI_WORDLEFT: return SCI_WORDLEFTEXTEND;
3266 case SCI_WORDRIGHT: return SCI_WORDRIGHTEXTEND;
3267 case SCI_WORDLEFTEND: return SCI_WORDLEFTENDEXTEND;
3268 case SCI_WORDRIGHTEND: return SCI_WORDRIGHTENDEXTEND;
3269 case SCI_WORDPARTLEFT: return SCI_WORDPARTLEFTEXTEND;
3270 case SCI_WORDPARTRIGHT: return SCI_WORDPARTRIGHTEXTEND;
3271
3272 case SCI_HOME: return SCI_HOMEEXTEND;
3273 case SCI_HOMEDISPLAY: return SCI_HOMEDISPLAYEXTEND;
3274 case SCI_HOMEWRAP: return SCI_HOMEWRAPEXTEND;
3275 case SCI_VCHOME: return SCI_VCHOMEEXTEND;
3276 case SCI_VCHOMEDISPLAY: return SCI_VCHOMEDISPLAYEXTEND;
3277 case SCI_VCHOMEWRAP: return SCI_VCHOMEWRAPEXTEND;
3278
3279 case SCI_LINEEND: return SCI_LINEENDEXTEND;
3280 case SCI_LINEENDDISPLAY: return SCI_LINEENDDISPLAYEXTEND;
3281 case SCI_LINEENDWRAP: return SCI_LINEENDWRAPEXTEND;
3282
3283 default: return iMessage;
3284 }
3285 }
3286
NaturalDirection(unsigned int iMessage)3287 constexpr int NaturalDirection(unsigned int iMessage) noexcept {
3288 switch (iMessage) {
3289 case SCI_CHARLEFT:
3290 case SCI_CHARLEFTEXTEND:
3291 case SCI_CHARLEFTRECTEXTEND:
3292 case SCI_WORDLEFT:
3293 case SCI_WORDLEFTEXTEND:
3294 case SCI_WORDLEFTEND:
3295 case SCI_WORDLEFTENDEXTEND:
3296 case SCI_WORDPARTLEFT:
3297 case SCI_WORDPARTLEFTEXTEND:
3298 case SCI_HOME:
3299 case SCI_HOMEEXTEND:
3300 case SCI_HOMEDISPLAY:
3301 case SCI_HOMEDISPLAYEXTEND:
3302 case SCI_HOMEWRAP:
3303 case SCI_HOMEWRAPEXTEND:
3304 // VC_HOME* mostly goes back
3305 case SCI_VCHOME:
3306 case SCI_VCHOMEEXTEND:
3307 case SCI_VCHOMEDISPLAY:
3308 case SCI_VCHOMEDISPLAYEXTEND:
3309 case SCI_VCHOMEWRAP:
3310 case SCI_VCHOMEWRAPEXTEND:
3311 return -1;
3312
3313 default:
3314 return 1;
3315 }
3316 }
3317
IsRectExtend(unsigned int iMessage,bool isRectMoveExtends)3318 constexpr bool IsRectExtend(unsigned int iMessage, bool isRectMoveExtends) noexcept {
3319 switch (iMessage) {
3320 case SCI_CHARLEFTRECTEXTEND:
3321 case SCI_CHARRIGHTRECTEXTEND:
3322 case SCI_HOMERECTEXTEND:
3323 case SCI_VCHOMERECTEXTEND:
3324 case SCI_LINEENDRECTEXTEND:
3325 return true;
3326 default:
3327 if (isRectMoveExtends) {
3328 // Handle SCI_SETSELECTIONMODE(SC_SEL_RECTANGLE) and subsequent movements.
3329 switch (iMessage) {
3330 case SCI_CHARLEFTEXTEND:
3331 case SCI_CHARRIGHTEXTEND:
3332 case SCI_HOMEEXTEND:
3333 case SCI_VCHOMEEXTEND:
3334 case SCI_LINEENDEXTEND:
3335 return true;
3336 default:
3337 return false;
3338 }
3339 }
3340 return false;
3341 }
3342 }
3343
3344 }
3345
VCHomeDisplayPosition(Sci::Position position)3346 Sci::Position Editor::VCHomeDisplayPosition(Sci::Position position) {
3347 const Sci::Position homePos = pdoc->VCHomePosition(position);
3348 const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3349 if (viewLineStart > homePos)
3350 return viewLineStart;
3351 else
3352 return homePos;
3353 }
3354
VCHomeWrapPosition(Sci::Position position)3355 Sci::Position Editor::VCHomeWrapPosition(Sci::Position position) {
3356 const Sci::Position homePos = pdoc->VCHomePosition(position);
3357 const Sci::Position viewLineStart = StartEndDisplayLine(position, true);
3358 if ((viewLineStart < position) && (viewLineStart > homePos))
3359 return viewLineStart;
3360 else
3361 return homePos;
3362 }
3363
LineEndWrapPosition(Sci::Position position)3364 Sci::Position Editor::LineEndWrapPosition(Sci::Position position) {
3365 const Sci::Position endPos = StartEndDisplayLine(position, false);
3366 const Sci::Position realEndPos = pdoc->LineEndPosition(position);
3367 if (endPos > realEndPos // if moved past visible EOLs
3368 || position >= endPos) // if at end of display line already
3369 return realEndPos;
3370 else
3371 return endPos;
3372 }
3373
HorizontalMove(unsigned int iMessage)3374 int Editor::HorizontalMove(unsigned int iMessage) {
3375 if (sel.selType == Selection::selLines) {
3376 return 0; // horizontal moves with line selection have no effect
3377 }
3378 if (sel.MoveExtends()) {
3379 iMessage = WithExtends(iMessage);
3380 }
3381
3382 if (!multipleSelection && !sel.IsRectangular()) {
3383 // Simplify selection down to 1
3384 sel.SetSelection(sel.RangeMain());
3385 }
3386
3387 // Invalidate each of the current selections
3388 InvalidateWholeSelection();
3389
3390 if (IsRectExtend(iMessage, sel.IsRectangular() && sel.MoveExtends())) {
3391 const SelectionRange rangeBase = sel.IsRectangular() ? sel.Rectangular() : sel.RangeMain();
3392 if (!sel.IsRectangular()) {
3393 sel.DropAdditionalRanges();
3394 }
3395 // Will change to rectangular if not currently rectangular
3396 SelectionPosition spCaret = rangeBase.caret;
3397 switch (iMessage) {
3398 case SCI_CHARLEFTRECTEXTEND:
3399 case SCI_CHARLEFTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3400 if (pdoc->IsLineEndPosition(spCaret.Position()) && spCaret.VirtualSpace()) {
3401 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3402 } else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3403 spCaret = SelectionPosition(spCaret.Position() - 1);
3404 }
3405 break;
3406 case SCI_CHARRIGHTRECTEXTEND:
3407 case SCI_CHARRIGHTEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3408 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
3409 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3410 } else {
3411 spCaret = SelectionPosition(spCaret.Position() + 1);
3412 }
3413 break;
3414 case SCI_HOMERECTEXTEND:
3415 case SCI_HOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3416 spCaret = SelectionPosition(
3417 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3418 break;
3419 case SCI_VCHOMERECTEXTEND:
3420 case SCI_VCHOMEEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3421 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3422 break;
3423 case SCI_LINEENDRECTEXTEND:
3424 case SCI_LINEENDEXTEND: // only when sel.IsRectangular() && sel.MoveExtends()
3425 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3426 break;
3427 }
3428 const int directionMove = (spCaret < rangeBase.caret) ? -1 : 1;
3429 spCaret = MovePositionSoVisible(spCaret, directionMove);
3430 sel.selType = Selection::selRectangle;
3431 sel.Rectangular() = SelectionRange(spCaret, rangeBase.anchor);
3432 SetRectangularRange();
3433 } else if (sel.IsRectangular()) {
3434 // Not a rectangular extension so switch to stream.
3435 SelectionPosition selAtLimit = (NaturalDirection(iMessage) > 0) ? sel.Limits().end : sel.Limits().start;
3436 switch (iMessage) {
3437 case SCI_HOME:
3438 selAtLimit = SelectionPosition(
3439 pdoc->LineStart(pdoc->LineFromPosition(selAtLimit.Position())));
3440 break;
3441 case SCI_VCHOME:
3442 selAtLimit = SelectionPosition(pdoc->VCHomePosition(selAtLimit.Position()));
3443 break;
3444 case SCI_LINEEND:
3445 selAtLimit = SelectionPosition(pdoc->LineEndPosition(selAtLimit.Position()));
3446 break;
3447 }
3448 sel.selType = Selection::selStream;
3449 sel.SetSelection(SelectionRange(selAtLimit));
3450 } else {
3451 if (!additionalSelectionTyping) {
3452 InvalidateWholeSelection();
3453 sel.DropAdditionalRanges();
3454 }
3455 for (size_t r = 0; r < sel.Count(); r++) {
3456 const SelectionPosition spCaretNow = sel.Range(r).caret;
3457 SelectionPosition spCaret = spCaretNow;
3458 switch (iMessage) {
3459 case SCI_CHARLEFT:
3460 case SCI_CHARLEFTEXTEND:
3461 if (spCaret.VirtualSpace()) {
3462 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
3463 } else if ((virtualSpaceOptions & SCVS_NOWRAPLINESTART) == 0 || pdoc->GetColumn(spCaret.Position()) > 0) {
3464 spCaret = SelectionPosition(spCaret.Position() - 1);
3465 }
3466 break;
3467 case SCI_CHARRIGHT:
3468 case SCI_CHARRIGHTEXTEND:
3469 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(spCaret.Position())) {
3470 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
3471 } else {
3472 spCaret = SelectionPosition(spCaret.Position() + 1);
3473 }
3474 break;
3475 case SCI_WORDLEFT:
3476 case SCI_WORDLEFTEXTEND:
3477 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), -1));
3478 break;
3479 case SCI_WORDRIGHT:
3480 case SCI_WORDRIGHTEXTEND:
3481 spCaret = SelectionPosition(pdoc->NextWordStart(spCaret.Position(), 1));
3482 break;
3483 case SCI_WORDLEFTEND:
3484 case SCI_WORDLEFTENDEXTEND:
3485 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), -1));
3486 break;
3487 case SCI_WORDRIGHTEND:
3488 case SCI_WORDRIGHTENDEXTEND:
3489 spCaret = SelectionPosition(pdoc->NextWordEnd(spCaret.Position(), 1));
3490 break;
3491 case SCI_WORDPARTLEFT:
3492 case SCI_WORDPARTLEFTEXTEND:
3493 spCaret = SelectionPosition(pdoc->WordPartLeft(spCaret.Position()));
3494 break;
3495 case SCI_WORDPARTRIGHT:
3496 case SCI_WORDPARTRIGHTEXTEND:
3497 spCaret = SelectionPosition(pdoc->WordPartRight(spCaret.Position()));
3498 break;
3499 case SCI_HOME:
3500 case SCI_HOMEEXTEND:
3501 spCaret = SelectionPosition(
3502 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3503 break;
3504 case SCI_HOMEDISPLAY:
3505 case SCI_HOMEDISPLAYEXTEND:
3506 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), true));
3507 break;
3508 case SCI_HOMEWRAP:
3509 case SCI_HOMEWRAPEXTEND:
3510 spCaret = MovePositionSoVisible(StartEndDisplayLine(spCaret.Position(), true), -1);
3511 if (spCaretNow <= spCaret)
3512 spCaret = SelectionPosition(
3513 pdoc->LineStart(pdoc->LineFromPosition(spCaret.Position())));
3514 break;
3515 case SCI_VCHOME:
3516 case SCI_VCHOMEEXTEND:
3517 // VCHome alternates between beginning of line and beginning of text so may move back or forwards
3518 spCaret = SelectionPosition(pdoc->VCHomePosition(spCaret.Position()));
3519 break;
3520 case SCI_VCHOMEDISPLAY:
3521 case SCI_VCHOMEDISPLAYEXTEND:
3522 spCaret = SelectionPosition(VCHomeDisplayPosition(spCaret.Position()));
3523 break;
3524 case SCI_VCHOMEWRAP:
3525 case SCI_VCHOMEWRAPEXTEND:
3526 spCaret = SelectionPosition(VCHomeWrapPosition(spCaret.Position()));
3527 break;
3528 case SCI_LINEEND:
3529 case SCI_LINEENDEXTEND:
3530 spCaret = SelectionPosition(pdoc->LineEndPosition(spCaret.Position()));
3531 break;
3532 case SCI_LINEENDDISPLAY:
3533 case SCI_LINEENDDISPLAYEXTEND:
3534 spCaret = SelectionPosition(StartEndDisplayLine(spCaret.Position(), false));
3535 break;
3536 case SCI_LINEENDWRAP:
3537 case SCI_LINEENDWRAPEXTEND:
3538 spCaret = SelectionPosition(LineEndWrapPosition(spCaret.Position()));
3539 break;
3540
3541 default:
3542 PLATFORM_ASSERT(false);
3543 }
3544
3545 const int directionMove = (spCaret < spCaretNow) ? -1 : 1;
3546 spCaret = MovePositionSoVisible(spCaret, directionMove);
3547
3548 // Handle move versus extend, and special behaviour for non-empty left/right
3549 switch (iMessage) {
3550 case SCI_CHARLEFT:
3551 case SCI_CHARRIGHT:
3552 if (sel.Range(r).Empty()) {
3553 sel.Range(r) = SelectionRange(spCaret);
3554 } else {
3555 sel.Range(r) = SelectionRange(
3556 (iMessage == SCI_CHARLEFT) ? sel.Range(r).Start() : sel.Range(r).End());
3557 }
3558 break;
3559
3560 case SCI_WORDLEFT:
3561 case SCI_WORDRIGHT:
3562 case SCI_WORDLEFTEND:
3563 case SCI_WORDRIGHTEND:
3564 case SCI_WORDPARTLEFT:
3565 case SCI_WORDPARTRIGHT:
3566 case SCI_HOME:
3567 case SCI_HOMEDISPLAY:
3568 case SCI_HOMEWRAP:
3569 case SCI_VCHOME:
3570 case SCI_VCHOMEDISPLAY:
3571 case SCI_VCHOMEWRAP:
3572 case SCI_LINEEND:
3573 case SCI_LINEENDDISPLAY:
3574 case SCI_LINEENDWRAP:
3575 sel.Range(r) = SelectionRange(spCaret);
3576 break;
3577
3578 case SCI_CHARLEFTEXTEND:
3579 case SCI_CHARRIGHTEXTEND:
3580 case SCI_WORDLEFTEXTEND:
3581 case SCI_WORDRIGHTEXTEND:
3582 case SCI_WORDLEFTENDEXTEND:
3583 case SCI_WORDRIGHTENDEXTEND:
3584 case SCI_WORDPARTLEFTEXTEND:
3585 case SCI_WORDPARTRIGHTEXTEND:
3586 case SCI_HOMEEXTEND:
3587 case SCI_HOMEDISPLAYEXTEND:
3588 case SCI_HOMEWRAPEXTEND:
3589 case SCI_VCHOMEEXTEND:
3590 case SCI_VCHOMEDISPLAYEXTEND:
3591 case SCI_VCHOMEWRAPEXTEND:
3592 case SCI_LINEENDEXTEND:
3593 case SCI_LINEENDDISPLAYEXTEND:
3594 case SCI_LINEENDWRAPEXTEND: {
3595 SelectionRange rangeNew = SelectionRange(spCaret, sel.Range(r).anchor);
3596 sel.TrimOtherSelections(r, SelectionRange(rangeNew));
3597 sel.Range(r) = rangeNew;
3598 }
3599 break;
3600
3601 default:
3602 PLATFORM_ASSERT(false);
3603 }
3604 }
3605 }
3606
3607 sel.RemoveDuplicates();
3608
3609 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true, caretPolicies);
3610
3611 // Invalidate the new state of the selection
3612 InvalidateWholeSelection();
3613
3614 SetLastXChosen();
3615 // Need the line moving and so forth from MovePositionTo
3616 return 0;
3617 }
3618
DelWordOrLine(unsigned int iMessage)3619 int Editor::DelWordOrLine(unsigned int iMessage) {
3620 // Virtual space may be realised for SCI_DELWORDRIGHT or SCI_DELWORDRIGHTEND
3621 // which means 2 actions so wrap in an undo group.
3622
3623 // Rightwards and leftwards deletions differ in treatment of virtual space.
3624 // Clear virtual space for leftwards, realise for rightwards.
3625 const bool leftwards = (iMessage == SCI_DELWORDLEFT) || (iMessage == SCI_DELLINELEFT);
3626
3627 if (!additionalSelectionTyping) {
3628 InvalidateWholeSelection();
3629 sel.DropAdditionalRanges();
3630 }
3631
3632 UndoGroup ug0(pdoc, (sel.Count() > 1) || !leftwards);
3633
3634 for (size_t r = 0; r < sel.Count(); r++) {
3635 if (leftwards) {
3636 // Delete to the left so first clear the virtual space.
3637 sel.Range(r).ClearVirtualSpace();
3638 } else {
3639 // Delete to the right so first realise the virtual space.
3640 sel.Range(r) = SelectionRange(
3641 RealizeVirtualSpace(sel.Range(r).caret));
3642 }
3643
3644 Range rangeDelete;
3645 switch (iMessage) {
3646 case SCI_DELWORDLEFT:
3647 rangeDelete = Range(
3648 pdoc->NextWordStart(sel.Range(r).caret.Position(), -1),
3649 sel.Range(r).caret.Position());
3650 break;
3651 case SCI_DELWORDRIGHT:
3652 rangeDelete = Range(
3653 sel.Range(r).caret.Position(),
3654 pdoc->NextWordStart(sel.Range(r).caret.Position(), 1));
3655 break;
3656 case SCI_DELWORDRIGHTEND:
3657 rangeDelete = Range(
3658 sel.Range(r).caret.Position(),
3659 pdoc->NextWordEnd(sel.Range(r).caret.Position(), 1));
3660 break;
3661 case SCI_DELLINELEFT:
3662 rangeDelete = Range(
3663 pdoc->LineStart(pdoc->LineFromPosition(sel.Range(r).caret.Position())),
3664 sel.Range(r).caret.Position());
3665 break;
3666 case SCI_DELLINERIGHT:
3667 rangeDelete = Range(
3668 sel.Range(r).caret.Position(),
3669 pdoc->LineEnd(pdoc->LineFromPosition(sel.Range(r).caret.Position())));
3670 break;
3671 }
3672 if (!RangeContainsProtected(rangeDelete.start, rangeDelete.end)) {
3673 pdoc->DeleteChars(rangeDelete.start, rangeDelete.end - rangeDelete.start);
3674 }
3675 }
3676
3677 // May need something stronger here: can selections overlap at this point?
3678 sel.RemoveDuplicates();
3679
3680 MovedCaret(sel.RangeMain().caret, SelectionPosition(INVALID_POSITION), true, caretPolicies);
3681
3682 // Invalidate the new state of the selection
3683 InvalidateWholeSelection();
3684
3685 SetLastXChosen();
3686 return 0;
3687 }
3688
KeyCommand(unsigned int iMessage)3689 int Editor::KeyCommand(unsigned int iMessage) {
3690 switch (iMessage) {
3691 case SCI_LINEDOWN:
3692 CursorUpOrDown(1, Selection::noSel);
3693 break;
3694 case SCI_LINEDOWNEXTEND:
3695 CursorUpOrDown(1, Selection::selStream);
3696 break;
3697 case SCI_LINEDOWNRECTEXTEND:
3698 CursorUpOrDown(1, Selection::selRectangle);
3699 break;
3700 case SCI_PARADOWN:
3701 ParaUpOrDown(1, Selection::noSel);
3702 break;
3703 case SCI_PARADOWNEXTEND:
3704 ParaUpOrDown(1, Selection::selStream);
3705 break;
3706 case SCI_LINESCROLLDOWN:
3707 ScrollTo(topLine + 1);
3708 MoveCaretInsideView(false);
3709 break;
3710 case SCI_LINEUP:
3711 CursorUpOrDown(-1, Selection::noSel);
3712 break;
3713 case SCI_LINEUPEXTEND:
3714 CursorUpOrDown(-1, Selection::selStream);
3715 break;
3716 case SCI_LINEUPRECTEXTEND:
3717 CursorUpOrDown(-1, Selection::selRectangle);
3718 break;
3719 case SCI_PARAUP:
3720 ParaUpOrDown(-1, Selection::noSel);
3721 break;
3722 case SCI_PARAUPEXTEND:
3723 ParaUpOrDown(-1, Selection::selStream);
3724 break;
3725 case SCI_LINESCROLLUP:
3726 ScrollTo(topLine - 1);
3727 MoveCaretInsideView(false);
3728 break;
3729
3730 case SCI_CHARLEFT:
3731 case SCI_CHARLEFTEXTEND:
3732 case SCI_CHARLEFTRECTEXTEND:
3733 case SCI_CHARRIGHT:
3734 case SCI_CHARRIGHTEXTEND:
3735 case SCI_CHARRIGHTRECTEXTEND:
3736 case SCI_WORDLEFT:
3737 case SCI_WORDLEFTEXTEND:
3738 case SCI_WORDRIGHT:
3739 case SCI_WORDRIGHTEXTEND:
3740 case SCI_WORDLEFTEND:
3741 case SCI_WORDLEFTENDEXTEND:
3742 case SCI_WORDRIGHTEND:
3743 case SCI_WORDRIGHTENDEXTEND:
3744 case SCI_WORDPARTLEFT:
3745 case SCI_WORDPARTLEFTEXTEND:
3746 case SCI_WORDPARTRIGHT:
3747 case SCI_WORDPARTRIGHTEXTEND:
3748 case SCI_HOME:
3749 case SCI_HOMEEXTEND:
3750 case SCI_HOMERECTEXTEND:
3751 case SCI_HOMEDISPLAY:
3752 case SCI_HOMEDISPLAYEXTEND:
3753 case SCI_HOMEWRAP:
3754 case SCI_HOMEWRAPEXTEND:
3755 case SCI_VCHOME:
3756 case SCI_VCHOMEEXTEND:
3757 case SCI_VCHOMERECTEXTEND:
3758 case SCI_VCHOMEDISPLAY:
3759 case SCI_VCHOMEDISPLAYEXTEND:
3760 case SCI_VCHOMEWRAP:
3761 case SCI_VCHOMEWRAPEXTEND:
3762 case SCI_LINEEND:
3763 case SCI_LINEENDEXTEND:
3764 case SCI_LINEENDRECTEXTEND:
3765 case SCI_LINEENDDISPLAY:
3766 case SCI_LINEENDDISPLAYEXTEND:
3767 case SCI_LINEENDWRAP:
3768 case SCI_LINEENDWRAPEXTEND:
3769 return HorizontalMove(iMessage);
3770
3771 case SCI_DOCUMENTSTART:
3772 MovePositionTo(0);
3773 SetLastXChosen();
3774 break;
3775 case SCI_DOCUMENTSTARTEXTEND:
3776 MovePositionTo(0, Selection::selStream);
3777 SetLastXChosen();
3778 break;
3779 case SCI_DOCUMENTEND:
3780 MovePositionTo(pdoc->Length());
3781 SetLastXChosen();
3782 break;
3783 case SCI_DOCUMENTENDEXTEND:
3784 MovePositionTo(pdoc->Length(), Selection::selStream);
3785 SetLastXChosen();
3786 break;
3787 case SCI_STUTTEREDPAGEUP:
3788 PageMove(-1, Selection::noSel, true);
3789 break;
3790 case SCI_STUTTEREDPAGEUPEXTEND:
3791 PageMove(-1, Selection::selStream, true);
3792 break;
3793 case SCI_STUTTEREDPAGEDOWN:
3794 PageMove(1, Selection::noSel, true);
3795 break;
3796 case SCI_STUTTEREDPAGEDOWNEXTEND:
3797 PageMove(1, Selection::selStream, true);
3798 break;
3799 case SCI_PAGEUP:
3800 PageMove(-1);
3801 break;
3802 case SCI_PAGEUPEXTEND:
3803 PageMove(-1, Selection::selStream);
3804 break;
3805 case SCI_PAGEUPRECTEXTEND:
3806 PageMove(-1, Selection::selRectangle);
3807 break;
3808 case SCI_PAGEDOWN:
3809 PageMove(1);
3810 break;
3811 case SCI_PAGEDOWNEXTEND:
3812 PageMove(1, Selection::selStream);
3813 break;
3814 case SCI_PAGEDOWNRECTEXTEND:
3815 PageMove(1, Selection::selRectangle);
3816 break;
3817 case SCI_EDITTOGGLEOVERTYPE:
3818 inOverstrike = !inOverstrike;
3819 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
3820 ShowCaretAtCurrentPosition();
3821 SetIdle(true);
3822 break;
3823 case SCI_CANCEL: // Cancel any modes - handled in subclass
3824 // Also unselect text
3825 CancelModes();
3826 if ((sel.Count() > 1) && !sel.IsRectangular()) {
3827 // Drop additional selections
3828 InvalidateWholeSelection();
3829 sel.DropAdditionalRanges();
3830 }
3831 break;
3832 case SCI_DELETEBACK:
3833 DelCharBack(true);
3834 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3835 SetLastXChosen();
3836 }
3837 EnsureCaretVisible();
3838 break;
3839 case SCI_DELETEBACKNOTLINE:
3840 DelCharBack(false);
3841 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3842 SetLastXChosen();
3843 }
3844 EnsureCaretVisible();
3845 break;
3846 case SCI_TAB:
3847 Indent(true);
3848 if (caretSticky == SC_CARETSTICKY_OFF) {
3849 SetLastXChosen();
3850 }
3851 EnsureCaretVisible();
3852 ShowCaretAtCurrentPosition(); // Avoid blinking
3853 break;
3854 case SCI_BACKTAB:
3855 Indent(false);
3856 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
3857 SetLastXChosen();
3858 }
3859 EnsureCaretVisible();
3860 ShowCaretAtCurrentPosition(); // Avoid blinking
3861 break;
3862 case SCI_NEWLINE:
3863 NewLine();
3864 break;
3865 case SCI_FORMFEED:
3866 AddChar('\f');
3867 break;
3868 case SCI_ZOOMIN:
3869 if (vs.zoomLevel < 20) {
3870 vs.zoomLevel++;
3871 InvalidateStyleRedraw();
3872 NotifyZoom();
3873 }
3874 break;
3875 case SCI_ZOOMOUT:
3876 if (vs.zoomLevel > -10) {
3877 vs.zoomLevel--;
3878 InvalidateStyleRedraw();
3879 NotifyZoom();
3880 }
3881 break;
3882
3883 case SCI_DELWORDLEFT:
3884 case SCI_DELWORDRIGHT:
3885 case SCI_DELWORDRIGHTEND:
3886 case SCI_DELLINELEFT:
3887 case SCI_DELLINERIGHT:
3888 return DelWordOrLine(iMessage);
3889
3890 case SCI_LINECOPY: {
3891 const Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());
3892 const Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());
3893 CopyRangeToClipboard(pdoc->LineStart(lineStart),
3894 pdoc->LineStart(lineEnd + 1));
3895 }
3896 break;
3897 case SCI_LINECUT: {
3898 const Sci::Line lineStart = pdoc->SciLineFromPosition(SelectionStart().Position());
3899 const Sci::Line lineEnd = pdoc->SciLineFromPosition(SelectionEnd().Position());
3900 const Sci::Position start = pdoc->LineStart(lineStart);
3901 const Sci::Position end = pdoc->LineStart(lineEnd + 1);
3902 SetSelection(start, end);
3903 Cut();
3904 SetLastXChosen();
3905 }
3906 break;
3907 case SCI_LINEDELETE: {
3908 const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
3909 const Sci::Position start = pdoc->LineStart(line);
3910 const Sci::Position end = pdoc->LineStart(line + 1);
3911 pdoc->DeleteChars(start, end - start);
3912 }
3913 break;
3914 case SCI_LINETRANSPOSE:
3915 LineTranspose();
3916 break;
3917 case SCI_LINEREVERSE:
3918 LineReverse();
3919 break;
3920 case SCI_LINEDUPLICATE:
3921 Duplicate(true);
3922 break;
3923 case SCI_SELECTIONDUPLICATE:
3924 Duplicate(false);
3925 break;
3926 case SCI_LOWERCASE:
3927 ChangeCaseOfSelection(cmLower);
3928 break;
3929 case SCI_UPPERCASE:
3930 ChangeCaseOfSelection(cmUpper);
3931 break;
3932 case SCI_SCROLLTOSTART:
3933 ScrollTo(0);
3934 break;
3935 case SCI_SCROLLTOEND:
3936 ScrollTo(MaxScrollPos());
3937 break;
3938 }
3939 return 0;
3940 }
3941
KeyDefault(int,int)3942 int Editor::KeyDefault(int, int) {
3943 return 0;
3944 }
3945
KeyDownWithModifiers(int key,int modifiers,bool * consumed)3946 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
3947 DwellEnd(false);
3948 const int msg = kmap.Find(key, modifiers);
3949 if (msg) {
3950 if (consumed)
3951 *consumed = true;
3952 return static_cast<int>(WndProc(msg, 0, 0));
3953 } else {
3954 if (consumed)
3955 *consumed = false;
3956 return KeyDefault(key, modifiers);
3957 }
3958 }
3959
Indent(bool forwards)3960 void Editor::Indent(bool forwards) {
3961 UndoGroup ug(pdoc);
3962 for (size_t r=0; r<sel.Count(); r++) {
3963 const Sci::Line lineOfAnchor =
3964 pdoc->SciLineFromPosition(sel.Range(r).anchor.Position());
3965 Sci::Position caretPosition = sel.Range(r).caret.Position();
3966 const Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(caretPosition);
3967 if (lineOfAnchor == lineCurrentPos) {
3968 if (forwards) {
3969 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
3970 caretPosition = sel.Range(r).caret.Position();
3971 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
3972 pdoc->tabIndents) {
3973 const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3974 const int indentationStep = pdoc->IndentSize();
3975 const Sci::Position posSelect = pdoc->SetLineIndentation(
3976 lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
3977 sel.Range(r) = SelectionRange(posSelect);
3978 } else {
3979 if (pdoc->useTabs) {
3980 const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, "\t", 1);
3981 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3982 } else {
3983 int numSpaces = (pdoc->tabInChars) -
3984 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
3985 if (numSpaces < 1)
3986 numSpaces = pdoc->tabInChars;
3987 const std::string spaceText(numSpaces, ' ');
3988 const Sci::Position lengthInserted = pdoc->InsertString(caretPosition, spaceText.c_str(),
3989 spaceText.length());
3990 sel.Range(r) = SelectionRange(caretPosition + lengthInserted);
3991 }
3992 }
3993 } else {
3994 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3995 pdoc->tabIndents) {
3996 const int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3997 const int indentationStep = pdoc->IndentSize();
3998 const Sci::Position posSelect = pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3999 sel.Range(r) = SelectionRange(posSelect);
4000 } else {
4001 Sci::Position newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
4002 pdoc->tabInChars;
4003 if (newColumn < 0)
4004 newColumn = 0;
4005 Sci::Position newPos = caretPosition;
4006 while (pdoc->GetColumn(newPos) > newColumn)
4007 newPos--;
4008 sel.Range(r) = SelectionRange(newPos);
4009 }
4010 }
4011 } else { // Multiline
4012 const Sci::Position anchorPosOnLine = sel.Range(r).anchor.Position() -
4013 pdoc->LineStart(lineOfAnchor);
4014 const Sci::Position currentPosPosOnLine = caretPosition -
4015 pdoc->LineStart(lineCurrentPos);
4016 // Multiple lines selected so indent / dedent
4017 const Sci::Line lineTopSel = std::min(lineOfAnchor, lineCurrentPos);
4018 Sci::Line lineBottomSel = std::max(lineOfAnchor, lineCurrentPos);
4019 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
4020 lineBottomSel--; // If not selecting any characters on a line, do not indent
4021 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
4022 if (lineOfAnchor < lineCurrentPos) {
4023 if (currentPosPosOnLine == 0)
4024 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4025 pdoc->LineStart(lineOfAnchor));
4026 else
4027 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1),
4028 pdoc->LineStart(lineOfAnchor));
4029 } else {
4030 if (anchorPosOnLine == 0)
4031 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4032 pdoc->LineStart(lineOfAnchor));
4033 else
4034 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos),
4035 pdoc->LineStart(lineOfAnchor + 1));
4036 }
4037 }
4038 }
4039 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4040 }
4041
4042 class CaseFolderASCII : public CaseFolderTable {
4043 public:
CaseFolderASCII()4044 CaseFolderASCII() noexcept {
4045 StandardASCII();
4046 }
4047 };
4048
4049
CaseFolderForEncoding()4050 CaseFolder *Editor::CaseFolderForEncoding() {
4051 // Simple default that only maps ASCII upper case to lower case.
4052 return new CaseFolderASCII();
4053 }
4054
4055 /**
4056 * Search of a text in the document, in the given range.
4057 * @return The position of the found text, -1 if not found.
4058 */
FindText(uptr_t wParam,sptr_t lParam)4059 Sci::Position Editor::FindText(
4060 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4061 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4062 sptr_t lParam) { ///< @c Sci_TextToFind structure: The text to search for in the given range.
4063
4064 Sci_TextToFind *ft = static_cast<Sci_TextToFind *>(PtrFromSPtr(lParam));
4065 Sci::Position lengthFound = strlen(ft->lpstrText);
4066 if (!pdoc->HasCaseFolder())
4067 pdoc->SetCaseFolder(CaseFolderForEncoding());
4068 try {
4069 const Sci::Position pos = pdoc->FindText(
4070 static_cast<Sci::Position>(ft->chrg.cpMin),
4071 static_cast<Sci::Position>(ft->chrg.cpMax),
4072 ft->lpstrText,
4073 static_cast<int>(wParam),
4074 &lengthFound);
4075 if (pos != -1) {
4076 ft->chrgText.cpMin = static_cast<Sci_PositionCR>(pos);
4077 ft->chrgText.cpMax = static_cast<Sci_PositionCR>(pos + lengthFound);
4078 }
4079 return pos;
4080 } catch (RegexError &) {
4081 errorStatus = SC_STATUS_WARN_REGEX;
4082 return -1;
4083 }
4084 }
4085
4086 /**
4087 * Relocatable search support : Searches relative to current selection
4088 * point and sets the selection to the found text range with
4089 * each search.
4090 */
4091 /**
4092 * Anchor following searches at current selection start: This allows
4093 * multiple incremental interactive searches to be macro recorded
4094 * while still setting the selection to found text so the find/select
4095 * operation is self-contained.
4096 */
SearchAnchor()4097 void Editor::SearchAnchor() {
4098 searchAnchor = SelectionStart().Position();
4099 }
4100
4101 /**
4102 * Find text from current search anchor: Must call @c SearchAnchor first.
4103 * Used for next text and previous text requests.
4104 * @return The position of the found text, -1 if not found.
4105 */
SearchText(unsigned int iMessage,uptr_t wParam,sptr_t lParam)4106 Sci::Position Editor::SearchText(
4107 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
4108 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4109 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4110 sptr_t lParam) { ///< The text to search for.
4111
4112 const char *txt = CharPtrFromSPtr(lParam);
4113 Sci::Position pos = INVALID_POSITION;
4114 Sci::Position lengthFound = strlen(txt);
4115 if (!pdoc->HasCaseFolder())
4116 pdoc->SetCaseFolder(CaseFolderForEncoding());
4117 try {
4118 if (iMessage == SCI_SEARCHNEXT) {
4119 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
4120 static_cast<int>(wParam),
4121 &lengthFound);
4122 } else {
4123 pos = pdoc->FindText(searchAnchor, 0, txt,
4124 static_cast<int>(wParam),
4125 &lengthFound);
4126 }
4127 } catch (RegexError &) {
4128 errorStatus = SC_STATUS_WARN_REGEX;
4129 return INVALID_POSITION;
4130 }
4131 if (pos != INVALID_POSITION) {
4132 SetSelection(pos, pos + lengthFound);
4133 }
4134
4135 return pos;
4136 }
4137
CaseMapString(const std::string & s,int caseMapping)4138 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
4139 std::string ret(s);
4140 for (char &ch : ret) {
4141 switch (caseMapping) {
4142 case cmUpper:
4143 ch = MakeUpperCase(ch);
4144 break;
4145 case cmLower:
4146 ch = MakeLowerCase(ch);
4147 break;
4148 }
4149 }
4150 return ret;
4151 }
4152
4153 /**
4154 * Search for text in the target range of the document.
4155 * @return The position of the found text, -1 if not found.
4156 */
SearchInTarget(const char * text,Sci::Position length)4157 Sci::Position Editor::SearchInTarget(const char *text, Sci::Position length) {
4158 Sci::Position lengthFound = length;
4159
4160 if (!pdoc->HasCaseFolder())
4161 pdoc->SetCaseFolder(CaseFolderForEncoding());
4162 try {
4163 const Sci::Position pos = pdoc->FindText(targetRange.start.Position(), targetRange.end.Position(), text,
4164 searchFlags,
4165 &lengthFound);
4166 if (pos != -1) {
4167 targetRange.start.SetPosition(pos);
4168 targetRange.end.SetPosition(pos + lengthFound);
4169 }
4170 return pos;
4171 } catch (RegexError &) {
4172 errorStatus = SC_STATUS_WARN_REGEX;
4173 return -1;
4174 }
4175 }
4176
GoToLine(Sci::Line lineNo)4177 void Editor::GoToLine(Sci::Line lineNo) {
4178 if (lineNo > pdoc->LinesTotal())
4179 lineNo = pdoc->LinesTotal();
4180 if (lineNo < 0)
4181 lineNo = 0;
4182 SetEmptySelection(pdoc->LineStart(lineNo));
4183 ShowCaretAtCurrentPosition();
4184 EnsureCaretVisible();
4185 }
4186
Close(Point pt1,Point pt2,Point threshold)4187 static bool Close(Point pt1, Point pt2, Point threshold) noexcept {
4188 const Point ptDifference = pt2 - pt1;
4189 if (std::abs(ptDifference.x) > threshold.x)
4190 return false;
4191 if (std::abs(ptDifference.y) > threshold.y)
4192 return false;
4193 return true;
4194 }
4195
RangeText(Sci::Position start,Sci::Position end) const4196 std::string Editor::RangeText(Sci::Position start, Sci::Position end) const {
4197 if (start < end) {
4198 const Sci::Position len = end - start;
4199 std::string ret(len, '\0');
4200 pdoc->GetCharRange(ret.data(), start, len);
4201 return ret;
4202 }
4203 return std::string();
4204 }
4205
CopySelectionRange(SelectionText * ss,bool allowLineCopy)4206 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4207 if (sel.Empty()) {
4208 if (allowLineCopy) {
4209 const Sci::Line currentLine = pdoc->SciLineFromPosition(sel.MainCaret());
4210 const Sci::Position start = pdoc->LineStart(currentLine);
4211 const Sci::Position end = pdoc->LineEnd(currentLine);
4212
4213 std::string text = RangeText(start, end);
4214 if (pdoc->eolMode != SC_EOL_LF)
4215 text.push_back('\r');
4216 if (pdoc->eolMode != SC_EOL_CR)
4217 text.push_back('\n');
4218 ss->Copy(text, pdoc->dbcsCodePage,
4219 vs.styles[STYLE_DEFAULT].characterSet, false, true);
4220 }
4221 } else {
4222 std::string text;
4223 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
4224 if (sel.selType == Selection::selRectangle)
4225 std::sort(rangesInOrder.begin(), rangesInOrder.end());
4226 for (const SelectionRange ¤t : rangesInOrder) {
4227 text.append(RangeText(current.Start().Position(), current.End().Position()));
4228 if (sel.selType == Selection::selRectangle) {
4229 if (pdoc->eolMode != SC_EOL_LF)
4230 text.push_back('\r');
4231 if (pdoc->eolMode != SC_EOL_CR)
4232 text.push_back('\n');
4233 }
4234 }
4235 ss->Copy(text, pdoc->dbcsCodePage,
4236 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
4237 }
4238 }
4239
CopyRangeToClipboard(Sci::Position start,Sci::Position end)4240 void Editor::CopyRangeToClipboard(Sci::Position start, Sci::Position end) {
4241 start = pdoc->ClampPositionIntoDocument(start);
4242 end = pdoc->ClampPositionIntoDocument(end);
4243 SelectionText selectedText;
4244 std::string text = RangeText(start, end);
4245 selectedText.Copy(text,
4246 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4247 CopyToClipboard(selectedText);
4248 }
4249
CopyText(size_t length,const char * text)4250 void Editor::CopyText(size_t length, const char *text) {
4251 SelectionText selectedText;
4252 selectedText.Copy(std::string(text, length),
4253 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4254 CopyToClipboard(selectedText);
4255 }
4256
SetDragPosition(SelectionPosition newPos)4257 void Editor::SetDragPosition(SelectionPosition newPos) {
4258 if (newPos.Position() >= 0) {
4259 newPos = MovePositionOutsideChar(newPos, 1);
4260 posDrop = newPos;
4261 }
4262 if (!(posDrag == newPos)) {
4263 const CaretPolicies dragCaretPolicies = {
4264 CaretPolicy(CARET_SLOP | CARET_STRICT | CARET_EVEN, 50),
4265 CaretPolicy(CARET_SLOP | CARET_STRICT | CARET_EVEN, 2)
4266 };
4267 MovedCaret(newPos, posDrag, true, dragCaretPolicies);
4268
4269 caret.on = true;
4270 FineTickerCancel(tickCaret);
4271 if ((caret.active) && (caret.period > 0) && (newPos.Position() < 0))
4272 FineTickerStart(tickCaret, caret.period, caret.period/10);
4273 InvalidateCaret();
4274 posDrag = newPos;
4275 InvalidateCaret();
4276 }
4277 }
4278
DisplayCursor(Window::Cursor c)4279 void Editor::DisplayCursor(Window::Cursor c) {
4280 if (cursorMode == SC_CURSORNORMAL)
4281 wMain.SetCursor(c);
4282 else
4283 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
4284 }
4285
DragThreshold(Point ptStart,Point ptNow)4286 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
4287 const Point ptDiff = ptStart - ptNow;
4288 const XYPOSITION distanceSquared = ptDiff.x * ptDiff.x + ptDiff.y * ptDiff.y;
4289 return distanceSquared > 16.0f;
4290 }
4291
StartDrag()4292 void Editor::StartDrag() {
4293 // Always handled by subclasses
4294 //SetMouseCapture(true);
4295 //DisplayCursor(Window::cursorArrow);
4296 }
4297
DropAt(SelectionPosition position,const char * value,size_t lengthValue,bool moving,bool rectangular)4298 void Editor::DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular) {
4299 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
4300 if (inDragDrop == ddDragging)
4301 dropWentOutside = false;
4302
4303 const bool positionWasInSelection = PositionInSelection(position.Position());
4304
4305 const bool positionOnEdgeOfSelection =
4306 (position == SelectionStart()) || (position == SelectionEnd());
4307
4308 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
4309 (positionOnEdgeOfSelection && !moving)) {
4310
4311 const SelectionPosition selStart = SelectionStart();
4312 const SelectionPosition selEnd = SelectionEnd();
4313
4314 UndoGroup ug(pdoc);
4315
4316 SelectionPosition positionAfterDeletion = position;
4317 if ((inDragDrop == ddDragging) && moving) {
4318 // Remove dragged out text
4319 if (rectangular || sel.selType == Selection::selLines) {
4320 for (size_t r=0; r<sel.Count(); r++) {
4321 if (position >= sel.Range(r).Start()) {
4322 if (position > sel.Range(r).End()) {
4323 positionAfterDeletion.Add(-sel.Range(r).Length());
4324 } else {
4325 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
4326 }
4327 }
4328 }
4329 } else {
4330 if (position > selStart) {
4331 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
4332 }
4333 }
4334 ClearSelection();
4335 }
4336 position = positionAfterDeletion;
4337
4338 std::string convertedText = Document::TransformLineEnds(value, lengthValue, pdoc->eolMode);
4339
4340 if (rectangular) {
4341 PasteRectangular(position, convertedText.c_str(), convertedText.length());
4342 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4343 SetEmptySelection(position);
4344 } else {
4345 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
4346 position = RealizeVirtualSpace(position);
4347 const Sci::Position lengthInserted = pdoc->InsertString(
4348 position.Position(), convertedText.c_str(), convertedText.length());
4349 if (lengthInserted > 0) {
4350 SelectionPosition posAfterInsertion = position;
4351 posAfterInsertion.Add(lengthInserted);
4352 SetSelection(posAfterInsertion, position);
4353 }
4354 }
4355 } else if (inDragDrop == ddDragging) {
4356 SetEmptySelection(position);
4357 }
4358 }
4359
DropAt(SelectionPosition position,const char * value,bool moving,bool rectangular)4360 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
4361 DropAt(position, value, strlen(value), moving, rectangular);
4362 }
4363
4364 /**
4365 * @return true if given position is inside the selection,
4366 */
PositionInSelection(Sci::Position pos)4367 bool Editor::PositionInSelection(Sci::Position pos) {
4368 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
4369 for (size_t r=0; r<sel.Count(); r++) {
4370 if (sel.Range(r).Contains(pos))
4371 return true;
4372 }
4373 return false;
4374 }
4375
PointInSelection(Point pt)4376 bool Editor::PointInSelection(Point pt) {
4377 const SelectionPosition pos = SPositionFromLocation(pt, false, true);
4378 const Point ptPos = LocationFromPosition(pos);
4379 for (size_t r=0; r<sel.Count(); r++) {
4380 const SelectionRange &range = sel.Range(r);
4381 if (range.Contains(pos)) {
4382 bool hit = true;
4383 if (pos == range.Start()) {
4384 // see if just before selection
4385 if (pt.x < ptPos.x) {
4386 hit = false;
4387 }
4388 }
4389 if (pos == range.End()) {
4390 // see if just after selection
4391 if (pt.x > ptPos.x) {
4392 hit = false;
4393 }
4394 }
4395 if (hit)
4396 return true;
4397 }
4398 }
4399 return false;
4400 }
4401
PointInSelMargin(Point pt) const4402 bool Editor::PointInSelMargin(Point pt) const {
4403 // Really means: "Point in a margin"
4404 if (vs.fixedColumnWidth > 0) { // There is a margin
4405 PRectangle rcSelMargin = GetClientRectangle();
4406 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart - vs.leftMarginWidth);
4407 rcSelMargin.left = static_cast<XYPOSITION>(vs.textStart - vs.fixedColumnWidth);
4408 const Point ptOrigin = GetVisibleOriginInMain();
4409 rcSelMargin.Move(0, -ptOrigin.y);
4410 return rcSelMargin.ContainsWholePixel(pt);
4411 } else {
4412 return false;
4413 }
4414 }
4415
GetMarginCursor(Point pt) const4416 Window::Cursor Editor::GetMarginCursor(Point pt) const noexcept {
4417 int x = 0;
4418 for (const MarginStyle &m : vs.ms) {
4419 if ((pt.x >= x) && (pt.x < x + m.width))
4420 return static_cast<Window::Cursor>(m.cursor);
4421 x += m.width;
4422 }
4423 return Window::cursorReverseArrow;
4424 }
4425
TrimAndSetSelection(Sci::Position currentPos_,Sci::Position anchor_)4426 void Editor::TrimAndSetSelection(Sci::Position currentPos_, Sci::Position anchor_) {
4427 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
4428 SetSelection(currentPos_, anchor_);
4429 }
4430
LineSelection(Sci::Position lineCurrentPos_,Sci::Position lineAnchorPos_,bool wholeLine)4431 void Editor::LineSelection(Sci::Position lineCurrentPos_, Sci::Position lineAnchorPos_, bool wholeLine) {
4432 Sci::Position selCurrentPos, selAnchorPos;
4433 if (wholeLine) {
4434 const Sci::Line lineCurrent_ = pdoc->SciLineFromPosition(lineCurrentPos_);
4435 const Sci::Line lineAnchor_ = pdoc->SciLineFromPosition(lineAnchorPos_);
4436 if (lineAnchorPos_ < lineCurrentPos_) {
4437 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
4438 selAnchorPos = pdoc->LineStart(lineAnchor_);
4439 } else if (lineAnchorPos_ > lineCurrentPos_) {
4440 selCurrentPos = pdoc->LineStart(lineCurrent_);
4441 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
4442 } else { // Same line, select it
4443 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
4444 selAnchorPos = pdoc->LineStart(lineAnchor_);
4445 }
4446 } else {
4447 if (lineAnchorPos_ < lineCurrentPos_) {
4448 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
4449 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4450 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4451 } else if (lineAnchorPos_ > lineCurrentPos_) {
4452 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
4453 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4454 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
4455 } else { // Same line, select it
4456 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
4457 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
4458 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
4459 }
4460 }
4461 TrimAndSetSelection(selCurrentPos, selAnchorPos);
4462 }
4463
WordSelection(Sci::Position pos)4464 void Editor::WordSelection(Sci::Position pos) {
4465 if (pos < wordSelectAnchorStartPos) {
4466 // Extend backward to the word containing pos.
4467 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
4468 // This ensures that a series of empty lines isn't counted as a single "word".
4469 if (!pdoc->IsLineEndPosition(pos))
4470 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
4471 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
4472 } else if (pos > wordSelectAnchorEndPos) {
4473 // Extend forward to the word containing the character to the left of pos.
4474 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
4475 // This ensures that a series of empty lines isn't counted as a single "word".
4476 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
4477 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
4478 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
4479 } else {
4480 // Select only the anchored word
4481 if (pos >= originalAnchorPos)
4482 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
4483 else
4484 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
4485 }
4486 }
4487
DwellEnd(bool mouseMoved)4488 void Editor::DwellEnd(bool mouseMoved) {
4489 if (mouseMoved)
4490 ticksToDwell = dwellDelay;
4491 else
4492 ticksToDwell = SC_TIME_FOREVER;
4493 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4494 dwelling = false;
4495 NotifyDwelling(ptMouseLast, dwelling);
4496 }
4497 FineTickerCancel(tickDwell);
4498 }
4499
MouseLeave()4500 void Editor::MouseLeave() {
4501 SetHotSpotRange(nullptr);
4502 if (!HaveMouseCapture()) {
4503 ptMouseLast = Point(-1, -1);
4504 DwellEnd(true);
4505 }
4506 }
4507
AllowVirtualSpace(int virtualSpaceOptions,bool rectangular)4508 static constexpr bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) noexcept {
4509 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
4510 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
4511 }
4512
ButtonDownWithModifiers(Point pt,unsigned int curTime,int modifiers)4513 void Editor::ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4514 SetHoverIndicatorPoint(pt);
4515 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
4516 ptMouseLast = pt;
4517 const bool ctrl = (modifiers & SCI_CTRL) != 0;
4518 const bool shift = (modifiers & SCI_SHIFT) != 0;
4519 const bool alt = (modifiers & SCI_ALT) != 0;
4520 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
4521 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4522 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4523 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4524 inDragDrop = ddNone;
4525 sel.SetMoveExtends(false);
4526
4527 if (NotifyMarginClick(pt, modifiers))
4528 return;
4529
4530 NotifyIndicatorClick(true, newPos.Position(), modifiers);
4531
4532 const bool inSelMargin = PointInSelMargin(pt);
4533 // In margin ctrl+(double)click should always select everything
4534 if (ctrl && inSelMargin) {
4535 SelectAll();
4536 lastClickTime = curTime;
4537 lastClick = pt;
4538 return;
4539 }
4540 if (shift && !inSelMargin) {
4541 SetSelection(newPos);
4542 }
4543 if ((curTime < (lastClickTime+Platform::DoubleClickTime())) && Close(pt, lastClick, doubleClickCloseThreshold)) {
4544 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4545 SetMouseCapture(true);
4546 FineTickerStart(tickScroll, 100, 10);
4547 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
4548 SetEmptySelection(newPos.Position());
4549 bool doubleClick = false;
4550 if (inSelMargin) {
4551 // Inside margin selection type should be either selSubLine or selWholeLine.
4552 if (selectionType == selSubLine) {
4553 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
4554 // so we switch to selWholeLine in order to select whole line.
4555 selectionType = selWholeLine;
4556 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
4557 // If it is neither, reset selection type to line selection.
4558 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4559 }
4560 } else {
4561 if (selectionType == selChar) {
4562 selectionType = selWord;
4563 doubleClick = true;
4564 } else if (selectionType == selWord) {
4565 // Since we ended up here, we're inside a *triple* click, which should always select
4566 // whole line regardless of word wrap being enabled or not.
4567 selectionType = selWholeLine;
4568 } else {
4569 selectionType = selChar;
4570 originalAnchorPos = sel.MainCaret();
4571 }
4572 }
4573
4574 if (selectionType == selWord) {
4575 Sci::Position charPos = originalAnchorPos;
4576 if (sel.MainCaret() == originalAnchorPos) {
4577 charPos = PositionFromLocation(pt, false, true);
4578 charPos = MovePositionOutsideChar(charPos, -1);
4579 }
4580
4581 Sci::Position startWord, endWord;
4582 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
4583 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
4584 endWord = pdoc->ExtendWordSelect(charPos, 1);
4585 } else {
4586 // Selecting backwards, or anchor beyond last character on line. In these cases,
4587 // we select the word containing the character to the *left* of the anchor.
4588 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
4589 startWord = pdoc->ExtendWordSelect(charPos, -1);
4590 endWord = pdoc->ExtendWordSelect(startWord, 1);
4591 } else {
4592 // Anchor at start of line; select nothing to begin with.
4593 startWord = charPos;
4594 endWord = charPos;
4595 }
4596 }
4597
4598 wordSelectAnchorStartPos = startWord;
4599 wordSelectAnchorEndPos = endWord;
4600 wordSelectInitialCaretPos = sel.MainCaret();
4601 WordSelection(wordSelectInitialCaretPos);
4602 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
4603 lineAnchorPos = newPos.Position();
4604 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4605 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4606 } else {
4607 SetEmptySelection(sel.MainCaret());
4608 }
4609 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4610 if (doubleClick) {
4611 NotifyDoubleClick(pt, modifiers);
4612 if (PositionIsHotspot(newCharPos.Position()))
4613 NotifyHotSpotDoubleClicked(newCharPos.Position(), modifiers);
4614 }
4615 } else { // Single click
4616 if (inSelMargin) {
4617 if (sel.IsRectangular() || (sel.Count() > 1)) {
4618 InvalidateWholeSelection();
4619 sel.Clear();
4620 }
4621 sel.selType = Selection::selStream;
4622 if (!shift) {
4623 // Single click in margin: select whole line or only subline if word wrap is enabled
4624 lineAnchorPos = newPos.Position();
4625 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4626 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
4627 } else {
4628 // Single shift+click in margin: select from line anchor to clicked line
4629 if (sel.MainAnchor() > sel.MainCaret())
4630 lineAnchorPos = sel.MainAnchor() - 1;
4631 else
4632 lineAnchorPos = sel.MainAnchor();
4633 // Reset selection type if there is an empty selection.
4634 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
4635 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
4636 // This ensures that we continue selecting in the same selection mode.
4637 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
4638 selectionType = (Wrapping() && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
4639 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
4640 }
4641
4642 SetDragPosition(SelectionPosition(Sci::invalidPosition));
4643 SetMouseCapture(true);
4644 FineTickerStart(tickScroll, 100, 10);
4645 } else {
4646 if (PointIsHotspot(pt)) {
4647 NotifyHotSpotClicked(newCharPos.Position(), modifiers);
4648 hotSpotClickPos = newCharPos.Position();
4649 }
4650 if (!shift) {
4651 if (PointInSelection(pt) && !SelectionEmpty())
4652 inDragDrop = ddInitial;
4653 else
4654 inDragDrop = ddNone;
4655 }
4656 SetMouseCapture(true);
4657 FineTickerStart(tickScroll, 100, 10);
4658 if (inDragDrop != ddInitial) {
4659 SetDragPosition(SelectionPosition(Sci::invalidPosition));
4660 if (!shift) {
4661 if (ctrl && multipleSelection) {
4662 const SelectionRange range(newPos);
4663 sel.TentativeSelection(range);
4664 InvalidateSelection(range, true);
4665 } else {
4666 InvalidateSelection(SelectionRange(newPos), true);
4667 if (sel.Count() > 1)
4668 Redraw();
4669 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
4670 sel.Clear();
4671 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4672 SetSelection(newPos, newPos);
4673 }
4674 }
4675 SelectionPosition anchorCurrent = newPos;
4676 if (shift)
4677 anchorCurrent = sel.IsRectangular() ?
4678 sel.Rectangular().anchor : sel.RangeMain().anchor;
4679 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
4680 selectionType = selChar;
4681 originalAnchorPos = sel.MainCaret();
4682 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
4683 SetRectangularRange();
4684 }
4685 }
4686 }
4687 lastClickTime = curTime;
4688 lastClick = pt;
4689 lastXChosen = static_cast<int>(pt.x) + xOffset;
4690 ShowCaretAtCurrentPosition();
4691 }
4692
RightButtonDownWithModifiers(Point pt,unsigned int,int modifiers)4693 void Editor::RightButtonDownWithModifiers(Point pt, unsigned int, int modifiers) {
4694 if (NotifyMarginRightClick(pt, modifiers))
4695 return;
4696 }
4697
PositionIsHotspot(Sci::Position position) const4698 bool Editor::PositionIsHotspot(Sci::Position position) const {
4699 return vs.styles[pdoc->StyleIndexAt(position)].hotspot;
4700 }
4701
PointIsHotspot(Point pt)4702 bool Editor::PointIsHotspot(Point pt) {
4703 const Sci::Position pos = PositionFromLocation(pt, true, true);
4704 if (pos == INVALID_POSITION)
4705 return false;
4706 return PositionIsHotspot(pos);
4707 }
4708
SetHoverIndicatorPosition(Sci::Position position)4709 void Editor::SetHoverIndicatorPosition(Sci::Position position) {
4710 const Sci::Position hoverIndicatorPosPrev = hoverIndicatorPos;
4711 hoverIndicatorPos = INVALID_POSITION;
4712 if (!vs.indicatorsDynamic)
4713 return;
4714 if (position != INVALID_POSITION) {
4715 for (const IDecoration *deco : pdoc->decorations->View()) {
4716 if (vs.indicators[deco->Indicator()].IsDynamic()) {
4717 if (pdoc->decorations->ValueAt(deco->Indicator(), position)) {
4718 hoverIndicatorPos = position;
4719 }
4720 }
4721 }
4722 }
4723 if (hoverIndicatorPosPrev != hoverIndicatorPos) {
4724 Redraw();
4725 }
4726 }
4727
SetHoverIndicatorPoint(Point pt)4728 void Editor::SetHoverIndicatorPoint(Point pt) {
4729 if (!vs.indicatorsDynamic) {
4730 SetHoverIndicatorPosition(INVALID_POSITION);
4731 } else {
4732 SetHoverIndicatorPosition(PositionFromLocation(pt, true, true));
4733 }
4734 }
4735
SetHotSpotRange(const Point * pt)4736 void Editor::SetHotSpotRange(const Point *pt) {
4737 if (pt) {
4738 const Sci::Position pos = PositionFromLocation(*pt, false, true);
4739
4740 // If we don't limit this to word characters then the
4741 // range can encompass more than the run range and then
4742 // the underline will not be drawn properly.
4743 Range hsNew;
4744 hsNew.start = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
4745 hsNew.end = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
4746
4747 // Only invalidate the range if the hotspot range has changed...
4748 if (!(hsNew == hotspot)) {
4749 if (hotspot.Valid()) {
4750 InvalidateRange(hotspot.start, hotspot.end);
4751 }
4752 hotspot = hsNew;
4753 InvalidateRange(hotspot.start, hotspot.end);
4754 }
4755 } else {
4756 if (hotspot.Valid()) {
4757 InvalidateRange(hotspot.start, hotspot.end);
4758 }
4759 hotspot = Range(Sci::invalidPosition);
4760 }
4761 }
4762
GetHotSpotRange() const4763 Range Editor::GetHotSpotRange() const noexcept {
4764 return hotspot;
4765 }
4766
ButtonMoveWithModifiers(Point pt,unsigned int,int modifiers)4767 void Editor::ButtonMoveWithModifiers(Point pt, unsigned int, int modifiers) {
4768 if (ptMouseLast != pt) {
4769 DwellEnd(true);
4770 }
4771
4772 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
4773 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4774 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
4775
4776 if (inDragDrop == ddInitial) {
4777 if (DragThreshold(ptMouseLast, pt)) {
4778 SetMouseCapture(false);
4779 FineTickerCancel(tickScroll);
4780 SetDragPosition(movePos);
4781 CopySelectionRange(&drag);
4782 StartDrag();
4783 }
4784 return;
4785 }
4786
4787 ptMouseLast = pt;
4788 PRectangle rcClient = GetClientRectangle();
4789 const Point ptOrigin = GetVisibleOriginInMain();
4790 rcClient.Move(0, -ptOrigin.y);
4791 if ((dwellDelay < SC_TIME_FOREVER) && rcClient.Contains(pt)) {
4792 FineTickerStart(tickDwell, dwellDelay, dwellDelay/10);
4793 }
4794 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
4795 if (HaveMouseCapture()) {
4796
4797 // Slow down autoscrolling/selection
4798 autoScrollTimer.ticksToWait -= timer.tickSize;
4799 if (autoScrollTimer.ticksToWait > 0)
4800 return;
4801 autoScrollTimer.ticksToWait = autoScrollDelay;
4802
4803 // Adjust selection
4804 if (posDrag.IsValid()) {
4805 SetDragPosition(movePos);
4806 } else {
4807 if (selectionType == selChar) {
4808 if (sel.selType == Selection::selStream && (modifiers & SCI_ALT) && mouseSelectionRectangularSwitch) {
4809 sel.selType = Selection::selRectangle;
4810 }
4811 if (sel.IsRectangular()) {
4812 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
4813 SetSelection(movePos, sel.RangeMain().anchor);
4814 } else if (sel.Count() > 1) {
4815 InvalidateSelection(sel.RangeMain(), false);
4816 const SelectionRange range(movePos, sel.RangeMain().anchor);
4817 sel.TentativeSelection(range);
4818 InvalidateSelection(range, true);
4819 } else {
4820 SetSelection(movePos, sel.RangeMain().anchor);
4821 }
4822 } else if (selectionType == selWord) {
4823 // Continue selecting by word
4824 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
4825 // No need to do anything. Previously this case was lumped
4826 // in with "Moved forward", but that can be harmful in this
4827 // case: a handler for the NotifyDoubleClick re-adjusts
4828 // the selection for a fancier definition of "word" (for
4829 // example, in Perl it is useful to include the leading
4830 // '$', '%' or '@' on variables for word selection). In this
4831 // the ButtonMove() called via TickFor() for auto-scrolling
4832 // could result in the fancier word selection adjustment
4833 // being unmade.
4834 } else {
4835 wordSelectInitialCaretPos = -1;
4836 WordSelection(movePos.Position());
4837 }
4838 } else {
4839 // Continue selecting by line
4840 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
4841 }
4842 }
4843
4844 // Autoscroll
4845 const Sci::Line lineMove = DisplayFromPosition(movePos.Position());
4846 if (pt.y > rcClient.bottom) {
4847 ScrollTo(lineMove - LinesOnScreen() + 1);
4848 Redraw();
4849 } else if (pt.y < rcClient.top) {
4850 ScrollTo(lineMove);
4851 Redraw();
4852 }
4853 EnsureCaretVisible(false, false, true);
4854
4855 if (hotspot.Valid() && !PointIsHotspot(pt))
4856 SetHotSpotRange(nullptr);
4857
4858 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt, true, true) != hotSpotClickPos) {
4859 if (inDragDrop == ddNone) {
4860 DisplayCursor(Window::cursorText);
4861 }
4862 hotSpotClickPos = INVALID_POSITION;
4863 }
4864
4865 } else {
4866 if (vs.fixedColumnWidth > 0) { // There is a margin
4867 if (PointInSelMargin(pt)) {
4868 DisplayCursor(GetMarginCursor(pt));
4869 SetHotSpotRange(nullptr);
4870 return; // No need to test for selection
4871 }
4872 }
4873 // Display regular (drag) cursor over selection
4874 if (PointInSelection(pt) && !SelectionEmpty()) {
4875 DisplayCursor(Window::cursorArrow);
4876 } else {
4877 SetHoverIndicatorPoint(pt);
4878 if (PointIsHotspot(pt)) {
4879 DisplayCursor(Window::cursorHand);
4880 SetHotSpotRange(&pt);
4881 } else {
4882 if (hoverIndicatorPos != Sci::invalidPosition)
4883 DisplayCursor(Window::cursorHand);
4884 else
4885 DisplayCursor(Window::cursorText);
4886 SetHotSpotRange(nullptr);
4887 }
4888 }
4889 }
4890 }
4891
ButtonUpWithModifiers(Point pt,unsigned int curTime,int modifiers)4892 void Editor::ButtonUpWithModifiers(Point pt, unsigned int curTime, int modifiers) {
4893 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
4894 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
4895 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
4896 if (hoverIndicatorPos != INVALID_POSITION)
4897 InvalidateRange(newPos.Position(), newPos.Position() + 1);
4898 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
4899 if (inDragDrop == ddInitial) {
4900 inDragDrop = ddNone;
4901 SetEmptySelection(newPos);
4902 selectionType = selChar;
4903 originalAnchorPos = sel.MainCaret();
4904 }
4905 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
4906 hotSpotClickPos = INVALID_POSITION;
4907 SelectionPosition newCharPos = SPositionFromLocation(pt, false, true, false);
4908 newCharPos = MovePositionOutsideChar(newCharPos, -1);
4909 NotifyHotSpotReleaseClick(newCharPos.Position(), modifiers & SCI_CTRL);
4910 }
4911 if (HaveMouseCapture()) {
4912 if (PointInSelMargin(pt)) {
4913 DisplayCursor(GetMarginCursor(pt));
4914 } else {
4915 DisplayCursor(Window::cursorText);
4916 SetHotSpotRange(nullptr);
4917 }
4918 ptMouseLast = pt;
4919 SetMouseCapture(false);
4920 FineTickerCancel(tickScroll);
4921 NotifyIndicatorClick(false, newPos.Position(), 0);
4922 if (inDragDrop == ddDragging) {
4923 const SelectionPosition selStart = SelectionStart();
4924 const SelectionPosition selEnd = SelectionEnd();
4925 if (selStart < selEnd) {
4926 if (drag.Length()) {
4927 const Sci::Position length = drag.Length();
4928 if (modifiers & SCI_CTRL) {
4929 const Sci::Position lengthInserted = pdoc->InsertString(
4930 newPos.Position(), drag.Data(), length);
4931 if (lengthInserted > 0) {
4932 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4933 }
4934 } else if (newPos < selStart) {
4935 pdoc->DeleteChars(selStart.Position(), drag.Length());
4936 const Sci::Position lengthInserted = pdoc->InsertString(
4937 newPos.Position(), drag.Data(), length);
4938 if (lengthInserted > 0) {
4939 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4940 }
4941 } else if (newPos > selEnd) {
4942 pdoc->DeleteChars(selStart.Position(), drag.Length());
4943 newPos.Add(-static_cast<Sci::Position>(drag.Length()));
4944 const Sci::Position lengthInserted = pdoc->InsertString(
4945 newPos.Position(), drag.Data(), length);
4946 if (lengthInserted > 0) {
4947 SetSelection(newPos.Position(), newPos.Position() + lengthInserted);
4948 }
4949 } else {
4950 SetEmptySelection(newPos.Position());
4951 }
4952 drag.Clear();
4953 }
4954 selectionType = selChar;
4955 }
4956 } else {
4957 if (selectionType == selChar) {
4958 if (sel.Count() > 1) {
4959 sel.RangeMain() =
4960 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
4961 InvalidateWholeSelection();
4962 } else {
4963 SetSelection(newPos, sel.RangeMain().anchor);
4964 }
4965 }
4966 sel.CommitTentative();
4967 }
4968 SetRectangularRange();
4969 lastClickTime = curTime;
4970 lastClick = pt;
4971 lastXChosen = static_cast<int>(pt.x) + xOffset;
4972 if (sel.selType == Selection::selStream) {
4973 SetLastXChosen();
4974 }
4975 inDragDrop = ddNone;
4976 EnsureCaretVisible(false);
4977 }
4978 }
4979
Idle()4980 bool Editor::Idle() {
4981 NotifyUpdateUI();
4982
4983 bool needWrap = Wrapping() && wrapPending.NeedsWrap();
4984
4985 if (needWrap) {
4986 // Wrap lines during idle.
4987 WrapLines(WrapScope::wsIdle);
4988 // No more wrapping
4989 needWrap = wrapPending.NeedsWrap();
4990 } else if (needIdleStyling) {
4991 IdleStyling();
4992 }
4993
4994 // Add more idle things to do here, but make sure idleDone is
4995 // set correctly before the function returns. returning
4996 // false will stop calling this idle function until SetIdle() is
4997 // called again.
4998
4999 const bool idleDone = !needWrap && !needIdleStyling; // && thatDone && theOtherThingDone...
5000
5001 return !idleDone;
5002 }
5003
TickFor(TickReason reason)5004 void Editor::TickFor(TickReason reason) {
5005 switch (reason) {
5006 case tickCaret:
5007 caret.on = !caret.on;
5008 if (caret.active) {
5009 InvalidateCaret();
5010 }
5011 break;
5012 case tickScroll:
5013 // Auto scroll
5014 ButtonMoveWithModifiers(ptMouseLast, 0, 0);
5015 break;
5016 case tickWiden:
5017 SetScrollBars();
5018 FineTickerCancel(tickWiden);
5019 break;
5020 case tickDwell:
5021 if ((!HaveMouseCapture()) &&
5022 (ptMouseLast.y >= 0)) {
5023 dwelling = true;
5024 NotifyDwelling(ptMouseLast, dwelling);
5025 }
5026 FineTickerCancel(tickDwell);
5027 break;
5028 default:
5029 // tickPlatform handled by subclass
5030 break;
5031 }
5032 }
5033
5034 // FineTickerStart is be overridden by subclasses that support fine ticking so
5035 // this method should never be called.
FineTickerRunning(TickReason)5036 bool Editor::FineTickerRunning(TickReason) {
5037 assert(false);
5038 return false;
5039 }
5040
5041 // FineTickerStart is be overridden by subclasses that support fine ticking so
5042 // this method should never be called.
FineTickerStart(TickReason,int,int)5043 void Editor::FineTickerStart(TickReason, int, int) {
5044 assert(false);
5045 }
5046
5047 // FineTickerCancel is be overridden by subclasses that support fine ticking so
5048 // this method should never be called.
FineTickerCancel(TickReason)5049 void Editor::FineTickerCancel(TickReason) {
5050 assert(false);
5051 }
5052
SetFocusState(bool focusState)5053 void Editor::SetFocusState(bool focusState) {
5054 hasFocus = focusState;
5055 NotifyFocus(hasFocus);
5056 if (!hasFocus) {
5057 CancelModes();
5058 }
5059 ShowCaretAtCurrentPosition();
5060 }
5061
PositionAfterArea(PRectangle rcArea) const5062 Sci::Position Editor::PositionAfterArea(PRectangle rcArea) const {
5063 // The start of the document line after the display line after the area
5064 // This often means that the line after a modification is restyled which helps
5065 // detect multiline comment additions and heals single line comments
5066 const Sci::Line lineAfter = TopLineOfMain() + static_cast<Sci::Line>(rcArea.bottom - 1) / vs.lineHeight + 1;
5067 if (lineAfter < pcs->LinesDisplayed())
5068 return pdoc->LineStart(pcs->DocFromDisplay(lineAfter) + 1);
5069 else
5070 return pdoc->Length();
5071 }
5072
5073 // Style to a position within the view. If this causes a change at end of last line then
5074 // affects later lines so style all the viewed text.
StyleToPositionInView(Sci::Position pos)5075 void Editor::StyleToPositionInView(Sci::Position pos) {
5076 Sci::Position endWindow = PositionAfterArea(GetClientDrawingRectangle());
5077 if (pos > endWindow)
5078 pos = endWindow;
5079 const int styleAtEnd = pdoc->StyleIndexAt(pos-1);
5080 pdoc->EnsureStyledTo(pos);
5081 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleIndexAt(pos-1))) {
5082 // Style at end of line changed so is multi-line change like starting a comment
5083 // so require rest of window to be styled.
5084 DiscardOverdraw(); // Prepared bitmaps may be invalid
5085 // DiscardOverdraw may have truncated client drawing area so recalculate endWindow
5086 endWindow = PositionAfterArea(GetClientDrawingRectangle());
5087 pdoc->EnsureStyledTo(endWindow);
5088 }
5089 }
5090
PositionAfterMaxStyling(Sci::Position posMax,bool scrolling) const5091 Sci::Position Editor::PositionAfterMaxStyling(Sci::Position posMax, bool scrolling) const {
5092 if (SynchronousStylingToVisible()) {
5093 // Both states do not limit styling
5094 return posMax;
5095 }
5096
5097 // Try to keep time taken by styling reasonable so interaction remains smooth.
5098 // When scrolling, allow less time to ensure responsive
5099 const double secondsAllowed = scrolling ? 0.005 : 0.02;
5100
5101 const Sci::Line linesToStyle = std::clamp(
5102 static_cast<int>(secondsAllowed / pdoc->durationStyleOneLine.Duration()),
5103 10, 0x10000);
5104 const Sci::Line stylingMaxLine = std::min(
5105 pdoc->SciLineFromPosition(pdoc->GetEndStyled()) + linesToStyle,
5106 pdoc->LinesTotal());
5107 return std::min(pdoc->LineStart(stylingMaxLine), posMax);
5108 }
5109
StartIdleStyling(bool truncatedLastStyling)5110 void Editor::StartIdleStyling(bool truncatedLastStyling) {
5111 if ((idleStyling == SC_IDLESTYLING_ALL) || (idleStyling == SC_IDLESTYLING_AFTERVISIBLE)) {
5112 if (pdoc->GetEndStyled() < pdoc->Length()) {
5113 // Style remainder of document in idle time
5114 needIdleStyling = true;
5115 }
5116 } else if (truncatedLastStyling) {
5117 needIdleStyling = true;
5118 }
5119
5120 if (needIdleStyling) {
5121 SetIdle(true);
5122 }
5123 }
5124
5125 // Style for an area but bound the amount of styling to remain responsive
StyleAreaBounded(PRectangle rcArea,bool scrolling)5126 void Editor::StyleAreaBounded(PRectangle rcArea, bool scrolling) {
5127 const Sci::Position posAfterArea = PositionAfterArea(rcArea);
5128 const Sci::Position posAfterMax = PositionAfterMaxStyling(posAfterArea, scrolling);
5129 if (posAfterMax < posAfterArea) {
5130 // Idle styling may be performed before current visible area
5131 // Style a bit now then style further in idle time
5132 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5133 } else {
5134 // Can style all wanted now.
5135 StyleToPositionInView(posAfterArea);
5136 }
5137 StartIdleStyling(posAfterMax < posAfterArea);
5138 }
5139
IdleStyling()5140 void Editor::IdleStyling() {
5141 const Sci::Position posAfterArea = PositionAfterArea(GetClientRectangle());
5142 const Sci::Position endGoal = (idleStyling >= SC_IDLESTYLING_AFTERVISIBLE) ?
5143 pdoc->Length() : posAfterArea;
5144 const Sci::Position posAfterMax = PositionAfterMaxStyling(endGoal, false);
5145 pdoc->StyleToAdjustingLineDuration(posAfterMax);
5146 if (pdoc->GetEndStyled() >= endGoal) {
5147 needIdleStyling = false;
5148 }
5149 }
5150
IdleWork()5151 void Editor::IdleWork() {
5152 // Style the line after the modification as this allows modifications that change just the
5153 // line of the modification to heal instead of propagating to the rest of the window.
5154 if (workNeeded.items & WorkNeeded::workStyle) {
5155 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
5156 }
5157 NotifyUpdateUI();
5158 workNeeded.Reset();
5159 }
5160
QueueIdleWork(WorkNeeded::workItems items,Sci::Position upTo)5161 void Editor::QueueIdleWork(WorkNeeded::workItems items, Sci::Position upTo) {
5162 workNeeded.Need(items, upTo);
5163 }
5164
PaintContains(PRectangle rc)5165 bool Editor::PaintContains(PRectangle rc) {
5166 if (rc.Empty()) {
5167 return true;
5168 } else {
5169 return rcPaint.Contains(rc);
5170 }
5171 }
5172
PaintContainsMargin()5173 bool Editor::PaintContainsMargin() {
5174 if (wMargin.GetID()) {
5175 // With separate margin view, paint of text view
5176 // never contains margin.
5177 return false;
5178 }
5179 PRectangle rcSelMargin = GetClientRectangle();
5180 rcSelMargin.right = static_cast<XYPOSITION>(vs.textStart);
5181 return PaintContains(rcSelMargin);
5182 }
5183
CheckForChangeOutsidePaint(Range r)5184 void Editor::CheckForChangeOutsidePaint(Range r) {
5185 if (paintState == painting && !paintingAllText) {
5186 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5187 if (!r.Valid())
5188 return;
5189
5190 PRectangle rcRange = RectangleFromRange(r, 0);
5191 const PRectangle rcText = GetTextRectangle();
5192 if (rcRange.top < rcText.top) {
5193 rcRange.top = rcText.top;
5194 }
5195 if (rcRange.bottom > rcText.bottom) {
5196 rcRange.bottom = rcText.bottom;
5197 }
5198
5199 if (!PaintContains(rcRange)) {
5200 AbandonPaint();
5201 paintAbandonedByStyling = true;
5202 }
5203 }
5204 }
5205
SetBraceHighlight(Sci::Position pos0,Sci::Position pos1,int matchStyle)5206 void Editor::SetBraceHighlight(Sci::Position pos0, Sci::Position pos1, int matchStyle) {
5207 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5208 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5209 CheckForChangeOutsidePaint(Range(braces[0]));
5210 CheckForChangeOutsidePaint(Range(pos0));
5211 braces[0] = pos0;
5212 }
5213 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5214 CheckForChangeOutsidePaint(Range(braces[1]));
5215 CheckForChangeOutsidePaint(Range(pos1));
5216 braces[1] = pos1;
5217 }
5218 bracesMatchStyle = matchStyle;
5219 if (paintState == notPainting) {
5220 Redraw();
5221 }
5222 }
5223 }
5224
SetAnnotationHeights(Sci::Line start,Sci::Line end)5225 void Editor::SetAnnotationHeights(Sci::Line start, Sci::Line end) {
5226 if (vs.annotationVisible) {
5227 RefreshStyleData();
5228 bool changedHeight = false;
5229 for (Sci::Line line=start; line<end && line<pdoc->LinesTotal(); line++) {
5230 int linesWrapped = 1;
5231 if (Wrapping()) {
5232 AutoSurface surface(this);
5233 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5234 if (surface && ll) {
5235 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5236 linesWrapped = ll->lines;
5237 }
5238 }
5239 if (pcs->SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
5240 changedHeight = true;
5241 }
5242 if (changedHeight) {
5243 Redraw();
5244 }
5245 }
5246 }
5247
SetDocPointer(Document * document)5248 void Editor::SetDocPointer(Document *document) {
5249 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5250 pdoc->RemoveWatcher(this, 0);
5251 pdoc->Release();
5252 if (!document) {
5253 pdoc = new Document(SC_DOCUMENTOPTION_DEFAULT);
5254 } else {
5255 pdoc = document;
5256 }
5257 pdoc->AddRef();
5258 pcs = ContractionStateCreate(pdoc->IsLarge());
5259
5260 // Ensure all positions within document
5261 sel.Clear();
5262 targetRange = SelectionSegment();
5263
5264 braces[0] = Sci::invalidPosition;
5265 braces[1] = Sci::invalidPosition;
5266
5267 vs.ReleaseAllExtendedStyles();
5268
5269 SetRepresentations();
5270
5271 // Reset the contraction state to fully shown.
5272 pcs->Clear();
5273 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
5274 SetAnnotationHeights(0, pdoc->LinesTotal());
5275 view.llc.Deallocate();
5276 NeedWrapping();
5277
5278 hotspot = Range(Sci::invalidPosition);
5279 hoverIndicatorPos = Sci::invalidPosition;
5280
5281 view.ClearAllTabstops();
5282
5283 pdoc->AddWatcher(this, 0);
5284 SetScrollBars();
5285 Redraw();
5286 }
5287
SetAnnotationVisible(int visible)5288 void Editor::SetAnnotationVisible(int visible) {
5289 if (vs.annotationVisible != visible) {
5290 const bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
5291 vs.annotationVisible = visible;
5292 if (changedFromOrToHidden) {
5293 const int dir = vs.annotationVisible ? 1 : -1;
5294 for (Sci::Line line=0; line<pdoc->LinesTotal(); line++) {
5295 const int annotationLines = pdoc->AnnotationLines(line);
5296 if (annotationLines > 0) {
5297 pcs->SetHeight(line, pcs->GetHeight(line) + annotationLines * dir);
5298 }
5299 }
5300 SetScrollBars();
5301 }
5302 Redraw();
5303 }
5304 }
5305
5306 /**
5307 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5308 */
ExpandLine(Sci::Line line)5309 Sci::Line Editor::ExpandLine(Sci::Line line) {
5310 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line);
5311 line++;
5312 while (line <= lineMaxSubord) {
5313 pcs->SetVisible(line, line, true);
5314 const int level = pdoc->GetLevel(line);
5315 if (level & SC_FOLDLEVELHEADERFLAG) {
5316 if (pcs->GetExpanded(line)) {
5317 line = ExpandLine(line);
5318 } else {
5319 line = pdoc->GetLastChild(line);
5320 }
5321 }
5322 line++;
5323 }
5324 return lineMaxSubord;
5325 }
5326
SetFoldExpanded(Sci::Line lineDoc,bool expanded)5327 void Editor::SetFoldExpanded(Sci::Line lineDoc, bool expanded) {
5328 if (pcs->SetExpanded(lineDoc, expanded)) {
5329 RedrawSelMargin();
5330 }
5331 }
5332
FoldLine(Sci::Line line,int action)5333 void Editor::FoldLine(Sci::Line line, int action) {
5334 if (line >= 0) {
5335 if (action == SC_FOLDACTION_TOGGLE) {
5336 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
5337 line = pdoc->GetFoldParent(line);
5338 if (line < 0)
5339 return;
5340 }
5341 action = (pcs->GetExpanded(line)) ? SC_FOLDACTION_CONTRACT : SC_FOLDACTION_EXPAND;
5342 }
5343
5344 if (action == SC_FOLDACTION_CONTRACT) {
5345 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line);
5346 if (lineMaxSubord > line) {
5347 pcs->SetExpanded(line, false);
5348 pcs->SetVisible(line + 1, lineMaxSubord, false);
5349
5350 const Sci::Line lineCurrent =
5351 pdoc->SciLineFromPosition(sel.MainCaret());
5352 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5353 // This does not re-expand the fold
5354 EnsureCaretVisible();
5355 }
5356 }
5357
5358 } else {
5359 if (!(pcs->GetVisible(line))) {
5360 EnsureLineVisible(line, false);
5361 GoToLine(line);
5362 }
5363 pcs->SetExpanded(line, true);
5364 ExpandLine(line);
5365 }
5366
5367 SetScrollBars();
5368 Redraw();
5369 }
5370 }
5371
FoldExpand(Sci::Line line,int action,int level)5372 void Editor::FoldExpand(Sci::Line line, int action, int level) {
5373 bool expanding = action == SC_FOLDACTION_EXPAND;
5374 if (action == SC_FOLDACTION_TOGGLE) {
5375 expanding = !pcs->GetExpanded(line);
5376 }
5377 // Ensure child lines lexed and fold information extracted before
5378 // flipping the state.
5379 pdoc->GetLastChild(line, LevelNumber(level));
5380 SetFoldExpanded(line, expanding);
5381 if (expanding && (pcs->HiddenLines() == 0))
5382 // Nothing to do
5383 return;
5384 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, LevelNumber(level));
5385 line++;
5386 pcs->SetVisible(line, lineMaxSubord, expanding);
5387 while (line <= lineMaxSubord) {
5388 const int levelLine = pdoc->GetLevel(line);
5389 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5390 SetFoldExpanded(line, expanding);
5391 }
5392 line++;
5393 }
5394 SetScrollBars();
5395 Redraw();
5396 }
5397
ContractedFoldNext(Sci::Line lineStart) const5398 Sci::Line Editor::ContractedFoldNext(Sci::Line lineStart) const {
5399 for (Sci::Line line = lineStart; line<pdoc->LinesTotal();) {
5400 if (!pcs->GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
5401 return line;
5402 line = pcs->ContractedNext(line+1);
5403 if (line < 0)
5404 return -1;
5405 }
5406
5407 return -1;
5408 }
5409
5410 /**
5411 * Recurse up from this line to find any folds that prevent this line from being visible
5412 * and unfold them all.
5413 */
EnsureLineVisible(Sci::Line lineDoc,bool enforcePolicy)5414 void Editor::EnsureLineVisible(Sci::Line lineDoc, bool enforcePolicy) {
5415
5416 // In case in need of wrapping to ensure DisplayFromDoc works.
5417 if (lineDoc >= wrapPending.start) {
5418 if (WrapLines(WrapScope::wsAll)) {
5419 Redraw();
5420 }
5421 }
5422
5423 if (!pcs->GetVisible(lineDoc)) {
5424 // Back up to find a non-blank line
5425 Sci::Line lookLine = lineDoc;
5426 int lookLineLevel = pdoc->GetLevel(lookLine);
5427 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
5428 lookLineLevel = pdoc->GetLevel(--lookLine);
5429 }
5430 Sci::Line lineParent = pdoc->GetFoldParent(lookLine);
5431 if (lineParent < 0) {
5432 // Backed up to a top level line, so try to find parent of initial line
5433 lineParent = pdoc->GetFoldParent(lineDoc);
5434 }
5435 if (lineParent >= 0) {
5436 if (lineDoc != lineParent)
5437 EnsureLineVisible(lineParent, enforcePolicy);
5438 if (!pcs->GetExpanded(lineParent)) {
5439 pcs->SetExpanded(lineParent, true);
5440 ExpandLine(lineParent);
5441 }
5442 }
5443 SetScrollBars();
5444 Redraw();
5445 }
5446 if (enforcePolicy) {
5447 const Sci::Line lineDisplay = pcs->DisplayFromDoc(lineDoc);
5448 if (visiblePolicy.policy & VISIBLE_SLOP) {
5449 if ((topLine > lineDisplay) || ((visiblePolicy.policy & VISIBLE_STRICT) && (topLine + visiblePolicy.slop > lineDisplay))) {
5450 SetTopLine(std::clamp<Sci::Line>(lineDisplay - visiblePolicy.slop, 0, MaxScrollPos()));
5451 SetVerticalScrollPos();
5452 Redraw();
5453 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5454 ((visiblePolicy.policy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visiblePolicy.slop))) {
5455 SetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() + 1 + visiblePolicy.slop, 0, MaxScrollPos()));
5456 SetVerticalScrollPos();
5457 Redraw();
5458 }
5459 } else {
5460 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy.policy & VISIBLE_STRICT)) {
5461 SetTopLine(std::clamp<Sci::Line>(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5462 SetVerticalScrollPos();
5463 Redraw();
5464 }
5465 }
5466 }
5467 }
5468
FoldAll(int action)5469 void Editor::FoldAll(int action) {
5470 pdoc->EnsureStyledTo(pdoc->Length());
5471 const Sci::Line maxLine = pdoc->LinesTotal();
5472 bool expanding = action == SC_FOLDACTION_EXPAND;
5473 if (action == SC_FOLDACTION_TOGGLE) {
5474 // Discover current state
5475 for (int lineSeek = 0; lineSeek < maxLine; lineSeek++) {
5476 if (pdoc->GetLevel(lineSeek) & SC_FOLDLEVELHEADERFLAG) {
5477 expanding = !pcs->GetExpanded(lineSeek);
5478 break;
5479 }
5480 }
5481 }
5482 if (expanding) {
5483 pcs->SetVisible(0, maxLine-1, true);
5484 for (int line = 0; line < maxLine; line++) {
5485 const int levelLine = pdoc->GetLevel(line);
5486 if (levelLine & SC_FOLDLEVELHEADERFLAG) {
5487 SetFoldExpanded(line, true);
5488 }
5489 }
5490 } else {
5491 for (Sci::Line line = 0; line < maxLine; line++) {
5492 const int level = pdoc->GetLevel(line);
5493 if ((level & SC_FOLDLEVELHEADERFLAG) &&
5494 (SC_FOLDLEVELBASE == LevelNumber(level))) {
5495 SetFoldExpanded(line, false);
5496 const Sci::Line lineMaxSubord = pdoc->GetLastChild(line, -1);
5497 if (lineMaxSubord > line) {
5498 pcs->SetVisible(line + 1, lineMaxSubord, false);
5499 }
5500 }
5501 }
5502 }
5503 SetScrollBars();
5504 Redraw();
5505 }
5506
FoldChanged(Sci::Line line,int levelNow,int levelPrev)5507 void Editor::FoldChanged(Sci::Line line, int levelNow, int levelPrev) {
5508 if (levelNow & SC_FOLDLEVELHEADERFLAG) {
5509 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
5510 // Adding a fold point.
5511 if (pcs->SetExpanded(line, true)) {
5512 RedrawSelMargin();
5513 }
5514 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5515 }
5516 } else if (levelPrev & SC_FOLDLEVELHEADERFLAG) {
5517 const Sci::Line prevLine = line - 1;
5518 const int prevLineLevel = pdoc->GetLevel(prevLine);
5519
5520 // Combining two blocks where the first block is collapsed (e.g. by deleting the line(s) which separate(s) the two blocks)
5521 if ((LevelNumber(prevLineLevel) == LevelNumber(levelNow)) && !pcs->GetVisible(prevLine))
5522 FoldLine(pdoc->GetFoldParent(prevLine), SC_FOLDACTION_EXPAND);
5523
5524 if (!pcs->GetExpanded(line)) {
5525 // Removing the fold from one that has been contracted so should expand
5526 // otherwise lines are left invisible with no way to make them visible
5527 if (pcs->SetExpanded(line, true)) {
5528 RedrawSelMargin();
5529 }
5530 // Combining two blocks where the second one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5531 FoldExpand(line, SC_FOLDACTION_EXPAND, levelPrev);
5532 }
5533 }
5534 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) &&
5535 (LevelNumber(levelPrev) > LevelNumber(levelNow))) {
5536 if (pcs->HiddenLines()) {
5537 // See if should still be hidden
5538 const Sci::Line parentLine = pdoc->GetFoldParent(line);
5539 if ((parentLine < 0) || (pcs->GetExpanded(parentLine) && pcs->GetVisible(parentLine))) {
5540 pcs->SetVisible(line, line, true);
5541 SetScrollBars();
5542 Redraw();
5543 }
5544 }
5545 }
5546
5547 // Combining two blocks where the first one is collapsed (e.g. by adding characters in the line which separates the two blocks)
5548 if (!(levelNow & SC_FOLDLEVELWHITEFLAG) && (LevelNumber(levelPrev) < LevelNumber(levelNow))) {
5549 if (pcs->HiddenLines()) {
5550 const Sci::Line parentLine = pdoc->GetFoldParent(line);
5551 if (!pcs->GetExpanded(parentLine) && pcs->GetVisible(line))
5552 FoldLine(parentLine, SC_FOLDACTION_EXPAND);
5553 }
5554 }
5555 }
5556
NeedShown(Sci::Position pos,Sci::Position len)5557 void Editor::NeedShown(Sci::Position pos, Sci::Position len) {
5558 if (foldAutomatic & SC_AUTOMATICFOLD_SHOW) {
5559 const Sci::Line lineStart = pdoc->SciLineFromPosition(pos);
5560 const Sci::Line lineEnd = pdoc->SciLineFromPosition(pos+len);
5561 for (Sci::Line line = lineStart; line <= lineEnd; line++) {
5562 EnsureLineVisible(line, false);
5563 }
5564 } else {
5565 NotifyNeedShown(pos, len);
5566 }
5567 }
5568
GetTag(char * tagValue,int tagNumber)5569 Sci::Position Editor::GetTag(char *tagValue, int tagNumber) {
5570 const char *text = nullptr;
5571 Sci::Position length = 0;
5572 if ((tagNumber >= 1) && (tagNumber <= 9)) {
5573 char name[3] = "\\?";
5574 name[1] = static_cast<char>(tagNumber + '0');
5575 length = 2;
5576 text = pdoc->SubstituteByPosition(name, &length);
5577 }
5578 if (tagValue) {
5579 if (text)
5580 memcpy(tagValue, text, length + 1);
5581 else
5582 *tagValue = '\0';
5583 }
5584 return length;
5585 }
5586
ReplaceTarget(bool replacePatterns,const char * text,Sci::Position length)5587 Sci::Position Editor::ReplaceTarget(bool replacePatterns, const char *text, Sci::Position length) {
5588 UndoGroup ug(pdoc);
5589 if (length == -1)
5590 length = strlen(text);
5591 if (replacePatterns) {
5592 text = pdoc->SubstituteByPosition(text, &length);
5593 if (!text) {
5594 return 0;
5595 }
5596 }
5597
5598 // Remove the text inside the range
5599 if (targetRange.Length() > 0)
5600 pdoc->DeleteChars(targetRange.start.Position(), targetRange.Length());
5601 targetRange.end = targetRange.start;
5602
5603 // Realize virtual space of target start
5604 Sci::Position startAfterSpaceInsertion = RealizeVirtualSpace(targetRange.start.Position(), targetRange.start.VirtualSpace());
5605 targetRange.start.SetPosition(startAfterSpaceInsertion);
5606 targetRange.end = targetRange.start;
5607
5608 // Insert the new text
5609 const Sci::Position lengthInserted = pdoc->InsertString(targetRange.start.Position(), text, length);
5610 targetRange.end.SetPosition(targetRange.start.Position() + lengthInserted);
5611 return length;
5612 }
5613
IsUnicodeMode() const5614 bool Editor::IsUnicodeMode() const noexcept {
5615 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5616 }
5617
CodePage() const5618 int Editor::CodePage() const noexcept {
5619 if (pdoc)
5620 return pdoc->dbcsCodePage;
5621 else
5622 return 0;
5623 }
5624
WrapCount(Sci::Line line)5625 Sci::Line Editor::WrapCount(Sci::Line line) {
5626 AutoSurface surface(this);
5627 AutoLineLayout ll(view.llc, view.RetrieveLineLayout(line, *this));
5628
5629 if (surface && ll) {
5630 view.LayoutLine(*this, line, surface, vs, ll, wrapWidth);
5631 return ll->lines;
5632 } else {
5633 return 1;
5634 }
5635 }
5636
AddStyledText(const char * buffer,Sci::Position appendLength)5637 void Editor::AddStyledText(const char *buffer, Sci::Position appendLength) {
5638 // The buffer consists of alternating character bytes and style bytes
5639 const Sci::Position textLength = appendLength / 2;
5640 std::string text(textLength, '\0');
5641 Sci::Position i;
5642 for (i = 0; i < textLength; i++) {
5643 text[i] = buffer[i*2];
5644 }
5645 const Sci::Position lengthInserted = pdoc->InsertString(CurrentPosition(), text.c_str(), textLength);
5646 for (i = 0; i < textLength; i++) {
5647 text[i] = buffer[i*2+1];
5648 }
5649 pdoc->StartStyling(CurrentPosition());
5650 pdoc->SetStyles(textLength, text.c_str());
5651 SetEmptySelection(sel.MainCaret() + lengthInserted);
5652 }
5653
ValidMargin(uptr_t wParam) const5654 bool Editor::ValidMargin(uptr_t wParam) const noexcept {
5655 return wParam < vs.ms.size();
5656 }
5657
StyleSetMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5658 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5659 vs.EnsureStyle(wParam);
5660 switch (iMessage) {
5661 case SCI_STYLESETFORE:
5662 vs.styles[wParam].fore = ColourDesired(static_cast<int>(lParam));
5663 break;
5664 case SCI_STYLESETBACK:
5665 vs.styles[wParam].back = ColourDesired(static_cast<int>(lParam));
5666 break;
5667 case SCI_STYLESETBOLD:
5668 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
5669 break;
5670 case SCI_STYLESETWEIGHT:
5671 vs.styles[wParam].weight = static_cast<int>(lParam);
5672 break;
5673 case SCI_STYLESETITALIC:
5674 vs.styles[wParam].italic = lParam != 0;
5675 break;
5676 case SCI_STYLESETEOLFILLED:
5677 vs.styles[wParam].eolFilled = lParam != 0;
5678 break;
5679 case SCI_STYLESETSIZE:
5680 vs.styles[wParam].size = static_cast<int>(lParam * SC_FONT_SIZE_MULTIPLIER);
5681 break;
5682 case SCI_STYLESETSIZEFRACTIONAL:
5683 vs.styles[wParam].size = static_cast<int>(lParam);
5684 break;
5685 case SCI_STYLESETFONT:
5686 if (lParam != 0) {
5687 vs.SetStyleFontName(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
5688 }
5689 break;
5690 case SCI_STYLESETUNDERLINE:
5691 vs.styles[wParam].underline = lParam != 0;
5692 break;
5693 case SCI_STYLESETCASE:
5694 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5695 break;
5696 case SCI_STYLESETCHARACTERSET:
5697 vs.styles[wParam].characterSet = static_cast<int>(lParam);
5698 pdoc->SetCaseFolder(nullptr);
5699 break;
5700 case SCI_STYLESETVISIBLE:
5701 vs.styles[wParam].visible = lParam != 0;
5702 break;
5703 case SCI_STYLESETCHANGEABLE:
5704 vs.styles[wParam].changeable = lParam != 0;
5705 break;
5706 case SCI_STYLESETHOTSPOT:
5707 vs.styles[wParam].hotspot = lParam != 0;
5708 break;
5709 }
5710 InvalidateStyleRedraw();
5711 }
5712
StyleGetMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5713 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5714 vs.EnsureStyle(wParam);
5715 switch (iMessage) {
5716 case SCI_STYLEGETFORE:
5717 return vs.styles[wParam].fore.AsInteger();
5718 case SCI_STYLEGETBACK:
5719 return vs.styles[wParam].back.AsInteger();
5720 case SCI_STYLEGETBOLD:
5721 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
5722 case SCI_STYLEGETWEIGHT:
5723 return vs.styles[wParam].weight;
5724 case SCI_STYLEGETITALIC:
5725 return vs.styles[wParam].italic ? 1 : 0;
5726 case SCI_STYLEGETEOLFILLED:
5727 return vs.styles[wParam].eolFilled ? 1 : 0;
5728 case SCI_STYLEGETSIZE:
5729 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
5730 case SCI_STYLEGETSIZEFRACTIONAL:
5731 return vs.styles[wParam].size;
5732 case SCI_STYLEGETFONT:
5733 return StringResult(lParam, vs.styles[wParam].fontName);
5734 case SCI_STYLEGETUNDERLINE:
5735 return vs.styles[wParam].underline ? 1 : 0;
5736 case SCI_STYLEGETCASE:
5737 return static_cast<int>(vs.styles[wParam].caseForce);
5738 case SCI_STYLEGETCHARACTERSET:
5739 return vs.styles[wParam].characterSet;
5740 case SCI_STYLEGETVISIBLE:
5741 return vs.styles[wParam].visible ? 1 : 0;
5742 case SCI_STYLEGETCHANGEABLE:
5743 return vs.styles[wParam].changeable ? 1 : 0;
5744 case SCI_STYLEGETHOTSPOT:
5745 return vs.styles[wParam].hotspot ? 1 : 0;
5746 }
5747 return 0;
5748 }
5749
SetSelectionNMessage(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5750 void Editor::SetSelectionNMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5751 InvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());
5752
5753 switch (iMessage) {
5754 case SCI_SETSELECTIONNCARET:
5755 sel.Range(wParam).caret.SetPosition(lParam);
5756 break;
5757
5758 case SCI_SETSELECTIONNANCHOR:
5759 sel.Range(wParam).anchor.SetPosition(lParam);
5760 break;
5761
5762 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
5763 sel.Range(wParam).caret.SetVirtualSpace(lParam);
5764 break;
5765
5766 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
5767 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
5768 break;
5769
5770 case SCI_SETSELECTIONNSTART:
5771 sel.Range(wParam).anchor.SetPosition(lParam);
5772 break;
5773
5774 case SCI_SETSELECTIONNEND:
5775 sel.Range(wParam).caret.SetPosition(lParam);
5776 break;
5777 }
5778
5779 InvalidateRange(sel.Range(wParam).Start().Position(), sel.Range(wParam).End().Position());
5780 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
5781 }
5782
StringResult(sptr_t lParam,const char * val)5783 sptr_t Editor::StringResult(sptr_t lParam, const char *val) noexcept {
5784 const size_t len = val ? strlen(val) : 0;
5785 if (lParam) {
5786 char *ptr = CharPtrFromSPtr(lParam);
5787 if (val)
5788 memcpy(ptr, val, len+1);
5789 else
5790 *ptr = 0;
5791 }
5792 return len; // Not including NUL
5793 }
5794
BytesResult(sptr_t lParam,const unsigned char * val,size_t len)5795 sptr_t Editor::BytesResult(sptr_t lParam, const unsigned char *val, size_t len) noexcept {
5796 // No NUL termination: len is number of valid/displayed bytes
5797 if ((lParam) && (len > 0)) {
5798 char *ptr = CharPtrFromSPtr(lParam);
5799 if (val)
5800 memcpy(ptr, val, len);
5801 else
5802 *ptr = 0;
5803 }
5804 return val ? len : 0;
5805 }
5806
WndProc(unsigned int iMessage,uptr_t wParam,sptr_t lParam)5807 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5808 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5809
5810 // Optional macro recording hook
5811 if (recordingMacro)
5812 NotifyMacroRecord(iMessage, wParam, lParam);
5813
5814 switch (iMessage) {
5815
5816 case SCI_GETTEXT: {
5817 if (lParam == 0)
5818 return pdoc->Length() + 1;
5819 if (wParam == 0)
5820 return 0;
5821 char *ptr = CharPtrFromSPtr(lParam);
5822 const Sci_Position len = std::min<Sci_Position>(wParam - 1, pdoc->Length());
5823 pdoc->GetCharRange(ptr, 0, len);
5824 ptr[len] = '\0';
5825 return len;
5826 }
5827
5828 case SCI_SETTEXT: {
5829 if (lParam == 0)
5830 return 0;
5831 UndoGroup ug(pdoc);
5832 pdoc->DeleteChars(0, pdoc->Length());
5833 SetEmptySelection(0);
5834 const char *text = CharPtrFromSPtr(lParam);
5835 pdoc->InsertString(0, text, strlen(text));
5836 return 1;
5837 }
5838
5839 case SCI_GETTEXTLENGTH:
5840 return pdoc->Length();
5841
5842 case SCI_CUT:
5843 Cut();
5844 SetLastXChosen();
5845 break;
5846
5847 case SCI_COPY:
5848 Copy();
5849 break;
5850
5851 case SCI_COPYALLOWLINE:
5852 CopyAllowLine();
5853 break;
5854
5855 case SCI_VERTICALCENTRECARET:
5856 VerticalCentreCaret();
5857 break;
5858
5859 case SCI_MOVESELECTEDLINESUP:
5860 MoveSelectedLinesUp();
5861 break;
5862
5863 case SCI_MOVESELECTEDLINESDOWN:
5864 MoveSelectedLinesDown();
5865 break;
5866
5867 case SCI_COPYRANGE:
5868 CopyRangeToClipboard(static_cast<Sci::Position>(wParam), lParam);
5869 break;
5870
5871 case SCI_COPYTEXT:
5872 CopyText(wParam, CharPtrFromSPtr(lParam));
5873 break;
5874
5875 case SCI_PASTE:
5876 Paste();
5877 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5878 SetLastXChosen();
5879 }
5880 EnsureCaretVisible();
5881 break;
5882
5883 case SCI_CLEAR:
5884 Clear();
5885 SetLastXChosen();
5886 EnsureCaretVisible();
5887 break;
5888
5889 case SCI_UNDO:
5890 Undo();
5891 SetLastXChosen();
5892 break;
5893
5894 case SCI_CANUNDO:
5895 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
5896
5897 case SCI_EMPTYUNDOBUFFER:
5898 pdoc->DeleteUndoHistory();
5899 return 0;
5900
5901 case SCI_GETFIRSTVISIBLELINE:
5902 return topLine;
5903
5904 case SCI_SETFIRSTVISIBLELINE:
5905 ScrollTo(static_cast<Sci::Line>(wParam));
5906 break;
5907
5908 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
5909 const Sci::Position lineStart =
5910 pdoc->LineStart(static_cast<Sci::Line>(wParam));
5911 const Sci::Position lineEnd =
5912 pdoc->LineStart(static_cast<Sci::Line>(wParam + 1));
5913 // not NUL terminated
5914 const Sci::Position len = lineEnd - lineStart;
5915 if (lParam == 0) {
5916 return len;
5917 }
5918 char *ptr = CharPtrFromSPtr(lParam);
5919 pdoc->GetCharRange(ptr, lineStart, len);
5920 return len;
5921 }
5922
5923 case SCI_GETLINECOUNT:
5924 if (pdoc->LinesTotal() == 0)
5925 return 1;
5926 else
5927 return pdoc->LinesTotal();
5928
5929 case SCI_GETMODIFY:
5930 return !pdoc->IsSavePoint();
5931
5932 case SCI_SETSEL: {
5933 Sci::Position nStart = static_cast<Sci::Position>(wParam);
5934 Sci::Position nEnd = lParam;
5935 if (nEnd < 0)
5936 nEnd = pdoc->Length();
5937 if (nStart < 0)
5938 nStart = nEnd; // Remove selection
5939 InvalidateSelection(SelectionRange(nStart, nEnd));
5940 sel.Clear();
5941 sel.selType = Selection::selStream;
5942 SetSelection(nEnd, nStart);
5943 EnsureCaretVisible();
5944 }
5945 break;
5946
5947 case SCI_GETSELTEXT: {
5948 SelectionText selectedText;
5949 CopySelectionRange(&selectedText);
5950 if (lParam == 0) {
5951 return selectedText.LengthWithTerminator();
5952 } else {
5953 char *ptr = CharPtrFromSPtr(lParam);
5954 size_t iChar = selectedText.Length();
5955 if (iChar) {
5956 memcpy(ptr, selectedText.Data(), iChar);
5957 ptr[iChar++] = '\0';
5958 } else {
5959 ptr[0] = '\0';
5960 }
5961 return iChar;
5962 }
5963 }
5964
5965 case SCI_LINEFROMPOSITION:
5966 if (static_cast<Sci::Position>(wParam) < 0)
5967 return 0;
5968 return pdoc->LineFromPosition(static_cast<Sci::Position>(wParam));
5969
5970 case SCI_POSITIONFROMLINE:
5971 if (static_cast<Sci::Position>(wParam) < 0)
5972 wParam = pdoc->LineFromPosition(SelectionStart().Position());
5973 if (wParam == 0)
5974 return 0; // Even if there is no text, there is a first line that starts at 0
5975 if (static_cast<Sci::Line>(wParam) > pdoc->LinesTotal())
5976 return -1;
5977 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
5978 // return -1;
5979 return pdoc->LineStart(static_cast<Sci::Position>(wParam));
5980
5981 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
5982 case SCI_LINELENGTH:
5983 if ((static_cast<Sci::Position>(wParam) < 0) ||
5984 (static_cast<Sci::Position>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5985 return 0;
5986 return pdoc->LineStart(static_cast<Sci::Position>(wParam) + 1) - pdoc->LineStart(static_cast<Sci::Position>(wParam));
5987
5988 case SCI_REPLACESEL: {
5989 if (lParam == 0)
5990 return 0;
5991 UndoGroup ug(pdoc);
5992 ClearSelection();
5993 const char *replacement = CharPtrFromSPtr(lParam);
5994 const Sci::Position lengthInserted = pdoc->InsertString(
5995 sel.MainCaret(), replacement, strlen(replacement));
5996 SetEmptySelection(sel.MainCaret() + lengthInserted);
5997 SetLastXChosen();
5998 EnsureCaretVisible();
5999 }
6000 break;
6001
6002 case SCI_SETTARGETSTART:
6003 targetRange.start.SetPosition(static_cast<Sci::Position>(wParam));
6004 break;
6005
6006 case SCI_GETTARGETSTART:
6007 return targetRange.start.Position();
6008
6009 case SCI_SETTARGETSTARTVIRTUALSPACE:
6010 targetRange.start.SetVirtualSpace(static_cast<Sci::Position>(wParam));
6011 break;
6012
6013 case SCI_GETTARGETSTARTVIRTUALSPACE:
6014 return targetRange.start.VirtualSpace();
6015
6016 case SCI_SETTARGETEND:
6017 targetRange.end.SetPosition(static_cast<Sci::Position>(wParam));
6018 break;
6019
6020 case SCI_GETTARGETEND:
6021 return targetRange.end.Position();
6022
6023 case SCI_SETTARGETENDVIRTUALSPACE:
6024 targetRange.end.SetVirtualSpace(static_cast<Sci::Position>(wParam));
6025 break;
6026
6027 case SCI_GETTARGETENDVIRTUALSPACE:
6028 return targetRange.end.VirtualSpace();
6029
6030 case SCI_SETTARGETRANGE:
6031 targetRange.start.SetPosition(static_cast<Sci::Position>(wParam));
6032 targetRange.end.SetPosition(lParam);
6033 break;
6034
6035 case SCI_TARGETWHOLEDOCUMENT:
6036 targetRange.start.SetPosition(0);
6037 targetRange.end.SetPosition(pdoc->Length());
6038 break;
6039
6040 case SCI_TARGETFROMSELECTION:
6041 targetRange.start = sel.RangeMain().Start();
6042 targetRange.end = sel.RangeMain().End();
6043 break;
6044
6045 case SCI_GETTARGETTEXT: {
6046 std::string text = RangeText(targetRange.start.Position(), targetRange.end.Position());
6047 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(text.c_str()), text.length());
6048 }
6049
6050 case SCI_REPLACETARGET:
6051 PLATFORM_ASSERT(lParam);
6052 return ReplaceTarget(false, CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6053
6054 case SCI_REPLACETARGETRE:
6055 PLATFORM_ASSERT(lParam);
6056 return ReplaceTarget(true, CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6057
6058 case SCI_SEARCHINTARGET:
6059 PLATFORM_ASSERT(lParam);
6060 return SearchInTarget(CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6061
6062 case SCI_SETSEARCHFLAGS:
6063 searchFlags = static_cast<int>(wParam);
6064 break;
6065
6066 case SCI_GETSEARCHFLAGS:
6067 return searchFlags;
6068
6069 case SCI_GETTAG:
6070 return GetTag(CharPtrFromSPtr(lParam), static_cast<int>(wParam));
6071
6072 case SCI_POSITIONBEFORE:
6073 return pdoc->MovePositionOutsideChar(static_cast<Sci::Position>(wParam) - 1, -1, true);
6074
6075 case SCI_POSITIONAFTER:
6076 return pdoc->MovePositionOutsideChar(static_cast<Sci::Position>(wParam) + 1, 1, true);
6077
6078 case SCI_POSITIONRELATIVE:
6079 return std::clamp<Sci::Position>(pdoc->GetRelativePosition(
6080 static_cast<Sci::Position>(wParam), lParam),
6081 0, pdoc->Length());
6082
6083 case SCI_POSITIONRELATIVECODEUNITS:
6084 return std::clamp<Sci::Position>(pdoc->GetRelativePositionUTF16(
6085 static_cast<Sci::Position>(wParam), lParam),
6086 0, pdoc->Length());
6087
6088 case SCI_LINESCROLL:
6089 ScrollTo(topLine + static_cast<Sci::Line>(lParam));
6090 HorizontalScrollTo(xOffset + static_cast<int>(wParam) * static_cast<int>(vs.spaceWidth));
6091 return 1;
6092
6093 case SCI_SETXOFFSET:
6094 xOffset = static_cast<int>(wParam);
6095 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6096 SetHorizontalScrollPos();
6097 Redraw();
6098 break;
6099
6100 case SCI_GETXOFFSET:
6101 return xOffset;
6102
6103 case SCI_CHOOSECARETX:
6104 SetLastXChosen();
6105 break;
6106
6107 case SCI_SCROLLCARET:
6108 EnsureCaretVisible();
6109 break;
6110
6111 case SCI_SETREADONLY:
6112 pdoc->SetReadOnly(wParam != 0);
6113 return 1;
6114
6115 case SCI_GETREADONLY:
6116 return pdoc->IsReadOnly();
6117
6118 case SCI_CANPASTE:
6119 return CanPaste();
6120
6121 case SCI_POINTXFROMPOSITION:
6122 if (lParam < 0) {
6123 return 0;
6124 } else {
6125 const Point pt = LocationFromPosition(lParam);
6126 // Convert to view-relative
6127 return static_cast<int>(pt.x) - vs.textStart + vs.fixedColumnWidth;
6128 }
6129
6130 case SCI_POINTYFROMPOSITION:
6131 if (lParam < 0) {
6132 return 0;
6133 } else {
6134 const Point pt = LocationFromPosition(lParam);
6135 return static_cast<int>(pt.y);
6136 }
6137
6138 case SCI_FINDTEXT:
6139 return FindText(wParam, lParam);
6140
6141 case SCI_GETTEXTRANGE: {
6142 if (lParam == 0)
6143 return 0;
6144 Sci_TextRange *tr = static_cast<Sci_TextRange *>(PtrFromSPtr(lParam));
6145 Sci::Position cpMax = static_cast<Sci::Position>(tr->chrg.cpMax);
6146 if (cpMax == -1)
6147 cpMax = pdoc->Length();
6148 PLATFORM_ASSERT(cpMax <= pdoc->Length());
6149 Sci::Position len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
6150 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
6151 // Spec says copied text is terminated with a NUL
6152 tr->lpstrText[len] = '\0';
6153 return len; // Not including NUL
6154 }
6155
6156 case SCI_HIDESELECTION:
6157 view.hideSelection = wParam != 0;
6158 Redraw();
6159 break;
6160
6161 case SCI_FORMATRANGE:
6162 return FormatRange(wParam != 0, static_cast<Sci_RangeToFormat *>(PtrFromSPtr(lParam)));
6163
6164 case SCI_GETMARGINLEFT:
6165 return vs.leftMarginWidth;
6166
6167 case SCI_GETMARGINRIGHT:
6168 return vs.rightMarginWidth;
6169
6170 case SCI_SETMARGINLEFT:
6171 lastXChosen += static_cast<int>(lParam) - vs.leftMarginWidth;
6172 vs.leftMarginWidth = static_cast<int>(lParam);
6173 InvalidateStyleRedraw();
6174 break;
6175
6176 case SCI_SETMARGINRIGHT:
6177 vs.rightMarginWidth = static_cast<int>(lParam);
6178 InvalidateStyleRedraw();
6179 break;
6180
6181 // Control specific mesages
6182
6183 case SCI_ADDTEXT: {
6184 if (lParam == 0)
6185 return 0;
6186 const Sci::Position lengthInserted = pdoc->InsertString(
6187 CurrentPosition(), CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6188 SetEmptySelection(sel.MainCaret() + lengthInserted);
6189 return 0;
6190 }
6191
6192 case SCI_ADDSTYLEDTEXT:
6193 if (lParam)
6194 AddStyledText(CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6195 return 0;
6196
6197 case SCI_INSERTTEXT: {
6198 if (lParam == 0)
6199 return 0;
6200 Sci::Position insertPos = static_cast<Sci::Position>(wParam);
6201 if (insertPos == -1)
6202 insertPos = CurrentPosition();
6203 Sci::Position newCurrent = CurrentPosition();
6204 const char *sz = CharPtrFromSPtr(lParam);
6205 const Sci::Position lengthInserted = pdoc->InsertString(insertPos, sz, strlen(sz));
6206 if (newCurrent > insertPos)
6207 newCurrent += lengthInserted;
6208 SetEmptySelection(newCurrent);
6209 return 0;
6210 }
6211
6212 case SCI_CHANGEINSERTION:
6213 PLATFORM_ASSERT(lParam);
6214 pdoc->ChangeInsertion(CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6215 return 0;
6216
6217 case SCI_APPENDTEXT:
6218 pdoc->InsertString(pdoc->Length(),
6219 CharPtrFromSPtr(lParam), static_cast<Sci::Position>(wParam));
6220 return 0;
6221
6222 case SCI_CLEARALL:
6223 ClearAll();
6224 return 0;
6225
6226 case SCI_DELETERANGE:
6227 pdoc->DeleteChars(static_cast<Sci::Position>(wParam), lParam);
6228 return 0;
6229
6230 case SCI_CLEARDOCUMENTSTYLE:
6231 ClearDocumentStyle();
6232 return 0;
6233
6234 case SCI_SETUNDOCOLLECTION:
6235 pdoc->SetUndoCollection(wParam != 0);
6236 return 0;
6237
6238 case SCI_GETUNDOCOLLECTION:
6239 return pdoc->IsCollectingUndo();
6240
6241 case SCI_BEGINUNDOACTION:
6242 pdoc->BeginUndoAction();
6243 return 0;
6244
6245 case SCI_ENDUNDOACTION:
6246 pdoc->EndUndoAction();
6247 return 0;
6248
6249 case SCI_GETCARETPERIOD:
6250 return caret.period;
6251
6252 case SCI_SETCARETPERIOD:
6253 CaretSetPeriod(static_cast<int>(wParam));
6254 break;
6255
6256 case SCI_GETWORDCHARS:
6257 return pdoc->GetCharsOfClass(CharClassify::ccWord, UCharPtrFromSPtr(lParam));
6258
6259 case SCI_SETWORDCHARS: {
6260 pdoc->SetDefaultCharClasses(false);
6261 if (lParam == 0)
6262 return 0;
6263 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharClassify::ccWord);
6264 }
6265 break;
6266
6267 case SCI_GETWHITESPACECHARS:
6268 return pdoc->GetCharsOfClass(CharClassify::ccSpace, UCharPtrFromSPtr(lParam));
6269
6270 case SCI_SETWHITESPACECHARS: {
6271 if (lParam == 0)
6272 return 0;
6273 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharClassify::ccSpace);
6274 }
6275 break;
6276
6277 case SCI_GETPUNCTUATIONCHARS:
6278 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, UCharPtrFromSPtr(lParam));
6279
6280 case SCI_SETPUNCTUATIONCHARS: {
6281 if (lParam == 0)
6282 return 0;
6283 pdoc->SetCharClasses(ConstUCharPtrFromSPtr(lParam), CharClassify::ccPunctuation);
6284 }
6285 break;
6286
6287 case SCI_SETCHARSDEFAULT:
6288 pdoc->SetDefaultCharClasses(true);
6289 break;
6290
6291 case SCI_SETCHARACTERCATEGORYOPTIMIZATION:
6292 pdoc->SetCharacterCategoryOptimization(static_cast<int>(wParam));
6293 break;
6294
6295 case SCI_GETCHARACTERCATEGORYOPTIMIZATION:
6296 return pdoc->CharacterCategoryOptimization();
6297
6298 case SCI_GETLENGTH:
6299 return pdoc->Length();
6300
6301 case SCI_ALLOCATE:
6302 pdoc->Allocate(static_cast<Sci::Position>(wParam));
6303 break;
6304
6305 case SCI_GETCHARAT:
6306 return pdoc->CharAt(static_cast<Sci::Position>(wParam));
6307
6308 case SCI_SETCURRENTPOS:
6309 if (sel.IsRectangular()) {
6310 sel.Rectangular().caret.SetPosition(static_cast<Sci::Position>(wParam));
6311 SetRectangularRange();
6312 Redraw();
6313 } else {
6314 SetSelection(static_cast<Sci::Position>(wParam), sel.MainAnchor());
6315 }
6316 break;
6317
6318 case SCI_GETCURRENTPOS:
6319 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6320
6321 case SCI_SETANCHOR:
6322 if (sel.IsRectangular()) {
6323 sel.Rectangular().anchor.SetPosition(static_cast<Sci::Position>(wParam));
6324 SetRectangularRange();
6325 Redraw();
6326 } else {
6327 SetSelection(sel.MainCaret(), static_cast<Sci::Position>(wParam));
6328 }
6329 break;
6330
6331 case SCI_GETANCHOR:
6332 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6333
6334 case SCI_SETSELECTIONSTART:
6335 SetSelection(std::max(sel.MainCaret(), static_cast<Sci::Position>(wParam)), static_cast<Sci::Position>(wParam));
6336 break;
6337
6338 case SCI_GETSELECTIONSTART:
6339 return sel.LimitsForRectangularElseMain().start.Position();
6340
6341 case SCI_SETSELECTIONEND:
6342 SetSelection(static_cast<Sci::Position>(wParam), std::min(sel.MainAnchor(), static_cast<Sci::Position>(wParam)));
6343 break;
6344
6345 case SCI_GETSELECTIONEND:
6346 return sel.LimitsForRectangularElseMain().end.Position();
6347
6348 case SCI_SETEMPTYSELECTION:
6349 SetEmptySelection(static_cast<Sci::Position>(wParam));
6350 break;
6351
6352 case SCI_SETPRINTMAGNIFICATION:
6353 view.printParameters.magnification = static_cast<int>(wParam);
6354 break;
6355
6356 case SCI_GETPRINTMAGNIFICATION:
6357 return view.printParameters.magnification;
6358
6359 case SCI_SETPRINTCOLOURMODE:
6360 view.printParameters.colourMode = static_cast<int>(wParam);
6361 break;
6362
6363 case SCI_GETPRINTCOLOURMODE:
6364 return view.printParameters.colourMode;
6365
6366 case SCI_SETPRINTWRAPMODE:
6367 view.printParameters.wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6368 break;
6369
6370 case SCI_GETPRINTWRAPMODE:
6371 return view.printParameters.wrapState;
6372
6373 case SCI_GETSTYLEAT:
6374 if (static_cast<Sci::Position>(wParam) >= pdoc->Length())
6375 return 0;
6376 else
6377 return pdoc->StyleAt(static_cast<Sci::Position>(wParam));
6378
6379 case SCI_REDO:
6380 Redo();
6381 break;
6382
6383 case SCI_SELECTALL:
6384 SelectAll();
6385 break;
6386
6387 case SCI_SETSAVEPOINT:
6388 pdoc->SetSavePoint();
6389 break;
6390
6391 case SCI_GETSTYLEDTEXT: {
6392 if (lParam == 0)
6393 return 0;
6394 Sci_TextRange *tr = static_cast<Sci_TextRange *>(PtrFromSPtr(lParam));
6395 Sci::Position iPlace = 0;
6396 for (Sci::Position iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6397 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6398 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6399 }
6400 tr->lpstrText[iPlace] = '\0';
6401 tr->lpstrText[iPlace + 1] = '\0';
6402 return iPlace;
6403 }
6404
6405 case SCI_CANREDO:
6406 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6407
6408 case SCI_MARKERLINEFROMHANDLE:
6409 return pdoc->LineFromHandle(static_cast<int>(wParam));
6410
6411 case SCI_MARKERDELETEHANDLE:
6412 pdoc->DeleteMarkFromHandle(static_cast<int>(wParam));
6413 break;
6414
6415 case SCI_GETVIEWWS:
6416 return vs.viewWhitespace;
6417
6418 case SCI_SETVIEWWS:
6419 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6420 Redraw();
6421 break;
6422
6423 case SCI_GETTABDRAWMODE:
6424 return vs.tabDrawMode;
6425
6426 case SCI_SETTABDRAWMODE:
6427 vs.tabDrawMode = static_cast<TabDrawMode>(wParam);
6428 Redraw();
6429 break;
6430
6431 case SCI_GETWHITESPACESIZE:
6432 return vs.whitespaceSize;
6433
6434 case SCI_SETWHITESPACESIZE:
6435 vs.whitespaceSize = static_cast<int>(wParam);
6436 Redraw();
6437 break;
6438
6439 case SCI_POSITIONFROMPOINT:
6440 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6441 false, false);
6442
6443 case SCI_POSITIONFROMPOINTCLOSE:
6444 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6445 true, false);
6446
6447 case SCI_CHARPOSITIONFROMPOINT:
6448 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6449 false, true);
6450
6451 case SCI_CHARPOSITIONFROMPOINTCLOSE:
6452 return PositionFromLocation(Point::FromInts(static_cast<int>(wParam) - vs.ExternalMarginWidth(), static_cast<int>(lParam)),
6453 true, true);
6454
6455 case SCI_GOTOLINE:
6456 GoToLine(static_cast<Sci::Line>(wParam));
6457 break;
6458
6459 case SCI_GOTOPOS:
6460 SetEmptySelection(static_cast<Sci::Position>(wParam));
6461 EnsureCaretVisible();
6462 break;
6463
6464 case SCI_GETCURLINE: {
6465 const Sci::Line lineCurrentPos = pdoc->SciLineFromPosition(sel.MainCaret());
6466 const Sci::Position lineStart = pdoc->LineStart(lineCurrentPos);
6467 const Sci::Position lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6468 if (lParam == 0) {
6469 return 1 + lineEnd - lineStart;
6470 }
6471 PLATFORM_ASSERT(wParam > 0);
6472 char *ptr = CharPtrFromSPtr(lParam);
6473 const Sci::Position len = std::min<uptr_t>(lineEnd - lineStart, wParam - 1);
6474 pdoc->GetCharRange(ptr, lineStart, len);
6475 ptr[len] = '\0';
6476 return sel.MainCaret() - lineStart;
6477 }
6478
6479 case SCI_GETENDSTYLED:
6480 return pdoc->GetEndStyled();
6481
6482 case SCI_GETEOLMODE:
6483 return pdoc->eolMode;
6484
6485 case SCI_SETEOLMODE:
6486 pdoc->eolMode = static_cast<int>(wParam);
6487 break;
6488
6489 case SCI_SETLINEENDTYPESALLOWED:
6490 if (pdoc->SetLineEndTypesAllowed(static_cast<int>(wParam))) {
6491 pcs->Clear();
6492 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
6493 SetAnnotationHeights(0, pdoc->LinesTotal());
6494 InvalidateStyleRedraw();
6495 }
6496 break;
6497
6498 case SCI_GETLINEENDTYPESALLOWED:
6499 return pdoc->GetLineEndTypesAllowed();
6500
6501 case SCI_GETLINEENDTYPESACTIVE:
6502 return pdoc->GetLineEndTypesActive();
6503
6504 case SCI_STARTSTYLING:
6505 pdoc->StartStyling(static_cast<Sci::Position>(wParam));
6506 break;
6507
6508 case SCI_SETSTYLING:
6509 if (static_cast<Sci::Position>(wParam) < 0)
6510 errorStatus = SC_STATUS_FAILURE;
6511 else
6512 pdoc->SetStyleFor(static_cast<Sci::Position>(wParam), static_cast<char>(lParam));
6513 break;
6514
6515 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
6516 if (lParam == 0)
6517 return 0;
6518 pdoc->SetStyles(static_cast<Sci::Position>(wParam), CharPtrFromSPtr(lParam));
6519 break;
6520
6521 case SCI_SETBUFFEREDDRAW:
6522 view.bufferedDraw = wParam != 0;
6523 break;
6524
6525 case SCI_GETBUFFEREDDRAW:
6526 return view.bufferedDraw;
6527
6528 #ifdef INCLUDE_DEPRECATED_FEATURES
6529 case SCI_GETTWOPHASEDRAW:
6530 return view.phasesDraw == EditView::phasesTwo;
6531
6532 case SCI_SETTWOPHASEDRAW:
6533 if (view.SetTwoPhaseDraw(wParam != 0))
6534 InvalidateStyleRedraw();
6535 break;
6536 #endif
6537
6538 case SCI_GETPHASESDRAW:
6539 return view.phasesDraw;
6540
6541 case SCI_SETPHASESDRAW:
6542 if (view.SetPhasesDraw(static_cast<int>(wParam)))
6543 InvalidateStyleRedraw();
6544 break;
6545
6546 case SCI_SETFONTQUALITY:
6547 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
6548 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
6549 InvalidateStyleRedraw();
6550 break;
6551
6552 case SCI_GETFONTQUALITY:
6553 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
6554
6555 case SCI_SETTABWIDTH:
6556 if (wParam > 0) {
6557 pdoc->tabInChars = static_cast<int>(wParam);
6558 if (pdoc->indentInChars == 0)
6559 pdoc->actualIndentInChars = pdoc->tabInChars;
6560 }
6561 InvalidateStyleRedraw();
6562 break;
6563
6564 case SCI_GETTABWIDTH:
6565 return pdoc->tabInChars;
6566
6567 case SCI_SETTABMINIMUMWIDTH:
6568 SetAppearance(view.tabWidthMinimumPixels, static_cast<int>(wParam));
6569 break;
6570
6571 case SCI_GETTABMINIMUMWIDTH:
6572 return view.tabWidthMinimumPixels;
6573
6574 case SCI_CLEARTABSTOPS:
6575 if (view.ClearTabstops(static_cast<Sci::Line>(wParam))) {
6576 const DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, nullptr, static_cast<Sci::Line>(wParam));
6577 NotifyModified(pdoc, mh, nullptr);
6578 }
6579 break;
6580
6581 case SCI_ADDTABSTOP:
6582 if (view.AddTabstop(static_cast<Sci::Line>(wParam), static_cast<int>(lParam))) {
6583 const DocModification mh(SC_MOD_CHANGETABSTOPS, 0, 0, 0, nullptr, static_cast<Sci::Line>(wParam));
6584 NotifyModified(pdoc, mh, nullptr);
6585 }
6586 break;
6587
6588 case SCI_GETNEXTTABSTOP:
6589 return view.GetNextTabstop(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6590
6591 case SCI_SETINDENT:
6592 pdoc->indentInChars = static_cast<int>(wParam);
6593 if (pdoc->indentInChars != 0)
6594 pdoc->actualIndentInChars = pdoc->indentInChars;
6595 else
6596 pdoc->actualIndentInChars = pdoc->tabInChars;
6597 InvalidateStyleRedraw();
6598 break;
6599
6600 case SCI_GETINDENT:
6601 return pdoc->indentInChars;
6602
6603 case SCI_SETUSETABS:
6604 pdoc->useTabs = wParam != 0;
6605 InvalidateStyleRedraw();
6606 break;
6607
6608 case SCI_GETUSETABS:
6609 return pdoc->useTabs;
6610
6611 case SCI_SETLINEINDENTATION:
6612 pdoc->SetLineIndentation(static_cast<Sci::Line>(wParam), lParam);
6613 break;
6614
6615 case SCI_GETLINEINDENTATION:
6616 return pdoc->GetLineIndentation(static_cast<Sci::Line>(wParam));
6617
6618 case SCI_GETLINEINDENTPOSITION:
6619 return pdoc->GetLineIndentPosition(static_cast<Sci::Line>(wParam));
6620
6621 case SCI_SETTABINDENTS:
6622 pdoc->tabIndents = wParam != 0;
6623 break;
6624
6625 case SCI_GETTABINDENTS:
6626 return pdoc->tabIndents;
6627
6628 case SCI_SETBACKSPACEUNINDENTS:
6629 pdoc->backspaceUnindents = wParam != 0;
6630 break;
6631
6632 case SCI_GETBACKSPACEUNINDENTS:
6633 return pdoc->backspaceUnindents;
6634
6635 case SCI_SETMOUSEDWELLTIME:
6636 dwellDelay = static_cast<int>(wParam);
6637 ticksToDwell = dwellDelay;
6638 break;
6639
6640 case SCI_GETMOUSEDWELLTIME:
6641 return dwellDelay;
6642
6643 case SCI_WORDSTARTPOSITION:
6644 return pdoc->ExtendWordSelect(static_cast<Sci::Position>(wParam), -1, lParam != 0);
6645
6646 case SCI_WORDENDPOSITION:
6647 return pdoc->ExtendWordSelect(static_cast<Sci::Position>(wParam), 1, lParam != 0);
6648
6649 case SCI_ISRANGEWORD:
6650 return pdoc->IsWordAt(static_cast<Sci::Position>(wParam), lParam);
6651
6652 case SCI_SETIDLESTYLING:
6653 idleStyling = static_cast<int>(wParam);
6654 break;
6655
6656 case SCI_GETIDLESTYLING:
6657 return idleStyling;
6658
6659 case SCI_SETWRAPMODE:
6660 if (vs.SetWrapState(static_cast<int>(wParam))) {
6661 xOffset = 0;
6662 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
6663 InvalidateStyleRedraw();
6664 ReconfigureScrollBars();
6665 }
6666 break;
6667
6668 case SCI_GETWRAPMODE:
6669 return vs.wrapState;
6670
6671 case SCI_SETWRAPVISUALFLAGS:
6672 if (vs.SetWrapVisualFlags(static_cast<int>(wParam))) {
6673 InvalidateStyleRedraw();
6674 ReconfigureScrollBars();
6675 }
6676 break;
6677
6678 case SCI_GETWRAPVISUALFLAGS:
6679 return vs.wrapVisualFlags;
6680
6681 case SCI_SETWRAPVISUALFLAGSLOCATION:
6682 if (vs.SetWrapVisualFlagsLocation(static_cast<int>(wParam))) {
6683 InvalidateStyleRedraw();
6684 }
6685 break;
6686
6687 case SCI_GETWRAPVISUALFLAGSLOCATION:
6688 return vs.wrapVisualFlagsLocation;
6689
6690 case SCI_SETWRAPSTARTINDENT:
6691 if (vs.SetWrapVisualStartIndent(static_cast<int>(wParam))) {
6692 InvalidateStyleRedraw();
6693 ReconfigureScrollBars();
6694 }
6695 break;
6696
6697 case SCI_GETWRAPSTARTINDENT:
6698 return vs.wrapVisualStartIndent;
6699
6700 case SCI_SETWRAPINDENTMODE:
6701 if (vs.SetWrapIndentMode(static_cast<int>(wParam))) {
6702 InvalidateStyleRedraw();
6703 ReconfigureScrollBars();
6704 }
6705 break;
6706
6707 case SCI_GETWRAPINDENTMODE:
6708 return vs.wrapIndentMode;
6709
6710 case SCI_SETLAYOUTCACHE:
6711 view.llc.SetLevel(static_cast<int>(wParam));
6712 break;
6713
6714 case SCI_GETLAYOUTCACHE:
6715 return view.llc.GetLevel();
6716
6717 case SCI_SETPOSITIONCACHE:
6718 view.posCache.SetSize(wParam);
6719 break;
6720
6721 case SCI_GETPOSITIONCACHE:
6722 return view.posCache.GetSize();
6723
6724 case SCI_SETSCROLLWIDTH:
6725 PLATFORM_ASSERT(wParam > 0);
6726 if ((wParam > 0) && (wParam != static_cast<unsigned int>(scrollWidth))) {
6727 view.lineWidthMaxSeen = 0;
6728 scrollWidth = static_cast<int>(wParam);
6729 SetScrollBars();
6730 }
6731 break;
6732
6733 case SCI_GETSCROLLWIDTH:
6734 return scrollWidth;
6735
6736 case SCI_SETSCROLLWIDTHTRACKING:
6737 trackLineWidth = wParam != 0;
6738 break;
6739
6740 case SCI_GETSCROLLWIDTHTRACKING:
6741 return trackLineWidth;
6742
6743 case SCI_LINESJOIN:
6744 LinesJoin();
6745 break;
6746
6747 case SCI_LINESSPLIT:
6748 LinesSplit(static_cast<int>(wParam));
6749 break;
6750
6751 case SCI_TEXTWIDTH:
6752 PLATFORM_ASSERT(wParam < vs.styles.size());
6753 PLATFORM_ASSERT(lParam);
6754 return TextWidth(static_cast<int>(wParam), CharPtrFromSPtr(lParam));
6755
6756 case SCI_TEXTHEIGHT:
6757 RefreshStyleData();
6758 return vs.lineHeight;
6759
6760 case SCI_SETENDATLASTLINE:
6761 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6762 if (endAtLastLine != (wParam != 0)) {
6763 endAtLastLine = wParam != 0;
6764 SetScrollBars();
6765 }
6766 break;
6767
6768 case SCI_GETENDATLASTLINE:
6769 return endAtLastLine;
6770
6771 case SCI_SETCARETSTICKY:
6772 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
6773 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
6774 caretSticky = static_cast<int>(wParam);
6775 }
6776 break;
6777
6778 case SCI_GETCARETSTICKY:
6779 return caretSticky;
6780
6781 case SCI_TOGGLECARETSTICKY:
6782 caretSticky = !caretSticky;
6783 break;
6784
6785 case SCI_GETCOLUMN:
6786 return pdoc->GetColumn(static_cast<Sci::Position>(wParam));
6787
6788 case SCI_FINDCOLUMN:
6789 return pdoc->FindColumn(static_cast<Sci::Line>(wParam), lParam);
6790
6791 case SCI_SETHSCROLLBAR :
6792 if (horizontalScrollBarVisible != (wParam != 0)) {
6793 horizontalScrollBarVisible = wParam != 0;
6794 SetScrollBars();
6795 ReconfigureScrollBars();
6796 }
6797 break;
6798
6799 case SCI_GETHSCROLLBAR:
6800 return horizontalScrollBarVisible;
6801
6802 case SCI_SETVSCROLLBAR:
6803 if (verticalScrollBarVisible != (wParam != 0)) {
6804 verticalScrollBarVisible = wParam != 0;
6805 SetScrollBars();
6806 ReconfigureScrollBars();
6807 if (verticalScrollBarVisible)
6808 SetVerticalScrollPos();
6809 }
6810 break;
6811
6812 case SCI_GETVSCROLLBAR:
6813 return verticalScrollBarVisible;
6814
6815 case SCI_SETINDENTATIONGUIDES:
6816 vs.viewIndentationGuides = static_cast<IndentView>(wParam);
6817 Redraw();
6818 break;
6819
6820 case SCI_GETINDENTATIONGUIDES:
6821 return vs.viewIndentationGuides;
6822
6823 case SCI_SETHIGHLIGHTGUIDE:
6824 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6825 highlightGuideColumn = static_cast<int>(wParam);
6826 Redraw();
6827 }
6828 break;
6829
6830 case SCI_GETHIGHLIGHTGUIDE:
6831 return highlightGuideColumn;
6832
6833 case SCI_GETLINEENDPOSITION:
6834 return pdoc->LineEnd(static_cast<Sci::Line>(wParam));
6835
6836 case SCI_SETCODEPAGE:
6837 if (ValidCodePage(static_cast<int>(wParam))) {
6838 if (pdoc->SetDBCSCodePage(static_cast<int>(wParam))) {
6839 pcs->Clear();
6840 pcs->InsertLines(0, pdoc->LinesTotal() - 1);
6841 SetAnnotationHeights(0, pdoc->LinesTotal());
6842 InvalidateStyleRedraw();
6843 SetRepresentations();
6844 }
6845 }
6846 break;
6847
6848 case SCI_GETCODEPAGE:
6849 return pdoc->dbcsCodePage;
6850
6851 case SCI_SETIMEINTERACTION:
6852 imeInteraction = static_cast<EditModel::IMEInteraction>(wParam);
6853 break;
6854
6855 case SCI_GETIMEINTERACTION:
6856 return imeInteraction;
6857
6858 case SCI_SETBIDIRECTIONAL:
6859 // SCI_SETBIDIRECTIONAL is implemented on platform subclasses if they support bidirectional text.
6860 break;
6861
6862 case SCI_GETBIDIRECTIONAL:
6863 return static_cast<sptr_t>(bidirectional);
6864
6865 case SCI_GETLINECHARACTERINDEX:
6866 return pdoc->LineCharacterIndex();
6867
6868 case SCI_ALLOCATELINECHARACTERINDEX:
6869 pdoc->AllocateLineCharacterIndex(static_cast<int>(wParam));
6870 break;
6871
6872 case SCI_RELEASELINECHARACTERINDEX:
6873 pdoc->ReleaseLineCharacterIndex(static_cast<int>(wParam));
6874 break;
6875
6876 case SCI_LINEFROMINDEXPOSITION:
6877 return pdoc->LineFromPositionIndex(static_cast<Sci::Position>(wParam), static_cast<int>(lParam));
6878
6879 case SCI_INDEXPOSITIONFROMLINE:
6880 return pdoc->IndexLineStart(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6881
6882 // Marker definition and setting
6883 case SCI_MARKERDEFINE:
6884 if (wParam <= MARKER_MAX) {
6885 vs.markers[wParam].markType = static_cast<int>(lParam);
6886 vs.CalcLargestMarkerHeight();
6887 }
6888 InvalidateStyleData();
6889 RedrawSelMargin();
6890 break;
6891
6892 case SCI_MARKERSYMBOLDEFINED:
6893 if (wParam <= MARKER_MAX)
6894 return vs.markers[wParam].markType;
6895 else
6896 return 0;
6897
6898 case SCI_MARKERSETFORE:
6899 if (wParam <= MARKER_MAX)
6900 vs.markers[wParam].fore = ColourDesired(static_cast<int>(lParam));
6901 InvalidateStyleData();
6902 RedrawSelMargin();
6903 break;
6904 case SCI_MARKERSETBACKSELECTED:
6905 if (wParam <= MARKER_MAX)
6906 vs.markers[wParam].backSelected = ColourDesired(static_cast<int>(lParam));
6907 InvalidateStyleData();
6908 RedrawSelMargin();
6909 break;
6910 case SCI_MARKERENABLEHIGHLIGHT:
6911 marginView.highlightDelimiter.isEnabled = wParam == 1;
6912 RedrawSelMargin();
6913 break;
6914 case SCI_MARKERSETBACK:
6915 if (wParam <= MARKER_MAX)
6916 vs.markers[wParam].back = ColourDesired(static_cast<int>(lParam));
6917 InvalidateStyleData();
6918 RedrawSelMargin();
6919 break;
6920 case SCI_MARKERSETALPHA:
6921 if (wParam <= MARKER_MAX)
6922 vs.markers[wParam].alpha = static_cast<int>(lParam);
6923 InvalidateStyleRedraw();
6924 break;
6925 case SCI_MARKERADD: {
6926 const int markerID = pdoc->AddMark(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6927 return markerID;
6928 }
6929 case SCI_MARKERADDSET:
6930 if (lParam != 0)
6931 pdoc->AddMarkSet(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6932 break;
6933
6934 case SCI_MARKERDELETE:
6935 pdoc->DeleteMark(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6936 break;
6937
6938 case SCI_MARKERDELETEALL:
6939 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6940 break;
6941
6942 case SCI_MARKERGET:
6943 return pdoc->GetMark(static_cast<Sci::Line>(wParam));
6944
6945 case SCI_MARKERNEXT:
6946 return pdoc->MarkerNext(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
6947
6948 case SCI_MARKERPREVIOUS: {
6949 for (Sci::Line iLine = static_cast<Sci::Line>(wParam); iLine >= 0; iLine--) {
6950 if ((pdoc->GetMark(iLine) & lParam) != 0)
6951 return iLine;
6952 }
6953 }
6954 return -1;
6955
6956 case SCI_MARKERDEFINEPIXMAP:
6957 if (wParam <= MARKER_MAX) {
6958 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6959 vs.CalcLargestMarkerHeight();
6960 }
6961 InvalidateStyleData();
6962 RedrawSelMargin();
6963 break;
6964
6965 case SCI_RGBAIMAGESETWIDTH:
6966 sizeRGBAImage.x = static_cast<XYPOSITION>(wParam);
6967 break;
6968
6969 case SCI_RGBAIMAGESETHEIGHT:
6970 sizeRGBAImage.y = static_cast<XYPOSITION>(wParam);
6971 break;
6972
6973 case SCI_RGBAIMAGESETSCALE:
6974 scaleRGBAImage = static_cast<float>(wParam);
6975 break;
6976
6977 case SCI_MARKERDEFINERGBAIMAGE:
6978 if (wParam <= MARKER_MAX) {
6979 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0f, ConstUCharPtrFromSPtr(lParam));
6980 vs.CalcLargestMarkerHeight();
6981 }
6982 InvalidateStyleData();
6983 RedrawSelMargin();
6984 break;
6985
6986 case SCI_SETMARGINTYPEN:
6987 if (ValidMargin(wParam)) {
6988 vs.ms[wParam].style = static_cast<int>(lParam);
6989 InvalidateStyleRedraw();
6990 }
6991 break;
6992
6993 case SCI_GETMARGINTYPEN:
6994 if (ValidMargin(wParam))
6995 return vs.ms[wParam].style;
6996 else
6997 return 0;
6998
6999 case SCI_SETMARGINWIDTHN:
7000 if (ValidMargin(wParam)) {
7001 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7002 if (vs.ms[wParam].width != lParam) {
7003 lastXChosen += static_cast<int>(lParam) - vs.ms[wParam].width;
7004 vs.ms[wParam].width = static_cast<int>(lParam);
7005 InvalidateStyleRedraw();
7006 }
7007 }
7008 break;
7009
7010 case SCI_GETMARGINWIDTHN:
7011 if (ValidMargin(wParam))
7012 return vs.ms[wParam].width;
7013 else
7014 return 0;
7015
7016 case SCI_SETMARGINMASKN:
7017 if (ValidMargin(wParam)) {
7018 vs.ms[wParam].mask = static_cast<int>(lParam);
7019 InvalidateStyleRedraw();
7020 }
7021 break;
7022
7023 case SCI_GETMARGINMASKN:
7024 if (ValidMargin(wParam))
7025 return vs.ms[wParam].mask;
7026 else
7027 return 0;
7028
7029 case SCI_SETMARGINSENSITIVEN:
7030 if (ValidMargin(wParam)) {
7031 vs.ms[wParam].sensitive = lParam != 0;
7032 InvalidateStyleRedraw();
7033 }
7034 break;
7035
7036 case SCI_GETMARGINSENSITIVEN:
7037 if (ValidMargin(wParam))
7038 return vs.ms[wParam].sensitive ? 1 : 0;
7039 else
7040 return 0;
7041
7042 case SCI_SETMARGINCURSORN:
7043 if (ValidMargin(wParam))
7044 vs.ms[wParam].cursor = static_cast<int>(lParam);
7045 break;
7046
7047 case SCI_GETMARGINCURSORN:
7048 if (ValidMargin(wParam))
7049 return vs.ms[wParam].cursor;
7050 else
7051 return 0;
7052
7053 case SCI_SETMARGINBACKN:
7054 if (ValidMargin(wParam)) {
7055 vs.ms[wParam].back = ColourDesired(static_cast<int>(lParam));
7056 InvalidateStyleRedraw();
7057 }
7058 break;
7059
7060 case SCI_GETMARGINBACKN:
7061 if (ValidMargin(wParam))
7062 return vs.ms[wParam].back.AsInteger();
7063 else
7064 return 0;
7065
7066 case SCI_SETMARGINS:
7067 if (wParam < 1000)
7068 vs.ms.resize(wParam);
7069 break;
7070
7071 case SCI_GETMARGINS:
7072 return vs.ms.size();
7073
7074 case SCI_STYLECLEARALL:
7075 vs.ClearStyles();
7076 InvalidateStyleRedraw();
7077 break;
7078
7079 case SCI_STYLESETFORE:
7080 case SCI_STYLESETBACK:
7081 case SCI_STYLESETBOLD:
7082 case SCI_STYLESETWEIGHT:
7083 case SCI_STYLESETITALIC:
7084 case SCI_STYLESETEOLFILLED:
7085 case SCI_STYLESETSIZE:
7086 case SCI_STYLESETSIZEFRACTIONAL:
7087 case SCI_STYLESETFONT:
7088 case SCI_STYLESETUNDERLINE:
7089 case SCI_STYLESETCASE:
7090 case SCI_STYLESETCHARACTERSET:
7091 case SCI_STYLESETVISIBLE:
7092 case SCI_STYLESETCHANGEABLE:
7093 case SCI_STYLESETHOTSPOT:
7094 StyleSetMessage(iMessage, wParam, lParam);
7095 break;
7096
7097 case SCI_STYLEGETFORE:
7098 case SCI_STYLEGETBACK:
7099 case SCI_STYLEGETBOLD:
7100 case SCI_STYLEGETWEIGHT:
7101 case SCI_STYLEGETITALIC:
7102 case SCI_STYLEGETEOLFILLED:
7103 case SCI_STYLEGETSIZE:
7104 case SCI_STYLEGETSIZEFRACTIONAL:
7105 case SCI_STYLEGETFONT:
7106 case SCI_STYLEGETUNDERLINE:
7107 case SCI_STYLEGETCASE:
7108 case SCI_STYLEGETCHARACTERSET:
7109 case SCI_STYLEGETVISIBLE:
7110 case SCI_STYLEGETCHANGEABLE:
7111 case SCI_STYLEGETHOTSPOT:
7112 return StyleGetMessage(iMessage, wParam, lParam);
7113
7114 case SCI_STYLERESETDEFAULT:
7115 vs.ResetDefaultStyle();
7116 InvalidateStyleRedraw();
7117 break;
7118
7119 #ifdef INCLUDE_DEPRECATED_FEATURES
7120 case SCI_SETSTYLEBITS:
7121 vs.EnsureStyle(0xff);
7122 break;
7123
7124 case SCI_GETSTYLEBITS:
7125 return 8;
7126 #endif
7127
7128 case SCI_SETLINESTATE:
7129 return pdoc->SetLineState(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7130
7131 case SCI_GETLINESTATE:
7132 return pdoc->GetLineState(static_cast<Sci::Line>(wParam));
7133
7134 case SCI_GETMAXLINESTATE:
7135 return pdoc->GetMaxLineState();
7136
7137 case SCI_GETCARETLINEVISIBLE:
7138 return vs.showCaretLineBackground;
7139 case SCI_SETCARETLINEVISIBLE:
7140 vs.showCaretLineBackground = wParam != 0;
7141 InvalidateStyleRedraw();
7142 break;
7143 case SCI_GETCARETLINEVISIBLEALWAYS:
7144 return vs.alwaysShowCaretLineBackground;
7145 case SCI_SETCARETLINEVISIBLEALWAYS:
7146 vs.alwaysShowCaretLineBackground = wParam != 0;
7147 InvalidateStyleRedraw();
7148 break;
7149
7150 case SCI_GETCARETLINEFRAME:
7151 return vs.caretLineFrame;
7152 case SCI_SETCARETLINEFRAME:
7153 vs.caretLineFrame = static_cast<int>(wParam);
7154 InvalidateStyleRedraw();
7155 break;
7156 case SCI_GETCARETLINEBACK:
7157 return vs.caretLineBackground.AsInteger();
7158 case SCI_SETCARETLINEBACK:
7159 vs.caretLineBackground = ColourDesired(static_cast<int>(wParam));
7160 InvalidateStyleRedraw();
7161 break;
7162 case SCI_GETCARETLINEBACKALPHA:
7163 return vs.caretLineAlpha;
7164 case SCI_SETCARETLINEBACKALPHA:
7165 vs.caretLineAlpha = static_cast<int>(wParam);
7166 InvalidateStyleRedraw();
7167 break;
7168
7169 // Folding messages
7170
7171 case SCI_VISIBLEFROMDOCLINE:
7172 return pcs->DisplayFromDoc(static_cast<Sci::Line>(wParam));
7173
7174 case SCI_DOCLINEFROMVISIBLE:
7175 return pcs->DocFromDisplay(static_cast<Sci::Line>(wParam));
7176
7177 case SCI_WRAPCOUNT:
7178 return WrapCount(static_cast<Sci::Line>(wParam));
7179
7180 case SCI_SETFOLDLEVEL: {
7181 const int prev = pdoc->SetLevel(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7182 if (prev != static_cast<int>(lParam))
7183 RedrawSelMargin();
7184 return prev;
7185 }
7186
7187 case SCI_GETFOLDLEVEL:
7188 return pdoc->GetLevel(static_cast<Sci::Line>(wParam));
7189
7190 case SCI_GETLASTCHILD:
7191 return pdoc->GetLastChild(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7192
7193 case SCI_GETFOLDPARENT:
7194 return pdoc->GetFoldParent(static_cast<Sci::Line>(wParam));
7195
7196 case SCI_SHOWLINES:
7197 pcs->SetVisible(static_cast<Sci::Line>(wParam), static_cast<Sci::Line>(lParam), true);
7198 SetScrollBars();
7199 Redraw();
7200 break;
7201
7202 case SCI_HIDELINES:
7203 if (wParam > 0)
7204 pcs->SetVisible(static_cast<Sci::Line>(wParam), static_cast<Sci::Line>(lParam), false);
7205 SetScrollBars();
7206 Redraw();
7207 break;
7208
7209 case SCI_GETLINEVISIBLE:
7210 return pcs->GetVisible(static_cast<Sci::Line>(wParam));
7211
7212 case SCI_GETALLLINESVISIBLE:
7213 return pcs->HiddenLines() ? 0 : 1;
7214
7215 case SCI_SETFOLDEXPANDED:
7216 SetFoldExpanded(static_cast<Sci::Line>(wParam), lParam != 0);
7217 break;
7218
7219 case SCI_GETFOLDEXPANDED:
7220 return pcs->GetExpanded(static_cast<Sci::Line>(wParam));
7221
7222 case SCI_SETAUTOMATICFOLD:
7223 foldAutomatic = static_cast<int>(wParam);
7224 break;
7225
7226 case SCI_GETAUTOMATICFOLD:
7227 return foldAutomatic;
7228
7229 case SCI_SETFOLDFLAGS:
7230 foldFlags = static_cast<int>(wParam);
7231 Redraw();
7232 break;
7233
7234 case SCI_TOGGLEFOLDSHOWTEXT:
7235 pcs->SetFoldDisplayText(static_cast<Sci::Line>(wParam), CharPtrFromSPtr(lParam));
7236 FoldLine(static_cast<Sci::Line>(wParam), SC_FOLDACTION_TOGGLE);
7237 break;
7238
7239 case SCI_FOLDDISPLAYTEXTSETSTYLE:
7240 foldDisplayTextStyle = static_cast<int>(wParam);
7241 Redraw();
7242 break;
7243
7244 case SCI_FOLDDISPLAYTEXTGETSTYLE:
7245 return foldDisplayTextStyle;
7246
7247 case SCI_SETDEFAULTFOLDDISPLAYTEXT:
7248 SetDefaultFoldDisplayText(CharPtrFromSPtr(lParam));
7249 Redraw();
7250 break;
7251
7252 case SCI_GETDEFAULTFOLDDISPLAYTEXT:
7253 return StringResult(lParam, GetDefaultFoldDisplayText());
7254
7255 case SCI_TOGGLEFOLD:
7256 FoldLine(static_cast<Sci::Line>(wParam), SC_FOLDACTION_TOGGLE);
7257 break;
7258
7259 case SCI_FOLDLINE:
7260 FoldLine(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7261 break;
7262
7263 case SCI_FOLDCHILDREN:
7264 FoldExpand(static_cast<Sci::Line>(wParam), static_cast<int>(lParam), pdoc->GetLevel(static_cast<int>(wParam)));
7265 break;
7266
7267 case SCI_FOLDALL:
7268 FoldAll(static_cast<int>(wParam));
7269 break;
7270
7271 case SCI_EXPANDCHILDREN:
7272 FoldExpand(static_cast<Sci::Line>(wParam), SC_FOLDACTION_EXPAND, static_cast<int>(lParam));
7273 break;
7274
7275 case SCI_CONTRACTEDFOLDNEXT:
7276 return ContractedFoldNext(static_cast<Sci::Line>(wParam));
7277
7278 case SCI_ENSUREVISIBLE:
7279 EnsureLineVisible(static_cast<Sci::Line>(wParam), false);
7280 break;
7281
7282 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7283 EnsureLineVisible(static_cast<Sci::Line>(wParam), true);
7284 break;
7285
7286 case SCI_SCROLLRANGE:
7287 ScrollRange(SelectionRange(static_cast<Sci::Position>(wParam), lParam));
7288 break;
7289
7290 case SCI_SEARCHANCHOR:
7291 SearchAnchor();
7292 break;
7293
7294 case SCI_SEARCHNEXT:
7295 case SCI_SEARCHPREV:
7296 return SearchText(iMessage, wParam, lParam);
7297
7298 case SCI_SETXCARETPOLICY:
7299 caretPolicies.x = CaretPolicy(wParam, lParam);
7300 break;
7301
7302 case SCI_SETYCARETPOLICY:
7303 caretPolicies.y = CaretPolicy(wParam, lParam);
7304 break;
7305
7306 case SCI_SETVISIBLEPOLICY:
7307 visiblePolicy = CaretPolicy(wParam, lParam);
7308 break;
7309
7310 case SCI_LINESONSCREEN:
7311 return LinesOnScreen();
7312
7313 case SCI_SETSELFORE:
7314 vs.selColours.fore = ColourOptional(wParam, lParam);
7315 vs.selAdditionalForeground = ColourDesired(static_cast<int>(lParam));
7316 InvalidateStyleRedraw();
7317 break;
7318
7319 case SCI_SETSELBACK:
7320 vs.selColours.back = ColourOptional(wParam, lParam);
7321 vs.selAdditionalBackground = ColourDesired(static_cast<int>(lParam));
7322 InvalidateStyleRedraw();
7323 break;
7324
7325 case SCI_SETSELALPHA:
7326 vs.selAlpha = static_cast<int>(wParam);
7327 vs.selAdditionalAlpha = static_cast<int>(wParam);
7328 InvalidateStyleRedraw();
7329 break;
7330
7331 case SCI_GETSELALPHA:
7332 return vs.selAlpha;
7333
7334 case SCI_GETSELEOLFILLED:
7335 return vs.selEOLFilled;
7336
7337 case SCI_SETSELEOLFILLED:
7338 vs.selEOLFilled = wParam != 0;
7339 InvalidateStyleRedraw();
7340 break;
7341
7342 case SCI_SETWHITESPACEFORE:
7343 vs.whitespaceColours.fore = ColourOptional(wParam, lParam);
7344 InvalidateStyleRedraw();
7345 break;
7346
7347 case SCI_SETWHITESPACEBACK:
7348 vs.whitespaceColours.back = ColourOptional(wParam, lParam);
7349 InvalidateStyleRedraw();
7350 break;
7351
7352 case SCI_SETCARETFORE:
7353 vs.caretcolour = ColourDesired(static_cast<int>(wParam));
7354 InvalidateStyleRedraw();
7355 break;
7356
7357 case SCI_GETCARETFORE:
7358 return vs.caretcolour.AsInteger();
7359
7360 case SCI_SETCARETSTYLE:
7361 if (wParam <= (CARETSTYLE_BLOCK | CARETSTYLE_OVERSTRIKE_BLOCK | CARETSTYLE_BLOCK_AFTER))
7362 vs.caretStyle = static_cast<int>(wParam);
7363 else
7364 /* Default to the line caret */
7365 vs.caretStyle = CARETSTYLE_LINE;
7366 InvalidateStyleRedraw();
7367 break;
7368
7369 case SCI_GETCARETSTYLE:
7370 return vs.caretStyle;
7371
7372 case SCI_SETCARETWIDTH:
7373 vs.caretWidth = std::clamp(static_cast<int>(wParam), 0, 3);
7374 InvalidateStyleRedraw();
7375 break;
7376
7377 case SCI_GETCARETWIDTH:
7378 return vs.caretWidth;
7379
7380 case SCI_ASSIGNCMDKEY:
7381 kmap.AssignCmdKey(LowShortFromWParam(wParam),
7382 HighShortFromWParam(wParam), static_cast<unsigned int>(lParam));
7383 break;
7384
7385 case SCI_CLEARCMDKEY:
7386 kmap.AssignCmdKey(LowShortFromWParam(wParam),
7387 HighShortFromWParam(wParam), SCI_NULL);
7388 break;
7389
7390 case SCI_CLEARALLCMDKEYS:
7391 kmap.Clear();
7392 break;
7393
7394 case SCI_INDICSETSTYLE:
7395 if (wParam <= INDICATOR_MAX) {
7396 vs.indicators[wParam].sacNormal.style = static_cast<int>(lParam);
7397 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7398 InvalidateStyleRedraw();
7399 }
7400 break;
7401
7402 case SCI_INDICGETSTYLE:
7403 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacNormal.style : 0;
7404
7405 case SCI_INDICSETFORE:
7406 if (wParam <= INDICATOR_MAX) {
7407 vs.indicators[wParam].sacNormal.fore = ColourDesired(static_cast<int>(lParam));
7408 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<int>(lParam));
7409 InvalidateStyleRedraw();
7410 }
7411 break;
7412
7413 case SCI_INDICGETFORE:
7414 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacNormal.fore.AsInteger() : 0;
7415
7416 case SCI_INDICSETHOVERSTYLE:
7417 if (wParam <= INDICATOR_MAX) {
7418 vs.indicators[wParam].sacHover.style = static_cast<int>(lParam);
7419 InvalidateStyleRedraw();
7420 }
7421 break;
7422
7423 case SCI_INDICGETHOVERSTYLE:
7424 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacHover.style : 0;
7425
7426 case SCI_INDICSETHOVERFORE:
7427 if (wParam <= INDICATOR_MAX) {
7428 vs.indicators[wParam].sacHover.fore = ColourDesired(static_cast<int>(lParam));
7429 InvalidateStyleRedraw();
7430 }
7431 break;
7432
7433 case SCI_INDICGETHOVERFORE:
7434 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].sacHover.fore.AsInteger() : 0;
7435
7436 case SCI_INDICSETFLAGS:
7437 if (wParam <= INDICATOR_MAX) {
7438 vs.indicators[wParam].SetFlags(static_cast<int>(lParam));
7439 InvalidateStyleRedraw();
7440 }
7441 break;
7442
7443 case SCI_INDICGETFLAGS:
7444 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].Flags() : 0;
7445
7446 case SCI_INDICSETUNDER:
7447 if (wParam <= INDICATOR_MAX) {
7448 vs.indicators[wParam].under = lParam != 0;
7449 InvalidateStyleRedraw();
7450 }
7451 break;
7452
7453 case SCI_INDICGETUNDER:
7454 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].under : 0;
7455
7456 case SCI_INDICSETALPHA:
7457 if (wParam <= INDICATOR_MAX && lParam >=0 && lParam <= 255) {
7458 vs.indicators[wParam].fillAlpha = static_cast<int>(lParam);
7459 InvalidateStyleRedraw();
7460 }
7461 break;
7462
7463 case SCI_INDICGETALPHA:
7464 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7465
7466 case SCI_INDICSETOUTLINEALPHA:
7467 if (wParam <= INDICATOR_MAX && lParam >=0 && lParam <= 255) {
7468 vs.indicators[wParam].outlineAlpha = static_cast<int>(lParam);
7469 InvalidateStyleRedraw();
7470 }
7471 break;
7472
7473 case SCI_INDICGETOUTLINEALPHA:
7474 return (wParam <= INDICATOR_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
7475
7476 case SCI_SETINDICATORCURRENT:
7477 pdoc->DecorationSetCurrentIndicator(static_cast<int>(wParam));
7478 break;
7479 case SCI_GETINDICATORCURRENT:
7480 return pdoc->decorations->GetCurrentIndicator();
7481 case SCI_SETINDICATORVALUE:
7482 pdoc->decorations->SetCurrentValue(static_cast<int>(wParam));
7483 break;
7484 case SCI_GETINDICATORVALUE:
7485 return pdoc->decorations->GetCurrentValue();
7486
7487 case SCI_INDICATORFILLRANGE:
7488 pdoc->DecorationFillRange(static_cast<Sci::Position>(wParam),
7489 pdoc->decorations->GetCurrentValue(), lParam);
7490 break;
7491
7492 case SCI_INDICATORCLEARRANGE:
7493 pdoc->DecorationFillRange(static_cast<Sci::Position>(wParam), 0,
7494 lParam);
7495 break;
7496
7497 case SCI_INDICATORALLONFOR:
7498 return pdoc->decorations->AllOnFor(static_cast<Sci::Position>(wParam));
7499
7500 case SCI_INDICATORVALUEAT:
7501 return pdoc->decorations->ValueAt(static_cast<int>(wParam), lParam);
7502
7503 case SCI_INDICATORSTART:
7504 return pdoc->decorations->Start(static_cast<int>(wParam), lParam);
7505
7506 case SCI_INDICATOREND:
7507 return pdoc->decorations->End(static_cast<int>(wParam), lParam);
7508
7509 case SCI_LINEDOWN:
7510 case SCI_LINEDOWNEXTEND:
7511 case SCI_PARADOWN:
7512 case SCI_PARADOWNEXTEND:
7513 case SCI_LINEUP:
7514 case SCI_LINEUPEXTEND:
7515 case SCI_PARAUP:
7516 case SCI_PARAUPEXTEND:
7517 case SCI_CHARLEFT:
7518 case SCI_CHARLEFTEXTEND:
7519 case SCI_CHARRIGHT:
7520 case SCI_CHARRIGHTEXTEND:
7521 case SCI_WORDLEFT:
7522 case SCI_WORDLEFTEXTEND:
7523 case SCI_WORDRIGHT:
7524 case SCI_WORDRIGHTEXTEND:
7525 case SCI_WORDLEFTEND:
7526 case SCI_WORDLEFTENDEXTEND:
7527 case SCI_WORDRIGHTEND:
7528 case SCI_WORDRIGHTENDEXTEND:
7529 case SCI_HOME:
7530 case SCI_HOMEEXTEND:
7531 case SCI_LINEEND:
7532 case SCI_LINEENDEXTEND:
7533 case SCI_HOMEWRAP:
7534 case SCI_HOMEWRAPEXTEND:
7535 case SCI_LINEENDWRAP:
7536 case SCI_LINEENDWRAPEXTEND:
7537 case SCI_DOCUMENTSTART:
7538 case SCI_DOCUMENTSTARTEXTEND:
7539 case SCI_DOCUMENTEND:
7540 case SCI_DOCUMENTENDEXTEND:
7541 case SCI_SCROLLTOSTART:
7542 case SCI_SCROLLTOEND:
7543
7544 case SCI_STUTTEREDPAGEUP:
7545 case SCI_STUTTEREDPAGEUPEXTEND:
7546 case SCI_STUTTEREDPAGEDOWN:
7547 case SCI_STUTTEREDPAGEDOWNEXTEND:
7548
7549 case SCI_PAGEUP:
7550 case SCI_PAGEUPEXTEND:
7551 case SCI_PAGEDOWN:
7552 case SCI_PAGEDOWNEXTEND:
7553 case SCI_EDITTOGGLEOVERTYPE:
7554 case SCI_CANCEL:
7555 case SCI_DELETEBACK:
7556 case SCI_TAB:
7557 case SCI_BACKTAB:
7558 case SCI_NEWLINE:
7559 case SCI_FORMFEED:
7560 case SCI_VCHOME:
7561 case SCI_VCHOMEEXTEND:
7562 case SCI_VCHOMEWRAP:
7563 case SCI_VCHOMEWRAPEXTEND:
7564 case SCI_VCHOMEDISPLAY:
7565 case SCI_VCHOMEDISPLAYEXTEND:
7566 case SCI_ZOOMIN:
7567 case SCI_ZOOMOUT:
7568 case SCI_DELWORDLEFT:
7569 case SCI_DELWORDRIGHT:
7570 case SCI_DELWORDRIGHTEND:
7571 case SCI_DELLINELEFT:
7572 case SCI_DELLINERIGHT:
7573 case SCI_LINECOPY:
7574 case SCI_LINECUT:
7575 case SCI_LINEDELETE:
7576 case SCI_LINETRANSPOSE:
7577 case SCI_LINEREVERSE:
7578 case SCI_LINEDUPLICATE:
7579 case SCI_LOWERCASE:
7580 case SCI_UPPERCASE:
7581 case SCI_LINESCROLLDOWN:
7582 case SCI_LINESCROLLUP:
7583 case SCI_WORDPARTLEFT:
7584 case SCI_WORDPARTLEFTEXTEND:
7585 case SCI_WORDPARTRIGHT:
7586 case SCI_WORDPARTRIGHTEXTEND:
7587 case SCI_DELETEBACKNOTLINE:
7588 case SCI_HOMEDISPLAY:
7589 case SCI_HOMEDISPLAYEXTEND:
7590 case SCI_LINEENDDISPLAY:
7591 case SCI_LINEENDDISPLAYEXTEND:
7592 case SCI_LINEDOWNRECTEXTEND:
7593 case SCI_LINEUPRECTEXTEND:
7594 case SCI_CHARLEFTRECTEXTEND:
7595 case SCI_CHARRIGHTRECTEXTEND:
7596 case SCI_HOMERECTEXTEND:
7597 case SCI_VCHOMERECTEXTEND:
7598 case SCI_LINEENDRECTEXTEND:
7599 case SCI_PAGEUPRECTEXTEND:
7600 case SCI_PAGEDOWNRECTEXTEND:
7601 case SCI_SELECTIONDUPLICATE:
7602 return KeyCommand(iMessage);
7603
7604 case SCI_BRACEHIGHLIGHT:
7605 SetBraceHighlight(static_cast<Sci::Position>(wParam), lParam, STYLE_BRACELIGHT);
7606 break;
7607
7608 case SCI_BRACEHIGHLIGHTINDICATOR:
7609 if (lParam >= 0 && lParam <= INDICATOR_MAX) {
7610 vs.braceHighlightIndicatorSet = wParam != 0;
7611 vs.braceHighlightIndicator = static_cast<int>(lParam);
7612 }
7613 break;
7614
7615 case SCI_BRACEBADLIGHT:
7616 SetBraceHighlight(static_cast<Sci::Position>(wParam), -1, STYLE_BRACEBAD);
7617 break;
7618
7619 case SCI_BRACEBADLIGHTINDICATOR:
7620 if (lParam >= 0 && lParam <= INDICATOR_MAX) {
7621 vs.braceBadLightIndicatorSet = wParam != 0;
7622 vs.braceBadLightIndicator = static_cast<int>(lParam);
7623 }
7624 break;
7625
7626 case SCI_BRACEMATCH:
7627 // wParam is position of char to find brace for,
7628 // lParam is maximum amount of text to restyle to find it
7629 return pdoc->BraceMatch(static_cast<Sci::Position>(wParam), lParam);
7630
7631 case SCI_GETVIEWEOL:
7632 return vs.viewEOL;
7633
7634 case SCI_SETVIEWEOL:
7635 vs.viewEOL = wParam != 0;
7636 InvalidateStyleRedraw();
7637 break;
7638
7639 case SCI_SETZOOM: {
7640 const int zoomLevel = static_cast<int>(wParam);
7641 if (zoomLevel != vs.zoomLevel) {
7642 vs.zoomLevel = zoomLevel;
7643 InvalidateStyleRedraw();
7644 NotifyZoom();
7645 }
7646 break;
7647 }
7648
7649 case SCI_GETZOOM:
7650 return vs.zoomLevel;
7651
7652 case SCI_GETEDGECOLUMN:
7653 return vs.theEdge.column;
7654
7655 case SCI_SETEDGECOLUMN:
7656 vs.theEdge.column = static_cast<int>(wParam);
7657 InvalidateStyleRedraw();
7658 break;
7659
7660 case SCI_GETEDGEMODE:
7661 return vs.edgeState;
7662
7663 case SCI_SETEDGEMODE:
7664 vs.edgeState = static_cast<int>(wParam);
7665 InvalidateStyleRedraw();
7666 break;
7667
7668 case SCI_GETEDGECOLOUR:
7669 return vs.theEdge.colour.AsInteger();
7670
7671 case SCI_SETEDGECOLOUR:
7672 vs.theEdge.colour = ColourDesired(static_cast<int>(wParam));
7673 InvalidateStyleRedraw();
7674 break;
7675
7676 case SCI_MULTIEDGEADDLINE:
7677 vs.theMultiEdge.push_back(EdgeProperties(wParam, lParam));
7678 InvalidateStyleRedraw();
7679 break;
7680
7681 case SCI_MULTIEDGECLEARALL:
7682 std::vector<EdgeProperties>().swap(vs.theMultiEdge); // Free vector and memory, C++03 compatible
7683 InvalidateStyleRedraw();
7684 break;
7685
7686 case SCI_GETACCESSIBILITY:
7687 return SC_ACCESSIBILITY_DISABLED;
7688
7689 case SCI_SETACCESSIBILITY:
7690 // May be implemented by platform code.
7691 break;
7692
7693 case SCI_GETDOCPOINTER:
7694 return reinterpret_cast<sptr_t>(pdoc);
7695
7696 case SCI_SETDOCPOINTER:
7697 CancelModes();
7698 SetDocPointer(static_cast<Document *>(PtrFromSPtr(lParam)));
7699 return 0;
7700
7701 case SCI_CREATEDOCUMENT: {
7702 Document *doc = new Document(static_cast<int>(lParam));
7703 doc->AddRef();
7704 doc->Allocate(static_cast<Sci::Position>(wParam));
7705 pcs = ContractionStateCreate(pdoc->IsLarge());
7706 return reinterpret_cast<sptr_t>(doc);
7707 }
7708
7709 case SCI_ADDREFDOCUMENT:
7710 (static_cast<Document *>(PtrFromSPtr(lParam)))->AddRef();
7711 break;
7712
7713 case SCI_RELEASEDOCUMENT:
7714 (static_cast<Document *>(PtrFromSPtr(lParam)))->Release();
7715 break;
7716
7717 case SCI_GETDOCUMENTOPTIONS:
7718 return pdoc->Options();
7719
7720 case SCI_CREATELOADER: {
7721 Document *doc = new Document(static_cast<int>(lParam));
7722 doc->AddRef();
7723 doc->Allocate(static_cast<Sci::Position>(wParam));
7724 doc->SetUndoCollection(false);
7725 pcs = ContractionStateCreate(pdoc->IsLarge());
7726 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
7727 }
7728
7729 case SCI_SETMODEVENTMASK:
7730 modEventMask = static_cast<int>(wParam);
7731 return 0;
7732
7733 case SCI_GETMODEVENTMASK:
7734 return modEventMask;
7735
7736 case SCI_SETCOMMANDEVENTS:
7737 commandEvents = static_cast<bool>(wParam);
7738 return 0;
7739
7740 case SCI_GETCOMMANDEVENTS:
7741 return commandEvents;
7742
7743 case SCI_CONVERTEOLS:
7744 pdoc->ConvertLineEnds(static_cast<int>(wParam));
7745 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7746 return 0;
7747
7748 case SCI_SETLENGTHFORENCODE:
7749 lengthForEncode = static_cast<Sci::Position>(wParam);
7750 return 0;
7751
7752 case SCI_SELECTIONISRECTANGLE:
7753 return sel.selType == Selection::selRectangle ? 1 : 0;
7754
7755 case SCI_SETSELECTIONMODE: {
7756 switch (wParam) {
7757 case SC_SEL_STREAM:
7758 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7759 sel.selType = Selection::selStream;
7760 break;
7761 case SC_SEL_RECTANGLE:
7762 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7763 sel.selType = Selection::selRectangle;
7764 sel.Rectangular() = sel.RangeMain(); // adjust current selection
7765 break;
7766 case SC_SEL_LINES:
7767 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7768 sel.selType = Selection::selLines;
7769 SetSelection(sel.RangeMain().caret, sel.RangeMain().anchor); // adjust current selection
7770 break;
7771 case SC_SEL_THIN:
7772 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7773 sel.selType = Selection::selThin;
7774 break;
7775 default:
7776 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7777 sel.selType = Selection::selStream;
7778 }
7779 InvalidateWholeSelection();
7780 break;
7781 }
7782 case SCI_GETSELECTIONMODE:
7783 switch (sel.selType) {
7784 case Selection::selStream:
7785 return SC_SEL_STREAM;
7786 case Selection::selRectangle:
7787 return SC_SEL_RECTANGLE;
7788 case Selection::selLines:
7789 return SC_SEL_LINES;
7790 case Selection::selThin:
7791 return SC_SEL_THIN;
7792 default: // ?!
7793 return SC_SEL_STREAM;
7794 }
7795 case SCI_GETMOVEEXTENDSSELECTION:
7796 return sel.MoveExtends();
7797 case SCI_GETLINESELSTARTPOSITION:
7798 case SCI_GETLINESELENDPOSITION: {
7799 const SelectionSegment segmentLine(
7800 SelectionPosition(pdoc->LineStart(static_cast<Sci::Position>(wParam))),
7801 SelectionPosition(pdoc->LineEnd(static_cast<Sci::Position>(wParam))));
7802 for (size_t r=0; r<sel.Count(); r++) {
7803 const SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7804 if (portion.start.IsValid()) {
7805 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7806 }
7807 }
7808 return INVALID_POSITION;
7809 }
7810
7811 case SCI_SETOVERTYPE:
7812 if (inOverstrike != (wParam != 0)) {
7813 inOverstrike = wParam != 0;
7814 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
7815 ShowCaretAtCurrentPosition();
7816 SetIdle(true);
7817 }
7818 break;
7819
7820 case SCI_GETOVERTYPE:
7821 return inOverstrike ? 1 : 0;
7822
7823 case SCI_SETFOCUS:
7824 SetFocusState(wParam != 0);
7825 break;
7826
7827 case SCI_GETFOCUS:
7828 return hasFocus;
7829
7830 case SCI_SETSTATUS:
7831 errorStatus = static_cast<int>(wParam);
7832 break;
7833
7834 case SCI_GETSTATUS:
7835 return errorStatus;
7836
7837 case SCI_SETMOUSEDOWNCAPTURES:
7838 mouseDownCaptures = wParam != 0;
7839 break;
7840
7841 case SCI_GETMOUSEDOWNCAPTURES:
7842 return mouseDownCaptures;
7843
7844 case SCI_SETMOUSEWHEELCAPTURES:
7845 mouseWheelCaptures = wParam != 0;
7846 break;
7847
7848 case SCI_GETMOUSEWHEELCAPTURES:
7849 return mouseWheelCaptures;
7850
7851 case SCI_SETCURSOR:
7852 cursorMode = static_cast<int>(wParam);
7853 DisplayCursor(Window::cursorText);
7854 break;
7855
7856 case SCI_GETCURSOR:
7857 return cursorMode;
7858
7859 case SCI_SETCONTROLCHARSYMBOL:
7860 vs.controlCharSymbol = static_cast<int>(wParam);
7861 InvalidateStyleRedraw();
7862 break;
7863
7864 case SCI_GETCONTROLCHARSYMBOL:
7865 return vs.controlCharSymbol;
7866
7867 case SCI_SETREPRESENTATION:
7868 reprs.SetRepresentation(ConstCharPtrFromUPtr(wParam), ConstCharPtrFromSPtr(lParam));
7869 break;
7870
7871 case SCI_GETREPRESENTATION: {
7872 const Representation *repr = reprs.RepresentationFromCharacter(
7873 ConstCharPtrFromUPtr(wParam), UTF8MaxBytes);
7874 if (repr) {
7875 return StringResult(lParam, repr->stringRep.c_str());
7876 }
7877 return 0;
7878 }
7879
7880 case SCI_CLEARREPRESENTATION:
7881 reprs.ClearRepresentation(ConstCharPtrFromUPtr(wParam));
7882 break;
7883
7884 case SCI_STARTRECORD:
7885 recordingMacro = true;
7886 return 0;
7887
7888 case SCI_STOPRECORD:
7889 recordingMacro = false;
7890 return 0;
7891
7892 case SCI_MOVECARETINSIDEVIEW:
7893 MoveCaretInsideView();
7894 break;
7895
7896 case SCI_SETFOLDMARGINCOLOUR:
7897 vs.foldmarginColour = ColourOptional(wParam, lParam);
7898 InvalidateStyleRedraw();
7899 break;
7900
7901 case SCI_SETFOLDMARGINHICOLOUR:
7902 vs.foldmarginHighlightColour = ColourOptional(wParam, lParam);
7903 InvalidateStyleRedraw();
7904 break;
7905
7906 case SCI_SETHOTSPOTACTIVEFORE:
7907 vs.hotspotColours.fore = ColourOptional(wParam, lParam);
7908 InvalidateStyleRedraw();
7909 break;
7910
7911 case SCI_GETHOTSPOTACTIVEFORE:
7912 return vs.hotspotColours.fore.AsInteger();
7913
7914 case SCI_SETHOTSPOTACTIVEBACK:
7915 vs.hotspotColours.back = ColourOptional(wParam, lParam);
7916 InvalidateStyleRedraw();
7917 break;
7918
7919 case SCI_GETHOTSPOTACTIVEBACK:
7920 return vs.hotspotColours.back.AsInteger();
7921
7922 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7923 vs.hotspotUnderline = wParam != 0;
7924 InvalidateStyleRedraw();
7925 break;
7926
7927 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7928 return vs.hotspotUnderline ? 1 : 0;
7929
7930 case SCI_SETHOTSPOTSINGLELINE:
7931 vs.hotspotSingleLine = wParam != 0;
7932 InvalidateStyleRedraw();
7933 break;
7934
7935 case SCI_GETHOTSPOTSINGLELINE:
7936 return vs.hotspotSingleLine ? 1 : 0;
7937
7938 case SCI_SETPASTECONVERTENDINGS:
7939 convertPastes = wParam != 0;
7940 break;
7941
7942 case SCI_GETPASTECONVERTENDINGS:
7943 return convertPastes ? 1 : 0;
7944
7945 case SCI_GETCHARACTERPOINTER:
7946 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7947
7948 case SCI_GETRANGEPOINTER:
7949 return reinterpret_cast<sptr_t>(pdoc->RangePointer(
7950 static_cast<Sci::Position>(wParam), lParam));
7951
7952 case SCI_GETGAPPOSITION:
7953 return pdoc->GapPosition();
7954
7955 case SCI_SETEXTRAASCENT:
7956 vs.extraAscent = static_cast<int>(wParam);
7957 InvalidateStyleRedraw();
7958 break;
7959
7960 case SCI_GETEXTRAASCENT:
7961 return vs.extraAscent;
7962
7963 case SCI_SETEXTRADESCENT:
7964 vs.extraDescent = static_cast<int>(wParam);
7965 InvalidateStyleRedraw();
7966 break;
7967
7968 case SCI_GETEXTRADESCENT:
7969 return vs.extraDescent;
7970
7971 case SCI_MARGINSETSTYLEOFFSET:
7972 vs.marginStyleOffset = static_cast<int>(wParam);
7973 InvalidateStyleRedraw();
7974 break;
7975
7976 case SCI_MARGINGETSTYLEOFFSET:
7977 return vs.marginStyleOffset;
7978
7979 case SCI_SETMARGINOPTIONS:
7980 marginOptions = static_cast<int>(wParam);
7981 break;
7982
7983 case SCI_GETMARGINOPTIONS:
7984 return marginOptions;
7985
7986 case SCI_MARGINSETTEXT:
7987 pdoc->MarginSetText(static_cast<Sci::Line>(wParam), CharPtrFromSPtr(lParam));
7988 break;
7989
7990 case SCI_MARGINGETTEXT: {
7991 const StyledText st = pdoc->MarginStyledText(static_cast<Sci::Line>(wParam));
7992 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
7993 }
7994
7995 case SCI_MARGINSETSTYLE:
7996 pdoc->MarginSetStyle(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
7997 break;
7998
7999 case SCI_MARGINGETSTYLE: {
8000 const StyledText st = pdoc->MarginStyledText(static_cast<Sci::Line>(wParam));
8001 return st.style;
8002 }
8003
8004 case SCI_MARGINSETSTYLES:
8005 pdoc->MarginSetStyles(static_cast<Sci::Line>(wParam), ConstUCharPtrFromSPtr(lParam));
8006 break;
8007
8008 case SCI_MARGINGETSTYLES: {
8009 const StyledText st = pdoc->MarginStyledText(static_cast<Sci::Line>(wParam));
8010 return BytesResult(lParam, st.styles, st.length);
8011 }
8012
8013 case SCI_MARGINTEXTCLEARALL:
8014 pdoc->MarginClearAll();
8015 break;
8016
8017 case SCI_ANNOTATIONSETTEXT:
8018 pdoc->AnnotationSetText(static_cast<Sci::Line>(wParam), CharPtrFromSPtr(lParam));
8019 break;
8020
8021 case SCI_ANNOTATIONGETTEXT: {
8022 const StyledText st = pdoc->AnnotationStyledText(static_cast<Sci::Line>(wParam));
8023 return BytesResult(lParam, reinterpret_cast<const unsigned char *>(st.text), st.length);
8024 }
8025
8026 case SCI_ANNOTATIONGETSTYLE: {
8027 const StyledText st = pdoc->AnnotationStyledText(static_cast<Sci::Line>(wParam));
8028 return st.style;
8029 }
8030
8031 case SCI_ANNOTATIONSETSTYLE:
8032 pdoc->AnnotationSetStyle(static_cast<Sci::Line>(wParam), static_cast<int>(lParam));
8033 break;
8034
8035 case SCI_ANNOTATIONSETSTYLES:
8036 pdoc->AnnotationSetStyles(static_cast<Sci::Line>(wParam), ConstUCharPtrFromSPtr(lParam));
8037 break;
8038
8039 case SCI_ANNOTATIONGETSTYLES: {
8040 const StyledText st = pdoc->AnnotationStyledText(static_cast<Sci::Line>(wParam));
8041 return BytesResult(lParam, st.styles, st.length);
8042 }
8043
8044 case SCI_ANNOTATIONGETLINES:
8045 return pdoc->AnnotationLines(static_cast<Sci::Line>(wParam));
8046
8047 case SCI_ANNOTATIONCLEARALL:
8048 pdoc->AnnotationClearAll();
8049 break;
8050
8051 case SCI_ANNOTATIONSETVISIBLE:
8052 SetAnnotationVisible(static_cast<int>(wParam));
8053 break;
8054
8055 case SCI_ANNOTATIONGETVISIBLE:
8056 return vs.annotationVisible;
8057
8058 case SCI_ANNOTATIONSETSTYLEOFFSET:
8059 vs.annotationStyleOffset = static_cast<int>(wParam);
8060 InvalidateStyleRedraw();
8061 break;
8062
8063 case SCI_ANNOTATIONGETSTYLEOFFSET:
8064 return vs.annotationStyleOffset;
8065
8066 case SCI_RELEASEALLEXTENDEDSTYLES:
8067 vs.ReleaseAllExtendedStyles();
8068 break;
8069
8070 case SCI_ALLOCATEEXTENDEDSTYLES:
8071 return vs.AllocateExtendedStyles(static_cast<int>(wParam));
8072
8073 case SCI_ADDUNDOACTION:
8074 pdoc->AddUndoAction(static_cast<Sci::Position>(wParam), lParam & UNDO_MAY_COALESCE);
8075 break;
8076
8077 case SCI_SETMOUSESELECTIONRECTANGULARSWITCH:
8078 mouseSelectionRectangularSwitch = wParam != 0;
8079 break;
8080
8081 case SCI_GETMOUSESELECTIONRECTANGULARSWITCH:
8082 return mouseSelectionRectangularSwitch;
8083
8084 case SCI_SETMULTIPLESELECTION:
8085 multipleSelection = wParam != 0;
8086 InvalidateCaret();
8087 break;
8088
8089 case SCI_GETMULTIPLESELECTION:
8090 return multipleSelection;
8091
8092 case SCI_SETADDITIONALSELECTIONTYPING:
8093 additionalSelectionTyping = wParam != 0;
8094 InvalidateCaret();
8095 break;
8096
8097 case SCI_GETADDITIONALSELECTIONTYPING:
8098 return additionalSelectionTyping;
8099
8100 case SCI_SETMULTIPASTE:
8101 multiPasteMode = static_cast<int>(wParam);
8102 break;
8103
8104 case SCI_GETMULTIPASTE:
8105 return multiPasteMode;
8106
8107 case SCI_SETADDITIONALCARETSBLINK:
8108 view.additionalCaretsBlink = wParam != 0;
8109 InvalidateCaret();
8110 break;
8111
8112 case SCI_GETADDITIONALCARETSBLINK:
8113 return view.additionalCaretsBlink;
8114
8115 case SCI_SETADDITIONALCARETSVISIBLE:
8116 view.additionalCaretsVisible = wParam != 0;
8117 InvalidateCaret();
8118 break;
8119
8120 case SCI_GETADDITIONALCARETSVISIBLE:
8121 return view.additionalCaretsVisible;
8122
8123 case SCI_GETSELECTIONS:
8124 return sel.Count();
8125
8126 case SCI_GETSELECTIONEMPTY:
8127 return sel.Empty();
8128
8129 case SCI_CLEARSELECTIONS:
8130 sel.Clear();
8131 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8132 Redraw();
8133 break;
8134
8135 case SCI_SETSELECTION:
8136 sel.SetSelection(SelectionRange(static_cast<Sci::Position>(wParam), lParam));
8137 Redraw();
8138 break;
8139
8140 case SCI_ADDSELECTION:
8141 sel.AddSelection(SelectionRange(static_cast<Sci::Position>(wParam), lParam));
8142 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8143 Redraw();
8144 break;
8145
8146 case SCI_DROPSELECTIONN:
8147 sel.DropSelection(static_cast<size_t>(wParam));
8148 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8149 Redraw();
8150 break;
8151
8152 case SCI_SETMAINSELECTION:
8153 sel.SetMain(static_cast<size_t>(wParam));
8154 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
8155 Redraw();
8156 break;
8157
8158 case SCI_GETMAINSELECTION:
8159 return sel.Main();
8160
8161 case SCI_SETSELECTIONNCARET:
8162 case SCI_SETSELECTIONNANCHOR:
8163 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
8164 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
8165 case SCI_SETSELECTIONNSTART:
8166 case SCI_SETSELECTIONNEND:
8167 SetSelectionNMessage(iMessage, wParam, lParam);
8168 break;
8169
8170 case SCI_GETSELECTIONNCARET:
8171 return sel.Range(wParam).caret.Position();
8172
8173 case SCI_GETSELECTIONNANCHOR:
8174 return sel.Range(wParam).anchor.Position();
8175
8176 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
8177 return sel.Range(wParam).caret.VirtualSpace();
8178
8179 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
8180 return sel.Range(wParam).anchor.VirtualSpace();
8181
8182 case SCI_GETSELECTIONNSTART:
8183 return sel.Range(wParam).Start().Position();
8184
8185 case SCI_GETSELECTIONNSTARTVIRTUALSPACE:
8186 return sel.Range(wParam).Start().VirtualSpace();
8187
8188 case SCI_GETSELECTIONNEND:
8189 return sel.Range(wParam).End().Position();
8190
8191 case SCI_GETSELECTIONNENDVIRTUALSPACE:
8192 return sel.Range(wParam).End().VirtualSpace();
8193
8194 case SCI_SETRECTANGULARSELECTIONCARET:
8195 if (!sel.IsRectangular())
8196 sel.Clear();
8197 sel.selType = Selection::selRectangle;
8198 sel.Rectangular().caret.SetPosition(static_cast<Sci::Position>(wParam));
8199 SetRectangularRange();
8200 Redraw();
8201 break;
8202
8203 case SCI_GETRECTANGULARSELECTIONCARET:
8204 return sel.Rectangular().caret.Position();
8205
8206 case SCI_SETRECTANGULARSELECTIONANCHOR:
8207 if (!sel.IsRectangular())
8208 sel.Clear();
8209 sel.selType = Selection::selRectangle;
8210 sel.Rectangular().anchor.SetPosition(static_cast<Sci::Position>(wParam));
8211 SetRectangularRange();
8212 Redraw();
8213 break;
8214
8215 case SCI_GETRECTANGULARSELECTIONANCHOR:
8216 return sel.Rectangular().anchor.Position();
8217
8218 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8219 if (!sel.IsRectangular())
8220 sel.Clear();
8221 sel.selType = Selection::selRectangle;
8222 sel.Rectangular().caret.SetVirtualSpace(static_cast<Sci::Position>(wParam));
8223 SetRectangularRange();
8224 Redraw();
8225 break;
8226
8227 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8228 return sel.Rectangular().caret.VirtualSpace();
8229
8230 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8231 if (!sel.IsRectangular())
8232 sel.Clear();
8233 sel.selType = Selection::selRectangle;
8234 sel.Rectangular().anchor.SetVirtualSpace(static_cast<Sci::Position>(wParam));
8235 SetRectangularRange();
8236 Redraw();
8237 break;
8238
8239 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8240 return sel.Rectangular().anchor.VirtualSpace();
8241
8242 case SCI_SETVIRTUALSPACEOPTIONS:
8243 virtualSpaceOptions = static_cast<int>(wParam);
8244 break;
8245
8246 case SCI_GETVIRTUALSPACEOPTIONS:
8247 return virtualSpaceOptions;
8248
8249 case SCI_SETADDITIONALSELFORE:
8250 vs.selAdditionalForeground = ColourDesired(static_cast<int>(wParam));
8251 InvalidateStyleRedraw();
8252 break;
8253
8254 case SCI_SETADDITIONALSELBACK:
8255 vs.selAdditionalBackground = ColourDesired(static_cast<int>(wParam));
8256 InvalidateStyleRedraw();
8257 break;
8258
8259 case SCI_SETADDITIONALSELALPHA:
8260 vs.selAdditionalAlpha = static_cast<int>(wParam);
8261 InvalidateStyleRedraw();
8262 break;
8263
8264 case SCI_GETADDITIONALSELALPHA:
8265 return vs.selAdditionalAlpha;
8266
8267 case SCI_SETADDITIONALCARETFORE:
8268 vs.additionalCaretColour = ColourDesired(static_cast<int>(wParam));
8269 InvalidateStyleRedraw();
8270 break;
8271
8272 case SCI_GETADDITIONALCARETFORE:
8273 return vs.additionalCaretColour.AsInteger();
8274
8275 case SCI_ROTATESELECTION:
8276 sel.RotateMain();
8277 InvalidateWholeSelection();
8278 break;
8279
8280 case SCI_SWAPMAINANCHORCARET:
8281 InvalidateSelection(sel.RangeMain());
8282 sel.RangeMain().Swap();
8283 break;
8284
8285 case SCI_MULTIPLESELECTADDNEXT:
8286 MultipleSelectAdd(addOne);
8287 break;
8288
8289 case SCI_MULTIPLESELECTADDEACH:
8290 MultipleSelectAdd(addEach);
8291 break;
8292
8293 case SCI_CHANGELEXERSTATE:
8294 pdoc->ChangeLexerState(static_cast<Sci::Position>(wParam), lParam);
8295 break;
8296
8297 case SCI_SETIDENTIFIER:
8298 SetCtrlID(static_cast<int>(wParam));
8299 break;
8300
8301 case SCI_GETIDENTIFIER:
8302 return GetCtrlID();
8303
8304 case SCI_SETTECHNOLOGY:
8305 // No action by default
8306 break;
8307
8308 case SCI_GETTECHNOLOGY:
8309 return technology;
8310
8311 case SCI_COUNTCHARACTERS:
8312 return pdoc->CountCharacters(static_cast<Sci::Position>(wParam), lParam);
8313
8314 case SCI_COUNTCODEUNITS:
8315 return pdoc->CountUTF16(static_cast<Sci::Position>(wParam), lParam);
8316
8317 default:
8318 return DefWndProc(iMessage, wParam, lParam);
8319 }
8320 //Platform::DebugPrintf("end wnd proc\n");
8321 return 0;
8322 }
8323