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