]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/Editor.cxx
Prevents crashes caused by negative line count being passed to wrapping functions...
[wxWidgets.git] / src / stc / scintilla / src / Editor.cxx
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 */
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
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 */
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
64 Caret::Caret() :
65 active(false), on(false), period(500) {}
66
67 Timer::Timer() :
68 ticking(false), ticksToWait(0), tickerID(0) {}
69
70 Idler::Idler() :
71 state(false), idlerID(0) {}
72
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
99 LineLayout::~LineLayout() {
100 Free();
101 }
102
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
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
129 void LineLayout::Invalidate(validLevel validity_) {
130 if (validity > validity_)
131 validity = validity_;
132 }
133
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
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
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
191 LineLayoutCache::LineLayoutCache() :
192 level(0), length(0), size(0), cache(0),
193 allInvalidated(false), styleClock(-1), useCount(0) {
194 Allocate(0);
195 }
196
197 LineLayoutCache::~LineLayoutCache() {
198 Deallocate();
199 }
200
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
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
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
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
265 void LineLayoutCache::SetLevel(int level_) {
266 allInvalidated = false;
267 if ((level_ != -1) && (level != level_)) {
268 level = level_;
269 Deallocate();
270 }
271 }
272
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
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
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
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
459 void Editor::Finalise() {
460 SetIdle(false);
461 CancelModes();
462 }
463
464 void Editor::DropGraphics() {
465 pixmapLine->Release();
466 pixmapSelMargin->Release();
467 pixmapSelPattern->Release();
468 pixmapIndentGuide->Release();
469 pixmapIndentGuideHighlight->Release();
470 }
471
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
483 void Editor::InvalidateStyleRedraw() {
484 NeedWrapping();
485 InvalidateStyleData();
486 Redraw();
487 }
488
489 void Editor::RefreshColourPalette(Palette &pal, bool want) {
490 vs.RefreshColourPalette(pal, want);
491 }
492
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
507 PRectangle Editor::GetClientRectangle() {
508 return wMain.GetClientPosition();
509 }
510
511 PRectangle Editor::GetTextRectangle() {
512 PRectangle rc = GetClientRectangle();
513 rc.left += vs.fixedColumnWidth;
514 rc.right -= vs.rightMarginWidth;
515 return rc;
516 }
517
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
528 int Editor::LinesToScroll() {
529 int retVal = LinesOnScreen() - 1;
530 if (retVal < 1)
531 return 1;
532 else
533 return retVal;
534 }
535
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
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
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;
577 AutoLineLayout &operator=(const AutoLineLayout &) { return * this; }
578 public:
579 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
580 ~AutoLineLayout() {
581 llc.Dispose(ll);
582 ll = 0;
583 }
584 LineLayout *operator->() const {
585 return ll;
586 }
587 operator LineLayout *() const {
588 return ll;
589 }
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
614 void Reset() {
615 if (forward) {
616 line = lineStart;
617 } else {
618 line = lineEnd;
619 }
620 }
621
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 }
635 ~SelectionLineIterator() {}
636
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 }
663 bool Iterate() {
664 SetAt(line);
665 if (forward) {
666 line++;
667 } else {
668 line--;
669 }
670 return startPos != INVALID_POSITION;
671 }
672 };
673
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
714 int Editor::XFromPosition(int pos) {
715 Point pt = LocationFromPosition(pos);
716 return pt.x - vs.fixedColumnWidth + xOffset;
717 }
718
719 int Editor::LineFromLocation(Point pt) {
720 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
721 }
722
723 void Editor::SetTopLine(int topLineNew) {
724 topLine = topLineNew;
725 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
726 }
727
728 static inline bool IsEOLChar(char ch) {
729 return (ch == '\r') || (ch == '\n');
730 }
731
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.
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 */
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 */
865 bool Editor::AbandonPaint() {
866 if ((paintState == painting) && !paintingAllText) {
867 paintState = paintAbandoned;
868 }
869 return paintState == paintAbandoned;
870 }
871
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
891 void Editor::Redraw() {
892 //Platform::DebugPrintf("Redraw all\n");
893 PRectangle rcClient = GetClientRectangle();
894 wMain.InvalidateRectangle(rcClient);
895 //wMain.InvalidateAll();
896 }
897
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
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
941 void Editor::InvalidateRange(int start, int end) {
942 RedrawRect(RectangleFromRange(start, end));
943 }
944
945 int Editor::CurrentPosition() {
946 return currentPos;
947 }
948
949 bool Editor::SelectionEmpty() {
950 return anchor == currentPos;
951 }
952
953 int Editor::SelectionStart() {
954 return Platform::Minimum(currentPos, anchor);
955 }
956
957 int Editor::SelectionEnd() {
958 return Platform::Maximum(currentPos, anchor);
959 }
960
961 void Editor::SetRectangularRange() {
962 if (selType == selRectangle) {
963 xStartSelect = XFromPosition(anchor);
964 xEndSelect = XFromPosition(currentPos);
965 }
966 }
967
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
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
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
1009 void Editor::SetEmptySelection(int currentPos_) {
1010 selType = selStream;
1011 moveExtendsSelection = false;
1012 SetSelection(currentPos_, currentPos_);
1013 }
1014
1015 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
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 */
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
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
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 */
1115 void Editor::SetLastXChosen() {
1116 Point pt = LocationFromPosition(currentPos);
1117 lastXChosen = pt.x;
1118 }
1119
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
1143 void Editor::ScrollText(int /* linesToMove */) {
1144 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1145 Redraw();
1146 }
1147
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
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
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 */
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
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
1476 void Editor::DropCaret() {
1477 caret.active = false;
1478 InvalidateCaret();
1479 }
1480
1481 void Editor::InvalidateCaret() {
1482 if (posDrag >= 0)
1483 InvalidateRange(posDrag, posDrag + 1);
1484 else
1485 InvalidateRange(currentPos, currentPos + 1);
1486 UpdateSystemCaret();
1487 }
1488
1489 void Editor::UpdateSystemCaret() {
1490 }
1491
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.
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
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
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
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
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
1686 static int istrlen(const char *s) {
1687 return static_cast<int>(strlen(s));
1688 }
1689
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
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
1914 static bool IsSpaceOrTab(char ch) {
1915 return ch == ' ' || ch == '\t';
1916 }
1917
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 */
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
2158 ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw) {
2159 return primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated;
2160 }
2161
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
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
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
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
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
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
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
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
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.
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
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
3293 void Editor::ReconfigureScrollBars() {}
3294
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
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
3333 void Editor::AddChar(char ch) {
3334 char s[2];
3335 s[0] = ch;
3336 s[1] = '\0';
3337 AddCharUTF(s, 1);
3338 }
3339
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
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
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
3447 void Editor::ClearDocumentStyle() {
3448 pdoc->StartStyling(0, '\377');
3449 pdoc->SetStyleFor(pdoc->Length(), 0);
3450 cs.ShowAll();
3451 pdoc->ClearLevels();
3452 }
3453
3454 void Editor::Cut() {
3455 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
3456 Copy();
3457 ClearSelection();
3458 }
3459 }
3460
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
3499 bool Editor::CanPaste() {
3500 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
3501 }
3502
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
3514 void Editor::SelectAll() {
3515 SetSelection(0, pdoc->Length());
3516 Redraw();
3517 }
3518
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
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
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
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
3576 void Editor::NotifyFocus(bool) {}
3577
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
3585 void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
3586 NotifyStyleToNeeded(endStyleNeeded);
3587 }
3588
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
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
3612 void Editor::NotifyModifyAttempt() {
3613 SCNotification scn = {0};
3614 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
3615 NotifyParent(scn);
3616 }
3617
3618 void Editor::NotifyDoubleClick(Point, bool) {
3619 SCNotification scn = {0};
3620 scn.nmhdr.code = SCN_DOUBLECLICK;
3621 NotifyParent(scn);
3622 }
3623
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
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
3642 void Editor::NotifyUpdateUI() {
3643 SCNotification scn = {0};
3644 scn.nmhdr.code = SCN_UPDATEUI;
3645 NotifyParent(scn);
3646 }
3647
3648 void Editor::NotifyPainted() {
3649 SCNotification scn = {0};
3650 scn.nmhdr.code = SCN_PAINTED;
3651 NotifyParent(scn);
3652 }
3653
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
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
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
3693 void Editor::NotifyZoom() {
3694 SCNotification scn = {0};
3695 scn.nmhdr.code = SCN_ZOOM;
3696 NotifyParent(scn);
3697 }
3698
3699 // Notifications from document
3700 void Editor::NotifyModifyAttempt(Document*, void *) {
3701 //Platform::DebugPrintf("** Modify Attempt\n");
3702 NotifyModifyAttempt();
3703 }
3704
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
3712 void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
3713 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
3714 NotifySavePoint(atSavePoint);
3715 }
3716
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.
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.
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
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
3867 void Editor::NotifyDeleted(Document *, void *) {
3868 /* Do nothing */
3869 }
3870
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 */
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
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
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
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
4108 void Editor::CancelModes() {
4109 moveExtendsSelection = false;
4110 }
4111
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
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
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
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
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
4604 int Editor::KeyDefault(int, int) {
4605 return 0;
4606 }
4607
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
4624 void Editor::SetWhitespaceVisible(int view) {
4625 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
4626 }
4627
4628 int Editor::GetWhitespaceVisible() {
4629 return vs.viewWhitespace;
4630 }
4631
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 */
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 */
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 */
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 */
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
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
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
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
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
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
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
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
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
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
4925 void Editor::StartDrag() {
4926 // Always handled by subclasses
4927 //SetMouseCapture(true);
4928 //DisplayCursor(Window::cursorArrow);
4929 }
4930
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 */
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
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
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
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
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
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
5198 bool Editor::PositionIsHotspot(int position) {
5199 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
5200 }
5201
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
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
5242 void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) {
5243 hsStart_ = hsStart;
5244 hsEnd_ = hsEnd;
5245 }
5246
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
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.
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
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
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
5458 bool Editor::PaintContains(PRectangle rc) {
5459 return rcPaint.Contains(rc);
5460 }
5461
5462 bool Editor::PaintContainsMargin() {
5463 PRectangle rcSelMargin = GetClientRectangle();
5464 rcSelMargin.right = vs.fixedColumnWidth;
5465 return PaintContains(rcSelMargin);
5466 }
5467
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
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
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 */
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
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 */
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
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
5662 bool Editor::IsUnicodeMode() const {
5663 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5664 }
5665
5666 int Editor::CodePage() const {
5667 if (pdoc)
5668 return pdoc->dbcsCodePage;
5669 else
5670 return 0;
5671 }
5672
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
5685 static bool ValidMargin(unsigned long wParam) {
5686 return wParam < ViewStyle::margins;
5687 }
5688
5689 static char *CharPtrFromSPtr(sptr_t lParam) {
5690 return reinterpret_cast<char *>(lParam);
5691 }
5692
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 }