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