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