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