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 &current : 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