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