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