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