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