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