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