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