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