]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/Editor.cxx
d32022ff118bfdc60ca6760c284dcf3dd0547469
[wxWidgets.git] / contrib / src / stc / scintilla / src / Editor.cxx
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12
13 #include "Platform.h"
14
15 #ifndef PLAT_QT
16 #define INCLUDE_DEPRECATED_FEATURES
17 #endif
18 #include "Scintilla.h"
19
20 #include "ContractionState.h"
21 #include "SVector.h"
22 #include "CellBuffer.h"
23 #include "KeyMap.h"
24 #include "Indicator.h"
25 #include "XPM.h"
26 #include "LineMarker.h"
27 #include "Style.h"
28 #include "ViewStyle.h"
29 #include "Document.h"
30 #include "Editor.h"
31
32 Caret::Caret() :
33 active(false), on(false), period(500) {}
34
35 Timer::Timer() :
36 ticking(false), ticksToWait(0), tickerID(0) {}
37
38 Idler::Idler() :
39 state(false), idlerID(0) {}
40
41 LineLayout::LineLayout(int maxLineLength_) :
42 lineStarts(0),
43 lenLineStarts(0),
44 lineNumber(-1),
45 inCache(false),
46 maxLineLength(-1),
47 numCharsInLine(0),
48 validity(llInvalid),
49 xHighlightGuide(0),
50 highlightColumn(0),
51 selStart(0),
52 selEnd(0),
53 containsCaret(false),
54 edgeColumn(0),
55 chars(0),
56 styles(0),
57 indicators(0),
58 positions(0),
59 hsStart(0),
60 hsEnd(0),
61 widthLine(wrapWidthInfinite),
62 lines(1) {
63 Resize(maxLineLength_);
64 }
65
66 LineLayout::~LineLayout() {
67 Free();
68 }
69
70 void LineLayout::Resize(int maxLineLength_) {
71 if (maxLineLength_ > maxLineLength) {
72 Free();
73 chars = new char[maxLineLength_ + 1];
74 styles = new unsigned char[maxLineLength_ + 1];
75 indicators = new char[maxLineLength_ + 1];
76 // Extra position allocated as sometimes the Windows
77 // GetTextExtentExPoint API writes an extra element.
78 positions = new int[maxLineLength_ + 1 + 1];
79 maxLineLength = maxLineLength_;
80 }
81 }
82
83 void LineLayout::Free() {
84 delete []chars;
85 chars = 0;
86 delete []styles;
87 styles = 0;
88 delete []indicators;
89 indicators = 0;
90 delete []positions;
91 positions = 0;
92 delete []lineStarts;
93 lineStarts = 0;
94 }
95
96 void LineLayout::Invalidate(validLevel validity_) {
97 if (validity > validity_)
98 validity = validity_;
99 }
100
101 void LineLayout::SetLineStart(int line, int start) {
102 if ((line >= lenLineStarts) && (line != 0)) {
103 int newMaxLines = line + 20;
104 int *newLineStarts = new int[newMaxLines];
105 if (!newLineStarts)
106 return;
107 for (int i = 0; i < newMaxLines; i++) {
108 if (i < lenLineStarts)
109 newLineStarts[i] = lineStarts[i];
110 else
111 newLineStarts[i] = 0;
112 }
113 delete []lineStarts;
114 lineStarts = newLineStarts;
115 lenLineStarts = newMaxLines;
116 }
117 lineStarts[line] = start;
118 }
119
120 void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[],
121 char bracesMatchStyle, int xHighlight) {
122 if (rangeLine.ContainsCharacter(braces[0])) {
123 int braceOffset = braces[0] - rangeLine.start;
124 if (braceOffset < numCharsInLine) {
125 bracePreviousStyles[0] = styles[braceOffset];
126 styles[braceOffset] = bracesMatchStyle;
127 }
128 }
129 if (rangeLine.ContainsCharacter(braces[1])) {
130 int braceOffset = braces[1] - rangeLine.start;
131 if (braceOffset < numCharsInLine) {
132 bracePreviousStyles[1] = styles[braceOffset];
133 styles[braceOffset] = bracesMatchStyle;
134 }
135 }
136 if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) ||
137 (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) {
138 xHighlightGuide = xHighlight;
139 }
140 }
141
142 void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) {
143 if (rangeLine.ContainsCharacter(braces[0])) {
144 int braceOffset = braces[0] - rangeLine.start;
145 if (braceOffset < numCharsInLine) {
146 styles[braceOffset] = bracePreviousStyles[0];
147 }
148 }
149 if (rangeLine.ContainsCharacter(braces[1])) {
150 int braceOffset = braces[1] - rangeLine.start;
151 if (braceOffset < numCharsInLine) {
152 styles[braceOffset] = bracePreviousStyles[1];
153 }
154 }
155 xHighlightGuide = 0;
156 }
157
158 LineLayoutCache::LineLayoutCache() :
159 level(0), length(0), size(0), cache(0),
160 allInvalidated(false), styleClock(-1) {
161 Allocate(0);
162 }
163
164 LineLayoutCache::~LineLayoutCache() {
165 Deallocate();
166 }
167
168 void LineLayoutCache::Allocate(int length_) {
169 allInvalidated = false;
170 length = length_;
171 size = length;
172 if (size > 1) {
173 size = (size / 16 + 1) * 16;
174 }
175 if (size > 0) {
176 cache = new LineLayout * [size];
177 }
178 for (int i = 0; i < size; i++)
179 cache[i] = 0;
180 }
181
182 void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) {
183 int lengthForLevel = 0;
184 if (level == llcCaret) {
185 lengthForLevel = 1;
186 } else if (level == llcPage) {
187 lengthForLevel = linesOnScreen + 1;
188 } else if (level == llcDocument) {
189 lengthForLevel = linesInDoc;
190 }
191 if (lengthForLevel > size) {
192 Deallocate();
193 } else if (lengthForLevel < length) {
194 for (int i = lengthForLevel; i < length; i++) {
195 delete cache[i];
196 cache[i] = 0;
197 }
198 }
199 if (!cache) {
200 Allocate(lengthForLevel);
201 }
202 }
203
204 void LineLayoutCache::Deallocate() {
205 for (int i = 0; i < length; i++)
206 delete cache[i];
207 delete []cache;
208 cache = 0;
209 length = 0;
210 }
211
212 void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) {
213 if (cache && !allInvalidated) {
214 for (int i = 0; i < length; i++) {
215 if (cache[i]) {
216 cache[i]->Invalidate(validity_);
217 }
218 }
219 if (validity_ == LineLayout::llInvalid) {
220 allInvalidated = true;
221 }
222 }
223 }
224
225 void LineLayoutCache::SetLevel(int level_) {
226 allInvalidated = false;
227 if ((level_ != -1) && (level != level_)) {
228 level = level_;
229 Deallocate();
230 }
231 }
232
233 LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,
234 int linesOnScreen, int linesInDoc) {
235 AllocateForLevel(linesOnScreen, linesInDoc);
236 if (styleClock != styleClock_) {
237 Invalidate(LineLayout::llCheckTextAndStyle);
238 styleClock = styleClock_;
239 }
240 allInvalidated = false;
241 int pos = -1;
242 LineLayout *ret = 0;
243 if (level == llcCaret) {
244 pos = 0;
245 } else if (level == llcPage) {
246 if (lineNumber == lineCaret) {
247 pos = length;
248 } else {
249 pos = lineNumber % length;
250 }
251 } else if (level == llcDocument) {
252 pos = lineNumber;
253 }
254 if (pos >= 0) {
255 if (cache && (pos < length)) {
256 if (cache[pos]) {
257 if ((cache[pos]->lineNumber != lineNumber) ||
258 (cache[pos]->maxLineLength < maxChars)) {
259 delete cache[pos];
260 cache[pos] = 0;
261 }
262 }
263 if (!cache[pos]) {
264 cache[pos] = new LineLayout(maxChars);
265 }
266 if (cache[pos]) {
267 cache[pos]->lineNumber = lineNumber;
268 cache[pos]->inCache = true;
269 ret = cache[pos];
270 }
271 }
272 }
273
274 if (!ret) {
275 ret = new LineLayout(maxChars);
276 ret->lineNumber = lineNumber;
277 }
278
279 return ret;
280 }
281
282 void LineLayoutCache::Dispose(LineLayout *ll) {
283 allInvalidated = false;
284 if (ll) {
285 if (!ll->inCache) {
286 delete ll;
287 }
288 }
289 }
290
291 Editor::Editor() {
292 ctrlID = 0;
293
294 stylesValid = false;
295
296 printMagnification = 0;
297 printColourMode = SC_PRINT_NORMAL;
298 printWrapState = eWrapWord;
299 cursorMode = SC_CURSORNORMAL;
300 controlCharSymbol = 0; /* Draw the control characters */
301
302 hasFocus = false;
303 hideSelection = false;
304 inOverstrike = false;
305 errorStatus = 0;
306 mouseDownCaptures = true;
307
308 bufferedDraw = true;
309 twoPhaseDraw = true;
310
311 lastClickTime = 0;
312 dwellDelay = SC_TIME_FOREVER;
313 ticksToDwell = SC_TIME_FOREVER;
314 dwelling = false;
315 ptMouseLast.x = 0;
316 ptMouseLast.y = 0;
317 inDragDrop = false;
318 dropWentOutside = false;
319 posDrag = invalidPosition;
320 posDrop = invalidPosition;
321 selectionType = selChar;
322
323 lastXChosen = 0;
324 lineAnchor = 0;
325 originalAnchorPos = 0;
326
327 selType = selStream;
328 moveExtendsSelection = false;
329 xStartSelect = 0;
330 xEndSelect = 0;
331 primarySelection = true;
332
333 caretXPolicy = CARET_SLOP | CARET_EVEN;
334 caretXSlop = 50;
335
336 caretYPolicy = CARET_EVEN;
337 caretYSlop = 0;
338
339 searchAnchor = 0;
340
341 xOffset = 0;
342 xCaretMargin = 50;
343 horizontalScrollBarVisible = true;
344 scrollWidth = 2000;
345 verticalScrollBarVisible = true;
346 endAtLastLine = true;
347
348 pixmapLine = Surface::Allocate();
349 pixmapSelMargin = Surface::Allocate();
350 pixmapSelPattern = Surface::Allocate();
351 pixmapIndentGuide = Surface::Allocate();
352 pixmapIndentGuideHighlight = Surface::Allocate();
353
354 currentPos = 0;
355 anchor = 0;
356
357 targetStart = 0;
358 targetEnd = 0;
359 searchFlags = 0;
360
361 topLine = 0;
362 posTopLine = 0;
363
364 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 InvalidateStyleRedraw(); // RPD: patch #1106564
3296 }
3297
3298 void Editor::ClearDocumentStyle() {
3299 pdoc->StartStyling(0, '\377');
3300 pdoc->SetStyleFor(pdoc->Length(), 0);
3301 cs.ShowAll();
3302 pdoc->ClearLevels();
3303 }
3304
3305 void Editor::Cut() {
3306 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
3307 Copy();
3308 ClearSelection();
3309 }
3310 }
3311
3312 void Editor::PasteRectangular(int pos, const char *ptr, int len) {
3313 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
3314 return;
3315 }
3316 currentPos = pos;
3317 int xInsert = XFromPosition(currentPos);
3318 int line = pdoc->LineFromPosition(currentPos);
3319 bool prevCr = false;
3320 pdoc->BeginUndoAction();
3321 for (int i = 0; i < len; i++) {
3322 if (IsEOLChar(ptr[i])) {
3323 if ((ptr[i] == '\r') || (!prevCr))
3324 line++;
3325 if (line >= pdoc->LinesTotal()) {
3326 if (pdoc->eolMode != SC_EOL_LF)
3327 pdoc->InsertChar(pdoc->Length(), '\r');
3328 if (pdoc->eolMode != SC_EOL_CR)
3329 pdoc->InsertChar(pdoc->Length(), '\n');
3330 }
3331 // Pad the end of lines with spaces if required
3332 currentPos = PositionFromLineX(line, xInsert);
3333 if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) {
3334 for (int i = 0; i < xInsert - XFromPosition(currentPos); i++) {
3335 pdoc->InsertChar(currentPos, ' ');
3336 currentPos++;
3337 }
3338 }
3339 prevCr = ptr[i] == '\r';
3340 } else {
3341 pdoc->InsertString(currentPos, ptr + i, 1);
3342 currentPos++;
3343 prevCr = false;
3344 }
3345 }
3346 pdoc->EndUndoAction();
3347 SetEmptySelection(pos);
3348 }
3349
3350 bool Editor::CanPaste() {
3351 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
3352 }
3353
3354 void Editor::Clear() {
3355 if (currentPos == anchor) {
3356 if (!RangeContainsProtected(currentPos, currentPos + 1)) {
3357 DelChar();
3358 }
3359 } else {
3360 ClearSelection();
3361 }
3362 SetEmptySelection(currentPos);
3363 }
3364
3365 void Editor::SelectAll() {
3366 SetSelection(0, pdoc->Length());
3367 Redraw();
3368 }
3369
3370 void Editor::Undo() {
3371 if (pdoc->CanUndo()) {
3372 InvalidateCaret();
3373 int newPos = pdoc->Undo();
3374 SetEmptySelection(newPos);
3375 EnsureCaretVisible();
3376 }
3377 }
3378
3379 void Editor::Redo() {
3380 if (pdoc->CanRedo()) {
3381 int newPos = pdoc->Redo();
3382 SetEmptySelection(newPos);
3383 EnsureCaretVisible();
3384 }
3385 }
3386
3387 void Editor::DelChar() {
3388 if (!RangeContainsProtected(currentPos, currentPos + 1)) {
3389 pdoc->DelChar(currentPos);
3390 }
3391 // Avoid blinking during rapid typing:
3392 ShowCaretAtCurrentPosition();
3393 }
3394
3395 void Editor::DelCharBack(bool allowLineStartDeletion) {
3396 if (currentPos == anchor) {
3397 if (!RangeContainsProtected(currentPos - 1, currentPos)) {
3398 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
3399 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != currentPos)) {
3400 if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3401 pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) {
3402 pdoc->BeginUndoAction();
3403 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3404 int indentationStep = pdoc->IndentSize();
3405 if (indentation % indentationStep == 0) {
3406 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3407 } else {
3408 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
3409 }
3410 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
3411 pdoc->EndUndoAction();
3412 } else {
3413 pdoc->DelCharBack(currentPos);
3414 }
3415 }
3416 }
3417 } else {
3418 ClearSelection();
3419 SetEmptySelection(currentPos);
3420 }
3421 // Avoid blinking during rapid typing:
3422 ShowCaretAtCurrentPosition();
3423 }
3424
3425 void Editor::NotifyFocus(bool) {}
3426
3427 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
3428 SCNotification scn;
3429 scn.nmhdr.code = SCN_STYLENEEDED;
3430 scn.position = endStyleNeeded;
3431 NotifyParent(scn);
3432 }
3433
3434 void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
3435 NotifyStyleToNeeded(endStyleNeeded);
3436 }
3437
3438 void Editor::NotifyChar(int ch) {
3439 SCNotification scn;
3440 scn.nmhdr.code = SCN_CHARADDED;
3441 scn.ch = ch;
3442 NotifyParent(scn);
3443 if (recordingMacro) {
3444 char txt[2];
3445 txt[0] = static_cast<char>(ch);
3446 txt[1] = '\0';
3447 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3448 }
3449 }
3450
3451 void Editor::NotifySavePoint(bool isSavePoint) {
3452 SCNotification scn;
3453 if (isSavePoint) {
3454 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
3455 } else {
3456 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
3457 }
3458 NotifyParent(scn);
3459 }
3460
3461 void Editor::NotifyModifyAttempt() {
3462 SCNotification scn;
3463 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
3464 NotifyParent(scn);
3465 }
3466
3467 void Editor::NotifyDoubleClick(Point, bool) {
3468 SCNotification scn;
3469 scn.nmhdr.code = SCN_DOUBLECLICK;
3470 NotifyParent(scn);
3471 }
3472
3473 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
3474 SCNotification scn;
3475 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
3476 scn.position = position;
3477 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
3478 (alt ? SCI_ALT : 0);
3479 NotifyParent(scn);
3480 }
3481
3482 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
3483 SCNotification scn;
3484 scn.nmhdr.code = SCN_HOTSPOTCLICK;
3485 scn.position = position;
3486 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
3487 (alt ? SCI_ALT : 0);
3488 NotifyParent(scn);
3489 }
3490
3491 void Editor::NotifyUpdateUI() {
3492 SCNotification scn;
3493 scn.nmhdr.code = SCN_UPDATEUI;
3494 NotifyParent(scn);
3495 }
3496
3497 void Editor::NotifyPainted() {
3498 SCNotification scn;
3499 scn.nmhdr.code = SCN_PAINTED;
3500 NotifyParent(scn);
3501 }
3502
3503 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
3504 int marginClicked = -1;
3505 int x = 0;
3506 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3507 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
3508 marginClicked = margin;
3509 x += vs.ms[margin].width;
3510 }
3511 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
3512 SCNotification scn;
3513 scn.nmhdr.code = SCN_MARGINCLICK;
3514 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
3515 (alt ? SCI_ALT : 0);
3516 scn.position = pdoc->LineStart(LineFromLocation(pt));
3517 scn.margin = marginClicked;
3518 NotifyParent(scn);
3519 return true;
3520 } else {
3521 return false;
3522 }
3523 }
3524
3525 void Editor::NotifyNeedShown(int pos, int len) {
3526 SCNotification scn;
3527 scn.nmhdr.code = SCN_NEEDSHOWN;
3528 scn.position = pos;
3529 scn.length = len;
3530 NotifyParent(scn);
3531 }
3532
3533 void Editor::NotifyDwelling(Point pt, bool state) {
3534 SCNotification scn;
3535 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
3536 scn.position = PositionFromLocationClose(pt);
3537 scn.x = pt.x;
3538 scn.y = pt.y;
3539 NotifyParent(scn);
3540 }
3541
3542 void Editor::NotifyZoom() {
3543 SCNotification scn;
3544 scn.nmhdr.code = SCN_ZOOM;
3545 NotifyParent(scn);
3546 }
3547
3548 // Notifications from document
3549 void Editor::NotifyModifyAttempt(Document*, void *) {
3550 //Platform::DebugPrintf("** Modify Attempt\n");
3551 NotifyModifyAttempt();
3552 }
3553
3554 void Editor::NotifyMove(int position) {
3555 SCNotification scn;
3556 scn.nmhdr.code = SCN_POSCHANGED;
3557 scn.position = position;
3558 NotifyParent(scn);
3559 }
3560
3561 void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
3562 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
3563 NotifySavePoint(atSavePoint);
3564 }
3565
3566 void Editor::CheckModificationForWrap(DocModification mh) {
3567 if ((mh.modificationType & SC_MOD_INSERTTEXT) ||
3568 (mh.modificationType & SC_MOD_DELETETEXT)) {
3569 llc.Invalidate(LineLayout::llCheckTextAndStyle);
3570 if (wrapState != eWrapNone) {
3571 int lineDoc = pdoc->LineFromPosition(mh.position);
3572 if (mh.linesAdded <= 0) {
3573 AutoSurface surface(this);
3574 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
3575 if (surface && ll) {
3576 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3577 if (cs.GetHeight(lineDoc) != ll->lines) {
3578 NeedWrapping(lineDoc - 1, lineDoc + 1);
3579 Redraw();
3580 }
3581 }
3582 } else {
3583 NeedWrapping(lineDoc, lineDoc + 1 + mh.linesAdded);
3584 }
3585 }
3586 }
3587 }
3588
3589 // Move a position so it is still after the same character as before the insertion.
3590 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
3591 if (position > startInsertion) {
3592 return position + length;
3593 }
3594 return position;
3595 }
3596
3597 // Move a position so it is still after the same character as before the deletion if that
3598 // character is still present else after the previous surviving character.
3599 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
3600 if (position > startDeletion) {
3601 int endDeletion = startDeletion + length;
3602 if (position > endDeletion) {
3603 return position - length;
3604 } else {
3605 return startDeletion;
3606 }
3607 } else {
3608 return position;
3609 }
3610 }
3611
3612 void Editor::NotifyModified(Document*, DocModification mh, void *) {
3613 needUpdateUI = true;
3614 if (paintState == painting) {
3615 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
3616 }
3617 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
3618 pdoc->IncrementStyleClock();
3619 if (paintState == notPainting) {
3620 if (mh.position < pdoc->LineStart(topLine)) {
3621 // Styling performed before this view
3622 Redraw();
3623 } else {
3624 InvalidateRange(mh.position, mh.position + mh.length);
3625 }
3626 }
3627 } else {
3628 // Move selection and brace highlights
3629 if (mh.modificationType & SC_MOD_INSERTTEXT) {
3630 currentPos = MovePositionForInsertion(currentPos, mh.position, mh.length);
3631 anchor = MovePositionForInsertion(anchor, mh.position, mh.length);
3632 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
3633 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
3634 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
3635 currentPos = MovePositionForDeletion(currentPos, mh.position, mh.length);
3636 anchor = MovePositionForDeletion(anchor, mh.position, mh.length);
3637 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
3638 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
3639 }
3640 if (cs.LinesDisplayed() < cs.LinesInDoc()) {
3641 // Some lines are hidden so may need shown.
3642 // TODO: check if the modified area is hidden.
3643 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
3644 NotifyNeedShown(mh.position, mh.length);
3645 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
3646 NotifyNeedShown(mh.position, mh.length);
3647 }
3648 }
3649 if (mh.linesAdded != 0) {
3650 // Update contraction state for inserted and removed lines
3651 // lineOfPos should be calculated in context of state before modification, shouldn't it
3652 int lineOfPos = pdoc->LineFromPosition(mh.position);
3653 if (mh.linesAdded > 0) {
3654 cs.InsertLines(lineOfPos, mh.linesAdded);
3655 } else {
3656 cs.DeleteLines(lineOfPos, -mh.linesAdded);
3657 }
3658 }
3659 CheckModificationForWrap(mh);
3660 if (mh.linesAdded != 0) {
3661 // Avoid scrolling of display if change before current display
3662 if (mh.position < posTopLine) {
3663 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
3664 if (newTop != topLine) {
3665 SetTopLine(newTop);
3666 SetVerticalScrollPos();
3667 }
3668 }
3669
3670 //Platform::DebugPrintf("** %x Doc Changed\n", this);
3671 // TODO: could invalidate from mh.startModification to end of screen
3672 //InvalidateRange(mh.position, mh.position + mh.length);
3673 if (paintState == notPainting) {
3674 Redraw();
3675 }
3676 } else {
3677 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
3678 // mh.position, mh.position + mh.length);
3679 if (paintState == notPainting) {
3680 InvalidateRange(mh.position, mh.position + mh.length);
3681 }
3682 }
3683 }
3684
3685 if (mh.linesAdded != 0) {
3686 SetScrollBars();
3687 }
3688
3689 if (mh.modificationType & SC_MOD_CHANGEMARKER) {
3690 if (paintState == notPainting) {
3691 RedrawSelMargin();
3692 }
3693 }
3694
3695 // If client wants to see this modification
3696 if (mh.modificationType & modEventMask) {
3697 if ((mh.modificationType & SC_MOD_CHANGESTYLE) == 0) {
3698 // Real modification made to text of document.
3699 NotifyChange(); // Send EN_CHANGE
3700 }
3701
3702 SCNotification scn;
3703 scn.nmhdr.code = SCN_MODIFIED;
3704 scn.position = mh.position;
3705 scn.modificationType = mh.modificationType;
3706 scn.text = mh.text;
3707 scn.length = mh.length;
3708 scn.linesAdded = mh.linesAdded;
3709 scn.line = mh.line;
3710 scn.foldLevelNow = mh.foldLevelNow;
3711 scn.foldLevelPrev = mh.foldLevelPrev;
3712 NotifyParent(scn);
3713 }
3714 }
3715
3716 void Editor::NotifyDeleted(Document *, void *) {
3717 /* Do nothing */
3718 }
3719
3720 void Editor::NotifyMacroRecord(unsigned int iMessage, unsigned long wParam, long lParam) {
3721
3722 // Enumerates all macroable messages
3723 switch (iMessage) {
3724 case SCI_CUT:
3725 case SCI_COPY:
3726 case SCI_PASTE:
3727 case SCI_CLEAR:
3728 case SCI_REPLACESEL:
3729 case SCI_ADDTEXT:
3730 case SCI_INSERTTEXT:
3731 case SCI_APPENDTEXT:
3732 case SCI_CLEARALL:
3733 case SCI_SELECTALL:
3734 case SCI_GOTOLINE:
3735 case SCI_GOTOPOS:
3736 case SCI_SEARCHANCHOR:
3737 case SCI_SEARCHNEXT:
3738 case SCI_SEARCHPREV:
3739 case SCI_LINEDOWN:
3740 case SCI_LINEDOWNEXTEND:
3741 case SCI_PARADOWN:
3742 case SCI_PARADOWNEXTEND:
3743 case SCI_LINEUP:
3744 case SCI_LINEUPEXTEND:
3745 case SCI_PARAUP:
3746 case SCI_PARAUPEXTEND:
3747 case SCI_CHARLEFT:
3748 case SCI_CHARLEFTEXTEND:
3749 case SCI_CHARRIGHT:
3750 case SCI_CHARRIGHTEXTEND:
3751 case SCI_WORDLEFT:
3752 case SCI_WORDLEFTEXTEND:
3753 case SCI_WORDRIGHT:
3754 case SCI_WORDRIGHTEXTEND:
3755 case SCI_WORDPARTLEFT:
3756 case SCI_WORDPARTLEFTEXTEND:
3757 case SCI_WORDPARTRIGHT:
3758 case SCI_WORDPARTRIGHTEXTEND:
3759 case SCI_WORDLEFTEND:
3760 case SCI_WORDLEFTENDEXTEND:
3761 case SCI_WORDRIGHTEND:
3762 case SCI_WORDRIGHTENDEXTEND:
3763 case SCI_HOME:
3764 case SCI_HOMEEXTEND:
3765 case SCI_LINEEND:
3766 case SCI_LINEENDEXTEND:
3767 case SCI_HOMEWRAP:
3768 case SCI_HOMEWRAPEXTEND:
3769 case SCI_LINEENDWRAP:
3770 case SCI_LINEENDWRAPEXTEND:
3771 case SCI_DOCUMENTSTART:
3772 case SCI_DOCUMENTSTARTEXTEND:
3773 case SCI_DOCUMENTEND:
3774 case SCI_DOCUMENTENDEXTEND:
3775 case SCI_STUTTEREDPAGEUP:
3776 case SCI_STUTTEREDPAGEUPEXTEND:
3777 case SCI_STUTTEREDPAGEDOWN:
3778 case SCI_STUTTEREDPAGEDOWNEXTEND:
3779 case SCI_PAGEUP:
3780 case SCI_PAGEUPEXTEND:
3781 case SCI_PAGEDOWN:
3782 case SCI_PAGEDOWNEXTEND:
3783 case SCI_EDITTOGGLEOVERTYPE:
3784 case SCI_CANCEL:
3785 case SCI_DELETEBACK:
3786 case SCI_TAB:
3787 case SCI_BACKTAB:
3788 case SCI_FORMFEED:
3789 case SCI_VCHOME:
3790 case SCI_VCHOMEEXTEND:
3791 case SCI_VCHOMEWRAP:
3792 case SCI_VCHOMEWRAPEXTEND:
3793 case SCI_DELWORDLEFT:
3794 case SCI_DELWORDRIGHT:
3795 case SCI_DELLINELEFT:
3796 case SCI_DELLINERIGHT:
3797 case SCI_LINECOPY:
3798 case SCI_LINECUT:
3799 case SCI_LINEDELETE:
3800 case SCI_LINETRANSPOSE:
3801 case SCI_LINEDUPLICATE:
3802 case SCI_LOWERCASE:
3803 case SCI_UPPERCASE:
3804 case SCI_LINESCROLLDOWN:
3805 case SCI_LINESCROLLUP:
3806 case SCI_DELETEBACKNOTLINE:
3807 case SCI_HOMEDISPLAY:
3808 case SCI_HOMEDISPLAYEXTEND:
3809 case SCI_LINEENDDISPLAY:
3810 case SCI_LINEENDDISPLAYEXTEND:
3811 case SCI_SETSELECTIONMODE:
3812 case SCI_LINEDOWNRECTEXTEND:
3813 case SCI_LINEUPRECTEXTEND:
3814 case SCI_CHARLEFTRECTEXTEND:
3815 case SCI_CHARRIGHTRECTEXTEND:
3816 case SCI_HOMERECTEXTEND:
3817 case SCI_VCHOMERECTEXTEND:
3818 case SCI_LINEENDRECTEXTEND:
3819 case SCI_PAGEUPRECTEXTEND:
3820 case SCI_PAGEDOWNRECTEXTEND:
3821 break;
3822
3823 // Filter out all others like display changes. Also, newlines are redundant
3824 // with char insert messages.
3825 case SCI_NEWLINE:
3826 default:
3827 // printf("Filtered out %ld of macro recording\n", iMessage);
3828 return ;
3829 }
3830
3831 // Send notification
3832 SCNotification scn;
3833 scn.nmhdr.code = SCN_MACRORECORD;
3834 scn.message = iMessage;
3835 scn.wParam = wParam;
3836 scn.lParam = lParam;
3837 NotifyParent(scn);
3838 }
3839
3840 /**
3841 * Force scroll and keep position relative to top of window.
3842 *
3843 * If stuttered = true and not already at first/last row, move to first/last row of window.
3844 * If stuttered = true and already at first/last row, scroll as normal.
3845 */
3846 void Editor::PageMove(int direction, selTypes sel, bool stuttered) {
3847 int topLineNew, newPos;
3848
3849 // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
3850 int currentLine = pdoc->LineFromPosition(currentPos);
3851 int topStutterLine = topLine + caretYSlop;
3852 int bottomStutterLine = topLine + LinesToScroll() - caretYSlop;
3853
3854 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
3855 topLineNew = topLine;
3856 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop));
3857
3858 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
3859 topLineNew = topLine;
3860 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
3861
3862 } else {
3863 Point pt = LocationFromPosition(currentPos);
3864
3865 topLineNew = Platform::Clamp(
3866 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
3867 newPos = PositionFromLocation(
3868 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
3869 }
3870
3871 if (topLineNew != topLine) {
3872 SetTopLine(topLineNew);
3873 MovePositionTo(newPos, sel);
3874 Redraw();
3875 SetVerticalScrollPos();
3876 } else {
3877 MovePositionTo(newPos, sel);
3878 }
3879 }
3880
3881 void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
3882 pdoc->BeginUndoAction();
3883 int startCurrent = currentPos;
3884 int startAnchor = anchor;
3885 if (selType == selStream) {
3886 pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()),
3887 makeUpperCase);
3888 SetSelection(startCurrent, startAnchor);
3889 } else {
3890 SelectionLineIterator lineIterator(this, false);
3891 while (lineIterator.Iterate()) {
3892 pdoc->ChangeCase(
3893 Range(lineIterator.startPos, lineIterator.endPos),
3894 makeUpperCase);
3895 }
3896 // Would be nicer to keep the rectangular selection but this is complex
3897 SetEmptySelection(startCurrent);
3898 }
3899 pdoc->EndUndoAction();
3900 }
3901
3902 void Editor::LineTranspose() {
3903 int line = pdoc->LineFromPosition(currentPos);
3904 if (line > 0) {
3905 int startPrev = pdoc->LineStart(line - 1);
3906 int endPrev = pdoc->LineEnd(line - 1);
3907 int start = pdoc->LineStart(line);
3908 int end = pdoc->LineEnd(line);
3909 int startNext = pdoc->LineStart(line + 1);
3910 if (end < pdoc->Length()) {
3911 end = startNext;
3912 char *thisLine = CopyRange(start, end);
3913 pdoc->DeleteChars(start, end - start);
3914 if (pdoc->InsertString(startPrev, thisLine, end - start)) {
3915 MovePositionTo(startPrev + end - start);
3916 }
3917 delete []thisLine;
3918 } else {
3919 // Last line so line has no line end
3920 char *thisLine = CopyRange(start, end);
3921 char *prevEnd = CopyRange(endPrev, start);
3922 pdoc->DeleteChars(endPrev, end - endPrev);
3923 pdoc->InsertString(startPrev, thisLine, end - start);
3924 if (pdoc->InsertString(startPrev + end - start, prevEnd, start - endPrev)) {
3925 MovePositionTo(startPrev + end - endPrev);
3926 }
3927 delete []thisLine;
3928 delete []prevEnd;
3929 }
3930
3931 }
3932 }
3933
3934 void Editor::LineDuplicate() {
3935 int line = pdoc->LineFromPosition(currentPos);
3936 int start = pdoc->LineStart(line);
3937 int end = pdoc->LineEnd(line);
3938 char *thisLine = CopyRange(start, end);
3939 const char *eol = StringFromEOLMode(pdoc->eolMode);
3940 pdoc->InsertString(end, eol);
3941 pdoc->InsertString(end + istrlen(eol), thisLine, end - start);
3942 delete []thisLine;
3943 }
3944
3945 void Editor::CancelModes() {
3946 moveExtendsSelection = false;
3947 }
3948
3949 void Editor::NewLine() {
3950 ClearSelection();
3951 const char *eol = "\n";
3952 if (pdoc->eolMode == SC_EOL_CRLF) {
3953 eol = "\r\n";
3954 } else if (pdoc->eolMode == SC_EOL_CR) {
3955 eol = "\r";
3956 } // else SC_EOL_LF -> "\n" already set
3957 if (pdoc->InsertString(currentPos, eol)) {
3958 SetEmptySelection(currentPos + istrlen(eol));
3959 while (*eol) {
3960 NotifyChar(*eol);
3961 eol++;
3962 }
3963 }
3964 SetLastXChosen();
3965 EnsureCaretVisible();
3966 }
3967
3968 void Editor::CursorUpOrDown(int direction, selTypes sel) {
3969 Point pt = LocationFromPosition(currentPos);
3970 int posNew = PositionFromLocation(
3971 Point(lastXChosen, pt.y + direction * vs.lineHeight));
3972 if (direction < 0) {
3973 // Line wrapping may lead to a location on the same line, so
3974 // seek back if that is the case.
3975 // There is an equivalent case when moving down which skips
3976 // over a line but as that does not trap the user it is fine.
3977 Point ptNew = LocationFromPosition(posNew);
3978 while ((posNew > 0) && (pt.y == ptNew.y)) {
3979 posNew--;
3980 ptNew = LocationFromPosition(posNew);
3981 }
3982 }
3983 MovePositionTo(posNew, sel);
3984 }
3985
3986 int Editor::StartEndDisplayLine(int pos, bool start) {
3987 RefreshStyleData();
3988 int line = pdoc->LineFromPosition(pos);
3989 AutoSurface surface(this);
3990 AutoLineLayout ll(llc, RetrieveLineLayout(line));
3991 int posRet = INVALID_POSITION;
3992 if (surface && ll) {
3993 unsigned int posLineStart = pdoc->LineStart(line);
3994 LayoutLine(line, surface, vs, ll, wrapWidth);
3995 int posInLine = pos - posLineStart;
3996 if (posInLine <= ll->maxLineLength) {
3997 for (int subLine = 0; subLine < ll->lines; subLine++) {
3998 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
3999 if (start) {
4000 posRet = ll->LineStart(subLine) + posLineStart;
4001 } else {
4002 if (subLine == ll->lines - 1)
4003 posRet = ll->LineStart(subLine + 1) + posLineStart;
4004 else
4005 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
4006 }
4007 }
4008 }
4009 }
4010 }
4011 if (posRet == INVALID_POSITION) {
4012 return pos;
4013 } else {
4014 return posRet;
4015 }
4016 }
4017
4018 int Editor::KeyCommand(unsigned int iMessage) {
4019 switch (iMessage) {
4020 case SCI_LINEDOWN:
4021 CursorUpOrDown(1);
4022 break;
4023 case SCI_LINEDOWNEXTEND:
4024 CursorUpOrDown(1, selStream);
4025 break;
4026 case SCI_LINEDOWNRECTEXTEND:
4027 CursorUpOrDown(1, selRectangle);
4028 break;
4029 case SCI_PARADOWN:
4030 MovePositionTo(pdoc->ParaDown(currentPos));
4031 break;
4032 case SCI_PARADOWNEXTEND:
4033 MovePositionTo(pdoc->ParaDown(currentPos), selStream);
4034 break;
4035 case SCI_LINESCROLLDOWN:
4036 ScrollTo(topLine + 1);
4037 MoveCaretInsideView(false);
4038 break;
4039 case SCI_LINEUP:
4040 CursorUpOrDown(-1);
4041 break;
4042 case SCI_LINEUPEXTEND:
4043 CursorUpOrDown(-1, selStream);
4044 break;
4045 case SCI_LINEUPRECTEXTEND:
4046 CursorUpOrDown(-1, selRectangle);
4047 break;
4048 case SCI_PARAUP:
4049 MovePositionTo(pdoc->ParaUp(currentPos));
4050 break;
4051 case SCI_PARAUPEXTEND:
4052 MovePositionTo(pdoc->ParaUp(currentPos), selStream);
4053 break;
4054 case SCI_LINESCROLLUP:
4055 ScrollTo(topLine - 1);
4056 MoveCaretInsideView(false);
4057 break;
4058 case SCI_CHARLEFT:
4059 if (SelectionEmpty() || moveExtendsSelection) {
4060 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1));
4061 } else {
4062 MovePositionTo(SelectionStart());
4063 }
4064 SetLastXChosen();
4065 break;
4066 case SCI_CHARLEFTEXTEND:
4067 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selStream);
4068 SetLastXChosen();
4069 break;
4070 case SCI_CHARLEFTRECTEXTEND:
4071 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selRectangle);
4072 SetLastXChosen();
4073 break;
4074 case SCI_CHARRIGHT:
4075 if (SelectionEmpty() || moveExtendsSelection) {
4076 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1));
4077 } else {
4078 MovePositionTo(SelectionEnd());
4079 }
4080 SetLastXChosen();
4081 break;
4082 case SCI_CHARRIGHTEXTEND:
4083 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selStream);
4084 SetLastXChosen();
4085 break;
4086 case SCI_CHARRIGHTRECTEXTEND:
4087 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selRectangle);
4088 SetLastXChosen();
4089 break;
4090 case SCI_WORDLEFT:
4091 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1));
4092 SetLastXChosen();
4093 break;
4094 case SCI_WORDLEFTEXTEND:
4095 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), selStream);
4096 SetLastXChosen();
4097 break;
4098 case SCI_WORDRIGHT:
4099 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1));
4100 SetLastXChosen();
4101 break;
4102 case SCI_WORDRIGHTEXTEND:
4103 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), selStream);
4104 SetLastXChosen();
4105 break;
4106
4107 case SCI_WORDLEFTEND:
4108 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1));
4109 SetLastXChosen();
4110 break;
4111 case SCI_WORDLEFTENDEXTEND:
4112 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1), selStream);
4113 SetLastXChosen();
4114 break;
4115 case SCI_WORDRIGHTEND:
4116 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1));
4117 SetLastXChosen();
4118 break;
4119 case SCI_WORDRIGHTENDEXTEND:
4120 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1), selStream);
4121 SetLastXChosen();
4122 break;
4123
4124 case SCI_HOME:
4125 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)));
4126 SetLastXChosen();
4127 break;
4128 case SCI_HOMEEXTEND:
4129 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selStream);
4130 SetLastXChosen();
4131 break;
4132 case SCI_HOMERECTEXTEND:
4133 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selRectangle);
4134 SetLastXChosen();
4135 break;
4136 case SCI_LINEEND:
4137 MovePositionTo(pdoc->LineEndPosition(currentPos));
4138 SetLastXChosen();
4139 break;
4140 case SCI_LINEENDEXTEND:
4141 MovePositionTo(pdoc->LineEndPosition(currentPos), selStream);
4142 SetLastXChosen();
4143 break;
4144 case SCI_LINEENDRECTEXTEND:
4145 MovePositionTo(pdoc->LineEndPosition(currentPos), selRectangle);
4146 SetLastXChosen();
4147 break;
4148 case SCI_HOMEWRAP: {
4149 int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4150 if (currentPos <= homePos)
4151 homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
4152 MovePositionTo(homePos);
4153 SetLastXChosen();
4154 }
4155 break;
4156 case SCI_HOMEWRAPEXTEND: {
4157 int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4158 if (currentPos <= homePos)
4159 homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
4160 MovePositionTo(homePos, selStream);
4161 SetLastXChosen();
4162 }
4163 break;
4164 case SCI_LINEENDWRAP: {
4165 int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
4166 int realEndPos = pdoc->LineEndPosition(currentPos);
4167 if (endPos > realEndPos // if moved past visible EOLs
4168 || currentPos >= endPos) // if at end of display line already
4169 endPos = realEndPos;
4170 MovePositionTo(endPos);
4171 SetLastXChosen();
4172 }
4173 break;
4174 case SCI_LINEENDWRAPEXTEND: {
4175 int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
4176 int realEndPos = pdoc->LineEndPosition(currentPos);
4177 if (endPos > realEndPos // if moved past visible EOLs
4178 || currentPos >= endPos) // if at end of display line already
4179 endPos = realEndPos;
4180 MovePositionTo(endPos, selStream);
4181 SetLastXChosen();
4182 }
4183 break;
4184 case SCI_DOCUMENTSTART:
4185 MovePositionTo(0);
4186 SetLastXChosen();
4187 break;
4188 case SCI_DOCUMENTSTARTEXTEND:
4189 MovePositionTo(0, selStream);
4190 SetLastXChosen();
4191 break;
4192 case SCI_DOCUMENTEND:
4193 MovePositionTo(pdoc->Length());
4194 SetLastXChosen();
4195 break;
4196 case SCI_DOCUMENTENDEXTEND:
4197 MovePositionTo(pdoc->Length(), selStream);
4198 SetLastXChosen();
4199 break;
4200 case SCI_STUTTEREDPAGEUP:
4201 PageMove(-1, noSel, true);
4202 break;
4203 case SCI_STUTTEREDPAGEUPEXTEND:
4204 PageMove(-1, selStream, true);
4205 break;
4206 case SCI_STUTTEREDPAGEDOWN:
4207 PageMove(1, noSel, true);
4208 break;
4209 case SCI_STUTTEREDPAGEDOWNEXTEND:
4210 PageMove(1, selStream, true);
4211 break;
4212 case SCI_PAGEUP:
4213 PageMove(-1);
4214 break;
4215 case SCI_PAGEUPEXTEND:
4216 PageMove(-1, selStream);
4217 break;
4218 case SCI_PAGEUPRECTEXTEND:
4219 PageMove(-1, selRectangle);
4220 break;
4221 case SCI_PAGEDOWN:
4222 PageMove(1);
4223 break;
4224 case SCI_PAGEDOWNEXTEND:
4225 PageMove(1, selStream);
4226 break;
4227 case SCI_PAGEDOWNRECTEXTEND:
4228 PageMove(1, selRectangle);
4229 break;
4230 case SCI_EDITTOGGLEOVERTYPE:
4231 inOverstrike = !inOverstrike;
4232 DropCaret();
4233 ShowCaretAtCurrentPosition();
4234 NotifyUpdateUI();
4235 break;
4236 case SCI_CANCEL: // Cancel any modes - handled in subclass
4237 // Also unselect text
4238 CancelModes();
4239 break;
4240 case SCI_DELETEBACK:
4241 DelCharBack(true);
4242 SetLastXChosen();
4243 EnsureCaretVisible();
4244 break;
4245 case SCI_DELETEBACKNOTLINE:
4246 DelCharBack(false);
4247 SetLastXChosen();
4248 EnsureCaretVisible();
4249 break;
4250 case SCI_TAB:
4251 Indent(true);
4252 SetLastXChosen();
4253 EnsureCaretVisible();
4254 break;
4255 case SCI_BACKTAB:
4256 Indent(false);
4257 SetLastXChosen();
4258 EnsureCaretVisible();
4259 break;
4260 case SCI_NEWLINE:
4261 NewLine();
4262 break;
4263 case SCI_FORMFEED:
4264 AddChar('\f');
4265 break;
4266 case SCI_VCHOME:
4267 MovePositionTo(pdoc->VCHomePosition(currentPos));
4268 SetLastXChosen();
4269 break;
4270 case SCI_VCHOMEEXTEND:
4271 MovePositionTo(pdoc->VCHomePosition(currentPos), selStream);
4272 SetLastXChosen();
4273 break;
4274 case SCI_VCHOMERECTEXTEND:
4275 MovePositionTo(pdoc->VCHomePosition(currentPos), selRectangle);
4276 SetLastXChosen();
4277 break;
4278 case SCI_VCHOMEWRAP: {
4279 int homePos = pdoc->VCHomePosition(currentPos);
4280 int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4281 if ((viewLineStart < currentPos) && (viewLineStart > homePos))
4282 homePos = viewLineStart;
4283
4284 MovePositionTo(homePos);
4285 SetLastXChosen();
4286 }
4287 break;
4288 case SCI_VCHOMEWRAPEXTEND: {
4289 int homePos = pdoc->VCHomePosition(currentPos);
4290 int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4291 if ((viewLineStart < currentPos) && (viewLineStart > homePos))
4292 homePos = viewLineStart;
4293
4294 MovePositionTo(homePos, selStream);
4295 SetLastXChosen();
4296 }
4297 break;
4298 case SCI_ZOOMIN:
4299 if (vs.zoomLevel < 20) {
4300 vs.zoomLevel++;
4301 InvalidateStyleRedraw();
4302 NotifyZoom();
4303 }
4304 break;
4305 case SCI_ZOOMOUT:
4306 if (vs.zoomLevel > -10) {
4307 vs.zoomLevel--;
4308 InvalidateStyleRedraw();
4309 NotifyZoom();
4310 }
4311 break;
4312 case SCI_DELWORDLEFT: {
4313 int startWord = pdoc->NextWordStart(currentPos, -1);
4314 pdoc->DeleteChars(startWord, currentPos - startWord);
4315 SetLastXChosen();
4316 }
4317 break;
4318 case SCI_DELWORDRIGHT: {
4319 int endWord = pdoc->NextWordStart(currentPos, 1);
4320 pdoc->DeleteChars(currentPos, endWord - currentPos);
4321 }
4322 break;
4323 case SCI_DELLINELEFT: {
4324 int line = pdoc->LineFromPosition(currentPos);
4325 int start = pdoc->LineStart(line);
4326 pdoc->DeleteChars(start, currentPos - start);
4327 SetLastXChosen();
4328 }
4329 break;
4330 case SCI_DELLINERIGHT: {
4331 int line = pdoc->LineFromPosition(currentPos);
4332 int end = pdoc->LineEnd(line);
4333 pdoc->DeleteChars(currentPos, end - currentPos);
4334 }
4335 break;
4336 case SCI_LINECOPY: {
4337 int lineStart = pdoc->LineFromPosition(SelectionStart());
4338 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
4339 CopyRangeToClipboard(pdoc->LineStart(lineStart),
4340 pdoc->LineStart(lineEnd + 1));
4341 }
4342 break;
4343 case SCI_LINECUT: {
4344 int lineStart = pdoc->LineFromPosition(SelectionStart());
4345 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
4346 int start = pdoc->LineStart(lineStart);
4347 int end = pdoc->LineStart(lineEnd + 1);
4348 SetSelection(start, end);
4349 Cut();
4350 SetLastXChosen();
4351 }
4352 break;
4353 case SCI_LINEDELETE: {
4354 int line = pdoc->LineFromPosition(currentPos);
4355 int start = pdoc->LineStart(line);
4356 int end = pdoc->LineStart(line + 1);
4357 pdoc->DeleteChars(start, end - start);
4358 }
4359 break;
4360 case SCI_LINETRANSPOSE:
4361 LineTranspose();
4362 break;
4363 case SCI_LINEDUPLICATE:
4364 LineDuplicate();
4365 break;
4366 case SCI_LOWERCASE:
4367 ChangeCaseOfSelection(false);
4368 break;
4369 case SCI_UPPERCASE:
4370 ChangeCaseOfSelection(true);
4371 break;
4372 case SCI_WORDPARTLEFT:
4373 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1));
4374 SetLastXChosen();
4375 break;
4376 case SCI_WORDPARTLEFTEXTEND:
4377 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1), selStream);
4378 SetLastXChosen();
4379 break;
4380 case SCI_WORDPARTRIGHT:
4381 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1));
4382 SetLastXChosen();
4383 break;
4384 case SCI_WORDPARTRIGHTEXTEND:
4385 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1), selStream);
4386 SetLastXChosen();
4387 break;
4388 case SCI_HOMEDISPLAY:
4389 MovePositionTo(MovePositionSoVisible(
4390 StartEndDisplayLine(currentPos, true), -1));
4391 SetLastXChosen();
4392 break;
4393 case SCI_HOMEDISPLAYEXTEND:
4394 MovePositionTo(MovePositionSoVisible(
4395 StartEndDisplayLine(currentPos, true), -1), selStream);
4396 SetLastXChosen();
4397 break;
4398 case SCI_LINEENDDISPLAY:
4399 MovePositionTo(MovePositionSoVisible(
4400 StartEndDisplayLine(currentPos, false), 1));
4401 SetLastXChosen();
4402 break;
4403 case SCI_LINEENDDISPLAYEXTEND:
4404 MovePositionTo(MovePositionSoVisible(
4405 StartEndDisplayLine(currentPos, false), 1), selStream);
4406 SetLastXChosen();
4407 break;
4408 }
4409 return 0;
4410 }
4411
4412 int Editor::KeyDefault(int, int) {
4413 return 0;
4414 }
4415
4416 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
4417 DwellEnd(false);
4418 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4419 (alt ? SCI_ALT : 0);
4420 int msg = kmap.Find(key, modifiers);
4421 if (msg) {
4422 if (consumed)
4423 *consumed = true;
4424 return WndProc(msg, 0, 0);
4425 } else {
4426 if (consumed)
4427 *consumed = false;
4428 return KeyDefault(key, modifiers);
4429 }
4430 }
4431
4432 void Editor::SetWhitespaceVisible(int view) {
4433 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
4434 }
4435
4436 int Editor::GetWhitespaceVisible() {
4437 return vs.viewWhitespace;
4438 }
4439
4440 void Editor::Indent(bool forwards) {
4441 //Platform::DebugPrintf("INdent %d\n", forwards);
4442 int lineOfAnchor = pdoc->LineFromPosition(anchor);
4443 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
4444 if (lineOfAnchor == lineCurrentPos) {
4445 if (forwards) {
4446 pdoc->BeginUndoAction();
4447 ClearSelection();
4448 if (pdoc->GetColumn(currentPos) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
4449 pdoc->tabIndents) {
4450 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4451 int indentationStep = pdoc->IndentSize();
4452 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
4453 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
4454 } else {
4455 if (pdoc->useTabs) {
4456 pdoc->InsertChar(currentPos, '\t');
4457 SetEmptySelection(currentPos + 1);
4458 } else {
4459 int numSpaces = (pdoc->tabInChars) -
4460 (pdoc->GetColumn(currentPos) % (pdoc->tabInChars));
4461 if (numSpaces < 1)
4462 numSpaces = pdoc->tabInChars;
4463 for (int i = 0; i < numSpaces; i++) {
4464 pdoc->InsertChar(currentPos + i, ' ');
4465 }
4466 SetEmptySelection(currentPos + numSpaces);
4467 }
4468 }
4469 pdoc->EndUndoAction();
4470 } else {
4471 if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4472 pdoc->tabIndents) {
4473 pdoc->BeginUndoAction();
4474 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4475 int indentationStep = pdoc->IndentSize();
4476 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4477 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
4478 pdoc->EndUndoAction();
4479 } else {
4480 int newColumn = ((pdoc->GetColumn(currentPos) - 1) / pdoc->tabInChars) *
4481 pdoc->tabInChars;
4482 if (newColumn < 0)
4483 newColumn = 0;
4484 int newPos = currentPos;
4485 while (pdoc->GetColumn(newPos) > newColumn)
4486 newPos--;
4487 SetEmptySelection(newPos);
4488 }
4489 }
4490 } else {
4491 int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor);
4492 int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos);
4493 // Multiple lines selected so indent / dedent
4494 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
4495 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
4496 if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos)
4497 lineBottomSel--; // If not selecting any characters on a line, do not indent
4498 pdoc->BeginUndoAction();
4499 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
4500 pdoc->EndUndoAction();
4501 if (lineOfAnchor < lineCurrentPos) {
4502 if (currentPosPosOnLine == 0)
4503 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
4504 else
4505 SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
4506 } else {
4507 if (anchorPosOnLine == 0)
4508 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
4509 else
4510 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
4511 }
4512 }
4513 }
4514
4515 /**
4516 * Search of a text in the document, in the given range.
4517 * @return The position of the found text, -1 if not found.
4518 */
4519 long Editor::FindText(
4520 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4521 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4522 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
4523
4524 TextToFind *ft = reinterpret_cast<TextToFind *>(lParam);
4525 int lengthFound = istrlen(ft->lpstrText);
4526 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
4527 (wParam & SCFIND_MATCHCASE) != 0,
4528 (wParam & SCFIND_WHOLEWORD) != 0,
4529 (wParam & SCFIND_WORDSTART) != 0,
4530 (wParam & SCFIND_REGEXP) != 0,
4531 (wParam & SCFIND_POSIX) != 0,
4532 &lengthFound);
4533 if (pos != -1) {
4534 ft->chrgText.cpMin = pos;
4535 ft->chrgText.cpMax = pos + lengthFound;
4536 }
4537 return pos;
4538 }
4539
4540 /**
4541 * Relocatable search support : Searches relative to current selection
4542 * point and sets the selection to the found text range with
4543 * each search.
4544 */
4545 /**
4546 * Anchor following searches at current selection start: This allows
4547 * multiple incremental interactive searches to be macro recorded
4548 * while still setting the selection to found text so the find/select
4549 * operation is self-contained.
4550 */
4551 void Editor::SearchAnchor() {
4552 searchAnchor = SelectionStart();
4553 }
4554
4555 /**
4556 * Find text from current search anchor: Must call @c SearchAnchor first.
4557 * Used for next text and previous text requests.
4558 * @return The position of the found text, -1 if not found.
4559 */
4560 long Editor::SearchText(
4561 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
4562 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4563 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4564 sptr_t lParam) { ///< The text to search for.
4565
4566 const char *txt = reinterpret_cast<char *>(lParam);
4567 int pos;
4568 int lengthFound = istrlen(txt);
4569 if (iMessage == SCI_SEARCHNEXT) {
4570 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
4571 (wParam & SCFIND_MATCHCASE) != 0,
4572 (wParam & SCFIND_WHOLEWORD) != 0,
4573 (wParam & SCFIND_WORDSTART) != 0,
4574 (wParam & SCFIND_REGEXP) != 0,
4575 (wParam & SCFIND_POSIX) != 0,
4576 &lengthFound);
4577 } else {
4578 pos = pdoc->FindText(searchAnchor, 0, txt,
4579 (wParam & SCFIND_MATCHCASE) != 0,
4580 (wParam & SCFIND_WHOLEWORD) != 0,
4581 (wParam & SCFIND_WORDSTART) != 0,
4582 (wParam & SCFIND_REGEXP) != 0,
4583 (wParam & SCFIND_POSIX) != 0,
4584 &lengthFound);
4585 }
4586
4587 if (pos != -1) {
4588 SetSelection(pos, pos + lengthFound);
4589 }
4590
4591 return pos;
4592 }
4593
4594 /**
4595 * Search for text in the target range of the document.
4596 * @return The position of the found text, -1 if not found.
4597 */
4598 long Editor::SearchInTarget(const char *text, int length) {
4599 int lengthFound = length;
4600 int pos = pdoc->FindText(targetStart, targetEnd, text,
4601 (searchFlags & SCFIND_MATCHCASE) != 0,
4602 (searchFlags & SCFIND_WHOLEWORD) != 0,
4603 (searchFlags & SCFIND_WORDSTART) != 0,
4604 (searchFlags & SCFIND_REGEXP) != 0,
4605 (searchFlags & SCFIND_POSIX) != 0,
4606 &lengthFound);
4607 if (pos != -1) {
4608 targetStart = pos;
4609 targetEnd = pos + lengthFound;
4610 }
4611 return pos;
4612 }
4613
4614 void Editor::GoToLine(int lineNo) {
4615 if (lineNo > pdoc->LinesTotal())
4616 lineNo = pdoc->LinesTotal();
4617 if (lineNo < 0)
4618 lineNo = 0;
4619 SetEmptySelection(pdoc->LineStart(lineNo));
4620 ShowCaretAtCurrentPosition();
4621 EnsureCaretVisible();
4622 }
4623
4624 static bool Close(Point pt1, Point pt2) {
4625 if (abs(pt1.x - pt2.x) > 3)
4626 return false;
4627 if (abs(pt1.y - pt2.y) > 3)
4628 return false;
4629 return true;
4630 }
4631
4632 char *Editor::CopyRange(int start, int end) {
4633 char *text = 0;
4634 if (start < end) {
4635 int len = end - start;
4636 text = new char[len + 1];
4637 if (text) {
4638 for (int i = 0; i < len; i++) {
4639 text[i] = pdoc->CharAt(start + i);
4640 }
4641 text[len] = '\0';
4642 }
4643 }
4644 return text;
4645 }
4646
4647 void Editor::CopySelectionFromRange(SelectionText *ss, int start, int end) {
4648 ss->Set(CopyRange(start, end), end - start + 1,
4649 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
4650 }
4651
4652 void Editor::CopySelectionRange(SelectionText *ss) {
4653 if (selType == selStream) {
4654 CopySelectionFromRange(ss, SelectionStart(), SelectionEnd());
4655 } else {
4656 char *text = 0;
4657 int size = 0;
4658 SelectionLineIterator lineIterator(this);
4659 while (lineIterator.Iterate()) {
4660 size += lineIterator.endPos - lineIterator.startPos;
4661 if (selType != selLines) {
4662 size++;
4663 if (pdoc->eolMode == SC_EOL_CRLF) {
4664 size++;
4665 }
4666 }
4667 }
4668 if (size > 0) {
4669 text = new char[size + 1];
4670 if (text) {
4671 int j = 0;
4672 lineIterator.Reset();
4673 while (lineIterator.Iterate()) {
4674 for (int i = lineIterator.startPos;
4675 i < lineIterator.endPos;
4676 i++) {
4677 text[j++] = pdoc->CharAt(i);
4678 }
4679 if (selType != selLines) {
4680 if (pdoc->eolMode != SC_EOL_LF) {
4681 text[j++] = '\r';
4682 }
4683 if (pdoc->eolMode != SC_EOL_CR) {
4684 text[j++] = '\n';
4685 }
4686 }
4687 }
4688 text[size] = '\0';
4689 }
4690 }
4691 ss->Set(text, size + 1, pdoc->dbcsCodePage,
4692 vs.styles[STYLE_DEFAULT].characterSet, selType == selRectangle);
4693 }
4694 }
4695
4696 void Editor::CopyRangeToClipboard(int start, int end) {
4697 start = pdoc->ClampPositionIntoDocument(start);
4698 end = pdoc->ClampPositionIntoDocument(end);
4699 SelectionText selectedText;
4700 selectedText.Set(CopyRange(start, end), end - start + 1,
4701 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
4702 CopyToClipboard(selectedText);
4703 }
4704
4705 void Editor::CopyText(int length, const char *text) {
4706 SelectionText selectedText;
4707 selectedText.Copy(text, length,
4708 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
4709 CopyToClipboard(selectedText);
4710 }
4711
4712 void Editor::SetDragPosition(int newPos) {
4713 if (newPos >= 0) {
4714 newPos = MovePositionOutsideChar(newPos, 1);
4715 posDrop = newPos;
4716 }
4717 if (posDrag != newPos) {
4718 caret.on = true;
4719 SetTicking(true);
4720 InvalidateCaret();
4721 posDrag = newPos;
4722 InvalidateCaret();
4723 }
4724 }
4725
4726 void Editor::DisplayCursor(Window::Cursor c) {
4727 if (cursorMode == SC_CURSORNORMAL)
4728 wMain.SetCursor(c);
4729 else
4730 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
4731 }
4732
4733 void Editor::StartDrag() {
4734 // Always handled by subclasses
4735 //SetMouseCapture(true);
4736 //DisplayCursor(Window::cursorArrow);
4737 }
4738
4739 void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) {
4740 //Platform::DebugPrintf("DropAt %d\n", inDragDrop);
4741 if (inDragDrop)
4742 dropWentOutside = false;
4743
4744 int positionWasInSelection = PositionInSelection(position);
4745
4746 bool positionOnEdgeOfSelection =
4747 (position == SelectionStart()) || (position == SelectionEnd());
4748
4749 if ((!inDragDrop) || !(0 == positionWasInSelection) ||
4750 (positionOnEdgeOfSelection && !moving)) {
4751
4752 int selStart = SelectionStart();
4753 int selEnd = SelectionEnd();
4754
4755 pdoc->BeginUndoAction();
4756
4757 int positionAfterDeletion = position;
4758 if (inDragDrop && moving) {
4759 // Remove dragged out text
4760 if (rectangular || selType == selLines) {
4761 SelectionLineIterator lineIterator(this);
4762 while (lineIterator.Iterate()) {
4763 if (position >= lineIterator.startPos) {
4764 if (position > lineIterator.endPos) {
4765 positionAfterDeletion -= lineIterator.endPos - lineIterator.startPos;
4766 } else {
4767 positionAfterDeletion -= position - lineIterator.startPos;
4768 }
4769 }
4770 }
4771 } else {
4772 if (position > selStart) {
4773 positionAfterDeletion -= selEnd - selStart;
4774 }
4775 }
4776 ClearSelection();
4777 }
4778 position = positionAfterDeletion;
4779
4780 if (rectangular) {
4781 PasteRectangular(position, value, istrlen(value));
4782 pdoc->EndUndoAction();
4783 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
4784 SetEmptySelection(position);
4785 } else {
4786 position = MovePositionOutsideChar(position, currentPos - position);
4787 if (pdoc->InsertString(position, value)) {
4788 SetSelection(position + istrlen(value), position);
4789 }
4790 pdoc->EndUndoAction();
4791 }
4792 } else if (inDragDrop) {
4793 SetEmptySelection(position);
4794 }
4795 }
4796
4797 /**
4798 * @return -1 if given position is before the selection,
4799 * 1 if position is after the selection,
4800 * 0 if position is inside the selection,
4801 */
4802 int Editor::PositionInSelection(int pos) {
4803 pos = MovePositionOutsideChar(pos, currentPos - pos);
4804 if (pos < SelectionStart()) {
4805 return -1;
4806 }
4807 if (pos > SelectionEnd()) {
4808 return 1;
4809 }
4810 if (selType == selStream) {
4811 return 0;
4812 } else {
4813 SelectionLineIterator lineIterator(this);
4814 lineIterator.SetAt(pdoc->LineFromPosition(pos));
4815 if (pos < lineIterator.startPos) {
4816 return -1;
4817 } else if (pos > lineIterator.endPos) {
4818 return 1;
4819 } else {
4820 return 0;
4821 }
4822 }
4823 }
4824
4825 bool Editor::PointInSelection(Point pt) {
4826 int pos = PositionFromLocation(pt);
4827 if (0 == PositionInSelection(pos)) {
4828 // Probably inside, but we must make a finer test
4829 int selStart, selEnd;
4830 if (selType == selStream) {
4831 selStart = SelectionStart();
4832 selEnd = SelectionEnd();
4833 } else {
4834 SelectionLineIterator lineIterator(this);
4835 lineIterator.SetAt(pdoc->LineFromPosition(pos));
4836 selStart = lineIterator.startPos;
4837 selEnd = lineIterator.endPos;
4838 }
4839 if (pos == selStart) {
4840 // see if just before selection
4841 Point locStart = LocationFromPosition(pos);
4842 if (pt.x < locStart.x) {
4843 return false;
4844 }
4845 }
4846 if (pos == selEnd) {
4847 // see if just after selection
4848 Point locEnd = LocationFromPosition(pos);
4849 if (pt.x > locEnd.x) {
4850 return false;
4851 }
4852 }
4853 return true;
4854 }
4855 return false;
4856 }
4857
4858 bool Editor::PointInSelMargin(Point pt) {
4859 // Really means: "Point in a margin"
4860 if (vs.fixedColumnWidth > 0) { // There is a margin
4861 PRectangle rcSelMargin = GetClientRectangle();
4862 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
4863 return rcSelMargin.Contains(pt);
4864 } else {
4865 return false;
4866 }
4867 }
4868
4869 void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
4870 if (lineAnchor_ < lineCurrent_) {
4871 SetSelection(pdoc->LineStart(lineCurrent_ + 1),
4872 pdoc->LineStart(lineAnchor_));
4873 } else if (lineAnchor_ > lineCurrent_) {
4874 SetSelection(pdoc->LineStart(lineCurrent_),
4875 pdoc->LineStart(lineAnchor_ + 1));
4876 } else { // Same line, select it
4877 SetSelection(pdoc->LineStart(lineAnchor_ + 1),
4878 pdoc->LineStart(lineAnchor_));
4879 }
4880 }
4881
4882 void Editor::DwellEnd(bool mouseMoved) {
4883 if (mouseMoved)
4884 ticksToDwell = dwellDelay;
4885 else
4886 ticksToDwell = SC_TIME_FOREVER;
4887 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
4888 dwelling = false;
4889 NotifyDwelling(ptMouseLast, dwelling);
4890 }
4891 }
4892
4893 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
4894 //Platform::DebugPrintf("Scintilla:ButtonDown %d %d = %d alt=%d\n", curTime, lastClickTime, curTime - lastClickTime, alt);
4895 ptMouseLast = pt;
4896 int newPos = PositionFromLocation(pt);
4897 newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
4898 inDragDrop = false;
4899 moveExtendsSelection = false;
4900
4901 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
4902 if (processed)
4903 return;
4904
4905 bool inSelMargin = PointInSelMargin(pt);
4906 if (shift & !inSelMargin) {
4907 SetSelection(newPos);
4908 }
4909 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
4910 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
4911 SetMouseCapture(true);
4912 SetEmptySelection(newPos);
4913 bool doubleClick = false;
4914 // Stop mouse button bounce changing selection type
4915 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
4916 if (selectionType == selChar) {
4917 selectionType = selWord;
4918 doubleClick = true;
4919 } else if (selectionType == selWord) {
4920 selectionType = selLine;
4921 } else {
4922 selectionType = selChar;
4923 originalAnchorPos = currentPos;
4924 }
4925 }
4926
4927 if (selectionType == selWord) {
4928 if (currentPos >= originalAnchorPos) { // Moved forward
4929 SetSelection(pdoc->ExtendWordSelect(currentPos, 1),
4930 pdoc->ExtendWordSelect(originalAnchorPos, -1));
4931 } else { // Moved backward
4932 SetSelection(pdoc->ExtendWordSelect(currentPos, -1),
4933 pdoc->ExtendWordSelect(originalAnchorPos, 1));
4934 }
4935 } else if (selectionType == selLine) {
4936 lineAnchor = LineFromLocation(pt);
4937 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
4938 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
4939 } else {
4940 SetEmptySelection(currentPos);
4941 }
4942 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
4943 if (doubleClick) {
4944 NotifyDoubleClick(pt, shift);
4945 if (PositionIsHotspot(newPos))
4946 NotifyHotSpotDoubleClicked(newPos, shift, ctrl, alt);
4947 }
4948 } else { // Single click
4949 if (inSelMargin) {
4950 selType = selStream;
4951 if (ctrl) {
4952 SelectAll();
4953 lastClickTime = curTime;
4954 return;
4955 }
4956 if (!shift) {
4957 lineAnchor = LineFromLocation(pt);
4958 // Single click in margin: select whole line
4959 LineSelection(lineAnchor, lineAnchor);
4960 SetSelection(pdoc->LineStart(lineAnchor + 1),
4961 pdoc->LineStart(lineAnchor));
4962 } else {
4963 // Single shift+click in margin: select from line anchor to clicked line
4964 if (anchor > currentPos)
4965 lineAnchor = pdoc->LineFromPosition(anchor - 1);
4966 else
4967 lineAnchor = pdoc->LineFromPosition(anchor);
4968 int lineStart = LineFromLocation(pt);
4969 LineSelection(lineStart, lineAnchor);
4970 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
4971 }
4972
4973 SetDragPosition(invalidPosition);
4974 SetMouseCapture(true);
4975 selectionType = selLine;
4976 } else {
4977 if (PointIsHotspot(pt)) {
4978 NotifyHotSpotClicked(newPos, shift, ctrl, alt);
4979 }
4980 if (!shift) {
4981 inDragDrop = PointInSelection(pt);
4982 }
4983 if (inDragDrop) {
4984 SetMouseCapture(false);
4985 SetDragPosition(newPos);
4986 CopySelectionRange(&drag);
4987 StartDrag();
4988 } else {
4989 SetDragPosition(invalidPosition);
4990 SetMouseCapture(true);
4991 if (!shift) {
4992 SetEmptySelection(newPos);
4993 }
4994 selType = alt ? selRectangle : selStream;
4995 xStartSelect = xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
4996 selectionType = selChar;
4997 originalAnchorPos = currentPos;
4998 }
4999 }
5000 }
5001 lastClickTime = curTime;
5002 lastXChosen = pt.x;
5003 ShowCaretAtCurrentPosition();
5004 }
5005
5006 bool Editor::PositionIsHotspot(int position) {
5007 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
5008 }
5009
5010 bool Editor::PointIsHotspot(Point pt) {
5011 int pos = PositionFromLocationClose(pt);
5012 if (pos == INVALID_POSITION)
5013 return false;
5014 return PositionIsHotspot(pos);
5015 }
5016
5017 void Editor::SetHotSpotRange(Point *pt) {
5018 if (pt) {
5019 int pos = PositionFromLocation(*pt);
5020
5021 // If we don't limit this to word characters then the
5022 // range can encompass more than the run range and then
5023 // the underline will not be drawn properly.
5024 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
5025 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
5026
5027 // Only invalidate the range if the hotspot range has changed...
5028 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
5029 if (hsStart != -1) {
5030 InvalidateRange(hsStart, hsEnd);
5031 }
5032 hsStart = hsStart_;
5033 hsEnd = hsEnd_;
5034 InvalidateRange(hsStart, hsEnd);
5035 }
5036 } else {
5037 if (hsStart != -1) {
5038 int hsStart_ = hsStart;
5039 int hsEnd_ = hsEnd;
5040 hsStart = -1;
5041 hsEnd = -1;
5042 InvalidateRange(hsStart_, hsEnd_);
5043 } else {
5044 hsStart = -1;
5045 hsEnd = -1;
5046 }
5047 }
5048 }
5049
5050 void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) {
5051 hsStart_ = hsStart;
5052 hsEnd_ = hsEnd;
5053 }
5054
5055 void Editor::ButtonMove(Point pt) {
5056 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
5057 DwellEnd(true);
5058 }
5059 ptMouseLast = pt;
5060 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
5061 if (HaveMouseCapture()) {
5062
5063 // Slow down autoscrolling/selection
5064 autoScrollTimer.ticksToWait -= timer.tickSize;
5065 if (autoScrollTimer.ticksToWait > 0)
5066 return;
5067 autoScrollTimer.ticksToWait = autoScrollDelay;
5068
5069 // Adjust selection
5070 int movePos = PositionFromLocation(pt);
5071 movePos = MovePositionOutsideChar(movePos, currentPos - movePos);
5072 if (posDrag >= 0) {
5073 SetDragPosition(movePos);
5074 } else {
5075 if (selectionType == selChar) {
5076 SetSelection(movePos);
5077 } else if (selectionType == selWord) {
5078 // Continue selecting by word
5079 if (movePos == originalAnchorPos) { // Didn't move
5080 // No need to do anything. Previously this case was lumped
5081 // in with "Moved forward", but that can be harmful in this
5082 // case: a handler for the NotifyDoubleClick re-adjusts
5083 // the selection for a fancier definition of "word" (for
5084 // example, in Perl it is useful to include the leading
5085 // '$', '%' or '@' on variables for word selection). In this
5086 // the ButtonMove() called via Tick() for auto-scrolling
5087 // could result in the fancier word selection adjustment
5088 // being unmade.
5089 } else if (movePos > originalAnchorPos) { // Moved forward
5090 SetSelection(pdoc->ExtendWordSelect(movePos, 1),
5091 pdoc->ExtendWordSelect(originalAnchorPos, -1));
5092 } else { // Moved backward
5093 SetSelection(pdoc->ExtendWordSelect(movePos, -1),
5094 pdoc->ExtendWordSelect(originalAnchorPos, 1));
5095 }
5096 } else {
5097 // Continue selecting by line
5098 int lineMove = LineFromLocation(pt);
5099 LineSelection(lineMove, lineAnchor);
5100 }
5101 }
5102 // While dragging to make rectangular selection, we don't want the current
5103 // position to jump to the end of smaller or empty lines.
5104 //xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
5105 xEndSelect = XFromPosition(movePos);
5106
5107 // Autoscroll
5108 PRectangle rcClient = GetClientRectangle();
5109 if (pt.y > rcClient.bottom) {
5110 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5111 if (lineMove < 0) {
5112 lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
5113 }
5114 ScrollTo(lineMove - LinesOnScreen() + 5);
5115 Redraw();
5116 } else if (pt.y < rcClient.top) {
5117 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5118 ScrollTo(lineMove - 5);
5119 Redraw();
5120 }
5121 EnsureCaretVisible(false, false, true);
5122
5123 if (hsStart != -1 && !PositionIsHotspot(movePos))
5124 SetHotSpotRange(NULL);
5125
5126 } else {
5127 if (vs.fixedColumnWidth > 0) { // There is a margin
5128 if (PointInSelMargin(pt)) {
5129 DisplayCursor(Window::cursorReverseArrow);
5130 return; // No need to test for selection
5131 }
5132 }
5133 // Display regular (drag) cursor over selection
5134 if (PointInSelection(pt)) {
5135 DisplayCursor(Window::cursorArrow);
5136 } else if (PointIsHotspot(pt)) {
5137 DisplayCursor(Window::cursorHand);
5138 SetHotSpotRange(&pt);
5139 } else {
5140 DisplayCursor(Window::cursorText);
5141 SetHotSpotRange(NULL);
5142 }
5143 }
5144 }
5145
5146 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
5147 //Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture());
5148 if (HaveMouseCapture()) {
5149 if (PointInSelMargin(pt)) {
5150 DisplayCursor(Window::cursorReverseArrow);
5151 } else {
5152 DisplayCursor(Window::cursorText);
5153 SetHotSpotRange(NULL);
5154 }
5155 ptMouseLast = pt;
5156 SetMouseCapture(false);
5157 int newPos = PositionFromLocation(pt);
5158 newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
5159 if (inDragDrop) {
5160 int selStart = SelectionStart();
5161 int selEnd = SelectionEnd();
5162 if (selStart < selEnd) {
5163 if (drag.len) {
5164 if (ctrl) {
5165 if (pdoc->InsertString(newPos, drag.s, drag.len)) {
5166 SetSelection(newPos, newPos + drag.len);
5167 }
5168 } else if (newPos < selStart) {
5169 pdoc->DeleteChars(selStart, drag.len);
5170 if (pdoc->InsertString(newPos, drag.s, drag.len)) {
5171 SetSelection(newPos, newPos + drag.len);
5172 }
5173 } else if (newPos > selEnd) {
5174 pdoc->DeleteChars(selStart, drag.len);
5175 newPos -= drag.len;
5176 if (pdoc->InsertString(newPos, drag.s, drag.len)) {
5177 SetSelection(newPos, newPos + drag.len);
5178 }
5179 } else {
5180 SetEmptySelection(newPos);
5181 }
5182 drag.Free();
5183 }
5184 selectionType = selChar;
5185 }
5186 } else {
5187 if (selectionType == selChar) {
5188 SetSelection(newPos);
5189 }
5190 }
5191 // Now we rely on the current pos to compute rectangular selection
5192 xStartSelect = XFromPosition(anchor);
5193 xEndSelect = XFromPosition(currentPos);
5194 lastClickTime = curTime;
5195 lastClick = pt;
5196 lastXChosen = pt.x;
5197 if (selType == selStream) {
5198 SetLastXChosen();
5199 }
5200 inDragDrop = false;
5201 EnsureCaretVisible(false);
5202 }
5203 }
5204
5205 // Called frequently to perform background UI including
5206 // caret blinking and automatic scrolling.
5207 void Editor::Tick() {
5208 if (HaveMouseCapture()) {
5209 // Auto scroll
5210 ButtonMove(ptMouseLast);
5211 }
5212 if (caret.period > 0) {
5213 timer.ticksToWait -= timer.tickSize;
5214 if (timer.ticksToWait <= 0) {
5215 caret.on = !caret.on;
5216 timer.ticksToWait = caret.period;
5217 InvalidateCaret();
5218 }
5219 }
5220 if ((dwellDelay < SC_TIME_FOREVER) &&
5221 (ticksToDwell > 0) &&
5222 (!HaveMouseCapture())) {
5223 ticksToDwell -= timer.tickSize;
5224 if (ticksToDwell <= 0) {
5225 dwelling = true;
5226 NotifyDwelling(ptMouseLast, dwelling);
5227 }
5228 }
5229 }
5230
5231 bool Editor::Idle() {
5232
5233 bool idleDone;
5234
5235 bool wrappingDone = (wrapState == eWrapNone) || (!backgroundWrapEnabled);
5236
5237 if (!wrappingDone) {
5238 // Wrap lines during idle.
5239 WrapLines(false, -1);
5240 // No more wrapping
5241 if (docLineLastWrapped == docLastLineToWrap)
5242 wrappingDone = true;
5243 }
5244
5245 // Add more idle things to do here, but make sure idleDone is
5246 // set correctly before the function returns. returning
5247 // false will stop calling this idle funtion until SetIdle() is
5248 // called again.
5249
5250 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
5251
5252 return !idleDone;
5253 }
5254
5255 void Editor::SetFocusState(bool focusState) {
5256 hasFocus = focusState;
5257 NotifyFocus(hasFocus);
5258 if (hasFocus) {
5259 ShowCaretAtCurrentPosition();
5260 } else {
5261 CancelModes();
5262 DropCaret();
5263 }
5264 }
5265
5266 static bool IsIn(int a, int minimum, int maximum) {
5267 return (a >= minimum) && (a <= maximum);
5268 }
5269
5270 static bool IsOverlap(int mina, int maxa, int minb, int maxb) {
5271 return
5272 IsIn(mina, minb, maxb) ||
5273 IsIn(maxa, minb, maxb) ||
5274 IsIn(minb, mina, maxa) ||
5275 IsIn(maxb, mina, maxa);
5276 }
5277
5278 void Editor::CheckForChangeOutsidePaint(Range r) {
5279 if (paintState == painting && !paintingAllText) {
5280 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5281 if (!r.Valid())
5282 return;
5283
5284 PRectangle rcText = GetTextRectangle();
5285 // Determine number of lines displayed including a possible partially displayed last line
5286 int linesDisplayed = (rcText.bottom - rcText.top - 1) / vs.lineHeight + 1;
5287 int bottomLine = topLine + linesDisplayed - 1;
5288
5289 int lineRangeStart = cs.DisplayFromDoc(pdoc->LineFromPosition(r.start));
5290 int lineRangeEnd = cs.DisplayFromDoc(pdoc->LineFromPosition(r.end));
5291 if (!IsOverlap(topLine, bottomLine, lineRangeStart, lineRangeEnd)) {
5292 //Platform::DebugPrintf("No overlap (%d-%d) with window(%d-%d)\n",
5293 // lineRangeStart, lineRangeEnd, topLine, bottomLine);
5294 return;
5295 }
5296
5297 // Assert rcPaint contained within or equal to rcText
5298 if (rcPaint.top > rcText.top) {
5299 // does range intersect rcText.top .. rcPaint.top
5300 int paintTopLine = ((rcPaint.top - rcText.top - 1) / vs.lineHeight) + topLine;
5301 // paintTopLine is the top line of the paint rectangle or the line just above if that line is completely inside the paint rectangle
5302 if (IsOverlap(topLine, paintTopLine, lineRangeStart, lineRangeEnd)) {
5303 //Platform::DebugPrintf("Change (%d-%d) in top npv(%d-%d)\n",
5304 // lineRangeStart, lineRangeEnd, topLine, paintTopLine);
5305 AbandonPaint();
5306 return;
5307 }
5308 }
5309 if (rcPaint.bottom < rcText.bottom) {
5310 // does range intersect rcPaint.bottom .. rcText.bottom
5311 int paintBottomLine = ((rcPaint.bottom - rcText.top - 1) / vs.lineHeight + 1) + topLine;
5312 // paintTopLine is the bottom line of the paint rectangle or the line just below if that line is completely inside the paint rectangle
5313 if (IsOverlap(paintBottomLine, bottomLine, lineRangeStart, lineRangeEnd)) {
5314 //Platform::DebugPrintf("Change (%d-%d) in bottom npv(%d-%d)\n",
5315 // lineRangeStart, lineRangeEnd, paintBottomLine, bottomLine);
5316 AbandonPaint();
5317 return;
5318 }
5319 }
5320 }
5321 }
5322
5323 char BraceOpposite(char ch) {
5324 switch (ch) {
5325 case '(':
5326 return ')';
5327 case ')':
5328 return '(';
5329 case '[':
5330 return ']';
5331 case ']':
5332 return '[';
5333 case '{':
5334 return '}';
5335 case '}':
5336 return '{';
5337 case '<':
5338 return '>';
5339 case '>':
5340 return '<';
5341 default:
5342 return '\0';
5343 }
5344 }
5345
5346 // TODO: should be able to extend styled region to find matching brace
5347 // TODO: may need to make DBCS safe
5348 // so should be moved into Document
5349 int Editor::BraceMatch(int position, int /*maxReStyle*/) {
5350 char chBrace = pdoc->CharAt(position);
5351 char chSeek = BraceOpposite(chBrace);
5352 if (chSeek == '\0')
5353 return - 1;
5354 char styBrace = static_cast<char>(
5355 pdoc->StyleAt(position) & pdoc->stylingBitsMask);
5356 int direction = -1;
5357 if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
5358 direction = 1;
5359 int depth = 1;
5360 position = position + direction;
5361 while ((position >= 0) && (position < pdoc->Length())) {
5362 char chAtPos = pdoc->CharAt(position);
5363 char styAtPos = static_cast<char>(pdoc->StyleAt(position) & pdoc->stylingBitsMask);
5364 if ((position > pdoc->GetEndStyled()) || (styAtPos == styBrace)) {
5365 if (chAtPos == chBrace)
5366 depth++;
5367 if (chAtPos == chSeek)
5368 depth--;
5369 if (depth == 0)
5370 return position;
5371 }
5372 position = position + direction;
5373 }
5374 return - 1;
5375 }
5376
5377 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
5378 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5379 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5380 CheckForChangeOutsidePaint(Range(braces[0]));
5381 CheckForChangeOutsidePaint(Range(pos0));
5382 braces[0] = pos0;
5383 }
5384 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5385 CheckForChangeOutsidePaint(Range(braces[1]));
5386 CheckForChangeOutsidePaint(Range(pos1));
5387 braces[1] = pos1;
5388 }
5389 bracesMatchStyle = matchStyle;
5390 if (paintState == notPainting) {
5391 Redraw();
5392 }
5393 }
5394 }
5395
5396 void Editor::SetDocPointer(Document *document) {
5397 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5398 pdoc->RemoveWatcher(this, 0);
5399 pdoc->Release();
5400 if (document == NULL) {
5401 pdoc = new Document();
5402 } else {
5403 pdoc = document;
5404 }
5405 pdoc->AddRef();
5406
5407 // Ensure all positions within document
5408 selType = selStream;
5409 currentPos = 0;
5410 anchor = 0;
5411 targetStart = 0;
5412 targetEnd = 0;
5413
5414 braces[0] = invalidPosition;
5415 braces[1] = invalidPosition;
5416
5417 // Reset the contraction state to fully shown.
5418 cs.Clear();
5419 cs.InsertLines(0, pdoc->LinesTotal() - 1);
5420 llc.Deallocate();
5421 NeedWrapping();
5422
5423 pdoc->AddWatcher(this, 0);
5424 Redraw();
5425 SetScrollBars();
5426 }
5427
5428 /**
5429 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5430 */
5431 void Editor::Expand(int &line, bool doExpand) {
5432 int lineMaxSubord = pdoc->GetLastChild(line);
5433 line++;
5434 while (line <= lineMaxSubord) {
5435 if (doExpand)
5436 cs.SetVisible(line, line, true);
5437 int level = pdoc->GetLevel(line);
5438 if (level & SC_FOLDLEVELHEADERFLAG) {
5439 if (doExpand && cs.GetExpanded(line)) {
5440 Expand(line, true);
5441 } else {
5442 Expand(line, false);
5443 }
5444 } else {
5445 line++;
5446 }
5447 }
5448 }
5449
5450 void Editor::ToggleContraction(int line) {
5451 if (line >= 0) {
5452 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
5453 line = pdoc->GetFoldParent(line);
5454 if (line < 0)
5455 return;
5456 }
5457
5458 if (cs.GetExpanded(line)) {
5459 int lineMaxSubord = pdoc->GetLastChild(line);
5460 cs.SetExpanded(line, 0);
5461 if (lineMaxSubord > line) {
5462 cs.SetVisible(line + 1, lineMaxSubord, false);
5463
5464 int lineCurrent = pdoc->LineFromPosition(currentPos);
5465 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5466 // This does not re-expand the fold
5467 EnsureCaretVisible();
5468 }
5469
5470 SetScrollBars();
5471 Redraw();
5472 }
5473
5474 } else {
5475 if (!(cs.GetVisible(line))) {
5476 EnsureLineVisible(line, false);
5477 GoToLine(line);
5478 }
5479 cs.SetExpanded(line, 1);
5480 Expand(line, true);
5481 SetScrollBars();
5482 Redraw();
5483 }
5484 }
5485 }
5486
5487 /**
5488 * Recurse up from this line to find any folds that prevent this line from being visible
5489 * and unfold them all.
5490 */
5491 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
5492
5493 // In case in need of wrapping to ensure DisplayFromDoc works.
5494 WrapLines(true, -1);
5495
5496 if (!cs.GetVisible(lineDoc)) {
5497 int lineParent = pdoc->GetFoldParent(lineDoc);
5498 if (lineParent >= 0) {
5499 if (lineDoc != lineParent)
5500 EnsureLineVisible(lineParent, enforcePolicy);
5501 if (!cs.GetExpanded(lineParent)) {
5502 cs.SetExpanded(lineParent, 1);
5503 Expand(lineParent, true);
5504 }
5505 }
5506 SetScrollBars();
5507 Redraw();
5508 }
5509 if (enforcePolicy) {
5510 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5511 if (visiblePolicy & VISIBLE_SLOP) {
5512 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
5513 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
5514 SetVerticalScrollPos();
5515 Redraw();
5516 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5517 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
5518 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
5519 SetVerticalScrollPos();
5520 Redraw();
5521 }
5522 } else {
5523 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
5524 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5525 SetVerticalScrollPos();
5526 Redraw();
5527 }
5528 }
5529 }
5530 }
5531
5532 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
5533 pdoc->BeginUndoAction();
5534 if (length == -1)
5535 length = istrlen(text);
5536 if (replacePatterns) {
5537 text = pdoc->SubstituteByPosition(text, &length);
5538 if (!text)
5539 return 0;
5540 }
5541 if (targetStart != targetEnd)
5542 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
5543 targetEnd = targetStart;
5544 pdoc->InsertString(targetStart, text, length);
5545 targetEnd = targetStart + length;
5546 pdoc->EndUndoAction();
5547 return length;
5548 }
5549
5550 bool Editor::IsUnicodeMode() const {
5551 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5552 }
5553
5554 int Editor::CodePage() const {
5555 if (pdoc)
5556 return pdoc->dbcsCodePage;
5557 else
5558 return 0;
5559 }
5560
5561 static bool ValidMargin(unsigned long wParam) {
5562 return wParam < ViewStyle::margins;
5563 }
5564
5565 static char *CharPtrFromSPtr(sptr_t lParam) {
5566 return reinterpret_cast<char *>(lParam);
5567 }
5568
5569 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5570 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5571
5572 // Optional macro recording hook
5573 if (recordingMacro)
5574 NotifyMacroRecord(iMessage, wParam, lParam);
5575
5576 switch (iMessage) {
5577
5578 case SCI_GETTEXT: {
5579 if (lParam == 0)
5580 return pdoc->Length() + 1;
5581 if (wParam == 0)
5582 return 0;
5583 char *ptr = CharPtrFromSPtr(lParam);
5584 unsigned int iChar = 0;
5585 for (; iChar < wParam - 1; iChar++)
5586 ptr[iChar] = pdoc->CharAt(iChar);
5587 ptr[iChar] = '\0';
5588 return iChar;
5589 }
5590
5591 case SCI_SETTEXT: {
5592 if (lParam == 0)
5593 return 0;
5594 pdoc->BeginUndoAction();
5595 pdoc->DeleteChars(0, pdoc->Length());
5596 SetEmptySelection(0);
5597 pdoc->InsertString(0, CharPtrFromSPtr(lParam));
5598 pdoc->EndUndoAction();
5599 return 1;
5600 }
5601
5602 case SCI_GETTEXTLENGTH:
5603 return pdoc->Length();
5604
5605 case SCI_CUT:
5606 Cut();
5607 SetLastXChosen();
5608 break;
5609
5610 case SCI_COPY:
5611 Copy();
5612 break;
5613
5614 case SCI_COPYRANGE:
5615 CopyRangeToClipboard(wParam, lParam);
5616 break;
5617
5618 case SCI_COPYTEXT:
5619 CopyText(wParam, CharPtrFromSPtr(lParam));
5620 break;
5621
5622 case SCI_PASTE:
5623 Paste();
5624 SetLastXChosen();
5625 EnsureCaretVisible();
5626 break;
5627
5628 case SCI_CLEAR:
5629 Clear();
5630 SetLastXChosen();
5631 EnsureCaretVisible();
5632 break;
5633
5634 case SCI_UNDO:
5635 Undo();
5636 SetLastXChosen();
5637 break;
5638
5639 case SCI_CANUNDO:
5640 return pdoc->CanUndo() ? 1 : 0;
5641
5642 case SCI_EMPTYUNDOBUFFER:
5643 pdoc->DeleteUndoHistory();
5644 return 0;
5645
5646 case SCI_GETFIRSTVISIBLELINE:
5647 return topLine;
5648
5649 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
5650 int lineStart = pdoc->LineStart(wParam);
5651 int lineEnd = pdoc->LineStart(wParam + 1);
5652 if (lParam == 0) {
5653 return lineEnd - lineStart;
5654 }
5655 char *ptr = CharPtrFromSPtr(lParam);
5656 int iPlace = 0;
5657 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
5658 ptr[iPlace++] = pdoc->CharAt(iChar);
5659 }
5660 return iPlace;
5661 }
5662
5663 case SCI_GETLINECOUNT:
5664 if (pdoc->LinesTotal() == 0)
5665 return 1;
5666 else
5667 return pdoc->LinesTotal();
5668
5669 case SCI_GETMODIFY:
5670 return !pdoc->IsSavePoint();
5671
5672 case SCI_SETSEL: {
5673 int nStart = static_cast<int>(wParam);
5674 int nEnd = static_cast<int>(lParam);
5675 if (nEnd < 0)
5676 nEnd = pdoc->Length();
5677 if (nStart < 0)
5678 nStart = nEnd; // Remove selection
5679 selType = selStream;
5680 SetSelection(nEnd, nStart);
5681 EnsureCaretVisible();
5682 }
5683 break;
5684
5685 case SCI_GETSELTEXT: {
5686 if (lParam == 0) {
5687 if (selType == selStream) {
5688 return 1 + SelectionEnd() - SelectionStart();
5689 } else {
5690 // TODO: why is selLines handled the slow way?
5691 int size = 0;
5692 int extraCharsPerLine = 0;
5693 if (selType != selLines)
5694 extraCharsPerLine = (pdoc->eolMode == SC_EOL_CRLF) ? 2 : 1;
5695 SelectionLineIterator lineIterator(this);
5696 while (lineIterator.Iterate()) {
5697 size += lineIterator.endPos + extraCharsPerLine - lineIterator.startPos;
5698 }
5699
5700 return 1 + size;
5701 }
5702 }
5703 SelectionText selectedText;
5704 CopySelectionRange(&selectedText);
5705 char *ptr = CharPtrFromSPtr(lParam);
5706 int iChar = 0;
5707 if (selectedText.len) {
5708 for (; iChar < selectedText.len; iChar++)
5709 ptr[iChar] = selectedText.s[iChar];
5710 } else {
5711 ptr[0] = '\0';
5712 }
5713 return iChar;
5714 }
5715
5716 case SCI_LINEFROMPOSITION:
5717 if (static_cast<int>(wParam) < 0)
5718 return 0;
5719 return pdoc->LineFromPosition(wParam);
5720
5721 case SCI_POSITIONFROMLINE:
5722 if (static_cast<int>(wParam) < 0)
5723 wParam = pdoc->LineFromPosition(SelectionStart());
5724 if (wParam == 0)
5725 return 0; // Even if there is no text, there is a first line that starts at 0
5726 if (static_cast<int>(wParam) > pdoc->LinesTotal())
5727 return -1;
5728 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
5729 // return -1;
5730 return pdoc->LineStart(wParam);
5731
5732 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
5733 case SCI_LINELENGTH:
5734 if ((static_cast<int>(wParam) < 0) ||
5735 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
5736 return 0;
5737 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
5738
5739 case SCI_REPLACESEL: {
5740 if (lParam == 0)
5741 return 0;
5742 pdoc->BeginUndoAction();
5743 ClearSelection();
5744 char *replacement = CharPtrFromSPtr(lParam);
5745 pdoc->InsertString(currentPos, replacement);
5746 pdoc->EndUndoAction();
5747 SetEmptySelection(currentPos + istrlen(replacement));
5748 EnsureCaretVisible();
5749 }
5750 break;
5751
5752 case SCI_SETTARGETSTART:
5753 targetStart = wParam;
5754 break;
5755
5756 case SCI_GETTARGETSTART:
5757 return targetStart;
5758
5759 case SCI_SETTARGETEND:
5760 targetEnd = wParam;
5761 break;
5762
5763 case SCI_GETTARGETEND:
5764 return targetEnd;
5765
5766 case SCI_TARGETFROMSELECTION:
5767 if (currentPos < anchor) {
5768 targetStart = currentPos;
5769 targetEnd = anchor;
5770 } else {
5771 targetStart = anchor;
5772 targetEnd = currentPos;
5773 }
5774 break;
5775
5776 case SCI_REPLACETARGET:
5777 PLATFORM_ASSERT(lParam);
5778 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
5779
5780 case SCI_REPLACETARGETRE:
5781 PLATFORM_ASSERT(lParam);
5782 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
5783
5784 case SCI_SEARCHINTARGET:
5785 PLATFORM_ASSERT(lParam);
5786 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
5787
5788 case SCI_SETSEARCHFLAGS:
5789 searchFlags = wParam;
5790 break;
5791
5792 case SCI_GETSEARCHFLAGS:
5793 return searchFlags;
5794
5795 case SCI_POSITIONBEFORE:
5796 return pdoc->MovePositionOutsideChar(wParam-1, -1, true);
5797
5798 case SCI_POSITIONAFTER:
5799 return pdoc->MovePositionOutsideChar(wParam+1, 1, true);
5800
5801 case SCI_LINESCROLL:
5802 ScrollTo(topLine + lParam);
5803 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
5804 return 1;
5805
5806 case SCI_SETXOFFSET:
5807 xOffset = wParam;
5808 SetHorizontalScrollPos();
5809 Redraw();
5810 break;
5811
5812 case SCI_GETXOFFSET:
5813 return xOffset;
5814
5815 case SCI_CHOOSECARETX:
5816 SetLastXChosen();
5817 break;
5818
5819 case SCI_SCROLLCARET:
5820 EnsureCaretVisible();
5821 break;
5822
5823 case SCI_SETREADONLY:
5824 pdoc->SetReadOnly(wParam != 0);
5825 return 1;
5826
5827 case SCI_GETREADONLY:
5828 return pdoc->IsReadOnly();
5829
5830 case SCI_CANPASTE:
5831 return CanPaste();
5832
5833 case SCI_POINTXFROMPOSITION:
5834 if (lParam < 0) {
5835 return 0;
5836 } else {
5837 Point pt = LocationFromPosition(lParam);
5838 return pt.x;
5839 }
5840
5841 case SCI_POINTYFROMPOSITION:
5842 if (lParam < 0) {
5843 return 0;
5844 } else {
5845 Point pt = LocationFromPosition(lParam);
5846 return pt.y;
5847 }
5848
5849 case SCI_FINDTEXT:
5850 return FindText(wParam, lParam);
5851
5852 case SCI_GETTEXTRANGE: {
5853 if (lParam == 0)
5854 return 0;
5855 TextRange *tr = reinterpret_cast<TextRange *>(lParam);
5856 int cpMax = tr->chrg.cpMax;
5857 if (cpMax == -1)
5858 cpMax = pdoc->Length();
5859 PLATFORM_ASSERT(cpMax <= pdoc->Length());
5860 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
5861 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
5862 // Spec says copied text is terminated with a NUL
5863 tr->lpstrText[len] = '\0';
5864 return len; // Not including NUL
5865 }
5866
5867 case SCI_HIDESELECTION:
5868 hideSelection = wParam != 0;
5869 Redraw();
5870 break;
5871
5872 case SCI_FORMATRANGE:
5873 return FormatRange(wParam != 0, reinterpret_cast<RangeToFormat *>(lParam));
5874
5875 case SCI_GETMARGINLEFT:
5876 return vs.leftMarginWidth;
5877
5878 case SCI_GETMARGINRIGHT:
5879 return vs.rightMarginWidth;
5880
5881 case SCI_SETMARGINLEFT:
5882 vs.leftMarginWidth = lParam;
5883 InvalidateStyleRedraw();
5884 break;
5885
5886 case SCI_SETMARGINRIGHT:
5887 vs.rightMarginWidth = lParam;
5888 InvalidateStyleRedraw();
5889 break;
5890
5891 // Control specific mesages
5892
5893 case SCI_ADDTEXT: {
5894 if (lParam == 0)
5895 return 0;
5896 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
5897 SetEmptySelection(currentPos + wParam);
5898 return 0;
5899 }
5900
5901 case SCI_ADDSTYLEDTEXT: {
5902 if (lParam == 0)
5903 return 0;
5904 pdoc->InsertStyledString(CurrentPosition() * 2, CharPtrFromSPtr(lParam), wParam);
5905 SetEmptySelection(currentPos + wParam / 2);
5906 return 0;
5907 }
5908
5909 case SCI_INSERTTEXT: {
5910 if (lParam == 0)
5911 return 0;
5912 int insertPos = wParam;
5913 if (static_cast<int>(wParam) == -1)
5914 insertPos = CurrentPosition();
5915 int newCurrent = CurrentPosition();
5916 char *sz = CharPtrFromSPtr(lParam);
5917 pdoc->InsertString(insertPos, sz);
5918 if (newCurrent > insertPos)
5919 newCurrent += istrlen(sz);
5920 SetEmptySelection(newCurrent);
5921 return 0;
5922 }
5923
5924 case SCI_APPENDTEXT:
5925 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
5926 return 0;
5927
5928 case SCI_CLEARALL:
5929 ClearAll();
5930 return 0;
5931
5932 case SCI_CLEARDOCUMENTSTYLE:
5933 ClearDocumentStyle();
5934 return 0;
5935
5936 case SCI_SETUNDOCOLLECTION:
5937 pdoc->SetUndoCollection(wParam != 0);
5938 return 0;
5939
5940 case SCI_GETUNDOCOLLECTION:
5941 return pdoc->IsCollectingUndo();
5942
5943 case SCI_BEGINUNDOACTION:
5944 pdoc->BeginUndoAction();
5945 return 0;
5946
5947 case SCI_ENDUNDOACTION:
5948 pdoc->EndUndoAction();
5949 return 0;
5950
5951 case SCI_GETCARETPERIOD:
5952 return caret.period;
5953
5954 case SCI_SETCARETPERIOD:
5955 caret.period = wParam;
5956 break;
5957
5958 case SCI_SETWORDCHARS: {
5959 pdoc->SetDefaultCharClasses(false);
5960 if (lParam == 0)
5961 return 0;
5962 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), Document::ccWord);
5963 }
5964 break;
5965
5966 case SCI_SETWHITESPACECHARS: {
5967 if (lParam == 0)
5968 return 0;
5969 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), Document::ccSpace);
5970 }
5971 break;
5972
5973 case SCI_SETCHARSDEFAULT:
5974 pdoc->SetDefaultCharClasses(true);
5975 break;
5976
5977 case SCI_GETLENGTH:
5978 return pdoc->Length();
5979
5980 case SCI_ALLOCATE:
5981 pdoc->Allocate(wParam);
5982 break;
5983
5984 case SCI_GETCHARAT:
5985 return pdoc->CharAt(wParam);
5986
5987 case SCI_SETCURRENTPOS:
5988 SetSelection(wParam, anchor);
5989 break;
5990
5991 case SCI_GETCURRENTPOS:
5992 return currentPos;
5993
5994 case SCI_SETANCHOR:
5995 SetSelection(currentPos, wParam);
5996 break;
5997
5998 case SCI_GETANCHOR:
5999 return anchor;
6000
6001 case SCI_SETSELECTIONSTART:
6002 SetSelection(Platform::Maximum(currentPos, wParam), wParam);
6003 break;
6004
6005 case SCI_GETSELECTIONSTART:
6006 return Platform::Minimum(anchor, currentPos);
6007
6008 case SCI_SETSELECTIONEND:
6009 SetSelection(wParam, Platform::Minimum(anchor, wParam));
6010 break;
6011
6012 case SCI_GETSELECTIONEND:
6013 return Platform::Maximum(anchor, currentPos);
6014
6015 case SCI_SETPRINTMAGNIFICATION:
6016 printMagnification = wParam;
6017 break;
6018
6019 case SCI_GETPRINTMAGNIFICATION:
6020 return printMagnification;
6021
6022 case SCI_SETPRINTCOLOURMODE:
6023 printColourMode = wParam;
6024 break;
6025
6026 case SCI_GETPRINTCOLOURMODE:
6027 return printColourMode;
6028
6029 case SCI_SETPRINTWRAPMODE:
6030 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6031 break;
6032
6033 case SCI_GETPRINTWRAPMODE:
6034 return printWrapState;
6035
6036 case SCI_GETSTYLEAT:
6037 if (static_cast<int>(wParam) >= pdoc->Length())
6038 return 0;
6039 else
6040 return pdoc->StyleAt(wParam);
6041
6042 case SCI_REDO:
6043 Redo();
6044 break;
6045
6046 case SCI_SELECTALL:
6047 SelectAll();
6048 break;
6049
6050 case SCI_SETSAVEPOINT:
6051 pdoc->SetSavePoint();
6052 break;
6053
6054 case SCI_GETSTYLEDTEXT: {
6055 if (lParam == 0)
6056 return 0;
6057 TextRange *tr = reinterpret_cast<TextRange *>(lParam);
6058 int iPlace = 0;
6059 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6060 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6061 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6062 }
6063 tr->lpstrText[iPlace] = '\0';
6064 tr->lpstrText[iPlace + 1] = '\0';
6065 return iPlace;
6066 }
6067
6068 case SCI_CANREDO:
6069 return pdoc->CanRedo() ? 1 : 0;
6070
6071 case SCI_MARKERLINEFROMHANDLE:
6072 return pdoc->LineFromHandle(wParam);
6073
6074 case SCI_MARKERDELETEHANDLE:
6075 pdoc->DeleteMarkFromHandle(wParam);
6076 break;
6077
6078 case SCI_GETVIEWWS:
6079 return vs.viewWhitespace;
6080
6081 case SCI_SETVIEWWS:
6082 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6083 Redraw();
6084 break;
6085
6086 case SCI_POSITIONFROMPOINT:
6087 return PositionFromLocation(Point(wParam, lParam));
6088
6089 case SCI_POSITIONFROMPOINTCLOSE:
6090 return PositionFromLocationClose(Point(wParam, lParam));
6091
6092 case SCI_GOTOLINE:
6093 GoToLine(wParam);
6094 break;
6095
6096 case SCI_GOTOPOS:
6097 SetEmptySelection(wParam);
6098 EnsureCaretVisible();
6099 Redraw();
6100 break;
6101
6102 case SCI_GETCURLINE: {
6103 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
6104 int lineStart = pdoc->LineStart(lineCurrentPos);
6105 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6106 if (lParam == 0) {
6107 return 1 + lineEnd - lineStart;
6108 }
6109 char *ptr = CharPtrFromSPtr(lParam);
6110 unsigned int iPlace = 0;
6111 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
6112 ptr[iPlace++] = pdoc->CharAt(iChar);
6113 }
6114 ptr[iPlace] = '\0';
6115 return currentPos - lineStart;
6116 }
6117
6118 case SCI_GETENDSTYLED:
6119 return pdoc->GetEndStyled();
6120
6121 case SCI_GETEOLMODE:
6122 return pdoc->eolMode;
6123
6124 case SCI_SETEOLMODE:
6125 pdoc->eolMode = wParam;
6126 break;
6127
6128 case SCI_STARTSTYLING:
6129 pdoc->StartStyling(wParam, static_cast<char>(lParam));
6130 break;
6131
6132 case SCI_SETSTYLING:
6133 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
6134 break;
6135
6136 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
6137 if (lParam == 0)
6138 return 0;
6139 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
6140 break;
6141
6142 case SCI_SETBUFFEREDDRAW:
6143 bufferedDraw = wParam != 0;
6144 break;
6145
6146 case SCI_GETBUFFEREDDRAW:
6147 return bufferedDraw;
6148
6149 case SCI_GETTWOPHASEDRAW:
6150 return twoPhaseDraw;
6151
6152 case SCI_SETTWOPHASEDRAW:
6153 twoPhaseDraw = wParam != 0;
6154 InvalidateStyleRedraw();
6155 break;
6156
6157 case SCI_SETTABWIDTH:
6158 if (wParam > 0) {
6159 pdoc->tabInChars = wParam;
6160 if (pdoc->indentInChars == 0)
6161 pdoc->actualIndentInChars = pdoc->tabInChars;
6162 }
6163 InvalidateStyleRedraw();
6164 break;
6165
6166 case SCI_GETTABWIDTH:
6167 return pdoc->tabInChars;
6168
6169 case SCI_SETINDENT:
6170 pdoc->indentInChars = wParam;
6171 if (pdoc->indentInChars != 0)
6172 pdoc->actualIndentInChars = pdoc->indentInChars;
6173 else
6174 pdoc->actualIndentInChars = pdoc->tabInChars;
6175 InvalidateStyleRedraw();
6176 break;
6177
6178 case SCI_GETINDENT:
6179 return pdoc->indentInChars;
6180
6181 case SCI_SETUSETABS:
6182 pdoc->useTabs = wParam != 0;
6183 InvalidateStyleRedraw();
6184 break;
6185
6186 case SCI_GETUSETABS:
6187 return pdoc->useTabs;
6188
6189 case SCI_SETLINEINDENTATION:
6190 pdoc->SetLineIndentation(wParam, lParam);
6191 break;
6192
6193 case SCI_GETLINEINDENTATION:
6194 return pdoc->GetLineIndentation(wParam);
6195
6196 case SCI_GETLINEINDENTPOSITION:
6197 return pdoc->GetLineIndentPosition(wParam);
6198
6199 case SCI_SETTABINDENTS:
6200 pdoc->tabIndents = wParam != 0;
6201 break;
6202
6203 case SCI_GETTABINDENTS:
6204 return pdoc->tabIndents;
6205
6206 case SCI_SETBACKSPACEUNINDENTS:
6207 pdoc->backspaceUnindents = wParam != 0;
6208 break;
6209
6210 case SCI_GETBACKSPACEUNINDENTS:
6211 return pdoc->backspaceUnindents;
6212
6213 case SCI_SETMOUSEDWELLTIME:
6214 dwellDelay = wParam;
6215 ticksToDwell = dwellDelay;
6216 break;
6217
6218 case SCI_GETMOUSEDWELLTIME:
6219 return dwellDelay;
6220
6221 case SCI_WORDSTARTPOSITION:
6222 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
6223
6224 case SCI_WORDENDPOSITION:
6225 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
6226
6227 case SCI_SETWRAPMODE:
6228 wrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6229 xOffset = 0;
6230 InvalidateStyleRedraw();
6231 ReconfigureScrollBars();
6232 break;
6233
6234 case SCI_GETWRAPMODE:
6235 return wrapState;
6236
6237 case SCI_SETWRAPVISUALFLAGS:
6238 wrapVisualFlags = wParam;
6239 actualWrapVisualStartIndent = wrapVisualStartIndent;
6240 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
6241 actualWrapVisualStartIndent = 1; // must indent to show start visual
6242 InvalidateStyleRedraw();
6243 ReconfigureScrollBars();
6244 break;
6245
6246 case SCI_GETWRAPVISUALFLAGS:
6247 return wrapVisualFlags;
6248
6249 case SCI_SETWRAPVISUALFLAGSLOCATION:
6250 wrapVisualFlagsLocation = wParam;
6251 InvalidateStyleRedraw();
6252 break;
6253
6254 case SCI_GETWRAPVISUALFLAGSLOCATION:
6255 return wrapVisualFlagsLocation;
6256
6257 case SCI_SETWRAPSTARTINDENT:
6258 wrapVisualStartIndent = wParam;
6259 actualWrapVisualStartIndent = wrapVisualStartIndent;
6260 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
6261 actualWrapVisualStartIndent = 1; // must indent to show start visual
6262 InvalidateStyleRedraw();
6263 ReconfigureScrollBars();
6264 break;
6265
6266 case SCI_GETWRAPSTARTINDENT:
6267 return wrapVisualStartIndent;
6268
6269 case SCI_SETLAYOUTCACHE:
6270 llc.SetLevel(wParam);
6271 break;
6272
6273 case SCI_GETLAYOUTCACHE:
6274 return llc.GetLevel();
6275
6276 case SCI_SETSCROLLWIDTH:
6277 PLATFORM_ASSERT(wParam > 0);
6278 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
6279 scrollWidth = wParam;
6280 SetScrollBars();
6281 }
6282 break;
6283
6284 case SCI_GETSCROLLWIDTH:
6285 return scrollWidth;
6286
6287 case SCI_LINESJOIN:
6288 LinesJoin();
6289 break;
6290
6291 case SCI_LINESSPLIT:
6292 LinesSplit(wParam);
6293 break;
6294
6295 case SCI_TEXTWIDTH:
6296 PLATFORM_ASSERT(wParam <= STYLE_MAX);
6297 PLATFORM_ASSERT(lParam);
6298 return TextWidth(wParam, CharPtrFromSPtr(lParam));
6299
6300 case SCI_TEXTHEIGHT:
6301 return vs.lineHeight;
6302
6303 case SCI_SETENDATLASTLINE:
6304 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6305 if (endAtLastLine != (wParam != 0)) {
6306 endAtLastLine = wParam != 0;
6307 SetScrollBars();
6308 }
6309 break;
6310
6311 case SCI_GETENDATLASTLINE:
6312 return endAtLastLine;
6313
6314 case SCI_GETCOLUMN:
6315 return pdoc->GetColumn(wParam);
6316
6317 case SCI_FINDCOLUMN:
6318 return pdoc->FindColumn(wParam, lParam);
6319
6320 case SCI_SETHSCROLLBAR :
6321 if (horizontalScrollBarVisible != (wParam != 0)) {
6322 horizontalScrollBarVisible = wParam != 0;
6323 SetScrollBars();
6324 ReconfigureScrollBars();
6325 }
6326 break;
6327
6328 case SCI_GETHSCROLLBAR:
6329 return horizontalScrollBarVisible;
6330
6331 case SCI_SETVSCROLLBAR:
6332 if (verticalScrollBarVisible != (wParam != 0)) {
6333 verticalScrollBarVisible = wParam != 0;
6334 SetScrollBars();
6335 ReconfigureScrollBars();
6336 }
6337 break;
6338
6339 case SCI_GETVSCROLLBAR:
6340 return verticalScrollBarVisible;
6341
6342 case SCI_SETINDENTATIONGUIDES:
6343 vs.viewIndentationGuides = wParam != 0;
6344 Redraw();
6345 break;
6346
6347 case SCI_GETINDENTATIONGUIDES:
6348 return vs.viewIndentationGuides;
6349
6350 case SCI_SETHIGHLIGHTGUIDE:
6351 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6352 highlightGuideColumn = wParam;
6353 Redraw();
6354 }
6355 break;
6356
6357 case SCI_GETHIGHLIGHTGUIDE:
6358 return highlightGuideColumn;
6359
6360 case SCI_GETLINEENDPOSITION:
6361 return pdoc->LineEnd(wParam);
6362
6363 case SCI_SETCODEPAGE:
6364 pdoc->dbcsCodePage = wParam;
6365 InvalidateStyleRedraw();
6366 break;
6367
6368 case SCI_GETCODEPAGE:
6369 return pdoc->dbcsCodePage;
6370
6371 case SCI_SETUSEPALETTE:
6372 palette.allowRealization = wParam != 0;
6373 InvalidateStyleRedraw();
6374 break;
6375
6376 case SCI_GETUSEPALETTE:
6377 return palette.allowRealization;
6378
6379 // Marker definition and setting
6380 case SCI_MARKERDEFINE:
6381 if (wParam <= MARKER_MAX)
6382 vs.markers[wParam].markType = lParam;
6383 InvalidateStyleData();
6384 RedrawSelMargin();
6385 break;
6386 case SCI_MARKERSETFORE:
6387 if (wParam <= MARKER_MAX)
6388 vs.markers[wParam].fore.desired = ColourDesired(lParam);
6389 InvalidateStyleData();
6390 RedrawSelMargin();
6391 break;
6392 case SCI_MARKERSETBACK:
6393 if (wParam <= MARKER_MAX)
6394 vs.markers[wParam].back.desired = ColourDesired(lParam);
6395 InvalidateStyleData();
6396 RedrawSelMargin();
6397 break;
6398 case SCI_MARKERADD: {
6399 int markerID = pdoc->AddMark(wParam, lParam);
6400 return markerID;
6401 }
6402
6403 case SCI_MARKERDELETE:
6404 pdoc->DeleteMark(wParam, lParam);
6405 break;
6406
6407 case SCI_MARKERDELETEALL:
6408 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6409 break;
6410
6411 case SCI_MARKERGET:
6412 return pdoc->GetMark(wParam);
6413
6414 case SCI_MARKERNEXT: {
6415 int lt = pdoc->LinesTotal();
6416 for (int iLine = wParam; iLine < lt; iLine++) {
6417 if ((pdoc->GetMark(iLine) & lParam) != 0)
6418 return iLine;
6419 }
6420 }
6421 return -1;
6422
6423 case SCI_MARKERPREVIOUS: {
6424 for (int iLine = wParam; iLine >= 0; iLine--) {
6425 if ((pdoc->GetMark(iLine) & lParam) != 0)
6426 return iLine;
6427 }
6428 }
6429 return -1;
6430
6431 case SCI_MARKERDEFINEPIXMAP:
6432 if (wParam <= MARKER_MAX) {
6433 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6434 };
6435 InvalidateStyleData();
6436 RedrawSelMargin();
6437 break;
6438
6439 case SCI_SETMARGINTYPEN:
6440 if (ValidMargin(wParam)) {
6441 vs.ms[wParam].symbol = (lParam == SC_MARGIN_SYMBOL);
6442 InvalidateStyleRedraw();
6443 }
6444 break;
6445
6446 case SCI_GETMARGINTYPEN:
6447 if (ValidMargin(wParam))
6448 return vs.ms[wParam].symbol ? SC_MARGIN_SYMBOL : SC_MARGIN_NUMBER;
6449 else
6450 return 0;
6451
6452 case SCI_SETMARGINWIDTHN:
6453 if (ValidMargin(wParam)) {
6454 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6455 if (vs.ms[wParam].width != lParam) {
6456 vs.ms[wParam].width = lParam;
6457 InvalidateStyleRedraw();
6458 }
6459 }
6460 break;
6461
6462 case SCI_GETMARGINWIDTHN:
6463 if (ValidMargin(wParam))
6464 return vs.ms[wParam].width;
6465 else
6466 return 0;
6467
6468 case SCI_SETMARGINMASKN:
6469 if (ValidMargin(wParam)) {
6470 vs.ms[wParam].mask = lParam;
6471 InvalidateStyleRedraw();
6472 }
6473 break;
6474
6475 case SCI_GETMARGINMASKN:
6476 if (ValidMargin(wParam))
6477 return vs.ms[wParam].mask;
6478 else
6479 return 0;
6480
6481 case SCI_SETMARGINSENSITIVEN:
6482 if (ValidMargin(wParam)) {
6483 vs.ms[wParam].sensitive = lParam != 0;
6484 InvalidateStyleRedraw();
6485 }
6486 break;
6487
6488 case SCI_GETMARGINSENSITIVEN:
6489 if (ValidMargin(wParam))
6490 return vs.ms[wParam].sensitive ? 1 : 0;
6491 else
6492 return 0;
6493
6494 case SCI_STYLECLEARALL:
6495 vs.ClearStyles();
6496 InvalidateStyleRedraw();
6497 break;
6498
6499 case SCI_STYLESETFORE:
6500 if (wParam <= STYLE_MAX) {
6501 vs.styles[wParam].fore.desired = ColourDesired(lParam);
6502 InvalidateStyleRedraw();
6503 }
6504 break;
6505 case SCI_STYLESETBACK:
6506 if (wParam <= STYLE_MAX) {
6507 vs.styles[wParam].back.desired = ColourDesired(lParam);
6508 InvalidateStyleRedraw();
6509 }
6510 break;
6511 case SCI_STYLESETBOLD:
6512 if (wParam <= STYLE_MAX) {
6513 vs.styles[wParam].bold = lParam != 0;
6514 InvalidateStyleRedraw();
6515 }
6516 break;
6517 case SCI_STYLESETITALIC:
6518 if (wParam <= STYLE_MAX) {
6519 vs.styles[wParam].italic = lParam != 0;
6520 InvalidateStyleRedraw();
6521 }
6522 break;
6523 case SCI_STYLESETEOLFILLED:
6524 if (wParam <= STYLE_MAX) {
6525 vs.styles[wParam].eolFilled = lParam != 0;
6526 InvalidateStyleRedraw();
6527 }
6528 break;
6529 case SCI_STYLESETSIZE:
6530 if (wParam <= STYLE_MAX) {
6531 vs.styles[wParam].size = lParam;
6532 InvalidateStyleRedraw();
6533 }
6534 break;
6535 case SCI_STYLESETFONT:
6536 if (lParam == 0)
6537 return 0;
6538 if (wParam <= STYLE_MAX) {
6539 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
6540 InvalidateStyleRedraw();
6541 }
6542 break;
6543 case SCI_STYLESETUNDERLINE:
6544 if (wParam <= STYLE_MAX) {
6545 vs.styles[wParam].underline = lParam != 0;
6546 InvalidateStyleRedraw();
6547 }
6548 break;
6549 case SCI_STYLESETCASE:
6550 if (wParam <= STYLE_MAX) {
6551 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
6552 InvalidateStyleRedraw();
6553 }
6554 break;
6555 case SCI_STYLESETCHARACTERSET:
6556 if (wParam <= STYLE_MAX) {
6557 vs.styles[wParam].characterSet = lParam;
6558 InvalidateStyleRedraw();
6559 }
6560 break;
6561 case SCI_STYLESETVISIBLE:
6562 if (wParam <= STYLE_MAX) {
6563 vs.styles[wParam].visible = lParam != 0;
6564 InvalidateStyleRedraw();
6565 }
6566 break;
6567 case SCI_STYLESETCHANGEABLE:
6568 if (wParam <= STYLE_MAX) {
6569 vs.styles[wParam].changeable = lParam != 0;
6570 InvalidateStyleRedraw();
6571 }
6572 break;
6573 case SCI_STYLESETHOTSPOT:
6574 if (wParam <= STYLE_MAX) {
6575 vs.styles[wParam].hotspot = lParam != 0;
6576 InvalidateStyleRedraw();
6577 }
6578 break;
6579
6580 case SCI_STYLERESETDEFAULT:
6581 vs.ResetDefaultStyle();
6582 InvalidateStyleRedraw();
6583 break;
6584 case SCI_SETSTYLEBITS:
6585 pdoc->SetStylingBits(wParam);
6586 break;
6587
6588 case SCI_GETSTYLEBITS:
6589 return pdoc->stylingBits;
6590
6591 case SCI_SETLINESTATE:
6592 return pdoc->SetLineState(wParam, lParam);
6593
6594 case SCI_GETLINESTATE:
6595 return pdoc->GetLineState(wParam);
6596
6597 case SCI_GETMAXLINESTATE:
6598 return pdoc->GetMaxLineState();
6599
6600 case SCI_GETCARETLINEVISIBLE:
6601 return vs.showCaretLineBackground;
6602 case SCI_SETCARETLINEVISIBLE:
6603 vs.showCaretLineBackground = wParam != 0;
6604 InvalidateStyleRedraw();
6605 break;
6606 case SCI_GETCARETLINEBACK:
6607 return vs.caretLineBackground.desired.AsLong();
6608 case SCI_SETCARETLINEBACK:
6609 vs.caretLineBackground.desired = wParam;
6610 InvalidateStyleRedraw();
6611 break;
6612
6613 // Folding messages
6614
6615 case SCI_VISIBLEFROMDOCLINE:
6616 return cs.DisplayFromDoc(wParam);
6617
6618 case SCI_DOCLINEFROMVISIBLE:
6619 return cs.DocFromDisplay(wParam);
6620
6621 case SCI_SETFOLDLEVEL: {
6622 int prev = pdoc->SetLevel(wParam, lParam);
6623 if (prev != lParam)
6624 RedrawSelMargin();
6625 return prev;
6626 }
6627
6628 case SCI_GETFOLDLEVEL:
6629 return pdoc->GetLevel(wParam);
6630
6631 case SCI_GETLASTCHILD:
6632 return pdoc->GetLastChild(wParam, lParam);
6633
6634 case SCI_GETFOLDPARENT:
6635 return pdoc->GetFoldParent(wParam);
6636
6637 case SCI_SHOWLINES:
6638 cs.SetVisible(wParam, lParam, true);
6639 SetScrollBars();
6640 Redraw();
6641 break;
6642
6643 case SCI_HIDELINES:
6644 cs.SetVisible(wParam, lParam, false);
6645 SetScrollBars();
6646 Redraw();
6647 break;
6648
6649 case SCI_GETLINEVISIBLE:
6650 return cs.GetVisible(wParam);
6651
6652 case SCI_SETFOLDEXPANDED:
6653 if (cs.SetExpanded(wParam, lParam != 0)) {
6654 RedrawSelMargin();
6655 }
6656 break;
6657
6658 case SCI_GETFOLDEXPANDED:
6659 return cs.GetExpanded(wParam);
6660
6661 case SCI_SETFOLDFLAGS:
6662 foldFlags = wParam;
6663 Redraw();
6664 break;
6665
6666 case SCI_TOGGLEFOLD:
6667 ToggleContraction(wParam);
6668 break;
6669
6670 case SCI_ENSUREVISIBLE:
6671 EnsureLineVisible(wParam, false);
6672 break;
6673
6674 case SCI_ENSUREVISIBLEENFORCEPOLICY:
6675 EnsureLineVisible(wParam, true);
6676 break;
6677
6678 case SCI_SEARCHANCHOR:
6679 SearchAnchor();
6680 break;
6681
6682 case SCI_SEARCHNEXT:
6683 case SCI_SEARCHPREV:
6684 return SearchText(iMessage, wParam, lParam);
6685
6686 #ifdef INCLUDE_DEPRECATED_FEATURES
6687 case SCI_SETCARETPOLICY: // Deprecated
6688 caretXPolicy = caretYPolicy = wParam;
6689 caretXSlop = caretYSlop = lParam;
6690 break;
6691 #endif
6692
6693 case SCI_SETXCARETPOLICY:
6694 caretXPolicy = wParam;
6695 caretXSlop = lParam;
6696 break;
6697
6698 case SCI_SETYCARETPOLICY:
6699 caretYPolicy = wParam;
6700 caretYSlop = lParam;
6701 break;
6702
6703 case SCI_SETVISIBLEPOLICY:
6704 visiblePolicy = wParam;
6705 visibleSlop = lParam;
6706 break;
6707
6708 case SCI_LINESONSCREEN:
6709 return LinesOnScreen();
6710
6711 case SCI_SETSELFORE:
6712 vs.selforeset = wParam != 0;
6713 vs.selforeground.desired = ColourDesired(lParam);
6714 InvalidateStyleRedraw();
6715 break;
6716
6717 case SCI_SETSELBACK:
6718 vs.selbackset = wParam != 0;
6719 vs.selbackground.desired = ColourDesired(lParam);
6720 InvalidateStyleRedraw();
6721 break;
6722
6723 case SCI_SETWHITESPACEFORE:
6724 vs.whitespaceForegroundSet = wParam != 0;
6725 vs.whitespaceForeground.desired = ColourDesired(lParam);
6726 InvalidateStyleRedraw();
6727 break;
6728
6729 case SCI_SETWHITESPACEBACK:
6730 vs.whitespaceBackgroundSet = wParam != 0;
6731 vs.whitespaceBackground.desired = ColourDesired(lParam);
6732 InvalidateStyleRedraw();
6733 break;
6734
6735 case SCI_SETCARETFORE:
6736 vs.caretcolour.desired = ColourDesired(wParam);
6737 InvalidateStyleRedraw();
6738 break;
6739
6740 case SCI_GETCARETFORE:
6741 return vs.caretcolour.desired.AsLong();
6742
6743 case SCI_SETCARETWIDTH:
6744 if (wParam <= 0)
6745 vs.caretWidth = 0;
6746 else if (wParam >= 3)
6747 vs.caretWidth = 3;
6748 else
6749 vs.caretWidth = wParam;
6750 InvalidateStyleRedraw();
6751 break;
6752
6753 case SCI_GETCARETWIDTH:
6754 return vs.caretWidth;
6755
6756 case SCI_ASSIGNCMDKEY:
6757 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
6758 Platform::HighShortFromLong(wParam), lParam);
6759 break;
6760
6761 case SCI_CLEARCMDKEY:
6762 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
6763 Platform::HighShortFromLong(wParam), SCI_NULL);
6764 break;
6765
6766 case SCI_CLEARALLCMDKEYS:
6767 kmap.Clear();
6768 break;
6769
6770 case SCI_INDICSETSTYLE:
6771 if (wParam <= INDIC_MAX) {
6772 vs.indicators[wParam].style = lParam;
6773 InvalidateStyleRedraw();
6774 }
6775 break;
6776
6777 case SCI_INDICGETSTYLE:
6778 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
6779
6780 case SCI_INDICSETFORE:
6781 if (wParam <= INDIC_MAX) {
6782 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
6783 InvalidateStyleRedraw();
6784 }
6785 break;
6786
6787 case SCI_INDICGETFORE:
6788 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
6789
6790 case SCI_LINEDOWN:
6791 case SCI_LINEDOWNEXTEND:
6792 case SCI_PARADOWN:
6793 case SCI_PARADOWNEXTEND:
6794 case SCI_LINEUP:
6795 case SCI_LINEUPEXTEND:
6796 case SCI_PARAUP:
6797 case SCI_PARAUPEXTEND:
6798 case SCI_CHARLEFT:
6799 case SCI_CHARLEFTEXTEND:
6800 case SCI_CHARRIGHT:
6801 case SCI_CHARRIGHTEXTEND:
6802 case SCI_WORDLEFT:
6803 case SCI_WORDLEFTEXTEND:
6804 case SCI_WORDRIGHT:
6805 case SCI_WORDRIGHTEXTEND:
6806 case SCI_WORDLEFTEND:
6807 case SCI_WORDLEFTENDEXTEND:
6808 case SCI_WORDRIGHTEND:
6809 case SCI_WORDRIGHTENDEXTEND:
6810 case SCI_HOME:
6811 case SCI_HOMEEXTEND:
6812 case SCI_LINEEND:
6813 case SCI_LINEENDEXTEND:
6814 case SCI_HOMEWRAP:
6815 case SCI_HOMEWRAPEXTEND:
6816 case SCI_LINEENDWRAP:
6817 case SCI_LINEENDWRAPEXTEND:
6818 case SCI_DOCUMENTSTART:
6819 case SCI_DOCUMENTSTARTEXTEND:
6820 case SCI_DOCUMENTEND:
6821 case SCI_DOCUMENTENDEXTEND:
6822
6823 case SCI_STUTTEREDPAGEUP:
6824 case SCI_STUTTEREDPAGEUPEXTEND:
6825 case SCI_STUTTEREDPAGEDOWN:
6826 case SCI_STUTTEREDPAGEDOWNEXTEND:
6827
6828 case SCI_PAGEUP:
6829 case SCI_PAGEUPEXTEND:
6830 case SCI_PAGEDOWN:
6831 case SCI_PAGEDOWNEXTEND:
6832 case SCI_EDITTOGGLEOVERTYPE:
6833 case SCI_CANCEL:
6834 case SCI_DELETEBACK:
6835 case SCI_TAB:
6836 case SCI_BACKTAB:
6837 case SCI_NEWLINE:
6838 case SCI_FORMFEED:
6839 case SCI_VCHOME:
6840 case SCI_VCHOMEEXTEND:
6841 case SCI_VCHOMEWRAP:
6842 case SCI_VCHOMEWRAPEXTEND:
6843 case SCI_ZOOMIN:
6844 case SCI_ZOOMOUT:
6845 case SCI_DELWORDLEFT:
6846 case SCI_DELWORDRIGHT:
6847 case SCI_DELLINELEFT:
6848 case SCI_DELLINERIGHT:
6849 case SCI_LINECOPY:
6850 case SCI_LINECUT:
6851 case SCI_LINEDELETE:
6852 case SCI_LINETRANSPOSE:
6853 case SCI_LINEDUPLICATE:
6854 case SCI_LOWERCASE:
6855 case SCI_UPPERCASE:
6856 case SCI_LINESCROLLDOWN:
6857 case SCI_LINESCROLLUP:
6858 case SCI_WORDPARTLEFT:
6859 case SCI_WORDPARTLEFTEXTEND:
6860 case SCI_WORDPARTRIGHT:
6861 case SCI_WORDPARTRIGHTEXTEND:
6862 case SCI_DELETEBACKNOTLINE:
6863 case SCI_HOMEDISPLAY:
6864 case SCI_HOMEDISPLAYEXTEND:
6865 case SCI_LINEENDDISPLAY:
6866 case SCI_LINEENDDISPLAYEXTEND:
6867 case SCI_LINEDOWNRECTEXTEND:
6868 case SCI_LINEUPRECTEXTEND:
6869 case SCI_CHARLEFTRECTEXTEND:
6870 case SCI_CHARRIGHTRECTEXTEND:
6871 case SCI_HOMERECTEXTEND:
6872 case SCI_VCHOMERECTEXTEND:
6873 case SCI_LINEENDRECTEXTEND:
6874 case SCI_PAGEUPRECTEXTEND:
6875 case SCI_PAGEDOWNRECTEXTEND:
6876 return KeyCommand(iMessage);
6877
6878 case SCI_BRACEHIGHLIGHT:
6879 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
6880 break;
6881
6882 case SCI_BRACEBADLIGHT:
6883 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
6884 break;
6885
6886 case SCI_BRACEMATCH:
6887 // wParam is position of char to find brace for,
6888 // lParam is maximum amount of text to restyle to find it
6889 return BraceMatch(wParam, lParam);
6890
6891 case SCI_GETVIEWEOL:
6892 return vs.viewEOL;
6893
6894 case SCI_SETVIEWEOL:
6895 vs.viewEOL = wParam != 0;
6896 InvalidateStyleRedraw();
6897 break;
6898
6899 case SCI_SETZOOM:
6900 vs.zoomLevel = wParam;
6901 InvalidateStyleRedraw();
6902 NotifyZoom();
6903 break;
6904
6905 case SCI_GETZOOM:
6906 return vs.zoomLevel;
6907
6908 case SCI_GETEDGECOLUMN:
6909 return theEdge;
6910
6911 case SCI_SETEDGECOLUMN:
6912 theEdge = wParam;
6913 InvalidateStyleRedraw();
6914 break;
6915
6916 case SCI_GETEDGEMODE:
6917 return vs.edgeState;
6918
6919 case SCI_SETEDGEMODE:
6920 vs.edgeState = wParam;
6921 InvalidateStyleRedraw();
6922 break;
6923
6924 case SCI_GETEDGECOLOUR:
6925 return vs.edgecolour.desired.AsLong();
6926
6927 case SCI_SETEDGECOLOUR:
6928 vs.edgecolour.desired = ColourDesired(wParam);
6929 InvalidateStyleRedraw();
6930 break;
6931
6932 case SCI_GETDOCPOINTER:
6933 return reinterpret_cast<sptr_t>(pdoc);
6934
6935 case SCI_SETDOCPOINTER:
6936 CancelModes();
6937 SetDocPointer(reinterpret_cast<Document *>(lParam));
6938 return 0;
6939
6940 case SCI_CREATEDOCUMENT: {
6941 Document *doc = new Document();
6942 if (doc) {
6943 doc->AddRef();
6944 }
6945 return reinterpret_cast<sptr_t>(doc);
6946 }
6947
6948 case SCI_ADDREFDOCUMENT:
6949 (reinterpret_cast<Document *>(lParam))->AddRef();
6950 break;
6951
6952 case SCI_RELEASEDOCUMENT:
6953 (reinterpret_cast<Document *>(lParam))->Release();
6954 break;
6955
6956 case SCI_SETMODEVENTMASK:
6957 modEventMask = wParam;
6958 return 0;
6959
6960 case SCI_GETMODEVENTMASK:
6961 return modEventMask;
6962
6963 case SCI_CONVERTEOLS:
6964 pdoc->ConvertLineEnds(wParam);
6965 SetSelection(currentPos, anchor); // Ensure selection inside document
6966 return 0;
6967
6968 case SCI_SETLENGTHFORENCODE:
6969 lengthForEncode = wParam;
6970 return 0;
6971
6972 case SCI_SELECTIONISRECTANGLE:
6973 return selType == selRectangle ? 1 : 0;
6974
6975 case SCI_SETSELECTIONMODE: {
6976 switch (wParam) {
6977 case SC_SEL_STREAM:
6978 moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
6979 selType = selStream;
6980 break;
6981 case SC_SEL_RECTANGLE:
6982 moveExtendsSelection = !moveExtendsSelection || (selType != selRectangle);
6983 selType = selRectangle;
6984 break;
6985 case SC_SEL_LINES:
6986 moveExtendsSelection = !moveExtendsSelection || (selType != selLines);
6987 selType = selLines;
6988 break;
6989 default:
6990 moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
6991 selType = selStream;
6992 }
6993 InvalidateSelection(currentPos, anchor);
6994 }
6995 case SCI_GETSELECTIONMODE:
6996 switch (selType) {
6997 case selStream:
6998 return SC_SEL_STREAM;
6999 case selRectangle:
7000 return SC_SEL_RECTANGLE;
7001 case selLines:
7002 return SC_SEL_LINES;
7003 default: // ?!
7004 return SC_SEL_STREAM;
7005 }
7006 case SCI_GETLINESELSTARTPOSITION: {
7007 SelectionLineIterator lineIterator(this);
7008 lineIterator.SetAt(wParam);
7009 return lineIterator.startPos;
7010 }
7011 case SCI_GETLINESELENDPOSITION: {
7012 SelectionLineIterator lineIterator(this);
7013 lineIterator.SetAt(wParam);
7014 return lineIterator.endPos;
7015 }
7016
7017 case SCI_SETOVERTYPE:
7018 inOverstrike = wParam != 0;
7019 break;
7020
7021 case SCI_GETOVERTYPE:
7022 return inOverstrike ? 1 : 0;
7023
7024 case SCI_SETFOCUS:
7025 SetFocusState(wParam != 0);
7026 break;
7027
7028 case SCI_GETFOCUS:
7029 return hasFocus;
7030
7031 case SCI_SETSTATUS:
7032 errorStatus = wParam;
7033 break;
7034
7035 case SCI_GETSTATUS:
7036 return errorStatus;
7037
7038 case SCI_SETMOUSEDOWNCAPTURES:
7039 mouseDownCaptures = wParam != 0;
7040 break;
7041
7042 case SCI_GETMOUSEDOWNCAPTURES:
7043 return mouseDownCaptures;
7044
7045 case SCI_SETCURSOR:
7046 cursorMode = wParam;
7047 DisplayCursor(Window::cursorText);
7048 break;
7049
7050 case SCI_GETCURSOR:
7051 return cursorMode;
7052
7053 case SCI_SETCONTROLCHARSYMBOL:
7054 controlCharSymbol = wParam;
7055 break;
7056
7057 case SCI_GETCONTROLCHARSYMBOL:
7058 return controlCharSymbol;
7059
7060 case SCI_STARTRECORD:
7061 recordingMacro = true;
7062 return 0;
7063
7064 case SCI_STOPRECORD:
7065 recordingMacro = false;
7066 return 0;
7067
7068 case SCI_MOVECARETINSIDEVIEW:
7069 MoveCaretInsideView();
7070 break;
7071
7072 case SCI_SETFOLDMARGINCOLOUR:
7073 vs.foldmarginColourSet = wParam != 0;
7074 vs.foldmarginColour.desired = ColourDesired(lParam);
7075 InvalidateStyleRedraw();
7076 break;
7077
7078 case SCI_SETFOLDMARGINHICOLOUR:
7079 vs.foldmarginHighlightColourSet = wParam != 0;
7080 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
7081 InvalidateStyleRedraw();
7082 break;
7083
7084 case SCI_SETHOTSPOTACTIVEFORE:
7085 vs.hotspotForegroundSet = wParam != 0;
7086 vs.hotspotForeground.desired = ColourDesired(lParam);
7087 InvalidateStyleRedraw();
7088 break;
7089
7090 case SCI_SETHOTSPOTACTIVEBACK:
7091 vs.hotspotBackgroundSet = wParam != 0;
7092 vs.hotspotBackground.desired = ColourDesired(lParam);
7093 InvalidateStyleRedraw();
7094 break;
7095
7096 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7097 vs.hotspotUnderline = wParam != 0;
7098 InvalidateStyleRedraw();
7099 break;
7100
7101 case SCI_SETHOTSPOTSINGLELINE:
7102 vs.hotspotSingleLine = wParam != 0;
7103 InvalidateStyleRedraw();
7104 break;
7105
7106 default:
7107 return DefWndProc(iMessage, wParam, lParam);
7108 }
7109 //Platform::DebugPrintf("end wnd proc\n");
7110 return 0l;
7111 }