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