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