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