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