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