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