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