]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/Editor.cxx
9cd4309c5332edec75a3b09366f7dbcb7cf3ac88
[wxWidgets.git] / src / stc / scintilla / src / Editor.cxx
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2004 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 <string>
14
15 // With Borland C++ 5.5, including <string> includes Windows.h leading to defining
16 // FindText to FindTextA which makes calls here to Document::FindText fail.
17 #ifdef __BORLANDC__
18 #ifdef FindText
19 #undef FindText
20 #endif
21 #endif
22
23 #include "Platform.h"
24
25 #include "Scintilla.h"
26
27 #include "SplitVector.h"
28 #include "Partitioning.h"
29 #include "RunStyles.h"
30 #include "ContractionState.h"
31 #include "CellBuffer.h"
32 #include "KeyMap.h"
33 #include "Indicator.h"
34 #include "XPM.h"
35 #include "LineMarker.h"
36 #include "Style.h"
37 #include "ViewStyle.h"
38 #include "CharClassify.h"
39 #include "Decoration.h"
40 #include "Document.h"
41 #include "Selection.h"
42 #include "PositionCache.h"
43 #include "Editor.h"
44
45 #ifdef SCI_NAMESPACE
46 using namespace Scintilla;
47 #endif
48
49 /*
50 return whether this modification represents an operation that
51 may reasonably be deferred (not done now OR [possibly] at all)
52 */
53 static bool CanDeferToLastStep(const DocModification& mh) {
54 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
55 return true; // CAN skip
56 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
57 return false; // MUST do
58 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
59 return true; // CAN skip
60 return false; // PRESUMABLY must do
61 }
62
63 static bool CanEliminate(const DocModification& mh) {
64 return
65 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
66 }
67
68 /*
69 return whether this modification represents the FINAL step
70 in a [possibly lengthy] multi-step Undo/Redo sequence
71 */
72 static bool IsLastStep(const DocModification& mh) {
73 return
74 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
75 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
76 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
77 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
78 }
79
80 Caret::Caret() :
81 active(false), on(false), period(500) {}
82
83 Timer::Timer() :
84 ticking(false), ticksToWait(0), tickerID(0) {}
85
86 Idler::Idler() :
87 state(false), idlerID(0) {}
88
89 static inline bool IsControlCharacter(int ch) {
90 // iscntrl returns true for lots of chars > 127 which are displayable
91 return ch >= 0 && ch < ' ';
92 }
93
94 Editor::Editor() {
95 ctrlID = 0;
96
97 stylesValid = false;
98
99 printMagnification = 0;
100 printColourMode = SC_PRINT_NORMAL;
101 printWrapState = eWrapWord;
102 cursorMode = SC_CURSORNORMAL;
103 controlCharSymbol = 0; /* Draw the control characters */
104
105 hasFocus = false;
106 hideSelection = false;
107 inOverstrike = false;
108 errorStatus = 0;
109 mouseDownCaptures = true;
110
111 bufferedDraw = true;
112 twoPhaseDraw = true;
113
114 lastClickTime = 0;
115 dwellDelay = SC_TIME_FOREVER;
116 ticksToDwell = SC_TIME_FOREVER;
117 dwelling = false;
118 ptMouseLast.x = 0;
119 ptMouseLast.y = 0;
120 inDragDrop = ddNone;
121 dropWentOutside = false;
122 posDrag = SelectionPosition(invalidPosition);
123 posDrop = SelectionPosition(invalidPosition);
124 selectionType = selChar;
125
126 lastXChosen = 0;
127 lineAnchor = 0;
128 originalAnchorPos = 0;
129
130 primarySelection = true;
131
132 caretXPolicy = CARET_SLOP | CARET_EVEN;
133 caretXSlop = 50;
134
135 caretYPolicy = CARET_EVEN;
136 caretYSlop = 0;
137
138 searchAnchor = 0;
139
140 xOffset = 0;
141 xCaretMargin = 50;
142 horizontalScrollBarVisible = true;
143 scrollWidth = 2000;
144 trackLineWidth = false;
145 lineWidthMaxSeen = 0;
146 verticalScrollBarVisible = true;
147 endAtLastLine = true;
148 caretSticky = false;
149 multipleSelection = false;
150 additionalSelectionTyping = false;
151 additionalCaretsBlink = true;
152 additionalCaretsVisible = true;
153 virtualSpaceOptions = SCVS_NONE;
154
155 pixmapLine = Surface::Allocate();
156 pixmapSelMargin = Surface::Allocate();
157 pixmapSelPattern = Surface::Allocate();
158 pixmapIndentGuide = Surface::Allocate();
159 pixmapIndentGuideHighlight = Surface::Allocate();
160
161 targetStart = 0;
162 targetEnd = 0;
163 searchFlags = 0;
164
165 topLine = 0;
166 posTopLine = 0;
167
168 lengthForEncode = -1;
169
170 needUpdateUI = true;
171 braces[0] = invalidPosition;
172 braces[1] = invalidPosition;
173 bracesMatchStyle = STYLE_BRACEBAD;
174 highlightGuideColumn = 0;
175
176 theEdge = 0;
177
178 paintState = notPainting;
179
180 modEventMask = SC_MODEVENTMASKALL;
181
182 pdoc = new Document();
183 pdoc->AddRef();
184 pdoc->AddWatcher(this, 0);
185
186 recordingMacro = false;
187 foldFlags = 0;
188
189 wrapState = eWrapNone;
190 wrapWidth = LineLayout::wrapWidthInfinite;
191 wrapStart = wrapLineLarge;
192 wrapEnd = wrapLineLarge;
193 wrapVisualFlags = 0;
194 wrapVisualFlagsLocation = 0;
195 wrapVisualStartIndent = 0;
196 wrapIndentMode = SC_WRAPINDENT_FIXED;
197 wrapAddIndent = 0;
198
199 convertPastes = true;
200
201 hsStart = -1;
202 hsEnd = -1;
203
204 llc.SetLevel(LineLayoutCache::llcCaret);
205 posCache.SetSize(0x400);
206 }
207
208 Editor::~Editor() {
209 pdoc->RemoveWatcher(this, 0);
210 pdoc->Release();
211 pdoc = 0;
212 DropGraphics();
213 delete pixmapLine;
214 delete pixmapSelMargin;
215 delete pixmapSelPattern;
216 delete pixmapIndentGuide;
217 delete pixmapIndentGuideHighlight;
218 }
219
220 void Editor::Finalise() {
221 SetIdle(false);
222 CancelModes();
223 }
224
225 void Editor::DropGraphics() {
226 pixmapLine->Release();
227 pixmapSelMargin->Release();
228 pixmapSelPattern->Release();
229 pixmapIndentGuide->Release();
230 pixmapIndentGuideHighlight->Release();
231 }
232
233 void Editor::InvalidateStyleData() {
234 stylesValid = false;
235 DropGraphics();
236 palette.Release();
237 llc.Invalidate(LineLayout::llInvalid);
238 posCache.Clear();
239 }
240
241 void Editor::InvalidateStyleRedraw() {
242 NeedWrapping();
243 InvalidateStyleData();
244 Redraw();
245 }
246
247 void Editor::RefreshColourPalette(Palette &pal, bool want) {
248 vs.RefreshColourPalette(pal, want);
249 }
250
251 void Editor::RefreshStyleData() {
252 if (!stylesValid) {
253 stylesValid = true;
254 AutoSurface surface(this);
255 if (surface) {
256 vs.Refresh(*surface);
257 RefreshColourPalette(palette, true);
258 palette.Allocate(wMain);
259 RefreshColourPalette(palette, false);
260 }
261 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
262 wrapAddIndent = pdoc->IndentSize() * vs.spaceWidth;
263 } else if (wrapIndentMode == SC_WRAPINDENT_SAME) {
264 wrapAddIndent = 0;
265 } else { //SC_WRAPINDENT_FIXED
266 wrapAddIndent = wrapVisualStartIndent * vs.aveCharWidth;
267 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (wrapAddIndent <= 0))
268 wrapAddIndent = vs.aveCharWidth; // must indent to show start visual
269 }
270 SetScrollBars();
271 SetRectangularRange();
272 }
273 }
274
275 PRectangle Editor::GetClientRectangle() {
276 return wMain.GetClientPosition();
277 }
278
279 PRectangle Editor::GetTextRectangle() {
280 PRectangle rc = GetClientRectangle();
281 rc.left += vs.fixedColumnWidth;
282 rc.right -= vs.rightMarginWidth;
283 return rc;
284 }
285
286 int Editor::LinesOnScreen() {
287 PRectangle rcClient = GetClientRectangle();
288 int htClient = rcClient.bottom - rcClient.top;
289 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
290 return htClient / vs.lineHeight;
291 }
292
293 int Editor::LinesToScroll() {
294 int retVal = LinesOnScreen() - 1;
295 if (retVal < 1)
296 return 1;
297 else
298 return retVal;
299 }
300
301 int Editor::MaxScrollPos() {
302 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
303 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
304 int retVal = cs.LinesDisplayed();
305 if (endAtLastLine) {
306 retVal -= LinesOnScreen();
307 } else {
308 retVal--;
309 }
310 if (retVal < 0) {
311 return 0;
312 } else {
313 return retVal;
314 }
315 }
316
317 const char *ControlCharacterString(unsigned char ch) {
318 const char *reps[] = {
319 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
320 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
321 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
322 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
323 };
324 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
325 return reps[ch];
326 } else {
327 return "BAD";
328 }
329 }
330
331 /**
332 * Convenience class to ensure LineLayout objects are always disposed.
333 */
334 class AutoLineLayout {
335 LineLayoutCache &llc;
336 LineLayout *ll;
337 AutoLineLayout &operator=(const AutoLineLayout &);
338 public:
339 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
340 ~AutoLineLayout() {
341 llc.Dispose(ll);
342 ll = 0;
343 }
344 LineLayout *operator->() const {
345 return ll;
346 }
347 operator LineLayout *() const {
348 return ll;
349 }
350 void Set(LineLayout *ll_) {
351 llc.Dispose(ll);
352 ll = ll_;
353 }
354 };
355
356 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
357 if (sp.Position() < 0) {
358 return SelectionPosition(0);
359 } else if (sp.Position() > pdoc->Length()) {
360 return SelectionPosition(pdoc->Length());
361 } else {
362 // If not at end of line then set offset to 0
363 if (!pdoc->IsLineEndPosition(sp.Position()))
364 sp.SetVirtualSpace(0);
365 return sp;
366 }
367 }
368
369 Point Editor::LocationFromPosition(SelectionPosition pos) {
370 Point pt;
371 RefreshStyleData();
372 if (pos.Position() == INVALID_POSITION)
373 return pt;
374 int line = pdoc->LineFromPosition(pos.Position());
375 int lineVisible = cs.DisplayFromDoc(line);
376 //Platform::DebugPrintf("line=%d\n", line);
377 AutoSurface surface(this);
378 AutoLineLayout ll(llc, RetrieveLineLayout(line));
379 if (surface && ll) {
380 // -1 because of adding in for visible lines in following loop.
381 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
382 pt.x = 0;
383 unsigned int posLineStart = pdoc->LineStart(line);
384 LayoutLine(line, surface, vs, ll, wrapWidth);
385 int posInLine = pos.Position() - posLineStart;
386 // In case of very long line put x at arbitrary large position
387 if (posInLine > ll->maxLineLength) {
388 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
389 }
390
391 for (int subLine = 0; subLine < ll->lines; subLine++) {
392 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
393 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
394 if (ll->wrapIndent != 0) {
395 int lineStart = ll->LineStart(subLine);
396 if (lineStart != 0) // Wrapped
397 pt.x += ll->wrapIndent;
398 }
399 }
400 if (posInLine >= ll->LineStart(subLine)) {
401 pt.y += vs.lineHeight;
402 }
403 }
404 pt.x += vs.fixedColumnWidth - xOffset;
405 }
406 pt.x += pos.VirtualSpace() * static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
407 return pt;
408 }
409
410 Point Editor::LocationFromPosition(int pos) {
411 return LocationFromPosition(SelectionPosition(pos));
412 }
413
414 int Editor::XFromPosition(int pos) {
415 Point pt = LocationFromPosition(pos);
416 return pt.x - vs.fixedColumnWidth + xOffset;
417 }
418
419 int Editor::XFromPosition(SelectionPosition sp) {
420 Point pt = LocationFromPosition(sp);
421 return pt.x - vs.fixedColumnWidth + xOffset;
422 }
423
424 int Editor::LineFromLocation(Point pt) {
425 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
426 }
427
428 void Editor::SetTopLine(int topLineNew) {
429 topLine = topLineNew;
430 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
431 }
432
433 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
434 RefreshStyleData();
435 if (canReturnInvalid) {
436 PRectangle rcClient = GetTextRectangle();
437 if (!rcClient.Contains(pt))
438 return SelectionPosition(INVALID_POSITION);
439 if (pt.x < vs.fixedColumnWidth)
440 return SelectionPosition(INVALID_POSITION);
441 if (pt.y < 0)
442 return SelectionPosition(INVALID_POSITION);
443 }
444 pt.x = pt.x - vs.fixedColumnWidth + xOffset;
445 int visibleLine = pt.y / vs.lineHeight + topLine;
446 if (pt.y < 0) { // Division rounds towards 0
447 visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
448 }
449 if (!canReturnInvalid && (visibleLine < 0))
450 visibleLine = 0;
451 int lineDoc = cs.DocFromDisplay(visibleLine);
452 if (canReturnInvalid && (lineDoc < 0))
453 return SelectionPosition(INVALID_POSITION);
454 if (lineDoc >= pdoc->LinesTotal())
455 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
456 unsigned int posLineStart = pdoc->LineStart(lineDoc);
457 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
458 AutoSurface surface(this);
459 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
460 if (surface && ll) {
461 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
462 int lineStartSet = cs.DisplayFromDoc(lineDoc);
463 int subLine = visibleLine - lineStartSet;
464 if (subLine < ll->lines) {
465 int lineStart = ll->LineStart(subLine);
466 int lineEnd = ll->LineLastVisible(subLine);
467 int subLineStart = ll->positions[lineStart];
468
469 if (ll->wrapIndent != 0) {
470 if (lineStart != 0) // Wrapped
471 pt.x -= ll->wrapIndent;
472 }
473 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
474 while (i < lineEnd) {
475 if (charPosition) {
476 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
477 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
478 }
479 } else {
480 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
481 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
482 }
483 }
484 i++;
485 }
486 if (virtualSpace) {
487 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
488 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
489 spaceWidth;
490 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
491 } else if (canReturnInvalid) {
492 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
493 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
494 }
495 } else {
496 return SelectionPosition(lineEnd + posLineStart);
497 }
498 }
499 if (!canReturnInvalid)
500 return SelectionPosition(ll->numCharsInLine + posLineStart);
501 }
502 return retVal;
503 }
504
505 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
506 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
507 }
508
509 /**
510 * Find the document position corresponding to an x coordinate on a particular document line.
511 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
512 */
513 int Editor::PositionFromLineX(int lineDoc, int x) {
514 RefreshStyleData();
515 if (lineDoc >= pdoc->LinesTotal())
516 return pdoc->Length();
517 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
518 AutoSurface surface(this);
519 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
520 int retVal = 0;
521 if (surface && ll) {
522 unsigned int posLineStart = pdoc->LineStart(lineDoc);
523 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
524 retVal = ll->numCharsBeforeEOL + posLineStart;
525 int subLine = 0;
526 int lineStart = ll->LineStart(subLine);
527 int lineEnd = ll->LineLastVisible(subLine);
528 int subLineStart = ll->positions[lineStart];
529
530 if (ll->wrapIndent != 0) {
531 if (lineStart != 0) // Wrapped
532 x -= ll->wrapIndent;
533 }
534 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
535 while (i < lineEnd) {
536 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
537 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
538 break;
539 }
540 i++;
541 }
542 }
543 return retVal;
544 }
545
546 /**
547 * Find the document position corresponding to an x coordinate on a particular document line.
548 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
549 */
550 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
551 RefreshStyleData();
552 if (lineDoc >= pdoc->LinesTotal())
553 return SelectionPosition(pdoc->Length());
554 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
555 AutoSurface surface(this);
556 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
557 int retVal = 0;
558 if (surface && ll) {
559 unsigned int posLineStart = pdoc->LineStart(lineDoc);
560 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
561 int subLine = 0;
562 int lineStart = ll->LineStart(subLine);
563 int lineEnd = ll->LineLastVisible(subLine);
564 int subLineStart = ll->positions[lineStart];
565
566 if (ll->wrapIndent != 0) {
567 if (lineStart != 0) // Wrapped
568 x -= ll->wrapIndent;
569 }
570 int i = ll->FindBefore(x + subLineStart, lineStart, lineEnd);
571 while (i < lineEnd) {
572 if ((x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
573 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
574 return SelectionPosition(retVal);
575 }
576 i++;
577 }
578 const int spaceWidth = static_cast<int>(vs.styles[ll->EndLineStyle()].spaceWidth);
579 int spaceOffset = (x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
580 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
581 }
582 return SelectionPosition(retVal);
583 }
584
585 /**
586 * If painting then abandon the painting because a wider redraw is needed.
587 * @return true if calling code should stop drawing.
588 */
589 bool Editor::AbandonPaint() {
590 if ((paintState == painting) && !paintingAllText) {
591 paintState = paintAbandoned;
592 }
593 return paintState == paintAbandoned;
594 }
595
596 void Editor::RedrawRect(PRectangle rc) {
597 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
598
599 // Clip the redraw rectangle into the client area
600 PRectangle rcClient = GetClientRectangle();
601 if (rc.top < rcClient.top)
602 rc.top = rcClient.top;
603 if (rc.bottom > rcClient.bottom)
604 rc.bottom = rcClient.bottom;
605 if (rc.left < rcClient.left)
606 rc.left = rcClient.left;
607 if (rc.right > rcClient.right)
608 rc.right = rcClient.right;
609
610 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
611 wMain.InvalidateRectangle(rc);
612 }
613 }
614
615 void Editor::Redraw() {
616 //Platform::DebugPrintf("Redraw all\n");
617 PRectangle rcClient = GetClientRectangle();
618 wMain.InvalidateRectangle(rcClient);
619 //wMain.InvalidateAll();
620 }
621
622 void Editor::RedrawSelMargin(int line) {
623 if (!AbandonPaint()) {
624 if (vs.maskInLine) {
625 Redraw();
626 } else {
627 PRectangle rcSelMargin = GetClientRectangle();
628 rcSelMargin.right = vs.fixedColumnWidth;
629 if (line != -1) {
630 int position = pdoc->LineStart(line);
631 PRectangle rcLine = RectangleFromRange(position, position);
632 rcSelMargin.top = rcLine.top;
633 rcSelMargin.bottom = rcLine.bottom;
634 }
635 wMain.InvalidateRectangle(rcSelMargin);
636 }
637 }
638 }
639
640 PRectangle Editor::RectangleFromRange(int start, int end) {
641 int minPos = start;
642 if (minPos > end)
643 minPos = end;
644 int maxPos = start;
645 if (maxPos < end)
646 maxPos = end;
647 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
648 int lineDocMax = pdoc->LineFromPosition(maxPos);
649 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
650 PRectangle rcClient = GetTextRectangle();
651 PRectangle rc;
652 rc.left = vs.fixedColumnWidth;
653 rc.top = (minLine - topLine) * vs.lineHeight;
654 if (rc.top < 0)
655 rc.top = 0;
656 rc.right = rcClient.right;
657 rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
658 // Ensure PRectangle is within 16 bit space
659 rc.top = Platform::Clamp(rc.top, -32000, 32000);
660 rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
661
662 return rc;
663 }
664
665 void Editor::InvalidateRange(int start, int end) {
666 RedrawRect(RectangleFromRange(start, end));
667 }
668
669 int Editor::CurrentPosition() {
670 return sel.MainCaret();
671 }
672
673 bool Editor::SelectionEmpty() {
674 return sel.Empty();
675 }
676
677 SelectionPosition Editor::SelectionStart() {
678 return sel.RangeMain().Start();
679 }
680
681 SelectionPosition Editor::SelectionEnd() {
682 return sel.RangeMain().End();
683 }
684
685 void Editor::SetRectangularRange() {
686 if (sel.IsRectangular()) {
687 int xAnchor = XFromPosition(sel.Rectangular().anchor);
688 int xCaret = XFromPosition(sel.Rectangular().caret);
689 if (sel.selType == Selection::selThin) {
690 xCaret = xAnchor;
691 }
692 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
693 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
694 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
695 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
696 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
697 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
698 range.ClearVirtualSpace();
699 if (line == lineAnchorRect)
700 sel.SetSelection(range);
701 else
702 sel.AddSelection(range);
703 }
704 }
705 }
706
707 void Editor::ThinRectangularRange() {
708 if (sel.IsRectangular()) {
709 sel.selType = Selection::selThin;
710 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
711 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
712 } else {
713 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
714 }
715 SetRectangularRange();
716 }
717 }
718
719 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
720 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
721 invalidateWholeSelection = true;
722 }
723 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
724 // +1 for lastAffected ensures caret repainted
725 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
726 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
727 if (invalidateWholeSelection) {
728 for (size_t r=0; r<sel.Count(); r++) {
729 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
730 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
731 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
732 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
733 }
734 }
735 needUpdateUI = true;
736 InvalidateRange(firstAffected, lastAffected);
737 }
738
739 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
740 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_),
741 ClampPositionIntoDocument(anchor_));
742 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
743 InvalidateSelection(rangeNew);
744 }
745 sel.RangeMain() = rangeNew;
746 SetRectangularRange();
747 ClaimSelection();
748 }
749
750 void Editor::SetSelection(int currentPos_, int anchor_) {
751 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
752 }
753
754 // Just move the caret on the main selection
755 void Editor::SetSelection(SelectionPosition currentPos_) {
756 currentPos_ = ClampPositionIntoDocument(currentPos_);
757 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
758 InvalidateSelection(SelectionRange(currentPos_));
759 }
760 if (sel.IsRectangular()) {
761 sel.Rectangular() =
762 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
763 SetRectangularRange();
764 } else {
765 sel.RangeMain() =
766 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
767 }
768 ClaimSelection();
769 }
770
771 void Editor::SetSelection(int currentPos_) {
772 SetSelection(SelectionPosition(currentPos_));
773 }
774
775 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
776 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
777 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
778 InvalidateSelection(rangeNew);
779 }
780 sel.Clear();
781 sel.RangeMain() = rangeNew;
782 SetRectangularRange();
783 ClaimSelection();
784
785 }
786
787 void Editor::SetEmptySelection(int currentPos_) {
788 SetEmptySelection(SelectionPosition(currentPos_));
789 }
790
791 bool Editor::RangeContainsProtected(int start, int end) const {
792 if (vs.ProtectionActive()) {
793 if (start > end) {
794 int t = start;
795 start = end;
796 end = t;
797 }
798 int mask = pdoc->stylingBitsMask;
799 for (int pos = start; pos < end; pos++) {
800 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
801 return true;
802 }
803 }
804 return false;
805 }
806
807 bool Editor::SelectionContainsProtected() {
808 for (size_t r=0; r<sel.Count(); r++) {
809 if (RangeContainsProtected(sel.Range(r).Start().Position(),
810 sel.Range(r).End().Position())) {
811 return true;
812 }
813 }
814 return false;
815 }
816
817 /**
818 * Asks document to find a good position and then moves out of any invisible positions.
819 */
820 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
821 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
822 }
823
824 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
825 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
826 if (posMoved != pos.Position())
827 pos.SetPosition(posMoved);
828 if (vs.ProtectionActive()) {
829 int mask = pdoc->stylingBitsMask;
830 if (moveDir > 0) {
831 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
832 while ((pos.Position() < pdoc->Length()) &&
833 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
834 pos.Add(1);
835 }
836 } else if (moveDir < 0) {
837 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
838 while ((pos.Position() > 0) &&
839 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
840 pos.Add(-1);
841 }
842 }
843 }
844 return pos;
845 }
846
847 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
848 int delta = newPos.Position() - sel.MainCaret();
849 newPos = ClampPositionIntoDocument(newPos);
850 newPos = MovePositionOutsideChar(newPos, delta);
851 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
852 // Switching to rectangular
853 SelectionRange rangeMain = sel.RangeMain();
854 sel.Clear();
855 sel.Rectangular() = rangeMain;
856 }
857 if (selt != Selection::noSel) {
858 sel.selType = selt;
859 }
860 if (selt != Selection::noSel || sel.MoveExtends()) {
861 SetSelection(newPos);
862 } else {
863 SetEmptySelection(newPos);
864 }
865 ShowCaretAtCurrentPosition();
866 if (ensureVisible) {
867 EnsureCaretVisible();
868 }
869 return 0;
870 }
871
872 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
873 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
874 }
875
876 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
877 pos = ClampPositionIntoDocument(pos);
878 pos = MovePositionOutsideChar(pos, moveDir);
879 int lineDoc = pdoc->LineFromPosition(pos.Position());
880 if (cs.GetVisible(lineDoc)) {
881 return pos;
882 } else {
883 int lineDisplay = cs.DisplayFromDoc(lineDoc);
884 if (moveDir > 0) {
885 // lineDisplay is already line before fold as lines in fold use display line of line after fold
886 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
887 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
888 } else {
889 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
890 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
891 }
892 }
893 }
894
895 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
896 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
897 }
898
899 Point Editor::PointMainCaret() {
900 return LocationFromPosition(sel.Range(sel.Main()).caret);
901 }
902
903 /**
904 * Choose the x position that the caret will try to stick to
905 * as it moves up and down.
906 */
907 void Editor::SetLastXChosen() {
908 Point pt = PointMainCaret();
909 lastXChosen = pt.x;
910 }
911
912 void Editor::ScrollTo(int line, bool moveThumb) {
913 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
914 if (topLineNew != topLine) {
915 // Try to optimise small scrolls
916 int linesToMove = topLine - topLineNew;
917 SetTopLine(topLineNew);
918 ShowCaretAtCurrentPosition();
919 // Perform redraw rather than scroll if many lines would be redrawn anyway.
920 #ifndef UNDER_CE
921 if ((abs(linesToMove) <= 10) && (paintState == notPainting)) {
922 ScrollText(linesToMove);
923 } else {
924 Redraw();
925 }
926 #else
927 Redraw();
928 #endif
929 if (moveThumb) {
930 SetVerticalScrollPos();
931 }
932 }
933 }
934
935 void Editor::ScrollText(int /* linesToMove */) {
936 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
937 Redraw();
938 }
939
940 void Editor::HorizontalScrollTo(int xPos) {
941 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
942 if (xPos < 0)
943 xPos = 0;
944 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
945 xOffset = xPos;
946 SetHorizontalScrollPos();
947 RedrawRect(GetClientRectangle());
948 }
949 }
950
951 void Editor::MoveCaretInsideView(bool ensureVisible) {
952 PRectangle rcClient = GetTextRectangle();
953 Point pt = PointMainCaret();
954 if (pt.y < rcClient.top) {
955 MovePositionTo(SPositionFromLocation(
956 Point(lastXChosen, rcClient.top)),
957 Selection::noSel, ensureVisible);
958 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
959 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
960 MovePositionTo(SPositionFromLocation(
961 Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
962 Selection::noSel, ensureVisible);
963 }
964 }
965
966 int Editor::DisplayFromPosition(int pos) {
967 int lineDoc = pdoc->LineFromPosition(pos);
968 int lineDisplay = cs.DisplayFromDoc(lineDoc);
969 AutoSurface surface(this);
970 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
971 if (surface && ll) {
972 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
973 unsigned int posLineStart = pdoc->LineStart(lineDoc);
974 int posInLine = pos - posLineStart;
975 lineDisplay--; // To make up for first increment ahead.
976 for (int subLine = 0; subLine < ll->lines; subLine++) {
977 if (posInLine >= ll->LineStart(subLine)) {
978 lineDisplay++;
979 }
980 }
981 }
982 return lineDisplay;
983 }
984
985 /**
986 * Ensure the caret is reasonably visible in context.
987 *
988 Caret policy in SciTE
989
990 If slop is set, we can define a slop value.
991 This value defines an unwanted zone (UZ) where the caret is... unwanted.
992 This zone is defined as a number of pixels near the vertical margins,
993 and as a number of lines near the horizontal margins.
994 By keeping the caret away from the edges, it is seen within its context,
995 so it is likely that the identifier that the caret is on can be completely seen,
996 and that the current line is seen with some of the lines following it which are
997 often dependent on that line.
998
999 If strict is set, the policy is enforced... strictly.
1000 The caret is centred on the display if slop is not set,
1001 and cannot go in the UZ if slop is set.
1002
1003 If jumps is set, the display is moved more energetically
1004 so the caret can move in the same direction longer before the policy is applied again.
1005 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1006
1007 If even is not set, instead of having symmetrical UZs,
1008 the left and bottom UZs are extended up to right and top UZs respectively.
1009 This way, we favour the displaying of useful information: the begining of lines,
1010 where most code reside, and the lines after the caret, eg. the body of a function.
1011
1012 | | | | |
1013 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1014 | | | | | visibility or going into the UZ) display is...
1015 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1016 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1017 0 | 0 | 0 | 1 | Yes | moved by one position
1018 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1019 0 | 0 | 1 | 1 | Yes | centred on the caret
1020 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1021 0 | 1 | - | 1 | No, caret is always centred | -
1022 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1023 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1024 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1025 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1026 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1027 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1028 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1029 */
1030 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1031 //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
1032 PRectangle rcClient = GetTextRectangle();
1033 //int rcClientFullWidth = rcClient.Width();
1034 SelectionPosition posCaret = sel.RangeMain().caret;
1035 if (posDrag.IsValid()) {
1036 posCaret = posDrag;
1037 }
1038 Point pt = LocationFromPosition(posCaret);
1039 Point ptBottomCaret = pt;
1040 ptBottomCaret.y += vs.lineHeight - 1;
1041 int lineCaret = DisplayFromPosition(posCaret.Position());
1042 bool bSlop, bStrict, bJump, bEven;
1043
1044 // Vertical positioning
1045 if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1046 int linesOnScreen = LinesOnScreen();
1047 int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1048 int newTopLine = topLine;
1049 bSlop = (caretYPolicy & CARET_SLOP) != 0;
1050 bStrict = (caretYPolicy & CARET_STRICT) != 0;
1051 bJump = (caretYPolicy & CARET_JUMPS) != 0;
1052 bEven = (caretYPolicy & CARET_EVEN) != 0;
1053
1054 // It should be possible to scroll the window to show the caret,
1055 // but this fails to remove the caret on GTK+
1056 if (bSlop) { // A margin is defined
1057 int yMoveT, yMoveB;
1058 if (bStrict) {
1059 int yMarginT, yMarginB;
1060 if (!useMargin) {
1061 // In drag mode, avoid moves
1062 // otherwise, a double click will select several lines.
1063 yMarginT = yMarginB = 0;
1064 } else {
1065 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1066 // a maximum of slightly less than half the heigth of the text area.
1067 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1068 if (bEven) {
1069 yMarginB = yMarginT;
1070 } else {
1071 yMarginB = linesOnScreen - yMarginT - 1;
1072 }
1073 }
1074 yMoveT = yMarginT;
1075 if (bEven) {
1076 if (bJump) {
1077 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1078 }
1079 yMoveB = yMoveT;
1080 } else {
1081 yMoveB = linesOnScreen - yMoveT - 1;
1082 }
1083 if (lineCaret < topLine + yMarginT) {
1084 // Caret goes too high
1085 newTopLine = lineCaret - yMoveT;
1086 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1087 // Caret goes too low
1088 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1089 }
1090 } else { // Not strict
1091 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1092 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1093 if (bEven) {
1094 yMoveB = yMoveT;
1095 } else {
1096 yMoveB = linesOnScreen - yMoveT - 1;
1097 }
1098 if (lineCaret < topLine) {
1099 // Caret goes too high
1100 newTopLine = lineCaret - yMoveT;
1101 } else if (lineCaret > topLine + linesOnScreen - 1) {
1102 // Caret goes too low
1103 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1104 }
1105 }
1106 } else { // No slop
1107 if (!bStrict && !bJump) {
1108 // Minimal move
1109 if (lineCaret < topLine) {
1110 // Caret goes too high
1111 newTopLine = lineCaret;
1112 } else if (lineCaret > topLine + linesOnScreen - 1) {
1113 // Caret goes too low
1114 if (bEven) {
1115 newTopLine = lineCaret - linesOnScreen + 1;
1116 } else {
1117 newTopLine = lineCaret;
1118 }
1119 }
1120 } else { // Strict or going out of display
1121 if (bEven) {
1122 // Always center caret
1123 newTopLine = lineCaret - halfScreen;
1124 } else {
1125 // Always put caret on top of display
1126 newTopLine = lineCaret;
1127 }
1128 }
1129 }
1130 newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos());
1131 if (newTopLine != topLine) {
1132 Redraw();
1133 SetTopLine(newTopLine);
1134 SetVerticalScrollPos();
1135 }
1136 }
1137
1138 // Horizontal positioning
1139 if (horiz && (wrapState == eWrapNone)) {
1140 int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1141 int xOffsetNew = xOffset;
1142 bSlop = (caretXPolicy & CARET_SLOP) != 0;
1143 bStrict = (caretXPolicy & CARET_STRICT) != 0;
1144 bJump = (caretXPolicy & CARET_JUMPS) != 0;
1145 bEven = (caretXPolicy & CARET_EVEN) != 0;
1146
1147 if (bSlop) { // A margin is defined
1148 int xMoveL, xMoveR;
1149 if (bStrict) {
1150 int xMarginL, xMarginR;
1151 if (!useMargin) {
1152 // In drag mode, avoid moves unless very near of the margin
1153 // otherwise, a simple click will select text.
1154 xMarginL = xMarginR = 2;
1155 } else {
1156 // xMargin must equal to caretXSlop, with a minimum of 2 and
1157 // a maximum of slightly less than half the width of the text area.
1158 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1159 if (bEven) {
1160 xMarginL = xMarginR;
1161 } else {
1162 xMarginL = rcClient.Width() - xMarginR - 4;
1163 }
1164 }
1165 if (bJump && bEven) {
1166 // Jump is used only in even mode
1167 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1168 } else {
1169 xMoveL = xMoveR = 0; // Not used, avoid a warning
1170 }
1171 if (pt.x < rcClient.left + xMarginL) {
1172 // Caret is on the left of the display
1173 if (bJump && bEven) {
1174 xOffsetNew -= xMoveL;
1175 } else {
1176 // Move just enough to allow to display the caret
1177 xOffsetNew -= (rcClient.left + xMarginL) - pt.x;
1178 }
1179 } else if (pt.x >= rcClient.right - xMarginR) {
1180 // Caret is on the right of the display
1181 if (bJump && bEven) {
1182 xOffsetNew += xMoveR;
1183 } else {
1184 // Move just enough to allow to display the caret
1185 xOffsetNew += pt.x - (rcClient.right - xMarginR) + 1;
1186 }
1187 }
1188 } else { // Not strict
1189 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1190 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1191 if (bEven) {
1192 xMoveL = xMoveR;
1193 } else {
1194 xMoveL = rcClient.Width() - xMoveR - 4;
1195 }
1196 if (pt.x < rcClient.left) {
1197 // Caret is on the left of the display
1198 xOffsetNew -= xMoveL;
1199 } else if (pt.x >= rcClient.right) {
1200 // Caret is on the right of the display
1201 xOffsetNew += xMoveR;
1202 }
1203 }
1204 } else { // No slop
1205 if (bStrict ||
1206 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1207 // Strict or going out of display
1208 if (bEven) {
1209 // Center caret
1210 xOffsetNew += pt.x - rcClient.left - halfScreen;
1211 } else {
1212 // Put caret on right
1213 xOffsetNew += pt.x - rcClient.right + 1;
1214 }
1215 } else {
1216 // Move just enough to allow to display the caret
1217 if (pt.x < rcClient.left) {
1218 // Caret is on the left of the display
1219 if (bEven) {
1220 xOffsetNew -= rcClient.left - pt.x;
1221 } else {
1222 xOffsetNew += pt.x - rcClient.right + 1;
1223 }
1224 } else if (pt.x >= rcClient.right) {
1225 // Caret is on the right of the display
1226 xOffsetNew += pt.x - rcClient.right + 1;
1227 }
1228 }
1229 }
1230 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1231 if (pt.x + xOffset < rcClient.left + xOffsetNew) {
1232 xOffsetNew = pt.x + xOffset - rcClient.left;
1233 } else if (pt.x + xOffset >= rcClient.right + xOffsetNew) {
1234 xOffsetNew = pt.x + xOffset - rcClient.right + 1;
1235 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1236 // Ensure we can see a good portion of the block caret
1237 xOffsetNew += vs.aveCharWidth;
1238 }
1239 }
1240 if (xOffsetNew < 0) {
1241 xOffsetNew = 0;
1242 }
1243 if (xOffset != xOffsetNew) {
1244 xOffset = xOffsetNew;
1245 if (xOffsetNew > 0) {
1246 PRectangle rcText = GetTextRectangle();
1247 if (horizontalScrollBarVisible &&
1248 rcText.Width() + xOffset > scrollWidth) {
1249 scrollWidth = xOffset + rcText.Width();
1250 SetScrollBars();
1251 }
1252 }
1253 SetHorizontalScrollPos();
1254 Redraw();
1255 }
1256 }
1257 UpdateSystemCaret();
1258 }
1259
1260 void Editor::ShowCaretAtCurrentPosition() {
1261 if (hasFocus) {
1262 caret.active = true;
1263 caret.on = true;
1264 SetTicking(true);
1265 } else {
1266 caret.active = false;
1267 caret.on = false;
1268 }
1269 InvalidateCaret();
1270 }
1271
1272 void Editor::DropCaret() {
1273 caret.active = false;
1274 InvalidateCaret();
1275 }
1276
1277 void Editor::InvalidateCaret() {
1278 if (posDrag.IsValid()) {
1279 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1280 } else {
1281 for (size_t r=0; r<sel.Count(); r++) {
1282 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1283 }
1284 }
1285 UpdateSystemCaret();
1286 }
1287
1288 void Editor::UpdateSystemCaret() {
1289 }
1290
1291 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1292 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1293 if (wrapStart > docLineStart) {
1294 wrapStart = docLineStart;
1295 llc.Invalidate(LineLayout::llPositions);
1296 }
1297 if (wrapEnd < docLineEnd) {
1298 wrapEnd = docLineEnd;
1299 }
1300 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1301 // Wrap lines during idle.
1302 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1303 SetIdle(true);
1304 }
1305 }
1306
1307 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1308 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1309 int linesWrapped = 1;
1310 if (ll) {
1311 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1312 linesWrapped = ll->lines;
1313 }
1314 return cs.SetHeight(lineToWrap, linesWrapped +
1315 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1316 }
1317
1318 // Check if wrapping needed and perform any needed wrapping.
1319 // fullwrap: if true, all lines which need wrapping will be done,
1320 // in this single call.
1321 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1322 // here to 1 page + 100 lines past will be wrapped (even if there are
1323 // more lines under wrapping process in idle).
1324 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1325 // wrapped, if there are any wrapping going on in idle. (Generally this
1326 // condition is called only from idler).
1327 // Return true if wrapping occurred.
1328 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1329 // If there are any pending wraps, do them during idle if possible.
1330 int linesInOneCall = LinesOnScreen() + 100;
1331 if (wrapState != eWrapNone) {
1332 if (wrapStart < wrapEnd) {
1333 if (!SetIdle(true)) {
1334 // Idle processing not supported so full wrap required.
1335 fullWrap = true;
1336 }
1337 }
1338 if (!fullWrap && priorityWrapLineStart >= 0 &&
1339 // .. and if the paint window is outside pending wraps
1340 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1341 (priorityWrapLineStart > wrapEnd))) {
1342 // No priority wrap pending
1343 return false;
1344 }
1345 }
1346 int goodTopLine = topLine;
1347 bool wrapOccurred = false;
1348 if (wrapStart <= pdoc->LinesTotal()) {
1349 if (wrapState == eWrapNone) {
1350 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1351 wrapWidth = LineLayout::wrapWidthInfinite;
1352 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1353 cs.SetHeight(lineDoc, 1 +
1354 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1355 }
1356 wrapOccurred = true;
1357 }
1358 wrapStart = wrapLineLarge;
1359 wrapEnd = wrapLineLarge;
1360 } else {
1361 if (wrapEnd >= pdoc->LinesTotal())
1362 wrapEnd = pdoc->LinesTotal();
1363 //ElapsedTime et;
1364 int lineDocTop = cs.DocFromDisplay(topLine);
1365 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1366 PRectangle rcTextArea = GetClientRectangle();
1367 rcTextArea.left = vs.fixedColumnWidth;
1368 rcTextArea.right -= vs.rightMarginWidth;
1369 wrapWidth = rcTextArea.Width();
1370 // Ensure all of the document is styled.
1371 pdoc->EnsureStyledTo(pdoc->Length());
1372 RefreshStyleData();
1373 AutoSurface surface(this);
1374 if (surface) {
1375 bool priorityWrap = false;
1376 int lastLineToWrap = wrapEnd;
1377 int lineToWrap = wrapStart;
1378 if (!fullWrap) {
1379 if (priorityWrapLineStart >= 0) {
1380 // This is a priority wrap.
1381 lineToWrap = priorityWrapLineStart;
1382 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1383 priorityWrap = true;
1384 } else {
1385 // This is idle wrap.
1386 lastLineToWrap = wrapStart + linesInOneCall;
1387 }
1388 if (lastLineToWrap >= wrapEnd)
1389 lastLineToWrap = wrapEnd;
1390 } // else do a fullWrap.
1391
1392 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1393 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1394 while (lineToWrap < lastLineToWrap) {
1395 if (WrapOneLine(surface, lineToWrap)) {
1396 wrapOccurred = true;
1397 }
1398 lineToWrap++;
1399 }
1400 if (!priorityWrap)
1401 wrapStart = lineToWrap;
1402 // If wrapping is done, bring it to resting position
1403 if (wrapStart >= wrapEnd) {
1404 wrapStart = wrapLineLarge;
1405 wrapEnd = wrapLineLarge;
1406 }
1407 }
1408 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1409 if (subLineTop < cs.GetHeight(lineDocTop))
1410 goodTopLine += subLineTop;
1411 else
1412 goodTopLine += cs.GetHeight(lineDocTop);
1413 //double durWrap = et.Duration(true);
1414 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1415 }
1416 }
1417 if (wrapOccurred) {
1418 SetScrollBars();
1419 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1420 SetVerticalScrollPos();
1421 }
1422 return wrapOccurred;
1423 }
1424
1425 void Editor::LinesJoin() {
1426 if (!RangeContainsProtected(targetStart, targetEnd)) {
1427 UndoGroup ug(pdoc);
1428 bool prevNonWS = true;
1429 for (int pos = targetStart; pos < targetEnd; pos++) {
1430 if (IsEOLChar(pdoc->CharAt(pos))) {
1431 targetEnd -= pdoc->LenChar(pos);
1432 pdoc->DelChar(pos);
1433 if (prevNonWS) {
1434 // Ensure at least one space separating previous lines
1435 pdoc->InsertChar(pos, ' ');
1436 targetEnd++;
1437 }
1438 } else {
1439 prevNonWS = pdoc->CharAt(pos) != ' ';
1440 }
1441 }
1442 }
1443 }
1444
1445 const char *Editor::StringFromEOLMode(int eolMode) {
1446 if (eolMode == SC_EOL_CRLF) {
1447 return "\r\n";
1448 } else if (eolMode == SC_EOL_CR) {
1449 return "\r";
1450 } else {
1451 return "\n";
1452 }
1453 }
1454
1455 void Editor::LinesSplit(int pixelWidth) {
1456 if (!RangeContainsProtected(targetStart, targetEnd)) {
1457 if (pixelWidth == 0) {
1458 PRectangle rcText = GetTextRectangle();
1459 pixelWidth = rcText.Width();
1460 }
1461 int lineStart = pdoc->LineFromPosition(targetStart);
1462 int lineEnd = pdoc->LineFromPosition(targetEnd);
1463 const char *eol = StringFromEOLMode(pdoc->eolMode);
1464 UndoGroup ug(pdoc);
1465 for (int line = lineStart; line <= lineEnd; line++) {
1466 AutoSurface surface(this);
1467 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1468 if (surface && ll) {
1469 unsigned int posLineStart = pdoc->LineStart(line);
1470 LayoutLine(line, surface, vs, ll, pixelWidth);
1471 for (int subLine = 1; subLine < ll->lines; subLine++) {
1472 pdoc->InsertCString(posLineStart + (subLine - 1) * strlen(eol) +
1473 ll->LineStart(subLine), eol);
1474 targetEnd += static_cast<int>(strlen(eol));
1475 }
1476 }
1477 lineEnd = pdoc->LineFromPosition(targetEnd);
1478 }
1479 }
1480 }
1481
1482 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1483 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1484 return markerDefault;
1485 return markerCheck;
1486 }
1487
1488 // Avoid 64 bit compiler warnings.
1489 // Scintilla does not support text buffers larger than 2**31
1490 static int istrlen(const char *s) {
1491 return static_cast<int>(strlen(s));
1492 }
1493
1494 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1495 if (st.multipleStyles) {
1496 for (size_t iStyle=0;iStyle<st.length; iStyle++) {
1497 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1498 return false;
1499 }
1500 } else {
1501 if (!vs.ValidStyle(styleOffset + st.style))
1502 return false;
1503 }
1504 return true;
1505 }
1506
1507 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1508 const char *text, const unsigned char *styles, size_t len) {
1509 int width = 0;
1510 size_t start = 0;
1511 while (start < len) {
1512 size_t style = styles[start];
1513 size_t endSegment = start;
1514 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1515 endSegment++;
1516 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start, endSegment - start + 1);
1517 start = endSegment + 1;
1518 }
1519 return width;
1520 }
1521
1522 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1523 int widthMax = 0;
1524 size_t start = 0;
1525 while (start < st.length) {
1526 size_t lenLine = st.LineLength(start);
1527 int widthSubLine;
1528 if (st.multipleStyles) {
1529 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1530 } else {
1531 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font, st.text + start, lenLine);
1532 }
1533 if (widthSubLine > widthMax)
1534 widthMax = widthSubLine;
1535 start += lenLine + 1;
1536 }
1537 return widthMax;
1538 }
1539
1540 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1541 const StyledText &st, size_t start, size_t length) {
1542
1543 if (st.multipleStyles) {
1544 int x = rcText.left;
1545 size_t i = 0;
1546 while (i < length) {
1547 size_t end = i;
1548 int style = st.styles[i + start];
1549 while (end < length-1 && st.styles[start+end+1] == style)
1550 end++;
1551 style += styleOffset;
1552 int width = surface->WidthText(vs.styles[style].font, st.text + start + i, end - i + 1);
1553 PRectangle rcSegment = rcText;
1554 rcSegment.left = x;
1555 rcSegment.right = x + width + 1;
1556 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1557 ascent, st.text + start + i, end - i + 1,
1558 vs.styles[style].fore.allocated,
1559 vs.styles[style].back.allocated);
1560 x += width;
1561 i = end + 1;
1562 }
1563 } else {
1564 int style = st.style + styleOffset;
1565 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1566 rcText.top + vs.maxAscent, st.text + start, length,
1567 vs.styles[style].fore.allocated,
1568 vs.styles[style].back.allocated);
1569 }
1570 }
1571
1572 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1573 if (vs.fixedColumnWidth == 0)
1574 return;
1575
1576 PRectangle rcMargin = GetClientRectangle();
1577 rcMargin.right = vs.fixedColumnWidth;
1578
1579 if (!rc.Intersects(rcMargin))
1580 return;
1581
1582 Surface *surface;
1583 if (bufferedDraw) {
1584 surface = pixmapSelMargin;
1585 } else {
1586 surface = surfWindow;
1587 }
1588
1589 PRectangle rcSelMargin = rcMargin;
1590 rcSelMargin.right = rcMargin.left;
1591
1592 for (int margin = 0; margin < vs.margins; margin++) {
1593 if (vs.ms[margin].width > 0) {
1594
1595 rcSelMargin.left = rcSelMargin.right;
1596 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1597
1598 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1599 /* alternate scheme:
1600 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1601 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1602 else
1603 // Required because of special way brush is created for selection margin
1604 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
1605 */
1606 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1607 // Required because of special way brush is created for selection margin
1608 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1609 else {
1610 ColourAllocated colour;
1611 switch (vs.ms[margin].style) {
1612 case SC_MARGIN_BACK:
1613 colour = vs.styles[STYLE_DEFAULT].back.allocated;
1614 break;
1615 case SC_MARGIN_FORE:
1616 colour = vs.styles[STYLE_DEFAULT].fore.allocated;
1617 break;
1618 default:
1619 colour = vs.styles[STYLE_LINENUMBER].back.allocated;
1620 break;
1621 }
1622 surface->FillRectangle(rcSelMargin, colour);
1623 }
1624 } else {
1625 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
1626 }
1627
1628 int visibleLine = topLine;
1629 int yposScreen = 0;
1630
1631 // Work out whether the top line is whitespace located after a
1632 // lessening of fold level which implies a 'fold tail' but which should not
1633 // be displayed until the last of a sequence of whitespace.
1634 bool needWhiteClosure = false;
1635 int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
1636 if (level & SC_FOLDLEVELWHITEFLAG) {
1637 int lineBack = cs.DocFromDisplay(topLine);
1638 int levelPrev = level;
1639 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1640 lineBack--;
1641 levelPrev = pdoc->GetLevel(lineBack);
1642 }
1643 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1644 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1645 needWhiteClosure = true;
1646 }
1647 }
1648
1649 // Old code does not know about new markers needed to distinguish all cases
1650 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1651 SC_MARKNUM_FOLDEROPEN);
1652 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1653 SC_MARKNUM_FOLDER);
1654
1655 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1656
1657 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1658
1659 int lineDoc = cs.DocFromDisplay(visibleLine);
1660 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1661 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1662
1663 // Decide which fold indicator should be displayed
1664 level = pdoc->GetLevel(lineDoc);
1665 int levelNext = pdoc->GetLevel(lineDoc + 1);
1666 int marks = pdoc->GetMark(lineDoc);
1667 if (!firstSubLine)
1668 marks = 0;
1669 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1670 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1671 if (level & SC_FOLDLEVELHEADERFLAG) {
1672 if (firstSubLine) {
1673 if (cs.GetExpanded(lineDoc)) {
1674 if (levelNum == SC_FOLDLEVELBASE)
1675 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1676 else
1677 marks |= 1 << folderOpenMid;
1678 } else {
1679 if (levelNum == SC_FOLDLEVELBASE)
1680 marks |= 1 << SC_MARKNUM_FOLDER;
1681 else
1682 marks |= 1 << folderEnd;
1683 }
1684 } else {
1685 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1686 }
1687 needWhiteClosure = false;
1688 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1689 if (needWhiteClosure) {
1690 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1691 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1692 } else if (levelNum > SC_FOLDLEVELBASE) {
1693 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1694 needWhiteClosure = false;
1695 } else {
1696 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1697 needWhiteClosure = false;
1698 }
1699 } else if (levelNum > SC_FOLDLEVELBASE) {
1700 if (levelNextNum < levelNum) {
1701 if (levelNextNum > SC_FOLDLEVELBASE) {
1702 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1703 } else {
1704 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1705 }
1706 } else {
1707 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1708 }
1709 }
1710 } else if (levelNum > SC_FOLDLEVELBASE) {
1711 if (levelNextNum < levelNum) {
1712 needWhiteClosure = false;
1713 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1714 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1715 needWhiteClosure = true;
1716 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1717 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1718 } else {
1719 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1720 }
1721 } else {
1722 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1723 }
1724 }
1725
1726 marks &= vs.ms[margin].mask;
1727 PRectangle rcMarker = rcSelMargin;
1728 rcMarker.top = yposScreen;
1729 rcMarker.bottom = yposScreen + vs.lineHeight;
1730 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1731 char number[100];
1732 number[0] = '\0';
1733 if (firstSubLine)
1734 sprintf(number, "%d", lineDoc + 1);
1735 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1736 int lev = pdoc->GetLevel(lineDoc);
1737 sprintf(number, "%c%c %03X %03X",
1738 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1739 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1740 lev & SC_FOLDLEVELNUMBERMASK,
1741 lev >> 16
1742 );
1743 }
1744 PRectangle rcNumber = rcMarker;
1745 // Right justify
1746 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1747 int xpos = rcNumber.right - width - 3;
1748 rcNumber.left = xpos;
1749 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1750 rcNumber.top + vs.maxAscent, number, istrlen(number),
1751 vs.styles[STYLE_LINENUMBER].fore.allocated,
1752 vs.styles[STYLE_LINENUMBER].back.allocated);
1753 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1754 if (firstSubLine) {
1755 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
1756 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1757 surface->FillRectangle(rcMarker,
1758 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back.allocated);
1759 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1760 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1761 rcMarker.left = rcMarker.right - width - 3;
1762 }
1763 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1764 stMargin, 0, stMargin.length);
1765 }
1766 }
1767 }
1768
1769 if (marks) {
1770 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1771 if (marks & 1) {
1772 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font);
1773 }
1774 marks >>= 1;
1775 }
1776 }
1777
1778 visibleLine++;
1779 yposScreen += vs.lineHeight;
1780 }
1781 }
1782 }
1783
1784 PRectangle rcBlankMargin = rcMargin;
1785 rcBlankMargin.left = rcSelMargin.right;
1786 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1787
1788 if (bufferedDraw) {
1789 surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
1790 }
1791 }
1792
1793 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
1794 int ydiff = (rcTab.bottom - rcTab.top) / 2;
1795 int xhead = rcTab.right - 1 - ydiff;
1796 if (xhead <= rcTab.left) {
1797 ydiff -= rcTab.left - xhead - 1;
1798 xhead = rcTab.left - 1;
1799 }
1800 if ((rcTab.left + 2) < (rcTab.right - 1))
1801 surface->MoveTo(rcTab.left + 2, ymid);
1802 else
1803 surface->MoveTo(rcTab.right - 1, ymid);
1804 surface->LineTo(rcTab.right - 1, ymid);
1805 surface->LineTo(xhead, ymid - ydiff);
1806 surface->MoveTo(rcTab.right - 1, ymid);
1807 surface->LineTo(xhead, ymid + ydiff);
1808 }
1809
1810 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
1811 int posLineStart = pdoc->LineStart(lineNumber);
1812 int posLineEnd = pdoc->LineStart(lineNumber + 1);
1813 PLATFORM_ASSERT(posLineEnd >= posLineStart);
1814 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
1815 return llc.Retrieve(lineNumber, lineCaret,
1816 posLineEnd - posLineStart, pdoc->GetStyleClock(),
1817 LinesOnScreen() + 1, pdoc->LinesTotal());
1818 }
1819
1820 static bool GoodTrailByte(int v) {
1821 return (v >= 0x80) && (v < 0xc0);
1822 }
1823
1824 bool BadUTF(const char *s, int len, int &trailBytes) {
1825 if (trailBytes) {
1826 trailBytes--;
1827 return false;
1828 }
1829 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1830 if (*us < 0x80) {
1831 // Single bytes easy
1832 return false;
1833 } else if (*us > 0xF4) {
1834 // Characters longer than 4 bytes not possible in current UTF-8
1835 return true;
1836 } else if (*us >= 0xF0) {
1837 // 4 bytes
1838 if (len < 4)
1839 return true;
1840 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2]) && GoodTrailByte(us[3])) {
1841 trailBytes = 3;
1842 return false;
1843 } else {
1844 return true;
1845 }
1846 } else if (*us >= 0xE0) {
1847 // 3 bytes
1848 if (len < 3)
1849 return true;
1850 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2])) {
1851 trailBytes = 2;
1852 return false;
1853 } else {
1854 return true;
1855 }
1856 } else if (*us >= 0xC2) {
1857 // 2 bytes
1858 if (len < 2)
1859 return true;
1860 if (GoodTrailByte(us[1])) {
1861 trailBytes = 1;
1862 return false;
1863 } else {
1864 return true;
1865 }
1866 } else if (*us >= 0xC0) {
1867 // Overlong encoding
1868 return true;
1869 } else {
1870 // Trail byte
1871 return true;
1872 }
1873 }
1874
1875 /**
1876 * Fill in the LineLayout data for the given line.
1877 * Copy the given @a line and its styles from the document into local arrays.
1878 * Also determine the x position at which each character starts.
1879 */
1880 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
1881 if (!ll)
1882 return;
1883
1884 PLATFORM_ASSERT(line < pdoc->LinesTotal());
1885 PLATFORM_ASSERT(ll->chars != NULL);
1886 int posLineStart = pdoc->LineStart(line);
1887 int posLineEnd = pdoc->LineStart(line + 1);
1888 // If the line is very long, limit the treatment to a length that should fit in the viewport
1889 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
1890 posLineEnd = posLineStart + ll->maxLineLength;
1891 }
1892 if (ll->validity == LineLayout::llCheckTextAndStyle) {
1893 int lineLength = posLineEnd - posLineStart;
1894 if (!vstyle.viewEOL) {
1895 int cid = posLineEnd - 1;
1896 while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
1897 cid--;
1898 lineLength--;
1899 }
1900 }
1901 if (lineLength == ll->numCharsInLine) {
1902 // See if chars, styles, indicators, are all the same
1903 bool allSame = true;
1904 const int styleMask = pdoc->stylingBitsMask;
1905 // Check base line layout
1906 char styleByte = 0;
1907 int numCharsInLine = 0;
1908 while (numCharsInLine < lineLength) {
1909 int charInDoc = numCharsInLine + posLineStart;
1910 char chDoc = pdoc->CharAt(charInDoc);
1911 styleByte = pdoc->StyleAt(charInDoc);
1912 allSame = allSame &&
1913 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
1914 allSame = allSame &&
1915 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
1916 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
1917 allSame = allSame &&
1918 (ll->chars[numCharsInLine] == chDoc);
1919 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
1920 allSame = allSame &&
1921 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
1922 else // Style::caseUpper
1923 allSame = allSame &&
1924 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
1925 numCharsInLine++;
1926 }
1927 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
1928 if (allSame) {
1929 ll->validity = LineLayout::llPositions;
1930 } else {
1931 ll->validity = LineLayout::llInvalid;
1932 }
1933 } else {
1934 ll->validity = LineLayout::llInvalid;
1935 }
1936 }
1937 if (ll->validity == LineLayout::llInvalid) {
1938 ll->widthLine = LineLayout::wrapWidthInfinite;
1939 ll->lines = 1;
1940 int numCharsInLine = 0;
1941 int numCharsBeforeEOL = 0;
1942 if (vstyle.edgeState == EDGE_BACKGROUND) {
1943 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
1944 if (ll->edgeColumn >= posLineStart) {
1945 ll->edgeColumn -= posLineStart;
1946 }
1947 } else {
1948 ll->edgeColumn = -1;
1949 }
1950
1951 char styleByte = 0;
1952 int styleMask = pdoc->stylingBitsMask;
1953 ll->styleBitsSet = 0;
1954 // Fill base line layout
1955 for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
1956 char chDoc = pdoc->CharAt(charInDoc);
1957 styleByte = pdoc->StyleAt(charInDoc);
1958 ll->styleBitsSet |= styleByte;
1959 if (vstyle.viewEOL || (!IsEOLChar(chDoc))) {
1960 ll->chars[numCharsInLine] = chDoc;
1961 ll->styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
1962 ll->indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
1963 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
1964 ll->chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
1965 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
1966 ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
1967 numCharsInLine++;
1968 if (!IsEOLChar(chDoc))
1969 numCharsBeforeEOL++;
1970 }
1971 }
1972 ll->xHighlightGuide = 0;
1973 // Extra element at the end of the line to hold end x position and act as
1974 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
1975 ll->styles[numCharsInLine] = styleByte; // For eolFilled
1976 ll->indicators[numCharsInLine] = 0;
1977
1978 // Layout the line, determining the position of each character,
1979 // with an extra element at the end for the end of the line.
1980 int startseg = 0; // Start of the current segment, in char. number
1981 int startsegx = 0; // Start of the current segment, in pixels
1982 ll->positions[0] = 0;
1983 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
1984 bool lastSegItalics = false;
1985 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
1986
1987 int ctrlCharWidth[32] = {0};
1988 bool isControlNext = IsControlCharacter(ll->chars[0]);
1989 int trailBytes = 0;
1990 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
1991 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
1992 bool isControl = isControlNext;
1993 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
1994 bool isBadUTF = isBadUTFNext;
1995 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
1996 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
1997 isControl || isControlNext || isBadUTF || isBadUTFNext) {
1998 ll->positions[startseg] = 0;
1999 if (vstyle.styles[ll->styles[charInLine]].visible) {
2000 if (isControl) {
2001 if (ll->chars[charInLine] == '\t') {
2002 ll->positions[charInLine + 1] = ((((startsegx + 2) /
2003 tabWidth) + 1) * tabWidth) - startsegx;
2004 } else if (controlCharSymbol < 32) {
2005 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2006 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2007 // +3 For a blank on front and rounded edge each side:
2008 ctrlCharWidth[ll->chars[charInLine]] =
2009 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
2010 }
2011 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2012 } else {
2013 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2014 surface->MeasureWidths(ctrlCharsFont, cc, 1,
2015 ll->positions + startseg + 1);
2016 }
2017 lastSegItalics = false;
2018 } else if (isBadUTF) {
2019 char hexits[3];
2020 sprintf(hexits, "%2X", ll->chars[charInLine] & 0xff);
2021 ll->positions[charInLine + 1] =
2022 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2023 } else { // Regular character
2024 int lenSeg = charInLine - startseg + 1;
2025 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2026 lastSegItalics = false;
2027 // Over half the segments are single characters and of these about half are space characters.
2028 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2029 } else {
2030 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2031 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2032 lenSeg, ll->positions + startseg + 1);
2033 }
2034 }
2035 } else { // invisible
2036 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2037 ll->positions[posToZero] = 0;
2038 }
2039 }
2040 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2041 ll->positions[posToIncrease] += startsegx;
2042 }
2043 startsegx = ll->positions[charInLine + 1];
2044 startseg = charInLine + 1;
2045 }
2046 }
2047 // Small hack to make lines that end with italics not cut off the edge of the last character
2048 if ((startseg > 0) && lastSegItalics) {
2049 ll->positions[startseg] += 2;
2050 }
2051 ll->numCharsInLine = numCharsInLine;
2052 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2053 ll->validity = LineLayout::llPositions;
2054 }
2055 // Hard to cope when too narrow, so just assume there is space
2056 if (width < 20) {
2057 width = 20;
2058 }
2059 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2060 ll->widthLine = width;
2061 if (width == LineLayout::wrapWidthInfinite) {
2062 ll->lines = 1;
2063 } else if (width > ll->positions[ll->numCharsInLine]) {
2064 // Simple common case where line does not need wrapping.
2065 ll->lines = 1;
2066 } else {
2067 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2068 width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
2069 }
2070 ll->wrapIndent = wrapAddIndent;
2071 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2072 for (int i = 0; i < ll->numCharsInLine; i++) {
2073 if (!IsSpaceOrTab(ll->chars[i])) {
2074 ll->wrapIndent += ll->positions[i]; // Add line indent
2075 break;
2076 }
2077 }
2078 // Check for text width minimum
2079 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2080 ll->wrapIndent = wrapAddIndent;
2081 // Check for wrapIndent minimum
2082 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < static_cast<int>(vstyle.aveCharWidth)))
2083 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2084 ll->lines = 0;
2085 // Calculate line start positions based upon width.
2086 int lastGoodBreak = 0;
2087 int lastLineStart = 0;
2088 int startOffset = 0;
2089 int p = 0;
2090 while (p < ll->numCharsInLine) {
2091 if ((ll->positions[p + 1] - startOffset) >= width) {
2092 if (lastGoodBreak == lastLineStart) {
2093 // Try moving to start of last character
2094 if (p > 0) {
2095 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2096 - posLineStart;
2097 }
2098 if (lastGoodBreak == lastLineStart) {
2099 // Ensure at least one character on line.
2100 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2101 - posLineStart;
2102 }
2103 }
2104 lastLineStart = lastGoodBreak;
2105 ll->lines++;
2106 ll->SetLineStart(ll->lines, lastGoodBreak);
2107 startOffset = ll->positions[lastGoodBreak];
2108 // take into account the space for start wrap mark and indent
2109 startOffset -= ll->wrapIndent;
2110 p = lastGoodBreak + 1;
2111 continue;
2112 }
2113 if (p > 0) {
2114 if (wrapState == eWrapChar) {
2115 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2116 - posLineStart;
2117 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2118 continue;
2119 } else if (ll->styles[p] != ll->styles[p - 1]) {
2120 lastGoodBreak = p;
2121 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2122 lastGoodBreak = p;
2123 }
2124 }
2125 p++;
2126 }
2127 ll->lines++;
2128 }
2129 ll->validity = LineLayout::llLines;
2130 }
2131 }
2132
2133 ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2134 return main ?
2135 (primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated) :
2136 vsDraw.selAdditionalBackground.allocated;
2137 }
2138
2139 ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2140 ColourAllocated background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2141 if (inSelection == 1) {
2142 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2143 return SelectionBackground(vsDraw, true);
2144 }
2145 } else if (inSelection == 2) {
2146 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2147 return SelectionBackground(vsDraw, false);
2148 }
2149 } else {
2150 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2151 (i >= ll->edgeColumn) &&
2152 !IsEOLChar(ll->chars[i]))
2153 return vsDraw.edgecolour.allocated;
2154 if (inHotspot && vsDraw.hotspotBackgroundSet)
2155 return vsDraw.hotspotBackground.allocated;
2156 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD))
2157 return background;
2158 }
2159 return vsDraw.styles[styleMain].back.allocated;
2160 }
2161
2162 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2163 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2164 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2165 surface->Copy(rcCopyArea, from,
2166 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2167 }
2168
2169 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2170 bool isEndMarker, ColourAllocated wrapColour) {
2171 surface->PenColour(wrapColour);
2172
2173 enum { xa = 1 }; // gap before start
2174 int w = rcPlace.right - rcPlace.left - xa - 1;
2175
2176 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2177 bool yStraight = true;
2178 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2179
2180 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2181 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2182
2183 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2184 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2185
2186 struct Relative {
2187 Surface *surface;
2188 int xBase;
2189 int xDir;
2190 int yBase;
2191 int yDir;
2192 void MoveTo(int xRelative, int yRelative) {
2193 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2194 }
2195 void LineTo(int xRelative, int yRelative) {
2196 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2197 }
2198 };
2199 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2200
2201 // arrow head
2202 rel.MoveTo(xa, y);
2203 rel.LineTo(xa + 2*w / 3, y - dy);
2204 rel.MoveTo(xa, y);
2205 rel.LineTo(xa + 2*w / 3, y + dy);
2206
2207 // arrow body
2208 rel.MoveTo(xa, y);
2209 rel.LineTo(xa + w, y);
2210 rel.LineTo(xa + w, y - 2 * dy);
2211 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2212 y - 2 * dy);
2213 }
2214
2215 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) {
2216 if (alpha != SC_ALPHA_NOALPHA) {
2217 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2218 }
2219 }
2220
2221 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2222 const char *s, ColourAllocated textBack, ColourAllocated textFore, bool twoPhaseDraw) {
2223 if (!twoPhaseDraw) {
2224 surface->FillRectangle(rcSegment, textBack);
2225 }
2226 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2227 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2228 surface->InternalLeading(ctrlCharsFont);
2229 PRectangle rcCChar = rcSegment;
2230 rcCChar.left = rcCChar.left + 1;
2231 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2232 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2233 PRectangle rcCentral = rcCChar;
2234 rcCentral.top++;
2235 rcCentral.bottom--;
2236 surface->FillRectangle(rcCentral, textFore);
2237 PRectangle rcChar = rcCChar;
2238 rcChar.left++;
2239 rcChar.right--;
2240 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2241 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2242 textBack, textFore);
2243 }
2244
2245 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2246 int line, int lineEnd, int xStart, int subLine, int subLineStart,
2247 bool overrideBackground, ColourAllocated background,
2248 bool drawWrapMarkEnd, ColourAllocated wrapColour) {
2249
2250 const int posLineStart = pdoc->LineStart(line);
2251 const int styleMask = pdoc->stylingBitsMask;
2252 PRectangle rcSegment = rcLine;
2253
2254 const bool lastSubLine = subLine == (ll->lines - 1);
2255 int virtualSpace = 0;
2256 if (lastSubLine) {
2257 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2258 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2259 }
2260
2261 // Fill in a PRectangle representing the end of line characters
2262
2263 int xEol = ll->positions[lineEnd] - subLineStart;
2264
2265 // Fill the virtual space and show selections within it
2266 if (virtualSpace) {
2267 rcSegment.left = xEol + xStart;
2268 rcSegment.right = xEol + xStart + virtualSpace;
2269 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2270 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2271 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2272 for (size_t r=0; r<sel.Count(); r++) {
2273 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2274 if (alpha == SC_ALPHA_NOALPHA) {
2275 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2276 if (!portion.Empty()) {
2277 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2278 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2279 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2280 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2281 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2282 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2283 }
2284 }
2285 }
2286 }
2287 }
2288
2289 int posAfterLineEnd = pdoc->LineStart(line + 1);
2290 int eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2291 int alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2292
2293 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2294 int blobsWidth = 0;
2295 if (lastSubLine) {
2296 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2297 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2298 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2299 blobsWidth += rcSegment.Width();
2300 const char *ctrlChar = ControlCharacterString(ll->chars[eolPos]);
2301 int inSelection = 0;
2302 bool inHotspot = false;
2303 int styleMain = ll->styles[eolPos];
2304 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, eolPos, ll);
2305 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2306 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2307 if (alpha == SC_ALPHA_NOALPHA) {
2308 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2309 } else {
2310 surface->FillRectangle(rcSegment, textBack);
2311 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2312 }
2313 } else {
2314 surface->FillRectangle(rcSegment, textBack);
2315 }
2316 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2317 }
2318 }
2319
2320 // Draw the eol-is-selected rectangle
2321 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2322 rcSegment.right = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
2323
2324 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2325 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2326 } else {
2327 if (overrideBackground) {
2328 surface->FillRectangle(rcSegment, background);
2329 } else if (line < pdoc->LinesTotal() - 1) {
2330 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2331 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2332 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2333 } else {
2334 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2335 }
2336 if (!hideSelection && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2337 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2338 }
2339 }
2340
2341 // Fill the remainder of the line
2342 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth + vsDraw.aveCharWidth;
2343 rcSegment.right = rcLine.right;
2344
2345 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2346 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2347 } else {
2348 if (overrideBackground) {
2349 surface->FillRectangle(rcSegment, background);
2350 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2351 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2352 } else {
2353 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
2354 }
2355 if (!hideSelection && vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2356 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2357 }
2358 }
2359
2360 if (drawWrapMarkEnd) {
2361 PRectangle rcPlace = rcSegment;
2362
2363 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2364 rcPlace.left = xEol + xStart + virtualSpace;
2365 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2366 } else {
2367 // draw left of the right text margin, to avoid clipping by the current clip rect
2368 rcPlace.right = rcLine.right - vs.rightMarginWidth;
2369 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2370 }
2371 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2372 }
2373 }
2374
2375 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2376 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2377 // Draw decorators
2378 const int posLineStart = pdoc->LineStart(line);
2379 const int lineStart = ll->LineStart(subLine);
2380 const int subLineStart = ll->positions[lineStart];
2381 const int posLineEnd = posLineStart + lineEnd;
2382
2383 if (!under) {
2384 // Draw indicators
2385 // foreach indicator...
2386 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2387 if (!(mask & ll->styleBitsSet)) {
2388 mask <<= 1;
2389 continue;
2390 }
2391 int startPos = -1;
2392 // foreach style pos in line...
2393 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2394 // look for starts...
2395 if (startPos < 0) {
2396 // NOT in indicator run, looking for START
2397 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2398 startPos = indicPos;
2399 }
2400 // ... or ends
2401 if (startPos >= 0) {
2402 // IN indicator run, looking for END
2403 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2404 // AT end of indicator run, DRAW it!
2405 PRectangle rcIndic(
2406 ll->positions[startPos] + xStart - subLineStart,
2407 rcLine.top + vsDraw.maxAscent,
2408 ll->positions[indicPos] + xStart - subLineStart,
2409 rcLine.top + vsDraw.maxAscent + 3);
2410 vsDraw.indicators[indicnum].Draw(surface, rcIndic, rcLine);
2411 // RESET control var
2412 startPos = -1;
2413 }
2414 }
2415 }
2416 mask <<= 1;
2417 }
2418 }
2419
2420 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2421 if (under == vsDraw.indicators[deco->indicator].under) {
2422 int startPos = posLineStart + lineStart;
2423 if (!deco->rs.ValueAt(startPos)) {
2424 startPos = deco->rs.EndRun(startPos);
2425 }
2426 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2427 int endPos = deco->rs.EndRun(startPos);
2428 if (endPos > posLineEnd)
2429 endPos = posLineEnd;
2430 PRectangle rcIndic(
2431 ll->positions[startPos - posLineStart] + xStart - subLineStart,
2432 rcLine.top + vsDraw.maxAscent,
2433 ll->positions[endPos - posLineStart] + xStart - subLineStart,
2434 rcLine.top + vsDraw.maxAscent + 3);
2435 vsDraw.indicators[deco->indicator].Draw(surface, rcIndic, rcLine);
2436 startPos = deco->rs.EndRun(endPos);
2437 }
2438 }
2439 }
2440 }
2441
2442 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2443 PRectangle rcLine, LineLayout *ll, int subLine) {
2444 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2445 PRectangle rcSegment = rcLine;
2446 int annotationLine = subLine - ll->lines;
2447 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2448 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2449 surface->FillRectangle(rcSegment, vsDraw.styles[0].back.allocated);
2450 if (vs.annotationVisible == ANNOTATION_BOXED) {
2451 // Only care about calculating width if need to draw box
2452 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2453 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2454 rcSegment.left = xStart + indent;
2455 rcSegment.right = rcSegment.left + widthAnnotation;
2456 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore.allocated);
2457 } else {
2458 rcSegment.left = xStart;
2459 }
2460 const int annotationLines = pdoc->AnnotationLines(line);
2461 size_t start = 0;
2462 size_t lengthAnnotation = stAnnotation.LineLength(start);
2463 int lineInAnnotation = 0;
2464 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2465 start += lengthAnnotation + 1;
2466 lengthAnnotation = stAnnotation.LineLength(start);
2467 lineInAnnotation++;
2468 }
2469 PRectangle rcText = rcSegment;
2470 if (vs.annotationVisible == ANNOTATION_BOXED) {
2471 surface->FillRectangle(rcText,
2472 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back.allocated);
2473 rcText.left += vsDraw.spaceWidth;
2474 }
2475 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2476 stAnnotation, start, lengthAnnotation);
2477 if (vs.annotationVisible == ANNOTATION_BOXED) {
2478 surface->MoveTo(rcSegment.left, rcSegment.top);
2479 surface->LineTo(rcSegment.left, rcSegment.bottom);
2480 surface->MoveTo(rcSegment.right, rcSegment.top);
2481 surface->LineTo(rcSegment.right, rcSegment.bottom);
2482 if (subLine == ll->lines){
2483 surface->MoveTo(rcSegment.left, rcSegment.top);
2484 surface->LineTo(rcSegment.right, rcSegment.top);
2485 }
2486 if (subLine == ll->lines+annotationLines-1) {
2487 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2488 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2489 }
2490 }
2491 }
2492 }
2493
2494 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2495 PRectangle rcLine, LineLayout *ll, int subLine) {
2496
2497 PRectangle rcSegment = rcLine;
2498
2499 // Using one font for all control characters so it can be controlled independently to ensure
2500 // the box goes around the characters tightly. Seems to be no way to work out what height
2501 // is taken by an individual character - internal leading gives varying results.
2502 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2503
2504 // See if something overrides the line background color: Either if caret is on the line
2505 // and background color is set for that, or if a marker is defined that forces its background
2506 // color onto the line, or if a marker is defined but has no selection margin in which to
2507 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2508 // with the earlier taking precedence. When multiple markers cause background override,
2509 // the color for the highest numbered one is used.
2510 bool overrideBackground = false;
2511 ColourAllocated background;
2512 if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2513 overrideBackground = true;
2514 background = vsDraw.caretLineBackground.allocated;
2515 }
2516 if (!overrideBackground) {
2517 int marks = pdoc->GetMark(line);
2518 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2519 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2520 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2521 background = vsDraw.markers[markBit].back.allocated;
2522 overrideBackground = true;
2523 }
2524 marks >>= 1;
2525 }
2526 }
2527 if (!overrideBackground) {
2528 if (vsDraw.maskInLine) {
2529 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2530 if (marksMasked) {
2531 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2532 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2533 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2534 overrideBackground = true;
2535 background = vsDraw.markers[markBit].back.allocated;
2536 }
2537 marksMasked >>= 1;
2538 }
2539 }
2540 }
2541 }
2542
2543 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2544 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2545
2546 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2547 int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2548
2549 int posLineStart = pdoc->LineStart(line);
2550
2551 int startseg = ll->LineStart(subLine);
2552 int subLineStart = ll->positions[startseg];
2553 if (subLine >= ll->lines) {
2554 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2555 return; // No further drawing
2556 }
2557 int lineStart = 0;
2558 int lineEnd = 0;
2559 if (subLine < ll->lines) {
2560 lineStart = ll->LineStart(subLine);
2561 lineEnd = ll->LineStart(subLine + 1);
2562 if (subLine == ll->lines - 1) {
2563 lineEnd = ll->numCharsBeforeEOL;
2564 }
2565 }
2566
2567 ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
2568 if (vsDraw.whitespaceForegroundSet)
2569 wrapColour = vsDraw.whitespaceForeground.allocated;
2570
2571 bool drawWrapMarkEnd = false;
2572
2573 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2574 if (subLine + 1 < ll->lines) {
2575 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2576 }
2577 }
2578
2579 if (ll->wrapIndent != 0) {
2580
2581 bool continuedWrapLine = false;
2582 if (subLine < ll->lines) {
2583 continuedWrapLine = ll->LineStart(subLine) != 0;
2584 }
2585
2586 if (continuedWrapLine) {
2587 // draw continuation rect
2588 PRectangle rcPlace = rcSegment;
2589
2590 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2591 rcPlace.right = rcPlace.left + ll->wrapIndent;
2592
2593 // default bgnd here..
2594 surface->FillRectangle(rcSegment, overrideBackground ? background :
2595 vsDraw.styles[STYLE_DEFAULT].back.allocated);
2596
2597 // main line style would be below but this would be inconsistent with end markers
2598 // also would possibly not be the style at wrap point
2599 //int styleMain = ll->styles[lineStart];
2600 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2601
2602 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2603
2604 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2605 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2606 else
2607 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2608
2609 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2610 }
2611
2612 xStart += ll->wrapIndent;
2613 }
2614 }
2615
2616 bool selBackDrawn = vsDraw.selbackset &&
2617 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2618
2619 // Does not take margin into account but not significant
2620 int xStartVisible = subLineStart - xStart;
2621
2622 ll->psel = &sel;
2623
2624 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible, selBackDrawn);
2625 int next = bfBack.First();
2626
2627 // Background drawing loop
2628 while (twoPhaseDraw && (next < lineEnd)) {
2629
2630 startseg = next;
2631 next = bfBack.Next();
2632 int i = next - 1;
2633 int iDoc = i + posLineStart;
2634
2635 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2636 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2637 // Only try to draw if really visible - enhances performance by not calling environment to
2638 // draw strings that are completely past the right side of the window.
2639 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2640 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2641 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2642 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2643
2644 int styleMain = ll->styles[i];
2645 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2646 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2647 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2648 if (ll->chars[i] == '\t') {
2649 // Tab display
2650 if (drawWhitespaceBackground &&
2651 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2652 textBack = vsDraw.whitespaceBackground.allocated;
2653 surface->FillRectangle(rcSegment, textBack);
2654 } else if (IsControlCharacter(ll->chars[i])) {
2655 // Control character display
2656 inIndentation = false;
2657 surface->FillRectangle(rcSegment, textBack);
2658 } else {
2659 // Normal text display
2660 surface->FillRectangle(rcSegment, textBack);
2661 if (vsDraw.viewWhitespace != wsInvisible ||
2662 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2663 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2664 if (ll->chars[cpos + startseg] == ' ') {
2665 if (drawWhitespaceBackground &&
2666 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2667 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2668 rcSegment.top,
2669 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2670 rcSegment.bottom);
2671 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
2672 }
2673 } else {
2674 inIndentation = false;
2675 }
2676 }
2677 }
2678 }
2679 } else if (rcSegment.left > rcLine.right) {
2680 break;
2681 }
2682 }
2683
2684 if (twoPhaseDraw) {
2685 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2686 xStart, subLine, subLineStart, overrideBackground, background,
2687 drawWrapMarkEnd, wrapColour);
2688 }
2689
2690 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2691
2692 if (vsDraw.edgeState == EDGE_LINE) {
2693 int edgeX = theEdge * vsDraw.spaceWidth;
2694 rcSegment.left = edgeX + xStart;
2695 rcSegment.right = rcSegment.left + 1;
2696 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
2697 }
2698
2699 // Draw underline mark as part of background if not transparent
2700 int marks = pdoc->GetMark(line);
2701 int markBit;
2702 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2703 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
2704 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2705 PRectangle rcUnderline = rcLine;
2706 rcUnderline.top = rcUnderline.bottom - 2;
2707 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back.allocated);
2708 }
2709 marks >>= 1;
2710 }
2711
2712 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2713 // Foreground drawing loop
2714 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible,
2715 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset));
2716 next = bfFore.First();
2717
2718 while (next < lineEnd) {
2719
2720 startseg = next;
2721 next = bfFore.Next();
2722 int i = next - 1;
2723
2724 int iDoc = i + posLineStart;
2725
2726 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2727 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2728 // Only try to draw if really visible - enhances performance by not calling environment to
2729 // draw strings that are completely past the right side of the window.
2730 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2731 int styleMain = ll->styles[i];
2732 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2733 Font &textFont = vsDraw.styles[styleMain].font;
2734 //hotspot foreground
2735 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
2736 if (vsDraw.hotspotForegroundSet)
2737 textFore = vsDraw.hotspotForeground.allocated;
2738 }
2739 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2740 if (inSelection && (vsDraw.selforeset)) {
2741 textFore = (inSelection == 1) ? vsDraw.selforeground.allocated : vsDraw.selAdditionalForeground.allocated;
2742 }
2743 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2744 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2745 if (ll->chars[i] == '\t') {
2746 // Tab display
2747 if (!twoPhaseDraw) {
2748 if (drawWhitespaceBackground &&
2749 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2750 textBack = vsDraw.whitespaceBackground.allocated;
2751 surface->FillRectangle(rcSegment, textBack);
2752 }
2753 if ((vsDraw.viewWhitespace != wsInvisible) ||
2754 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2755 if (vsDraw.whitespaceForegroundSet)
2756 textFore = vsDraw.whitespaceForeground.allocated;
2757 surface->PenColour(textFore);
2758 }
2759 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2760 for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
2761 if (xIG >= ll->positions[i] && xIG > 0) {
2762 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
2763 (ll->xHighlightGuide == xIG));
2764 }
2765 }
2766 }
2767 if (vsDraw.viewWhitespace != wsInvisible) {
2768 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2769 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
2770 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
2771 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
2772 }
2773 }
2774 } else if (IsControlCharacter(ll->chars[i])) {
2775 // Control character display
2776 inIndentation = false;
2777 if (controlCharSymbol < 32) {
2778 // Draw the character
2779 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
2780 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2781 } else {
2782 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2783 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
2784 rcSegment.top + vsDraw.maxAscent,
2785 cc, 1, textBack, textFore);
2786 }
2787 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
2788 char hexits[3];
2789 sprintf(hexits, "%2X", ll->chars[i] & 0xff);
2790 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
2791 } else {
2792 // Normal text display
2793 if (vsDraw.styles[styleMain].visible) {
2794 if (twoPhaseDraw) {
2795 surface->DrawTextTransparent(rcSegment, textFont,
2796 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2797 i - startseg + 1, textFore);
2798 } else {
2799 surface->DrawTextNoClip(rcSegment, textFont,
2800 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2801 i - startseg + 1, textFore, textBack);
2802 }
2803 }
2804 if (vsDraw.viewWhitespace != wsInvisible ||
2805 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2806 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2807 if (ll->chars[cpos + startseg] == ' ') {
2808 if (vsDraw.viewWhitespace != wsInvisible) {
2809 if (vsDraw.whitespaceForegroundSet)
2810 textFore = vsDraw.whitespaceForeground.allocated;
2811 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2812 int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
2813 if (!twoPhaseDraw && drawWhitespaceBackground &&
2814 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2815 textBack = vsDraw.whitespaceBackground.allocated;
2816 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2817 rcSegment.top,
2818 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2819 rcSegment.bottom);
2820 surface->FillRectangle(rcSpace, textBack);
2821 }
2822 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
2823 rcDot.right = rcDot.left + vs.whitespaceSize;
2824 rcDot.bottom = rcDot.top + vs.whitespaceSize;
2825 surface->FillRectangle(rcDot, textFore);
2826 }
2827 }
2828 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2829 int startSpace = ll->positions[cpos + startseg];
2830 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
2831 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
2832 (ll->xHighlightGuide == ll->positions[cpos + startseg]));
2833 }
2834 }
2835 } else {
2836 inIndentation = false;
2837 }
2838 }
2839 }
2840 }
2841 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) {
2842 PRectangle rcUL = rcSegment;
2843 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2844 rcUL.bottom = rcUL.top + 1;
2845 if (vsDraw.hotspotForegroundSet)
2846 surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
2847 else
2848 surface->FillRectangle(rcUL, textFore);
2849 } else if (vsDraw.styles[styleMain].underline) {
2850 PRectangle rcUL = rcSegment;
2851 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2852 rcUL.bottom = rcUL.top + 1;
2853 surface->FillRectangle(rcUL, textFore);
2854 }
2855 } else if (rcSegment.left > rcLine.right) {
2856 break;
2857 }
2858 }
2859 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
2860 && (subLine == 0)) {
2861 int indentSpace = pdoc->GetLineIndentation(line);
2862 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
2863
2864 // Find the most recent line with some text
2865
2866 int lineLastWithText = line;
2867 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
2868 lineLastWithText--;
2869 }
2870 if (lineLastWithText < line) {
2871 xStartText = 100000; // Don't limit to visible indentation on empty line
2872 // This line is empty, so use indentation of last line with text
2873 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
2874 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
2875 if (isFoldHeader) {
2876 // Level is one more level than parent
2877 indentLastWithText += pdoc->IndentSize();
2878 }
2879 if (vsDraw.viewIndentationGuides == ivLookForward) {
2880 // In viLookForward mode, previous line only used if it is a fold header
2881 if (isFoldHeader) {
2882 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
2883 }
2884 } else { // viLookBoth
2885 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
2886 }
2887 }
2888
2889 int lineNextWithText = line;
2890 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
2891 lineNextWithText++;
2892 }
2893 if (lineNextWithText > line) {
2894 // This line is empty, so use indentation of last line with text
2895 indentSpace = Platform::Maximum(indentSpace,
2896 pdoc->GetLineIndentation(lineNextWithText));
2897 }
2898
2899 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
2900 int xIndent = indentPos * vsDraw.spaceWidth;
2901 if (xIndent < xStartText) {
2902 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
2903 (ll->xHighlightGuide == xIndent));
2904 }
2905 }
2906 }
2907
2908 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
2909
2910 // End of the drawing of the current line
2911 if (!twoPhaseDraw) {
2912 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2913 xStart, subLine, subLineStart, overrideBackground, background,
2914 drawWrapMarkEnd, wrapColour);
2915 }
2916 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
2917 // For each selection draw
2918 int virtualSpaces = 0;
2919 if (subLine == (ll->lines - 1)) {
2920 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
2921 }
2922 SelectionPosition posStart(posLineStart);
2923 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
2924 SelectionSegment virtualSpaceRange(posStart, posEnd);
2925 for (size_t r=0; r<sel.Count(); r++) {
2926 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2927 if (alpha != SC_ALPHA_NOALPHA) {
2928 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2929 if (!portion.Empty()) {
2930 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
2931 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2932 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2933 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2934 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2935 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
2936 }
2937 }
2938 }
2939 }
2940
2941 // Draw any translucent whole line states
2942 rcSegment.left = xStart;
2943 rcSegment.right = rcLine.right - 1;
2944 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
2945 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
2946 }
2947 marks = pdoc->GetMark(line);
2948 for (markBit = 0; (markBit < 32) && marks; markBit++) {
2949 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
2950 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2951 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
2952 PRectangle rcUnderline = rcSegment;
2953 rcUnderline.top = rcUnderline.bottom - 2;
2954 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2955 }
2956 marks >>= 1;
2957 }
2958 if (vsDraw.maskInLine) {
2959 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2960 if (marksMasked) {
2961 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2962 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
2963 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2964 }
2965 marksMasked >>= 1;
2966 }
2967 }
2968 }
2969 }
2970
2971 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
2972 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourAllocated caretColour) {
2973
2974 int lineStart = ll->LineStart(subLine);
2975 int posBefore = posCaret;
2976 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
2977 int numCharsToDraw = posAfter - posCaret;
2978
2979 // Work out where the starting and ending offsets are. We need to
2980 // see if the previous character shares horizontal space, such as a
2981 // glyph / combining character. If so we'll need to draw that too.
2982 int offsetFirstChar = offset;
2983 int offsetLastChar = offset + (posAfter - posCaret);
2984 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
2985 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
2986 // The char does not share horizontal space
2987 break;
2988 }
2989 // Char shares horizontal space, update the numChars to draw
2990 // Update posBefore to point to the prev char
2991 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
2992 numCharsToDraw = posAfter - posBefore;
2993 offsetFirstChar = offset - (posCaret - posBefore);
2994 }
2995
2996 // See if the next character shares horizontal space, if so we'll
2997 // need to draw that too.
2998 numCharsToDraw = offsetLastChar - offsetFirstChar;
2999 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3000 // Update posAfter to point to the 2nd next char, this is where
3001 // the next character ends, and 2nd next begins. We'll need
3002 // to compare these two
3003 posBefore = posAfter;
3004 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3005 offsetLastChar = offset + (posAfter - posCaret);
3006 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3007 // The char does not share horizontal space
3008 break;
3009 }
3010 // Char shares horizontal space, update the numChars to draw
3011 numCharsToDraw = offsetLastChar - offsetFirstChar;
3012 }
3013
3014 // We now know what to draw, update the caret drawing rectangle
3015 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3016 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3017
3018 // Adjust caret position to take into account any word wrapping symbols.
3019 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3020 int wordWrapCharWidth = ll->wrapIndent;
3021 rcCaret.left += wordWrapCharWidth;
3022 rcCaret.right += wordWrapCharWidth;
3023 }
3024
3025 // This character is where the caret block is, we override the colours
3026 // (inversed) for drawing the caret here.
3027 int styleMain = ll->styles[offsetFirstChar];
3028 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3029 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3030 numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
3031 caretColour);
3032 }
3033
3034 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3035 if (!pixmapSelPattern->Initialised()) {
3036 const int patternSize = 8;
3037 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3038 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3039 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3040 // way between the chrome colour and the chrome highlight colour making a nice transition
3041 // between the window chrome and the content area. And it works in low colour depths.
3042 PRectangle rcPattern(0, 0, patternSize, patternSize);
3043
3044 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3045 ColourAllocated colourFMFill = vs.selbar.allocated;
3046 ColourAllocated colourFMStripes = vs.selbarlight.allocated;
3047
3048 if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
3049 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3050 // (Typically, the highlight colour is white.)
3051 colourFMFill = vs.selbarlight.allocated;
3052 }
3053
3054 if (vs.foldmarginColourSet) {
3055 // override default fold margin colour
3056 colourFMFill = vs.foldmarginColour.allocated;
3057 }
3058 if (vs.foldmarginHighlightColourSet) {
3059 // override default fold margin highlight colour
3060 colourFMStripes = vs.foldmarginHighlightColour.allocated;
3061 }
3062
3063 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3064 pixmapSelPattern->PenColour(colourFMStripes);
3065 for (int stripe = 0; stripe < patternSize; stripe++) {
3066 // Alternating 1 pixel stripes is same as checkerboard.
3067 pixmapSelPattern->MoveTo(0, stripe * 2);
3068 pixmapSelPattern->LineTo(patternSize, stripe * 2 - patternSize);
3069 }
3070 }
3071
3072 if (!pixmapIndentGuide->Initialised()) {
3073 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3074 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3075 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3076 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3077 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
3078 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
3079 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
3080 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
3081 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3082 pixmapIndentGuide->MoveTo(0, stripe);
3083 pixmapIndentGuide->LineTo(2, stripe);
3084 pixmapIndentGuideHighlight->MoveTo(0, stripe);
3085 pixmapIndentGuideHighlight->LineTo(2, stripe);
3086 }
3087 }
3088
3089 if (bufferedDraw) {
3090 if (!pixmapLine->Initialised()) {
3091 PRectangle rcClient = GetClientRectangle();
3092 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3093 surfaceWindow, wMain.GetID());
3094 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3095 rcClient.Height(), surfaceWindow, wMain.GetID());
3096 }
3097 }
3098 }
3099
3100 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3101 PRectangle rcLine, LineLayout *ll, int subLine) {
3102 // When drag is active it is the only caret drawn
3103 bool drawDrag = posDrag.IsValid();
3104 if (hideSelection && !drawDrag)
3105 return;
3106 const int posLineStart = pdoc->LineStart(lineDoc);
3107 // For each selection draw
3108 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3109 const bool mainCaret = r == sel.Main();
3110 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3111 const int offset = posCaret.Position() - posLineStart;
3112 const int spaceWidth = static_cast<int>(vsDraw.styles[ll->EndLineStyle()].spaceWidth);
3113 const int virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3114 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3115 int xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3116 if (ll->wrapIndent != 0) {
3117 int lineStart = ll->LineStart(subLine);
3118 if (lineStart != 0) // Wrapped
3119 xposCaret += ll->wrapIndent;
3120 }
3121 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3122 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3123 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3124 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3125 bool caretAtEOF = false;
3126 bool caretAtEOL = false;
3127 bool drawBlockCaret = false;
3128 int widthOverstrikeCaret;
3129 int caretWidthOffset = 0;
3130 PRectangle rcCaret = rcLine;
3131
3132 if (posCaret.Position() == pdoc->Length()) { // At end of document
3133 caretAtEOF = true;
3134 widthOverstrikeCaret = vsDraw.aveCharWidth;
3135 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3136 caretAtEOL = true;
3137 widthOverstrikeCaret = vsDraw.aveCharWidth;
3138 } else {
3139 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3140 }
3141 if (widthOverstrikeCaret < 3) // Make sure its visible
3142 widthOverstrikeCaret = 3;
3143
3144 if (xposCaret > 0)
3145 caretWidthOffset = 1; // Move back so overlaps both character cells.
3146 xposCaret += xStart;
3147 if (posDrag.IsValid()) {
3148 /* Dragging text, use a line caret */
3149 rcCaret.left = xposCaret - caretWidthOffset;
3150 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3151 } else if (inOverstrike) {
3152 /* Overstrike (insert mode), use a modified bar caret */
3153 rcCaret.top = rcCaret.bottom - 2;
3154 rcCaret.left = xposCaret + 1;
3155 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3156 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3157 /* Block caret */
3158 rcCaret.left = xposCaret;
3159 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3160 drawBlockCaret = true;
3161 rcCaret.right = xposCaret + widthOverstrikeCaret;
3162 } else {
3163 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3164 }
3165 } else {
3166 /* Line caret */
3167 rcCaret.left = xposCaret - caretWidthOffset;
3168 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3169 }
3170 ColourAllocated caretColour = mainCaret ? vsDraw.caretcolour.allocated : vsDraw.additionalCaretColour.allocated;
3171 if (drawBlockCaret) {
3172 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3173 } else {
3174 surface->FillRectangle(rcCaret, caretColour);
3175 }
3176 }
3177 }
3178 if (drawDrag)
3179 break;
3180 }
3181 }
3182
3183 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3184 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3185 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3186
3187 pixmapLine->Release();
3188 RefreshStyleData();
3189 RefreshPixMaps(surfaceWindow);
3190
3191 PRectangle rcClient = GetClientRectangle();
3192 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3193 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3194
3195 surfaceWindow->SetPalette(&palette, true);
3196 pixmapLine->SetPalette(&palette, !hasFocus);
3197
3198 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3199 // The area to be painted plus one extra line is styled.
3200 // The extra line is to determine when a style change, such as starting a comment flows on to other lines.
3201 int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
3202 //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
3203 int endPosPaint = pdoc->Length();
3204 if (lineStyleLast < cs.LinesDisplayed())
3205 endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast) + 1);
3206
3207 int xStart = vs.fixedColumnWidth - xOffset;
3208 int ypos = 0;
3209 if (!bufferedDraw)
3210 ypos += screenLinePaintFirst * vs.lineHeight;
3211 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3212
3213 // Ensure we are styled as far as we are painting.
3214 pdoc->EnsureStyledTo(endPosPaint);
3215 bool paintAbandonedByStyling = paintState == paintAbandoned;
3216 if (needUpdateUI) {
3217 // Deselect palette by selecting a temporary palette
3218 Palette palTemp;
3219 surfaceWindow->SetPalette(&palTemp, true);
3220
3221 NotifyUpdateUI();
3222 needUpdateUI = false;
3223
3224 RefreshStyleData();
3225 RefreshPixMaps(surfaceWindow);
3226 surfaceWindow->SetPalette(&palette, true);
3227 pixmapLine->SetPalette(&palette, !hasFocus);
3228 }
3229
3230 // Call priority lines wrap on a window of lines which are likely
3231 // to rendered with the following paint (that is wrap the visible
3232 // lines first).
3233 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3234 if (startLineToWrap < 0)
3235 startLineToWrap = 0;
3236 if (WrapLines(false, startLineToWrap)) {
3237 // The wrapping process has changed the height of some lines so
3238 // abandon this paint for a complete repaint.
3239 if (AbandonPaint()) {
3240 return;
3241 }
3242 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3243 }
3244 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3245
3246 PaintSelMargin(surfaceWindow, rcArea);
3247
3248 PRectangle rcRightMargin = rcClient;
3249 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3250 if (rcArea.Intersects(rcRightMargin)) {
3251 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
3252 }
3253
3254 if (paintState == paintAbandoned) {
3255 // Either styling or NotifyUpdateUI noticed that painting is needed
3256 // outside the current painting rectangle
3257 //Platform::DebugPrintf("Abandoning paint\n");
3258 if (wrapState != eWrapNone) {
3259 if (paintAbandonedByStyling) {
3260 // Styling has spilled over a line end, such as occurs by starting a multiline
3261 // comment. The width of subsequent text may have changed, so rewrap.
3262 NeedWrapping(cs.DocFromDisplay(topLine));
3263 }
3264 }
3265 return;
3266 }
3267 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3268
3269 // Do the painting
3270 if (rcArea.right > vs.fixedColumnWidth) {
3271
3272 Surface *surface = surfaceWindow;
3273 if (bufferedDraw) {
3274 surface = pixmapLine;
3275 PLATFORM_ASSERT(pixmapLine->Initialised());
3276 }
3277 surface->SetUnicodeMode(IsUnicodeMode());
3278 surface->SetDBCSMode(CodePage());
3279
3280 int visibleLine = topLine + screenLinePaintFirst;
3281
3282 SelectionPosition posCaret = sel.RangeMain().caret;
3283 if (posDrag.IsValid())
3284 posCaret = posDrag;
3285 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3286
3287 // Remove selection margin from drawing area so text will not be drawn
3288 // on it in unbuffered mode.
3289 PRectangle rcTextArea = rcClient;
3290 rcTextArea.left = vs.fixedColumnWidth;
3291 rcTextArea.right -= vs.rightMarginWidth;
3292 surfaceWindow->SetClip(rcTextArea);
3293
3294 // Loop on visible lines
3295 //double durLayout = 0.0;
3296 //double durPaint = 0.0;
3297 //double durCopy = 0.0;
3298 //ElapsedTime etWhole;
3299 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3300 AutoLineLayout ll(llc, 0);
3301 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3302
3303 int lineDoc = cs.DocFromDisplay(visibleLine);
3304 // Only visible lines should be handled by the code within the loop
3305 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3306 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3307 int subLine = visibleLine - lineStartSet;
3308
3309 // Copy this line and its styles from the document into local arrays
3310 // and determine the x position at which each character starts.
3311 //ElapsedTime et;
3312 if (lineDoc != lineDocPrevious) {
3313 ll.Set(0);
3314 ll.Set(RetrieveLineLayout(lineDoc));
3315 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3316 lineDocPrevious = lineDoc;
3317 }
3318 //durLayout += et.Duration(true);
3319
3320 if (ll) {
3321 ll->containsCaret = lineDoc == lineCaret;
3322 if (hideSelection) {
3323 ll->containsCaret = false;
3324 }
3325
3326 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3327
3328 PRectangle rcLine = rcClient;
3329 rcLine.top = ypos;
3330 rcLine.bottom = ypos + vs.lineHeight;
3331
3332 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3333 // Highlight the current braces if any
3334 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3335 highlightGuideColumn * vs.spaceWidth);
3336
3337 // Draw the line
3338 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3339 //durPaint += et.Duration(true);
3340
3341 // Restore the previous styles for the brace highlights in case layout is in cache.
3342 ll->RestoreBracesHighlight(rangeLine, braces);
3343
3344 bool expanded = cs.GetExpanded(lineDoc);
3345 // Paint the line above the fold
3346 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3347 ||
3348 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3349 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
3350 PRectangle rcFoldLine = rcLine;
3351 rcFoldLine.bottom = rcFoldLine.top + 1;
3352 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3353 }
3354 }
3355 // Paint the line below the fold
3356 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3357 ||
3358 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3359 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
3360 PRectangle rcFoldLine = rcLine;
3361 rcFoldLine.top = rcFoldLine.bottom - 1;
3362 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3363 }
3364 }
3365
3366 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3367
3368 if (bufferedDraw) {
3369 Point from(vs.fixedColumnWidth, 0);
3370 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
3371 rcClient.right, yposScreen + vs.lineHeight);
3372 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3373 }
3374
3375 lineWidthMaxSeen = Platform::Maximum(
3376 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3377 //durCopy += et.Duration(true);
3378 }
3379
3380 if (!bufferedDraw) {
3381 ypos += vs.lineHeight;
3382 }
3383
3384 yposScreen += vs.lineHeight;
3385 visibleLine++;
3386
3387 //gdk_flush();
3388 }
3389 ll.Set(0);
3390 //if (durPaint < 0.00000001)
3391 // durPaint = 0.00000001;
3392
3393 // Right column limit indicator
3394 PRectangle rcBeyondEOF = rcClient;
3395 rcBeyondEOF.left = vs.fixedColumnWidth;
3396 rcBeyondEOF.right = rcBeyondEOF.right;
3397 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3398 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3399 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
3400 if (vs.edgeState == EDGE_LINE) {
3401 int edgeX = theEdge * vs.spaceWidth;
3402 rcBeyondEOF.left = edgeX + xStart;
3403 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3404 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
3405 }
3406 }
3407 //Platform::DebugPrintf(
3408 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3409 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3410 NotifyPainted();
3411 }
3412 }
3413
3414 // Space (3 space characters) between line numbers and text when printing.
3415 #define lineNumberPrintSpace " "
3416
3417 ColourDesired InvertedLight(ColourDesired orig) {
3418 unsigned int r = orig.GetRed();
3419 unsigned int g = orig.GetGreen();
3420 unsigned int b = orig.GetBlue();
3421 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3422 unsigned int il = 0xff - l;
3423 if (l == 0)
3424 return ColourDesired(0xff, 0xff, 0xff);
3425 r = r * il / l;
3426 g = g * il / l;
3427 b = b * il / l;
3428 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3429 }
3430
3431 // This is mostly copied from the Paint method but with some things omitted
3432 // such as the margin markers, line numbers, selection and caret
3433 // Should be merged back into a combined Draw method.
3434 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3435 if (!pfr)
3436 return 0;
3437
3438 AutoSurface surface(pfr->hdc, this);
3439 if (!surface)
3440 return 0;
3441 AutoSurface surfaceMeasure(pfr->hdcTarget, this);
3442 if (!surfaceMeasure) {
3443 return 0;
3444 }
3445
3446 // Can't use measurements cached for screen
3447 posCache.Clear();
3448
3449 ViewStyle vsPrint(vs);
3450
3451 // Modify the view style for printing as do not normally want any of the transient features to be printed
3452 // Printing supports only the line number margin.
3453 int lineNumberIndex = -1;
3454 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3455 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3456 lineNumberIndex = margin;
3457 } else {
3458 vsPrint.ms[margin].width = 0;
3459 }
3460 }
3461 vsPrint.showMarkedLines = false;
3462 vsPrint.fixedColumnWidth = 0;
3463 vsPrint.zoomLevel = printMagnification;
3464 vsPrint.viewIndentationGuides = ivNone;
3465 // Don't show the selection when printing
3466 vsPrint.selbackset = false;
3467 vsPrint.selforeset = false;
3468 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3469 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3470 vsPrint.whitespaceBackgroundSet = false;
3471 vsPrint.whitespaceForegroundSet = false;
3472 vsPrint.showCaretLineBackground = false;
3473
3474 // Set colours for printing according to users settings
3475 for (size_t sty = 0;sty < vsPrint.stylesSize;sty++) {
3476 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3477 vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
3478 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
3479 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3480 vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
3481 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3482 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3483 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3484 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3485 if (sty <= STYLE_DEFAULT) {
3486 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3487 }
3488 }
3489 }
3490 // White background for the line numbers
3491 vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
3492
3493 vsPrint.Refresh(*surfaceMeasure);
3494 // Determining width must hapen after fonts have been realised in Refresh
3495 int lineNumberWidth = 0;
3496 if (lineNumberIndex >= 0) {
3497 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3498 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3499 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3500 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3501 }
3502 // Ensure colours are set up
3503 vsPrint.RefreshColourPalette(palette, true);
3504 vsPrint.RefreshColourPalette(palette, false);
3505
3506 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3507 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3508 if (linePrintLast < linePrintStart)
3509 linePrintLast = linePrintStart;
3510 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3511 if (linePrintLast > linePrintMax)
3512 linePrintLast = linePrintMax;
3513 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3514 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3515 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3516 int endPosPrint = pdoc->Length();
3517 if (linePrintLast < pdoc->LinesTotal())
3518 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3519
3520 // Ensure we are styled to where we are formatting.
3521 pdoc->EnsureStyledTo(endPosPrint);
3522
3523 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3524 int ypos = pfr->rc.top;
3525
3526 int lineDoc = linePrintStart;
3527
3528 int nPrintPos = pfr->chrg.cpMin;
3529 int visibleLine = 0;
3530 int widthPrint = pfr->rc.Width() - vsPrint.fixedColumnWidth;
3531 if (printWrapState == eWrapNone)
3532 widthPrint = LineLayout::wrapWidthInfinite;
3533
3534 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3535
3536 // When printing, the hdc and hdcTarget may be the same, so
3537 // changing the state of surfaceMeasure may change the underlying
3538 // state of surface. Therefore, any cached state is discarded before
3539 // using each surface.
3540 surfaceMeasure->FlushCachedState();
3541
3542 // Copy this line and its styles from the document into local arrays
3543 // and determine the x position at which each character starts.
3544 LineLayout ll(8000);
3545 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3546
3547 ll.containsCaret = false;
3548
3549 PRectangle rcLine;
3550 rcLine.left = pfr->rc.left;
3551 rcLine.top = ypos;
3552 rcLine.right = pfr->rc.right - 1;
3553 rcLine.bottom = ypos + vsPrint.lineHeight;
3554
3555 // When document line is wrapped over multiple display lines, find where
3556 // to start printing from to ensure a particular position is on the first
3557 // line of the page.
3558 if (visibleLine == 0) {
3559 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3560 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3561 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3562 visibleLine = -iwl;
3563 }
3564 }
3565
3566 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3567 visibleLine = -(ll.lines - 1);
3568 }
3569 }
3570
3571 if (draw && lineNumberWidth &&
3572 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3573 (visibleLine >= 0)) {
3574 char number[100];
3575 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3576 PRectangle rcNumber = rcLine;
3577 rcNumber.right = rcNumber.left + lineNumberWidth;
3578 // Right justify
3579 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3580 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3581 surface->FlushCachedState();
3582 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3583 ypos + vsPrint.maxAscent, number, istrlen(number),
3584 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
3585 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
3586 }
3587
3588 // Draw the line
3589 surface->FlushCachedState();
3590
3591 for (int iwl = 0; iwl < ll.lines; iwl++) {
3592 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3593 if (visibleLine >= 0) {
3594 if (draw) {
3595 rcLine.top = ypos;
3596 rcLine.bottom = ypos + vsPrint.lineHeight;
3597 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3598 }
3599 ypos += vsPrint.lineHeight;
3600 }
3601 visibleLine++;
3602 if (iwl == ll.lines - 1)
3603 nPrintPos = pdoc->LineStart(lineDoc + 1);
3604 else
3605 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3606 }
3607 }
3608
3609 ++lineDoc;
3610 }
3611
3612 // Clear cache so measurements are not used for screen
3613 posCache.Clear();
3614
3615 return nPrintPos;
3616 }
3617
3618 int Editor::TextWidth(int style, const char *text) {
3619 RefreshStyleData();
3620 AutoSurface surface(this);
3621 if (surface) {
3622 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3623 } else {
3624 return 1;
3625 }
3626 }
3627
3628 // Empty method is overridden on GTK+ to show / hide scrollbars
3629 void Editor::ReconfigureScrollBars() {}
3630
3631 void Editor::SetScrollBars() {
3632 RefreshStyleData();
3633
3634 int nMax = MaxScrollPos();
3635 int nPage = LinesOnScreen();
3636 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3637 if (modified) {
3638 DwellEnd(true);
3639 }
3640
3641 // TODO: ensure always showing as many lines as possible
3642 // May not be, if, for example, window made larger
3643 if (topLine > MaxScrollPos()) {
3644 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3645 SetVerticalScrollPos();
3646 Redraw();
3647 }
3648 if (modified) {
3649 if (!AbandonPaint())
3650 Redraw();
3651 }
3652 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3653 }
3654
3655 void Editor::ChangeSize() {
3656 DropGraphics();
3657 SetScrollBars();
3658 if (wrapState != eWrapNone) {
3659 PRectangle rcTextArea = GetClientRectangle();
3660 rcTextArea.left = vs.fixedColumnWidth;
3661 rcTextArea.right -= vs.rightMarginWidth;
3662 if (wrapWidth != rcTextArea.Width()) {
3663 NeedWrapping();
3664 Redraw();
3665 }
3666 }
3667 }
3668
3669 int Editor::InsertSpace(int position, unsigned int spaces) {
3670 if (spaces > 0) {
3671 std::string spaceText(spaces, ' ');
3672 pdoc->InsertString(position, spaceText.c_str(), spaces);
3673 position += spaces;
3674 }
3675 return position;
3676 }
3677
3678 void Editor::AddChar(char ch) {
3679 char s[2];
3680 s[0] = ch;
3681 s[1] = '\0';
3682 AddCharUTF(s, 1);
3683 }
3684
3685 void Editor::FilterSelections() {
3686 if (!additionalSelectionTyping && (sel.Count() > 1)) {
3687 SelectionRange rangeOnly = sel.RangeMain();
3688 InvalidateSelection(rangeOnly, true);
3689 sel.SetSelection(rangeOnly);
3690 }
3691 }
3692
3693 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3694 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3695 FilterSelections();
3696 {
3697 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
3698 for (size_t r=0; r<sel.Count(); r++) {
3699 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
3700 sel.Range(r).End().Position())) {
3701 int positionInsert = sel.Range(r).Start().Position();
3702 if (!sel.Range(r).Empty()) {
3703 if (sel.Range(r).Length()) {
3704 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
3705 sel.Range(r).ClearVirtualSpace();
3706 } else {
3707 // Range is all virtual so collapse to start of virtual space
3708 sel.Range(r).MinimizeVirtualSpace();
3709 }
3710 } else if (inOverstrike) {
3711 if (positionInsert < pdoc->Length()) {
3712 if (!IsEOLChar(pdoc->CharAt(positionInsert))) {
3713 pdoc->DelChar(positionInsert);
3714 sel.Range(r).ClearVirtualSpace();
3715 }
3716 }
3717 }
3718 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
3719 if (pdoc->InsertString(positionInsert, s, len)) {
3720 sel.Range(r).caret.SetPosition(positionInsert + len);
3721 sel.Range(r).anchor.SetPosition(positionInsert + len);
3722 }
3723 sel.Range(r).ClearVirtualSpace();
3724 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3725 if (wrapState != eWrapNone) {
3726 AutoSurface surface(this);
3727 if (surface) {
3728 WrapOneLine(surface, pdoc->LineFromPosition(positionInsert));
3729 }
3730 }
3731 }
3732 }
3733 }
3734 if (wrapState != eWrapNone) {
3735 SetScrollBars();
3736 }
3737 ThinRectangularRange();
3738 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3739 EnsureCaretVisible();
3740 // Avoid blinking during rapid typing:
3741 ShowCaretAtCurrentPosition();
3742 if (!caretSticky) {
3743 SetLastXChosen();
3744 }
3745
3746 if (treatAsDBCS) {
3747 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
3748 static_cast<unsigned char>(s[1]));
3749 } else {
3750 int byte = static_cast<unsigned char>(s[0]);
3751 if ((byte < 0xC0) || (1 == len)) {
3752 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
3753 // characters when not in UTF-8 mode.
3754 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
3755 // characters representing themselves.
3756 } else {
3757 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
3758 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
3759 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
3760 if (byte < 0xE0) {
3761 int byte2 = static_cast<unsigned char>(s[1]);
3762 if ((byte2 & 0xC0) == 0x80) {
3763 // Two-byte-character lead-byte followed by a trail-byte.
3764 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
3765 }
3766 // A two-byte-character lead-byte not followed by trail-byte
3767 // represents itself.
3768 } else if (byte < 0xF0) {
3769 int byte2 = static_cast<unsigned char>(s[1]);
3770 int byte3 = static_cast<unsigned char>(s[2]);
3771 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
3772 // Three-byte-character lead byte followed by two trail bytes.
3773 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
3774 (byte3 & 0x3F));
3775 }
3776 // A three-byte-character lead-byte not followed by two trail-bytes
3777 // represents itself.
3778 }
3779 }
3780 NotifyChar(byte);
3781 }
3782
3783 if (recordingMacro) {
3784 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
3785 }
3786 }
3787
3788 void Editor::ClearSelection() {
3789 if (!sel.IsRectangular())
3790 FilterSelections();
3791 UndoGroup ug(pdoc);
3792 for (size_t r=0; r<sel.Count(); r++) {
3793 if (!sel.Range(r).Empty()) {
3794 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
3795 sel.Range(r).End().Position())) {
3796 pdoc->DeleteChars(sel.Range(r).Start().Position(),
3797 sel.Range(r).Length());
3798 sel.Range(r) = sel.Range(r).Start();
3799 }
3800 }
3801 }
3802 ThinRectangularRange();
3803 sel.RemoveDuplicates();
3804 ClaimSelection();
3805 }
3806
3807 void Editor::ClearAll() {
3808 {
3809 UndoGroup ug(pdoc);
3810 if (0 != pdoc->Length()) {
3811 pdoc->DeleteChars(0, pdoc->Length());
3812 }
3813 if (!pdoc->IsReadOnly()) {
3814 cs.Clear();
3815 pdoc->AnnotationClearAll();
3816 pdoc->MarginClearAll();
3817 }
3818 }
3819 sel.Clear();
3820 SetTopLine(0);
3821 SetVerticalScrollPos();
3822 InvalidateStyleRedraw();
3823 }
3824
3825 void Editor::ClearDocumentStyle() {
3826 Decoration *deco = pdoc->decorations.root;
3827 while (deco) {
3828 // Save next in case deco deleted
3829 Decoration *decoNext = deco->next;
3830 if (deco->indicator < INDIC_CONTAINER) {
3831 pdoc->decorations.SetCurrentIndicator(deco->indicator);
3832 pdoc->DecorationFillRange(0, 0, pdoc->Length());
3833 }
3834 deco = decoNext;
3835 }
3836 pdoc->StartStyling(0, '\377');
3837 pdoc->SetStyleFor(pdoc->Length(), 0);
3838 cs.ShowAll();
3839 pdoc->ClearLevels();
3840 }
3841
3842 void Editor::CopyAllowLine() {
3843 SelectionText selectedText;
3844 CopySelectionRange(&selectedText, true);
3845 CopyToClipboard(selectedText);
3846 }
3847
3848 void Editor::Cut() {
3849 pdoc->CheckReadOnly();
3850 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
3851 Copy();
3852 ClearSelection();
3853 }
3854 }
3855
3856 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
3857 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
3858 return;
3859 }
3860 sel.Clear();
3861 sel.RangeMain() = SelectionRange(pos);
3862 int line = pdoc->LineFromPosition(sel.MainCaret());
3863 UndoGroup ug(pdoc);
3864 sel.RangeMain().caret = SelectionPosition(
3865 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
3866 int xInsert = XFromPosition(sel.RangeMain().caret);
3867 bool prevCr = false;
3868 while ((len > 0) && IsEOLChar(ptr[len-1]))
3869 len--;
3870 for (int i = 0; i < len; i++) {
3871 if (IsEOLChar(ptr[i])) {
3872 if ((ptr[i] == '\r') || (!prevCr))
3873 line++;
3874 if (line >= pdoc->LinesTotal()) {
3875 if (pdoc->eolMode != SC_EOL_LF)
3876 pdoc->InsertChar(pdoc->Length(), '\r');
3877 if (pdoc->eolMode != SC_EOL_CR)
3878 pdoc->InsertChar(pdoc->Length(), '\n');
3879 }
3880 // Pad the end of lines with spaces if required
3881 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
3882 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
3883 while (XFromPosition(sel.MainCaret()) < xInsert) {
3884 pdoc->InsertChar(sel.MainCaret(), ' ');
3885 sel.RangeMain().caret.Add(1);
3886 }
3887 }
3888 prevCr = ptr[i] == '\r';
3889 } else {
3890 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
3891 sel.RangeMain().caret.Add(1);
3892 prevCr = false;
3893 }
3894 }
3895 SetEmptySelection(pos);
3896 }
3897
3898 bool Editor::CanPaste() {
3899 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
3900 }
3901
3902 void Editor::Clear() {
3903 UndoGroup ug(pdoc);
3904 // If multiple selections, don't delete EOLS
3905 if (sel.Empty()) {
3906 for (size_t r=0; r<sel.Count(); r++) {
3907 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
3908 if (sel.Range(r).Start().VirtualSpace()) {
3909 if (sel.Range(r).anchor < sel.Range(r).caret)
3910 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
3911 else
3912 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
3913 }
3914 if ((sel.Count() == 1) || !IsEOLChar(pdoc->CharAt(sel.Range(r).caret.Position()))) {
3915 pdoc->DelChar(sel.Range(r).caret.Position());
3916 sel.Range(r).ClearVirtualSpace();
3917 } // else multiple selection so don't eat line ends
3918 } else {
3919 sel.Range(r).ClearVirtualSpace();
3920 }
3921 }
3922 } else {
3923 ClearSelection();
3924 }
3925 sel.RemoveDuplicates();
3926 }
3927
3928 void Editor::SelectAll() {
3929 sel.Clear();
3930 SetSelection(0, pdoc->Length());
3931 Redraw();
3932 }
3933
3934 void Editor::Undo() {
3935 if (pdoc->CanUndo()) {
3936 InvalidateCaret();
3937 int newPos = pdoc->Undo();
3938 if (newPos >= 0)
3939 SetEmptySelection(newPos);
3940 EnsureCaretVisible();
3941 }
3942 }
3943
3944 void Editor::Redo() {
3945 if (pdoc->CanRedo()) {
3946 int newPos = pdoc->Redo();
3947 if (newPos >= 0)
3948 SetEmptySelection(newPos);
3949 EnsureCaretVisible();
3950 }
3951 }
3952
3953 void Editor::DelChar() {
3954 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
3955 pdoc->DelChar(sel.MainCaret());
3956 }
3957 // Avoid blinking during rapid typing:
3958 ShowCaretAtCurrentPosition();
3959 }
3960
3961 void Editor::DelCharBack(bool allowLineStartDeletion) {
3962 if (!sel.IsRectangular())
3963 FilterSelections();
3964 if (sel.IsRectangular())
3965 allowLineStartDeletion = false;
3966 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
3967 if (sel.Empty()) {
3968 for (size_t r=0; r<sel.Count(); r++) {
3969 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
3970 if (sel.Range(r).caret.VirtualSpace()) {
3971 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
3972 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
3973 } else {
3974 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
3975 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
3976 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3977 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
3978 UndoGroup ugInner(pdoc, !ug.Needed());
3979 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3980 int indentationStep = pdoc->IndentSize();
3981 if (indentation % indentationStep == 0) {
3982 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3983 } else {
3984 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
3985 }
3986 // SetEmptySelection
3987 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
3988 pdoc->GetLineIndentPosition(lineCurrentPos));
3989 } else {
3990 pdoc->DelCharBack(sel.Range(r).caret.Position());
3991 }
3992 }
3993 }
3994 } else {
3995 sel.Range(r).ClearVirtualSpace();
3996 }
3997 }
3998 } else {
3999 ClearSelection();
4000 }
4001 sel.RemoveDuplicates();
4002 // Avoid blinking during rapid typing:
4003 ShowCaretAtCurrentPosition();
4004 }
4005
4006 void Editor::NotifyFocus(bool) {}
4007
4008 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4009 SCNotification scn = {0};
4010 scn.nmhdr.code = SCN_STYLENEEDED;
4011 scn.position = endStyleNeeded;
4012 NotifyParent(scn);
4013 }
4014
4015 void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
4016 NotifyStyleToNeeded(endStyleNeeded);
4017 }
4018
4019 void Editor::NotifyChar(int ch) {
4020 SCNotification scn = {0};
4021 scn.nmhdr.code = SCN_CHARADDED;
4022 scn.ch = ch;
4023 NotifyParent(scn);
4024 }
4025
4026 void Editor::NotifySavePoint(bool isSavePoint) {
4027 SCNotification scn = {0};
4028 if (isSavePoint) {
4029 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4030 } else {
4031 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4032 }
4033 NotifyParent(scn);
4034 }
4035
4036 void Editor::NotifyModifyAttempt() {
4037 SCNotification scn = {0};
4038 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4039 NotifyParent(scn);
4040 }
4041
4042 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4043 SCNotification scn = {0};
4044 scn.nmhdr.code = SCN_DOUBLECLICK;
4045 scn.line = LineFromLocation(pt);
4046 scn.position = PositionFromLocation(pt, true);
4047 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4048 (alt ? SCI_ALT : 0);
4049 NotifyParent(scn);
4050 }
4051
4052 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4053 SCNotification scn = {0};
4054 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4055 scn.position = position;
4056 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4057 (alt ? SCI_ALT : 0);
4058 NotifyParent(scn);
4059 }
4060
4061 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4062 SCNotification scn = {0};
4063 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4064 scn.position = position;
4065 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4066 (alt ? SCI_ALT : 0);
4067 NotifyParent(scn);
4068 }
4069
4070 void Editor::NotifyUpdateUI() {
4071 SCNotification scn = {0};
4072 scn.nmhdr.code = SCN_UPDATEUI;
4073 NotifyParent(scn);
4074 }
4075
4076 void Editor::NotifyPainted() {
4077 SCNotification scn = {0};
4078 scn.nmhdr.code = SCN_PAINTED;
4079 NotifyParent(scn);
4080 }
4081
4082 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4083 int mask = pdoc->decorations.AllOnFor(position);
4084 if ((click && mask) || pdoc->decorations.clickNotified) {
4085 SCNotification scn = {0};
4086 pdoc->decorations.clickNotified = click;
4087 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4088 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4089 scn.position = position;
4090 NotifyParent(scn);
4091 }
4092 }
4093
4094 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4095 int marginClicked = -1;
4096 int x = 0;
4097 for (int margin = 0; margin < ViewStyle::margins; margin++) {
4098 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
4099 marginClicked = margin;
4100 x += vs.ms[margin].width;
4101 }
4102 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4103 SCNotification scn = {0};
4104 scn.nmhdr.code = SCN_MARGINCLICK;
4105 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4106 (alt ? SCI_ALT : 0);
4107 scn.position = pdoc->LineStart(LineFromLocation(pt));
4108 scn.margin = marginClicked;
4109 NotifyParent(scn);
4110 return true;
4111 } else {
4112 return false;
4113 }
4114 }
4115
4116 void Editor::NotifyNeedShown(int pos, int len) {
4117 SCNotification scn = {0};
4118 scn.nmhdr.code = SCN_NEEDSHOWN;
4119 scn.position = pos;
4120 scn.length = len;
4121 NotifyParent(scn);
4122 }
4123
4124 void Editor::NotifyDwelling(Point pt, bool state) {
4125 SCNotification scn = {0};
4126 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4127 scn.position = PositionFromLocation(pt, true);
4128 scn.x = pt.x;
4129 scn.y = pt.y;
4130 NotifyParent(scn);
4131 }
4132
4133 void Editor::NotifyZoom() {
4134 SCNotification scn = {0};
4135 scn.nmhdr.code = SCN_ZOOM;
4136 NotifyParent(scn);
4137 }
4138
4139 // Notifications from document
4140 void Editor::NotifyModifyAttempt(Document*, void *) {
4141 //Platform::DebugPrintf("** Modify Attempt\n");
4142 NotifyModifyAttempt();
4143 }
4144
4145 void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
4146 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4147 NotifySavePoint(atSavePoint);
4148 }
4149
4150 void Editor::CheckModificationForWrap(DocModification mh) {
4151 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4152 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4153 if (wrapState != eWrapNone) {
4154 int lineDoc = pdoc->LineFromPosition(mh.position);
4155 int lines = Platform::Maximum(0, mh.linesAdded);
4156 NeedWrapping(lineDoc, lineDoc + lines + 1);
4157 }
4158 // Fix up annotation heights
4159 int lineDoc = pdoc->LineFromPosition(mh.position);
4160 int lines = Platform::Maximum(0, mh.linesAdded);
4161 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4162 }
4163 }
4164
4165 // Move a position so it is still after the same character as before the insertion.
4166 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4167 if (position > startInsertion) {
4168 return position + length;
4169 }
4170 return position;
4171 }
4172
4173 // Move a position so it is still after the same character as before the deletion if that
4174 // character is still present else after the previous surviving character.
4175 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4176 if (position > startDeletion) {
4177 int endDeletion = startDeletion + length;
4178 if (position > endDeletion) {
4179 return position - length;
4180 } else {
4181 return startDeletion;
4182 }
4183 } else {
4184 return position;
4185 }
4186 }
4187
4188 void Editor::NotifyModified(Document*, DocModification mh, void *) {
4189 needUpdateUI = true;
4190 if (paintState == painting) {
4191 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4192 }
4193 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4194 if (paintState == painting) {
4195 CheckForChangeOutsidePaint(
4196 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4197 } else {
4198 // Could check that change is before last visible line.
4199 Redraw();
4200 }
4201 }
4202 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4203 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4204 pdoc->IncrementStyleClock();
4205 }
4206 if (paintState == notPainting) {
4207 if (mh.position < pdoc->LineStart(topLine)) {
4208 // Styling performed before this view
4209 Redraw();
4210 } else {
4211 InvalidateRange(mh.position, mh.position + mh.length);
4212 }
4213 }
4214 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4215 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4216 }
4217 } else {
4218 // Move selection and brace highlights
4219 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4220 sel.MovePositions(true, mh.position, mh.length);
4221 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4222 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4223 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4224 sel.MovePositions(false, mh.position, mh.length);
4225 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4226 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4227 }
4228 if (cs.LinesDisplayed() < cs.LinesInDoc()) {
4229 // Some lines are hidden so may need shown.
4230 // TODO: check if the modified area is hidden.
4231 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4232 NotifyNeedShown(mh.position, 0);
4233 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4234 NotifyNeedShown(mh.position, mh.length);
4235 }
4236 }
4237 if (mh.linesAdded != 0) {
4238 // Update contraction state for inserted and removed lines
4239 // lineOfPos should be calculated in context of state before modification, shouldn't it
4240 int lineOfPos = pdoc->LineFromPosition(mh.position);
4241 if (mh.linesAdded > 0) {
4242 cs.InsertLines(lineOfPos, mh.linesAdded);
4243 } else {
4244 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4245 }
4246 }
4247 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4248 int lineDoc = pdoc->LineFromPosition(mh.position);
4249 if (vs.annotationVisible) {
4250 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4251 }
4252 }
4253 CheckModificationForWrap(mh);
4254 if (mh.linesAdded != 0) {
4255 // Avoid scrolling of display if change before current display
4256 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4257 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4258 if (newTop != topLine) {
4259 SetTopLine(newTop);
4260 SetVerticalScrollPos();
4261 }
4262 }
4263
4264 //Platform::DebugPrintf("** %x Doc Changed\n", this);
4265 // TODO: could invalidate from mh.startModification to end of screen
4266 //InvalidateRange(mh.position, mh.position + mh.length);
4267 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4268 Redraw();
4269 }
4270 } else {
4271 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
4272 // mh.position, mh.position + mh.length);
4273 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4274 InvalidateRange(mh.position, mh.position + mh.length);
4275 }
4276 }
4277 }
4278
4279 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4280 SetScrollBars();
4281 }
4282
4283 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4284 if ((paintState == notPainting) || !PaintContainsMargin()) {
4285 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4286 // Fold changes can affect the drawing of following lines so redraw whole margin
4287 RedrawSelMargin();
4288 } else {
4289 RedrawSelMargin(mh.line);
4290 }
4291 }
4292 }
4293
4294 // NOW pay the piper WRT "deferred" visual updates
4295 if (IsLastStep(mh)) {
4296 SetScrollBars();
4297 Redraw();
4298 }
4299
4300 // If client wants to see this modification
4301 if (mh.modificationType & modEventMask) {
4302 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4303 // Real modification made to text of document.
4304 NotifyChange(); // Send EN_CHANGE
4305 }
4306
4307 SCNotification scn = {0};
4308 scn.nmhdr.code = SCN_MODIFIED;
4309 scn.position = mh.position;
4310 scn.modificationType = mh.modificationType;
4311 scn.text = mh.text;
4312 scn.length = mh.length;
4313 scn.linesAdded = mh.linesAdded;
4314 scn.line = mh.line;
4315 scn.foldLevelNow = mh.foldLevelNow;
4316 scn.foldLevelPrev = mh.foldLevelPrev;
4317 scn.token = mh.token;
4318 scn.annotationLinesAdded = mh.annotationLinesAdded;
4319 NotifyParent(scn);
4320 }
4321 }
4322
4323 void Editor::NotifyDeleted(Document *, void *) {
4324 /* Do nothing */
4325 }
4326
4327 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4328
4329 // Enumerates all macroable messages
4330 switch (iMessage) {
4331 case SCI_CUT:
4332 case SCI_COPY:
4333 case SCI_PASTE:
4334 case SCI_CLEAR:
4335 case SCI_REPLACESEL:
4336 case SCI_ADDTEXT:
4337 case SCI_INSERTTEXT:
4338 case SCI_APPENDTEXT:
4339 case SCI_CLEARALL:
4340 case SCI_SELECTALL:
4341 case SCI_GOTOLINE:
4342 case SCI_GOTOPOS:
4343 case SCI_SEARCHANCHOR:
4344 case SCI_SEARCHNEXT:
4345 case SCI_SEARCHPREV:
4346 case SCI_LINEDOWN:
4347 case SCI_LINEDOWNEXTEND:
4348 case SCI_PARADOWN:
4349 case SCI_PARADOWNEXTEND:
4350 case SCI_LINEUP:
4351 case SCI_LINEUPEXTEND:
4352 case SCI_PARAUP:
4353 case SCI_PARAUPEXTEND:
4354 case SCI_CHARLEFT:
4355 case SCI_CHARLEFTEXTEND:
4356 case SCI_CHARRIGHT:
4357 case SCI_CHARRIGHTEXTEND:
4358 case SCI_WORDLEFT:
4359 case SCI_WORDLEFTEXTEND:
4360 case SCI_WORDRIGHT:
4361 case SCI_WORDRIGHTEXTEND:
4362 case SCI_WORDPARTLEFT:
4363 case SCI_WORDPARTLEFTEXTEND:
4364 case SCI_WORDPARTRIGHT:
4365 case SCI_WORDPARTRIGHTEXTEND:
4366 case SCI_WORDLEFTEND:
4367 case SCI_WORDLEFTENDEXTEND:
4368 case SCI_WORDRIGHTEND:
4369 case SCI_WORDRIGHTENDEXTEND:
4370 case SCI_HOME:
4371 case SCI_HOMEEXTEND:
4372 case SCI_LINEEND:
4373 case SCI_LINEENDEXTEND:
4374 case SCI_HOMEWRAP:
4375 case SCI_HOMEWRAPEXTEND:
4376 case SCI_LINEENDWRAP:
4377 case SCI_LINEENDWRAPEXTEND:
4378 case SCI_DOCUMENTSTART:
4379 case SCI_DOCUMENTSTARTEXTEND:
4380 case SCI_DOCUMENTEND:
4381 case SCI_DOCUMENTENDEXTEND:
4382 case SCI_STUTTEREDPAGEUP:
4383 case SCI_STUTTEREDPAGEUPEXTEND:
4384 case SCI_STUTTEREDPAGEDOWN:
4385 case SCI_STUTTEREDPAGEDOWNEXTEND:
4386 case SCI_PAGEUP:
4387 case SCI_PAGEUPEXTEND:
4388 case SCI_PAGEDOWN:
4389 case SCI_PAGEDOWNEXTEND:
4390 case SCI_EDITTOGGLEOVERTYPE:
4391 case SCI_CANCEL:
4392 case SCI_DELETEBACK:
4393 case SCI_TAB:
4394 case SCI_BACKTAB:
4395 case SCI_FORMFEED:
4396 case SCI_VCHOME:
4397 case SCI_VCHOMEEXTEND:
4398 case SCI_VCHOMEWRAP:
4399 case SCI_VCHOMEWRAPEXTEND:
4400 case SCI_DELWORDLEFT:
4401 case SCI_DELWORDRIGHT:
4402 case SCI_DELWORDRIGHTEND:
4403 case SCI_DELLINELEFT:
4404 case SCI_DELLINERIGHT:
4405 case SCI_LINECOPY:
4406 case SCI_LINECUT:
4407 case SCI_LINEDELETE:
4408 case SCI_LINETRANSPOSE:
4409 case SCI_LINEDUPLICATE:
4410 case SCI_LOWERCASE:
4411 case SCI_UPPERCASE:
4412 case SCI_LINESCROLLDOWN:
4413 case SCI_LINESCROLLUP:
4414 case SCI_DELETEBACKNOTLINE:
4415 case SCI_HOMEDISPLAY:
4416 case SCI_HOMEDISPLAYEXTEND:
4417 case SCI_LINEENDDISPLAY:
4418 case SCI_LINEENDDISPLAYEXTEND:
4419 case SCI_SETSELECTIONMODE:
4420 case SCI_LINEDOWNRECTEXTEND:
4421 case SCI_LINEUPRECTEXTEND:
4422 case SCI_CHARLEFTRECTEXTEND:
4423 case SCI_CHARRIGHTRECTEXTEND:
4424 case SCI_HOMERECTEXTEND:
4425 case SCI_VCHOMERECTEXTEND:
4426 case SCI_LINEENDRECTEXTEND:
4427 case SCI_PAGEUPRECTEXTEND:
4428 case SCI_PAGEDOWNRECTEXTEND:
4429 case SCI_SELECTIONDUPLICATE:
4430 case SCI_COPYALLOWLINE:
4431 break;
4432
4433 // Filter out all others like display changes. Also, newlines are redundant
4434 // with char insert messages.
4435 case SCI_NEWLINE:
4436 default:
4437 // printf("Filtered out %ld of macro recording\n", iMessage);
4438 return ;
4439 }
4440
4441 // Send notification
4442 SCNotification scn = {0};
4443 scn.nmhdr.code = SCN_MACRORECORD;
4444 scn.message = iMessage;
4445 scn.wParam = wParam;
4446 scn.lParam = lParam;
4447 NotifyParent(scn);
4448 }
4449
4450 /**
4451 * Force scroll and keep position relative to top of window.
4452 *
4453 * If stuttered = true and not already at first/last row, move to first/last row of window.
4454 * If stuttered = true and already at first/last row, scroll as normal.
4455 */
4456 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4457 int topLineNew, newPos;
4458
4459 // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
4460 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4461 int topStutterLine = topLine + caretYSlop;
4462 int bottomStutterLine =
4463 pdoc->LineFromPosition(PositionFromLocation(
4464 Point(lastXChosen, direction * vs.lineHeight * LinesToScroll())))
4465 - caretYSlop - 1;
4466
4467 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4468 topLineNew = topLine;
4469 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop));
4470
4471 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4472 topLineNew = topLine;
4473 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
4474
4475 } else {
4476 Point pt = LocationFromPosition(sel.MainCaret());
4477
4478 topLineNew = Platform::Clamp(
4479 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4480 newPos = PositionFromLocation(
4481 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
4482 }
4483
4484 if (topLineNew != topLine) {
4485 SetTopLine(topLineNew);
4486 MovePositionTo(SelectionPosition(newPos), selt);
4487 Redraw();
4488 SetVerticalScrollPos();
4489 } else {
4490 MovePositionTo(SelectionPosition(newPos), selt);
4491 }
4492 }
4493
4494 void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
4495 UndoGroup ug(pdoc);
4496 for (size_t r=0; r<sel.Count(); r++) {
4497 SelectionRange current = sel.Range(r);
4498 pdoc->ChangeCase(Range(current.Start().Position(), current.End().Position()),
4499 makeUpperCase);
4500 // Automatic movement cuts off last character so reset to exactly the same as it was.
4501 sel.Range(r) = current;
4502 }
4503 }
4504
4505 void Editor::LineTranspose() {
4506 int line = pdoc->LineFromPosition(sel.MainCaret());
4507 if (line > 0) {
4508 UndoGroup ug(pdoc);
4509 int startPrev = pdoc->LineStart(line - 1);
4510 int endPrev = pdoc->LineEnd(line - 1);
4511 int start = pdoc->LineStart(line);
4512 int end = pdoc->LineEnd(line);
4513 char *line1 = CopyRange(startPrev, endPrev);
4514 int len1 = endPrev - startPrev;
4515 char *line2 = CopyRange(start, end);
4516 int len2 = end - start;
4517 pdoc->DeleteChars(start, len2);
4518 pdoc->DeleteChars(startPrev, len1);
4519 pdoc->InsertString(startPrev, line2, len2);
4520 pdoc->InsertString(start - len1 + len2, line1, len1);
4521 MovePositionTo(SelectionPosition(start - len1 + len2));
4522 delete []line1;
4523 delete []line2;
4524 }
4525 }
4526
4527 void Editor::Duplicate(bool forLine) {
4528 if (sel.Empty()) {
4529 forLine = true;
4530 }
4531 UndoGroup ug(pdoc, sel.Count() > 1);
4532 SelectionPosition last;
4533 const char *eol = "";
4534 int eolLen = 0;
4535 if (forLine) {
4536 eol = StringFromEOLMode(pdoc->eolMode);
4537 eolLen = istrlen(eol);
4538 }
4539 for (size_t r=0; r<sel.Count(); r++) {
4540 SelectionPosition start = sel.Range(r).Start();
4541 SelectionPosition end = sel.Range(r).End();
4542 if (forLine) {
4543 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4544 start = SelectionPosition(pdoc->LineStart(line));
4545 end = SelectionPosition(pdoc->LineEnd(line));
4546 }
4547 char *text = CopyRange(start.Position(), end.Position());
4548 if (forLine)
4549 pdoc->InsertString(end.Position(), eol, eolLen);
4550 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
4551 delete []text;
4552 }
4553 if (sel.Count() && sel.IsRectangular()) {
4554 SelectionPosition last = sel.Last();
4555 if (forLine) {
4556 int line = pdoc->LineFromPosition(last.Position());
4557 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
4558 }
4559 if (sel.Rectangular().anchor > sel.Rectangular().caret)
4560 sel.Rectangular().anchor = last;
4561 else
4562 sel.Rectangular().caret = last;
4563 SetRectangularRange();
4564 }
4565 }
4566
4567 void Editor::CancelModes() {
4568 sel.SetMoveExtends(false);
4569 }
4570
4571 void Editor::NewLine() {
4572 ClearSelection();
4573 const char *eol = "\n";
4574 if (pdoc->eolMode == SC_EOL_CRLF) {
4575 eol = "\r\n";
4576 } else if (pdoc->eolMode == SC_EOL_CR) {
4577 eol = "\r";
4578 } // else SC_EOL_LF -> "\n" already set
4579 if (pdoc->InsertCString(sel.MainCaret(), eol)) {
4580 SetEmptySelection(sel.MainCaret() + istrlen(eol));
4581 while (*eol) {
4582 NotifyChar(*eol);
4583 if (recordingMacro) {
4584 char txt[2];
4585 txt[0] = *eol;
4586 txt[1] = '\0';
4587 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
4588 }
4589 eol++;
4590 }
4591 }
4592 SetLastXChosen();
4593 SetScrollBars();
4594 EnsureCaretVisible();
4595 // Avoid blinking during rapid typing:
4596 ShowCaretAtCurrentPosition();
4597 }
4598
4599 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
4600 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
4601 if (sel.IsRectangular()) {
4602 if (selt == Selection::noSel) {
4603 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
4604 } else {
4605 caretToUse = sel.Rectangular().caret;
4606 }
4607 }
4608 Point pt = LocationFromPosition(caretToUse);
4609 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
4610 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
4611 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
4612 int commentLines = vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0;
4613 SelectionPosition posNew = SPositionFromLocation(
4614 Point(lastXChosen, pt.y + direction * vs.lineHeight), false, false, UserVirtualSpace());
4615 if ((direction > 0) && (subLine >= (cs.GetHeight(lineDoc) - 1 - commentLines))) {
4616 posNew = SPositionFromLocation(
4617 Point(lastXChosen, pt.y + (commentLines + 1) * vs.lineHeight), false, false, UserVirtualSpace());
4618 }
4619 if (direction < 0) {
4620 // Line wrapping may lead to a location on the same line, so
4621 // seek back if that is the case.
4622 // There is an equivalent case when moving down which skips
4623 // over a line but as that does not trap the user it is fine.
4624 Point ptNew = LocationFromPosition(posNew.Position());
4625 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
4626 posNew.Add(- 1);
4627 posNew.SetVirtualSpace(0);
4628 ptNew = LocationFromPosition(posNew.Position());
4629 }
4630 }
4631 MovePositionTo(posNew, selt);
4632 }
4633
4634 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
4635 int lineDoc, savedPos = sel.MainCaret();
4636 do {
4637 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
4638 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
4639 if (direction > 0) {
4640 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
4641 if (selt == Selection::noSel) {
4642 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
4643 }
4644 break;
4645 }
4646 }
4647 } while (!cs.GetVisible(lineDoc));
4648 }
4649
4650 int Editor::StartEndDisplayLine(int pos, bool start) {
4651 RefreshStyleData();
4652 int line = pdoc->LineFromPosition(pos);
4653 AutoSurface surface(this);
4654 AutoLineLayout ll(llc, RetrieveLineLayout(line));
4655 int posRet = INVALID_POSITION;
4656 if (surface && ll) {
4657 unsigned int posLineStart = pdoc->LineStart(line);
4658 LayoutLine(line, surface, vs, ll, wrapWidth);
4659 int posInLine = pos - posLineStart;
4660 if (posInLine <= ll->maxLineLength) {
4661 for (int subLine = 0; subLine < ll->lines; subLine++) {
4662 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
4663 if (start) {
4664 posRet = ll->LineStart(subLine) + posLineStart;
4665 } else {
4666 if (subLine == ll->lines - 1)
4667 posRet = ll->LineStart(subLine + 1) + posLineStart;
4668 else
4669 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
4670 }
4671 }
4672 }
4673 }
4674 }
4675 if (posRet == INVALID_POSITION) {
4676 return pos;
4677 } else {
4678 return posRet;
4679 }
4680 }
4681
4682 int Editor::KeyCommand(unsigned int iMessage) {
4683 switch (iMessage) {
4684 case SCI_LINEDOWN:
4685 CursorUpOrDown(1);
4686 break;
4687 case SCI_LINEDOWNEXTEND:
4688 CursorUpOrDown(1, Selection::selStream);
4689 break;
4690 case SCI_LINEDOWNRECTEXTEND:
4691 CursorUpOrDown(1, Selection::selRectangle);
4692 break;
4693 case SCI_PARADOWN:
4694 ParaUpOrDown(1);
4695 break;
4696 case SCI_PARADOWNEXTEND:
4697 ParaUpOrDown(1, Selection::selStream);
4698 break;
4699 case SCI_LINESCROLLDOWN:
4700 ScrollTo(topLine + 1);
4701 MoveCaretInsideView(false);
4702 break;
4703 case SCI_LINEUP:
4704 CursorUpOrDown(-1);
4705 break;
4706 case SCI_LINEUPEXTEND:
4707 CursorUpOrDown(-1, Selection::selStream);
4708 break;
4709 case SCI_LINEUPRECTEXTEND:
4710 CursorUpOrDown(-1, Selection::selRectangle);
4711 break;
4712 case SCI_PARAUP:
4713 ParaUpOrDown(-1);
4714 break;
4715 case SCI_PARAUPEXTEND:
4716 ParaUpOrDown(-1, Selection::selStream);
4717 break;
4718 case SCI_LINESCROLLUP:
4719 ScrollTo(topLine - 1);
4720 MoveCaretInsideView(false);
4721 break;
4722 case SCI_CHARLEFT:
4723 if (SelectionEmpty() || sel.MoveExtends()) {
4724 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4725 SelectionPosition spCaret = sel.RangeMain().caret;
4726 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4727 MovePositionTo(spCaret);
4728 } else {
4729 MovePositionTo(MovePositionSoVisible(
4730 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
4731 }
4732 } else {
4733 MovePositionTo(sel.LimitsForRectangularElseMain().start);
4734 }
4735 SetLastXChosen();
4736 break;
4737 case SCI_CHARLEFTEXTEND:
4738 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4739 SelectionPosition spCaret = sel.RangeMain().caret;
4740 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4741 MovePositionTo(spCaret, Selection::selStream);
4742 } else {
4743 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
4744 }
4745 SetLastXChosen();
4746 break;
4747 case SCI_CHARLEFTRECTEXTEND:
4748 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
4749 SelectionPosition spCaret = sel.RangeMain().caret;
4750 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
4751 MovePositionTo(spCaret, Selection::selRectangle);
4752 } else {
4753 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
4754 }
4755 SetLastXChosen();
4756 break;
4757 case SCI_CHARRIGHT:
4758 if (SelectionEmpty() || sel.MoveExtends()) {
4759 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4760 SelectionPosition spCaret = sel.RangeMain().caret;
4761 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4762 MovePositionTo(spCaret);
4763 } else {
4764 MovePositionTo(MovePositionSoVisible(
4765 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
4766 }
4767 } else {
4768 MovePositionTo(sel.LimitsForRectangularElseMain().end);
4769 }
4770 SetLastXChosen();
4771 break;
4772 case SCI_CHARRIGHTEXTEND:
4773 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4774 SelectionPosition spCaret = sel.RangeMain().caret;
4775 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4776 MovePositionTo(spCaret, Selection::selStream);
4777 } else {
4778 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
4779 }
4780 SetLastXChosen();
4781 break;
4782 case SCI_CHARRIGHTRECTEXTEND:
4783 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
4784 SelectionPosition spCaret = sel.RangeMain().caret;
4785 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
4786 MovePositionTo(spCaret, Selection::selRectangle);
4787 } else {
4788 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
4789 }
4790 SetLastXChosen();
4791 break;
4792 case SCI_WORDLEFT:
4793 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
4794 SetLastXChosen();
4795 break;
4796 case SCI_WORDLEFTEXTEND:
4797 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
4798 SetLastXChosen();
4799 break;
4800 case SCI_WORDRIGHT:
4801 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
4802 SetLastXChosen();
4803 break;
4804 case SCI_WORDRIGHTEXTEND:
4805 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
4806 SetLastXChosen();
4807 break;
4808
4809 case SCI_WORDLEFTEND:
4810 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
4811 SetLastXChosen();
4812 break;
4813 case SCI_WORDLEFTENDEXTEND:
4814 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
4815 SetLastXChosen();
4816 break;
4817 case SCI_WORDRIGHTEND:
4818 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
4819 SetLastXChosen();
4820 break;
4821 case SCI_WORDRIGHTENDEXTEND:
4822 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
4823 SetLastXChosen();
4824 break;
4825
4826 case SCI_HOME:
4827 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
4828 SetLastXChosen();
4829 break;
4830 case SCI_HOMEEXTEND:
4831 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
4832 SetLastXChosen();
4833 break;
4834 case SCI_HOMERECTEXTEND:
4835 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
4836 SetLastXChosen();
4837 break;
4838 case SCI_LINEEND:
4839 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
4840 SetLastXChosen();
4841 break;
4842 case SCI_LINEENDEXTEND:
4843 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
4844 SetLastXChosen();
4845 break;
4846 case SCI_LINEENDRECTEXTEND:
4847 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
4848 SetLastXChosen();
4849 break;
4850 case SCI_HOMEWRAP: {
4851 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4852 if (sel.RangeMain().caret <= homePos)
4853 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
4854 MovePositionTo(homePos);
4855 SetLastXChosen();
4856 }
4857 break;
4858 case SCI_HOMEWRAPEXTEND: {
4859 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4860 if (sel.RangeMain().caret <= homePos)
4861 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
4862 MovePositionTo(homePos, Selection::selStream);
4863 SetLastXChosen();
4864 }
4865 break;
4866 case SCI_LINEENDWRAP: {
4867 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
4868 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
4869 if (endPos > realEndPos // if moved past visible EOLs
4870 || sel.RangeMain().caret >= endPos) // if at end of display line already
4871 endPos = realEndPos;
4872 MovePositionTo(endPos);
4873 SetLastXChosen();
4874 }
4875 break;
4876 case SCI_LINEENDWRAPEXTEND: {
4877 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
4878 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
4879 if (endPos > realEndPos // if moved past visible EOLs
4880 || sel.RangeMain().caret >= endPos) // if at end of display line already
4881 endPos = realEndPos;
4882 MovePositionTo(endPos, Selection::selStream);
4883 SetLastXChosen();
4884 }
4885 break;
4886 case SCI_DOCUMENTSTART:
4887 MovePositionTo(0);
4888 SetLastXChosen();
4889 break;
4890 case SCI_DOCUMENTSTARTEXTEND:
4891 MovePositionTo(0, Selection::selStream);
4892 SetLastXChosen();
4893 break;
4894 case SCI_DOCUMENTEND:
4895 MovePositionTo(pdoc->Length());
4896 SetLastXChosen();
4897 break;
4898 case SCI_DOCUMENTENDEXTEND:
4899 MovePositionTo(pdoc->Length(), Selection::selStream);
4900 SetLastXChosen();
4901 break;
4902 case SCI_STUTTEREDPAGEUP:
4903 PageMove(-1, Selection::noSel, true);
4904 break;
4905 case SCI_STUTTEREDPAGEUPEXTEND:
4906 PageMove(-1, Selection::selStream, true);
4907 break;
4908 case SCI_STUTTEREDPAGEDOWN:
4909 PageMove(1, Selection::noSel, true);
4910 break;
4911 case SCI_STUTTEREDPAGEDOWNEXTEND:
4912 PageMove(1, Selection::selStream, true);
4913 break;
4914 case SCI_PAGEUP:
4915 PageMove(-1);
4916 break;
4917 case SCI_PAGEUPEXTEND:
4918 PageMove(-1, Selection::selStream);
4919 break;
4920 case SCI_PAGEUPRECTEXTEND:
4921 PageMove(-1, Selection::selRectangle);
4922 break;
4923 case SCI_PAGEDOWN:
4924 PageMove(1);
4925 break;
4926 case SCI_PAGEDOWNEXTEND:
4927 PageMove(1, Selection::selStream);
4928 break;
4929 case SCI_PAGEDOWNRECTEXTEND:
4930 PageMove(1, Selection::selRectangle);
4931 break;
4932 case SCI_EDITTOGGLEOVERTYPE:
4933 inOverstrike = !inOverstrike;
4934 DropCaret();
4935 ShowCaretAtCurrentPosition();
4936 NotifyUpdateUI();
4937 break;
4938 case SCI_CANCEL: // Cancel any modes - handled in subclass
4939 // Also unselect text
4940 CancelModes();
4941 break;
4942 case SCI_DELETEBACK:
4943 DelCharBack(true);
4944 if (!caretSticky) {
4945 SetLastXChosen();
4946 }
4947 EnsureCaretVisible();
4948 break;
4949 case SCI_DELETEBACKNOTLINE:
4950 DelCharBack(false);
4951 if (!caretSticky) {
4952 SetLastXChosen();
4953 }
4954 EnsureCaretVisible();
4955 break;
4956 case SCI_TAB:
4957 Indent(true);
4958 if (!caretSticky) {
4959 SetLastXChosen();
4960 }
4961 EnsureCaretVisible();
4962 ShowCaretAtCurrentPosition(); // Avoid blinking
4963 break;
4964 case SCI_BACKTAB:
4965 Indent(false);
4966 if (!caretSticky) {
4967 SetLastXChosen();
4968 }
4969 EnsureCaretVisible();
4970 ShowCaretAtCurrentPosition(); // Avoid blinking
4971 break;
4972 case SCI_NEWLINE:
4973 NewLine();
4974 break;
4975 case SCI_FORMFEED:
4976 AddChar('\f');
4977 break;
4978 case SCI_VCHOME:
4979 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
4980 SetLastXChosen();
4981 break;
4982 case SCI_VCHOMEEXTEND:
4983 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
4984 SetLastXChosen();
4985 break;
4986 case SCI_VCHOMERECTEXTEND:
4987 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
4988 SetLastXChosen();
4989 break;
4990 case SCI_VCHOMEWRAP: {
4991 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
4992 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
4993 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
4994 homePos = viewLineStart;
4995
4996 MovePositionTo(homePos);
4997 SetLastXChosen();
4998 }
4999 break;
5000 case SCI_VCHOMEWRAPEXTEND: {
5001 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5002 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5003 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5004 homePos = viewLineStart;
5005
5006 MovePositionTo(homePos, Selection::selStream);
5007 SetLastXChosen();
5008 }
5009 break;
5010 case SCI_ZOOMIN:
5011 if (vs.zoomLevel < 20) {
5012 vs.zoomLevel++;
5013 InvalidateStyleRedraw();
5014 NotifyZoom();
5015 }
5016 break;
5017 case SCI_ZOOMOUT:
5018 if (vs.zoomLevel > -10) {
5019 vs.zoomLevel--;
5020 InvalidateStyleRedraw();
5021 NotifyZoom();
5022 }
5023 break;
5024 case SCI_DELWORDLEFT: {
5025 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5026 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5027 sel.RangeMain().ClearVirtualSpace();
5028 SetLastXChosen();
5029 }
5030 break;
5031 case SCI_DELWORDRIGHT: {
5032 UndoGroup ug(pdoc);
5033 sel.RangeMain().caret = SelectionPosition(
5034 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5035 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5036 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5037 }
5038 break;
5039 case SCI_DELWORDRIGHTEND: {
5040 UndoGroup ug(pdoc);
5041 sel.RangeMain().caret = SelectionPosition(
5042 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5043 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5044 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5045 }
5046 break;
5047 case SCI_DELLINELEFT: {
5048 int line = pdoc->LineFromPosition(sel.MainCaret());
5049 int start = pdoc->LineStart(line);
5050 pdoc->DeleteChars(start, sel.MainCaret() - start);
5051 sel.RangeMain().ClearVirtualSpace();
5052 SetLastXChosen();
5053 }
5054 break;
5055 case SCI_DELLINERIGHT: {
5056 int line = pdoc->LineFromPosition(sel.MainCaret());
5057 int end = pdoc->LineEnd(line);
5058 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5059 }
5060 break;
5061 case SCI_LINECOPY: {
5062 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5063 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5064 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5065 pdoc->LineStart(lineEnd + 1));
5066 }
5067 break;
5068 case SCI_LINECUT: {
5069 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5070 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5071 int start = pdoc->LineStart(lineStart);
5072 int end = pdoc->LineStart(lineEnd + 1);
5073 SetSelection(start, end);
5074 Cut();
5075 SetLastXChosen();
5076 }
5077 break;
5078 case SCI_LINEDELETE: {
5079 int line = pdoc->LineFromPosition(sel.MainCaret());
5080 int start = pdoc->LineStart(line);
5081 int end = pdoc->LineStart(line + 1);
5082 pdoc->DeleteChars(start, end - start);
5083 }
5084 break;
5085 case SCI_LINETRANSPOSE:
5086 LineTranspose();
5087 break;
5088 case SCI_LINEDUPLICATE:
5089 Duplicate(true);
5090 break;
5091 case SCI_SELECTIONDUPLICATE:
5092 Duplicate(false);
5093 break;
5094 case SCI_LOWERCASE:
5095 ChangeCaseOfSelection(false);
5096 break;
5097 case SCI_UPPERCASE:
5098 ChangeCaseOfSelection(true);
5099 break;
5100 case SCI_WORDPARTLEFT:
5101 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5102 SetLastXChosen();
5103 break;
5104 case SCI_WORDPARTLEFTEXTEND:
5105 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5106 SetLastXChosen();
5107 break;
5108 case SCI_WORDPARTRIGHT:
5109 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5110 SetLastXChosen();
5111 break;
5112 case SCI_WORDPARTRIGHTEXTEND:
5113 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5114 SetLastXChosen();
5115 break;
5116 case SCI_HOMEDISPLAY:
5117 MovePositionTo(MovePositionSoVisible(
5118 StartEndDisplayLine(sel.MainCaret(), true), -1));
5119 SetLastXChosen();
5120 break;
5121 case SCI_HOMEDISPLAYEXTEND:
5122 MovePositionTo(MovePositionSoVisible(
5123 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5124 SetLastXChosen();
5125 break;
5126 case SCI_LINEENDDISPLAY:
5127 MovePositionTo(MovePositionSoVisible(
5128 StartEndDisplayLine(sel.MainCaret(), false), 1));
5129 SetLastXChosen();
5130 break;
5131 case SCI_LINEENDDISPLAYEXTEND:
5132 MovePositionTo(MovePositionSoVisible(
5133 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5134 SetLastXChosen();
5135 break;
5136 }
5137 return 0;
5138 }
5139
5140 int Editor::KeyDefault(int, int) {
5141 return 0;
5142 }
5143
5144 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5145 DwellEnd(false);
5146 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5147 (alt ? SCI_ALT : 0);
5148 int msg = kmap.Find(key, modifiers);
5149 if (msg) {
5150 if (consumed)
5151 *consumed = true;
5152 return WndProc(msg, 0, 0);
5153 } else {
5154 if (consumed)
5155 *consumed = false;
5156 return KeyDefault(key, modifiers);
5157 }
5158 }
5159
5160 void Editor::SetWhitespaceVisible(int view) {
5161 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
5162 }
5163
5164 int Editor::GetWhitespaceVisible() {
5165 return vs.viewWhitespace;
5166 }
5167
5168 void Editor::Indent(bool forwards) {
5169 for (size_t r=0; r<sel.Count(); r++) {
5170 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5171 int caretPosition = sel.Range(r).caret.Position();
5172 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5173 if (lineOfAnchor == lineCurrentPos) {
5174 if (forwards) {
5175 UndoGroup ug(pdoc);
5176 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5177 caretPosition = sel.Range(r).caret.Position();
5178 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5179 pdoc->tabIndents) {
5180 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5181 int indentationStep = pdoc->IndentSize();
5182 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5183 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5184 } else {
5185 if (pdoc->useTabs) {
5186 pdoc->InsertChar(caretPosition, '\t');
5187 sel.Range(r) = SelectionRange(caretPosition+1);
5188 } else {
5189 int numSpaces = (pdoc->tabInChars) -
5190 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5191 if (numSpaces < 1)
5192 numSpaces = pdoc->tabInChars;
5193 for (int i = 0; i < numSpaces; i++) {
5194 pdoc->InsertChar(caretPosition + i, ' ');
5195 }
5196 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5197 }
5198 }
5199 } else {
5200 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5201 pdoc->tabIndents) {
5202 UndoGroup ug(pdoc);
5203 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5204 int indentationStep = pdoc->IndentSize();
5205 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5206 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
5207 } else {
5208 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5209 pdoc->tabInChars;
5210 if (newColumn < 0)
5211 newColumn = 0;
5212 int newPos = caretPosition;
5213 while (pdoc->GetColumn(newPos) > newColumn)
5214 newPos--;
5215 sel.Range(r) = SelectionRange(newPos);
5216 }
5217 }
5218 } else { // Multiline
5219 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5220 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5221 // Multiple lines selected so indent / dedent
5222 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5223 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5224 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5225 lineBottomSel--; // If not selecting any characters on a line, do not indent
5226 {
5227 UndoGroup ug(pdoc);
5228 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5229 }
5230 if (lineOfAnchor < lineCurrentPos) {
5231 if (currentPosPosOnLine == 0)
5232 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5233 else
5234 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5235 } else {
5236 if (anchorPosOnLine == 0)
5237 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5238 else
5239 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5240 }
5241 }
5242 }
5243 }
5244
5245 /**
5246 * Search of a text in the document, in the given range.
5247 * @return The position of the found text, -1 if not found.
5248 */
5249 long Editor::FindText(
5250 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5251 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5252 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5253
5254 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5255 int lengthFound = istrlen(ft->lpstrText);
5256 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5257 (wParam & SCFIND_MATCHCASE) != 0,
5258 (wParam & SCFIND_WHOLEWORD) != 0,
5259 (wParam & SCFIND_WORDSTART) != 0,
5260 (wParam & SCFIND_REGEXP) != 0,
5261 wParam,
5262 &lengthFound);
5263 if (pos != -1) {
5264 ft->chrgText.cpMin = pos;
5265 ft->chrgText.cpMax = pos + lengthFound;
5266 }
5267 return pos;
5268 }
5269
5270 /**
5271 * Relocatable search support : Searches relative to current selection
5272 * point and sets the selection to the found text range with
5273 * each search.
5274 */
5275 /**
5276 * Anchor following searches at current selection start: This allows
5277 * multiple incremental interactive searches to be macro recorded
5278 * while still setting the selection to found text so the find/select
5279 * operation is self-contained.
5280 */
5281 void Editor::SearchAnchor() {
5282 searchAnchor = SelectionStart().Position();
5283 }
5284
5285 /**
5286 * Find text from current search anchor: Must call @c SearchAnchor first.
5287 * Used for next text and previous text requests.
5288 * @return The position of the found text, -1 if not found.
5289 */
5290 long Editor::SearchText(
5291 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5292 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5293 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5294 sptr_t lParam) { ///< The text to search for.
5295
5296 const char *txt = reinterpret_cast<char *>(lParam);
5297 int pos;
5298 int lengthFound = istrlen(txt);
5299 if (iMessage == SCI_SEARCHNEXT) {
5300 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5301 (wParam & SCFIND_MATCHCASE) != 0,
5302 (wParam & SCFIND_WHOLEWORD) != 0,
5303 (wParam & SCFIND_WORDSTART) != 0,
5304 (wParam & SCFIND_REGEXP) != 0,
5305 wParam,
5306 &lengthFound);
5307 } else {
5308 pos = pdoc->FindText(searchAnchor, 0, txt,
5309 (wParam & SCFIND_MATCHCASE) != 0,
5310 (wParam & SCFIND_WHOLEWORD) != 0,
5311 (wParam & SCFIND_WORDSTART) != 0,
5312 (wParam & SCFIND_REGEXP) != 0,
5313 wParam,
5314 &lengthFound);
5315 }
5316
5317 if (pos != -1) {
5318 SetSelection(pos, pos + lengthFound);
5319 }
5320
5321 return pos;
5322 }
5323
5324 /**
5325 * Search for text in the target range of the document.
5326 * @return The position of the found text, -1 if not found.
5327 */
5328 long Editor::SearchInTarget(const char *text, int length) {
5329 int lengthFound = length;
5330 int pos = pdoc->FindText(targetStart, targetEnd, text,
5331 (searchFlags & SCFIND_MATCHCASE) != 0,
5332 (searchFlags & SCFIND_WHOLEWORD) != 0,
5333 (searchFlags & SCFIND_WORDSTART) != 0,
5334 (searchFlags & SCFIND_REGEXP) != 0,
5335 searchFlags,
5336 &lengthFound);
5337 if (pos != -1) {
5338 targetStart = pos;
5339 targetEnd = pos + lengthFound;
5340 }
5341 return pos;
5342 }
5343
5344 void Editor::GoToLine(int lineNo) {
5345 if (lineNo > pdoc->LinesTotal())
5346 lineNo = pdoc->LinesTotal();
5347 if (lineNo < 0)
5348 lineNo = 0;
5349 SetEmptySelection(pdoc->LineStart(lineNo));
5350 ShowCaretAtCurrentPosition();
5351 EnsureCaretVisible();
5352 }
5353
5354 static bool Close(Point pt1, Point pt2) {
5355 if (abs(pt1.x - pt2.x) > 3)
5356 return false;
5357 if (abs(pt1.y - pt2.y) > 3)
5358 return false;
5359 return true;
5360 }
5361
5362 char *Editor::CopyRange(int start, int end) {
5363 char *text = 0;
5364 if (start < end) {
5365 int len = end - start;
5366 text = new char[len + 1];
5367 for (int i = 0; i < len; i++) {
5368 text[i] = pdoc->CharAt(start + i);
5369 }
5370 text[len] = '\0';
5371 }
5372 return text;
5373 }
5374
5375 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
5376 if (sel.Empty()) {
5377 if (allowLineCopy) {
5378 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
5379 int start = pdoc->LineStart(currentLine);
5380 int end = pdoc->LineEnd(currentLine);
5381
5382 char *text = CopyRange(start, end);
5383 int textLen = text ? strlen(text) : 0;
5384 // include room for \r\n\0
5385 textLen += 3;
5386 char *textWithEndl = new char[textLen];
5387 textWithEndl[0] = '\0';
5388 if (text)
5389 strncat(textWithEndl, text, textLen);
5390 if (pdoc->eolMode != SC_EOL_LF)
5391 strncat(textWithEndl, "\r", textLen);
5392 if (pdoc->eolMode != SC_EOL_CR)
5393 strncat(textWithEndl, "\n", textLen);
5394 ss->Set(textWithEndl, strlen(textWithEndl) + 1,
5395 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
5396 delete []text;
5397 }
5398 } else {
5399 int delimiterLength = 0;
5400 if (sel.selType == Selection::selRectangle) {
5401 if (pdoc->eolMode == SC_EOL_CRLF) {
5402 delimiterLength = 2;
5403 } else {
5404 delimiterLength = 1;
5405 }
5406 }
5407 int size = sel.Length() + delimiterLength * sel.Count();
5408 char *text = new char[size + 1];
5409 int j = 0;
5410 wxVector<SelectionRange> rangesInOrder = sel.RangesCopy();
5411 if (sel.selType == Selection::selRectangle)
5412 wxVectorSort(rangesInOrder);
5413 for (size_t r=0; r<rangesInOrder.size(); r++) {
5414 SelectionRange current = rangesInOrder[r];
5415 for (int i = current.Start().Position();
5416 i < current.End().Position();
5417 i++) {
5418 text[j++] = pdoc->CharAt(i);
5419 }
5420 if (sel.selType == Selection::selRectangle) {
5421 if (pdoc->eolMode != SC_EOL_LF) {
5422 text[j++] = '\r';
5423 }
5424 if (pdoc->eolMode != SC_EOL_CR) {
5425 text[j++] = '\n';
5426 }
5427 }
5428 }
5429 text[size] = '\0';
5430 ss->Set(text, size + 1, pdoc->dbcsCodePage,
5431 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
5432 }
5433 }
5434
5435 void Editor::CopyRangeToClipboard(int start, int end) {
5436 start = pdoc->ClampPositionIntoDocument(start);
5437 end = pdoc->ClampPositionIntoDocument(end);
5438 SelectionText selectedText;
5439 selectedText.Set(CopyRange(start, end), end - start + 1,
5440 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5441 CopyToClipboard(selectedText);
5442 }
5443
5444 void Editor::CopyText(int length, const char *text) {
5445 SelectionText selectedText;
5446 selectedText.Copy(text, length + 1,
5447 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5448 CopyToClipboard(selectedText);
5449 }
5450
5451 void Editor::SetDragPosition(SelectionPosition newPos) {
5452 if (newPos.Position() >= 0) {
5453 newPos = MovePositionOutsideChar(newPos, 1);
5454 posDrop = newPos;
5455 }
5456 if (!(posDrag == newPos)) {
5457 caret.on = true;
5458 SetTicking(true);
5459 InvalidateCaret();
5460 posDrag = newPos;
5461 InvalidateCaret();
5462 }
5463 }
5464
5465 void Editor::DisplayCursor(Window::Cursor c) {
5466 if (cursorMode == SC_CURSORNORMAL)
5467 wMain.SetCursor(c);
5468 else
5469 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5470 }
5471
5472 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5473 int xMove = ptStart.x - ptNow.x;
5474 int yMove = ptStart.y - ptNow.y;
5475 int distanceSquared = xMove * xMove + yMove * yMove;
5476 return distanceSquared > 16;
5477 }
5478
5479 void Editor::StartDrag() {
5480 // Always handled by subclasses
5481 //SetMouseCapture(true);
5482 //DisplayCursor(Window::cursorArrow);
5483 }
5484
5485 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
5486 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5487 if (inDragDrop == ddDragging)
5488 dropWentOutside = false;
5489
5490 bool positionWasInSelection = PositionInSelection(position.Position());
5491
5492 bool positionOnEdgeOfSelection =
5493 (position == SelectionStart()) || (position == SelectionEnd());
5494
5495 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
5496 (positionOnEdgeOfSelection && !moving)) {
5497
5498 SelectionPosition selStart = SelectionStart();
5499 SelectionPosition selEnd = SelectionEnd();
5500
5501 UndoGroup ug(pdoc);
5502
5503 SelectionPosition positionAfterDeletion = position;
5504 if ((inDragDrop == ddDragging) && moving) {
5505 // Remove dragged out text
5506 if (rectangular || sel.selType == Selection::selLines) {
5507 for (size_t r=0; r<sel.Count(); r++) {
5508 if (position >= sel.Range(r).Start()) {
5509 if (position > sel.Range(r).End()) {
5510 positionAfterDeletion.Add(-sel.Range(r).Length());
5511 } else {
5512 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
5513 }
5514 }
5515 }
5516 } else {
5517 if (position > selStart) {
5518 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
5519 }
5520 }
5521 ClearSelection();
5522 }
5523 position = positionAfterDeletion;
5524
5525 if (rectangular) {
5526 PasteRectangular(position, value, istrlen(value));
5527 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
5528 SetEmptySelection(position);
5529 } else {
5530 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
5531 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
5532 if (pdoc->InsertCString(position.Position(), value)) {
5533 SelectionPosition posAfterInsertion = position;
5534 posAfterInsertion.Add(istrlen(value));
5535 SetSelection(posAfterInsertion, position);
5536 }
5537 }
5538 } else if (inDragDrop == ddDragging) {
5539 SetEmptySelection(position);
5540 }
5541 }
5542
5543 /**
5544 * @return true if given position is inside the selection,
5545 */
5546 bool Editor::PositionInSelection(int pos) {
5547 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
5548 for (size_t r=0; r<sel.Count(); r++) {
5549 if (sel.Range(r).Contains(pos))
5550 return true;
5551 }
5552 return false;
5553 }
5554
5555 bool Editor::PointInSelection(Point pt) {
5556 SelectionPosition pos = SPositionFromLocation(pt);
5557 int xPos = XFromPosition(pos);
5558 for (size_t r=0; r<sel.Count(); r++) {
5559 SelectionRange range = sel.Range(r);
5560 if (range.Contains(pos)) {
5561 bool hit = true;
5562 if (pos == range.Start()) {
5563 // see if just before selection
5564 if (pt.x < xPos) {
5565 hit = false;
5566 }
5567 }
5568 if (pos == range.End()) {
5569 // see if just after selection
5570 if (pt.x > xPos) {
5571 hit = false;
5572 }
5573 }
5574 if (hit)
5575 return true;
5576 }
5577 }
5578 return false;
5579 }
5580
5581 bool Editor::PointInSelMargin(Point pt) {
5582 // Really means: "Point in a margin"
5583 if (vs.fixedColumnWidth > 0) { // There is a margin
5584 PRectangle rcSelMargin = GetClientRectangle();
5585 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
5586 return rcSelMargin.Contains(pt);
5587 } else {
5588 return false;
5589 }
5590 }
5591
5592 void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
5593 if (lineAnchor_ < lineCurrent_) {
5594 SetSelection(pdoc->LineStart(lineCurrent_ + 1),
5595 pdoc->LineStart(lineAnchor_));
5596 } else if (lineAnchor_ > lineCurrent_) {
5597 SetSelection(pdoc->LineStart(lineCurrent_),
5598 pdoc->LineStart(lineAnchor_ + 1));
5599 } else { // Same line, select it
5600 SetSelection(pdoc->LineStart(lineAnchor_ + 1),
5601 pdoc->LineStart(lineAnchor_));
5602 }
5603 }
5604
5605 void Editor::DwellEnd(bool mouseMoved) {
5606 if (mouseMoved)
5607 ticksToDwell = dwellDelay;
5608 else
5609 ticksToDwell = SC_TIME_FOREVER;
5610 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
5611 dwelling = false;
5612 NotifyDwelling(ptMouseLast, dwelling);
5613 }
5614 }
5615
5616 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
5617 return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0)
5618 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
5619 }
5620
5621 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
5622 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
5623 ptMouseLast = pt;
5624 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
5625 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
5626 inDragDrop = ddNone;
5627 sel.SetMoveExtends(false);
5628
5629 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
5630 if (processed)
5631 return;
5632
5633 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
5634
5635 bool inSelMargin = PointInSelMargin(pt);
5636 if (shift & !inSelMargin) {
5637 SetSelection(newPos.Position());
5638 }
5639 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
5640 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
5641 SetMouseCapture(true);
5642 SetEmptySelection(newPos.Position());
5643 bool doubleClick = false;
5644 // Stop mouse button bounce changing selection type
5645 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
5646 if (selectionType == selChar) {
5647 selectionType = selWord;
5648 doubleClick = true;
5649 } else if (selectionType == selWord) {
5650 selectionType = selLine;
5651 } else {
5652 selectionType = selChar;
5653 originalAnchorPos = sel.MainCaret();
5654 }
5655 }
5656
5657 if (selectionType == selWord) {
5658 if (sel.MainCaret() >= originalAnchorPos) { // Moved forward
5659 SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), 1),
5660 pdoc->ExtendWordSelect(originalAnchorPos, -1));
5661 } else { // Moved backward
5662 SetSelection(pdoc->ExtendWordSelect(sel.MainCaret(), -1),
5663 pdoc->ExtendWordSelect(originalAnchorPos, 1));
5664 }
5665 } else if (selectionType == selLine) {
5666 lineAnchor = LineFromLocation(pt);
5667 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
5668 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
5669 } else {
5670 SetEmptySelection(sel.MainCaret());
5671 }
5672 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
5673 if (doubleClick) {
5674 NotifyDoubleClick(pt, shift, ctrl, alt);
5675 if (PositionIsHotspot(newPos.Position()))
5676 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
5677 }
5678 } else { // Single click
5679 if (inSelMargin) {
5680 sel.selType = Selection::selStream;
5681 if (ctrl) {
5682 SelectAll();
5683 lastClickTime = curTime;
5684 return;
5685 }
5686 if (!shift) {
5687 lineAnchor = LineFromLocation(pt);
5688 // Single click in margin: select whole line
5689 LineSelection(lineAnchor, lineAnchor);
5690 SetSelection(pdoc->LineStart(lineAnchor + 1),
5691 pdoc->LineStart(lineAnchor));
5692 } else {
5693 // Single shift+click in margin: select from line anchor to clicked line
5694 if (sel.MainAnchor() > sel.MainCaret())
5695 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor() - 1);
5696 else
5697 lineAnchor = pdoc->LineFromPosition(sel.MainAnchor());
5698 int lineStart = LineFromLocation(pt);
5699 LineSelection(lineStart, lineAnchor);
5700 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
5701 }
5702
5703 SetDragPosition(SelectionPosition(invalidPosition));
5704 SetMouseCapture(true);
5705 selectionType = selLine;
5706 } else {
5707 if (PointIsHotspot(pt)) {
5708 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
5709 }
5710 if (!shift) {
5711 if (PointInSelection(pt) && !SelectionEmpty())
5712 inDragDrop = ddInitial;
5713 else
5714 inDragDrop = ddNone;
5715 }
5716 SetMouseCapture(true);
5717 if (inDragDrop != ddInitial) {
5718 SetDragPosition(SelectionPosition(invalidPosition));
5719 if (!shift) {
5720 if (ctrl && multipleSelection) {
5721 SelectionRange range(newPos);
5722 sel.TentativeSelection(range);
5723 InvalidateSelection(range, true);
5724 } else {
5725 InvalidateSelection(SelectionRange(newPos), true);
5726 if (sel.Count() > 1)
5727 Redraw();
5728 sel.Clear();
5729 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
5730 SetSelection(newPos, newPos);
5731 }
5732 }
5733 SelectionPosition anchorCurrent = newPos;
5734 if (shift)
5735 anchorCurrent = sel.IsRectangular() ?
5736 sel.Rectangular().anchor : sel.RangeMain().anchor;
5737 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
5738 selectionType = selChar;
5739 originalAnchorPos = sel.MainCaret();
5740 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
5741 SetRectangularRange();
5742 }
5743 }
5744 }
5745 lastClickTime = curTime;
5746 lastXChosen = pt.x;
5747 ShowCaretAtCurrentPosition();
5748 }
5749
5750 bool Editor::PositionIsHotspot(int position) {
5751 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
5752 }
5753
5754 bool Editor::PointIsHotspot(Point pt) {
5755 int pos = PositionFromLocation(pt, true);
5756 if (pos == INVALID_POSITION)
5757 return false;
5758 return PositionIsHotspot(pos);
5759 }
5760
5761 void Editor::SetHotSpotRange(Point *pt) {
5762 if (pt) {
5763 int pos = PositionFromLocation(*pt);
5764
5765 // If we don't limit this to word characters then the
5766 // range can encompass more than the run range and then
5767 // the underline will not be drawn properly.
5768 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
5769 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
5770
5771 // Only invalidate the range if the hotspot range has changed...
5772 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
5773 if (hsStart != -1) {
5774 InvalidateRange(hsStart, hsEnd);
5775 }
5776 hsStart = hsStart_;
5777 hsEnd = hsEnd_;
5778 InvalidateRange(hsStart, hsEnd);
5779 }
5780 } else {
5781 if (hsStart != -1) {
5782 int hsStart_ = hsStart;
5783 int hsEnd_ = hsEnd;
5784 hsStart = -1;
5785 hsEnd = -1;
5786 InvalidateRange(hsStart_, hsEnd_);
5787 } else {
5788 hsStart = -1;
5789 hsEnd = -1;
5790 }
5791 }
5792 }
5793
5794 void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) {
5795 hsStart_ = hsStart;
5796 hsEnd_ = hsEnd;
5797 }
5798
5799 void Editor::ButtonMove(Point pt) {
5800 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
5801 DwellEnd(true);
5802 }
5803
5804 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
5805 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
5806 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
5807
5808 if (inDragDrop == ddInitial) {
5809 if (DragThreshold(ptMouseLast, pt)) {
5810 SetMouseCapture(false);
5811 SetDragPosition(movePos);
5812 CopySelectionRange(&drag);
5813 StartDrag();
5814 }
5815 return;
5816 }
5817
5818 ptMouseLast = pt;
5819 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
5820 if (HaveMouseCapture()) {
5821
5822 // Slow down autoscrolling/selection
5823 autoScrollTimer.ticksToWait -= timer.tickSize;
5824 if (autoScrollTimer.ticksToWait > 0)
5825 return;
5826 autoScrollTimer.ticksToWait = autoScrollDelay;
5827
5828 // Adjust selection
5829 if (posDrag.IsValid()) {
5830 SetDragPosition(movePos);
5831 } else {
5832 if (selectionType == selChar) {
5833 if (sel.IsRectangular()) {
5834 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
5835 SetSelection(movePos, sel.RangeMain().anchor);
5836 } else if (sel.Count() > 1) {
5837 SelectionRange range(movePos, sel.RangeMain().anchor);
5838 sel.TentativeSelection(range);
5839 InvalidateSelection(range, true);
5840 } else {
5841 SetSelection(movePos, sel.RangeMain().anchor);
5842 }
5843 } else if (selectionType == selWord) {
5844 // Continue selecting by word
5845 if (movePos.Position() == originalAnchorPos) { // Didn't move
5846 // No need to do anything. Previously this case was lumped
5847 // in with "Moved forward", but that can be harmful in this
5848 // case: a handler for the NotifyDoubleClick re-adjusts
5849 // the selection for a fancier definition of "word" (for
5850 // example, in Perl it is useful to include the leading
5851 // '$', '%' or '@' on variables for word selection). In this
5852 // the ButtonMove() called via Tick() for auto-scrolling
5853 // could result in the fancier word selection adjustment
5854 // being unmade.
5855 } else if (movePos.Position() > originalAnchorPos) { // Moved forward
5856 SetSelection(pdoc->ExtendWordSelect(movePos.Position(), 1),
5857 pdoc->ExtendWordSelect(originalAnchorPos, -1));
5858 } else { // Moved backward
5859 SetSelection(pdoc->ExtendWordSelect(movePos.Position(), -1),
5860 pdoc->ExtendWordSelect(originalAnchorPos, 1));
5861 }
5862 } else {
5863 // Continue selecting by line
5864 int lineMove = LineFromLocation(pt);
5865 LineSelection(lineMove, lineAnchor);
5866 }
5867 }
5868
5869 // Autoscroll
5870 PRectangle rcClient = GetClientRectangle();
5871 if (pt.y > rcClient.bottom) {
5872 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5873 if (lineMove < 0) {
5874 lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
5875 }
5876 ScrollTo(lineMove - LinesOnScreen() + 1);
5877 Redraw();
5878 } else if (pt.y < rcClient.top) {
5879 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5880 ScrollTo(lineMove - 1);
5881 Redraw();
5882 }
5883 EnsureCaretVisible(false, false, true);
5884
5885 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
5886 SetHotSpotRange(NULL);
5887
5888 } else {
5889 if (vs.fixedColumnWidth > 0) { // There is a margin
5890 if (PointInSelMargin(pt)) {
5891 DisplayCursor(Window::cursorReverseArrow);
5892 return; // No need to test for selection
5893 }
5894 }
5895 // Display regular (drag) cursor over selection
5896 if (PointInSelection(pt) && !SelectionEmpty()) {
5897 DisplayCursor(Window::cursorArrow);
5898 } else if (PointIsHotspot(pt)) {
5899 DisplayCursor(Window::cursorHand);
5900 SetHotSpotRange(&pt);
5901 } else {
5902 DisplayCursor(Window::cursorText);
5903 SetHotSpotRange(NULL);
5904 }
5905 }
5906 }
5907
5908 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
5909 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
5910 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
5911 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
5912 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
5913 if (inDragDrop == ddInitial) {
5914 inDragDrop = ddNone;
5915 SetEmptySelection(newPos.Position());
5916 }
5917 if (HaveMouseCapture()) {
5918 if (PointInSelMargin(pt)) {
5919 DisplayCursor(Window::cursorReverseArrow);
5920 } else {
5921 DisplayCursor(Window::cursorText);
5922 SetHotSpotRange(NULL);
5923 }
5924 ptMouseLast = pt;
5925 SetMouseCapture(false);
5926 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
5927 if (inDragDrop == ddDragging) {
5928 SelectionPosition selStart = SelectionStart();
5929 SelectionPosition selEnd = SelectionEnd();
5930 if (selStart < selEnd) {
5931 if (drag.len) {
5932 if (ctrl) {
5933 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5934 SetSelection(newPos.Position(), newPos.Position() + drag.len);
5935 }
5936 } else if (newPos < selStart) {
5937 pdoc->DeleteChars(selStart.Position(), drag.len);
5938 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5939 SetSelection(newPos.Position(), newPos.Position() + drag.len);
5940 }
5941 } else if (newPos > selEnd) {
5942 pdoc->DeleteChars(selStart.Position(), drag.len);
5943 newPos.Add(-drag.len);
5944 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
5945 SetSelection(newPos.Position(), newPos.Position() + drag.len);
5946 }
5947 } else {
5948 SetEmptySelection(newPos.Position());
5949 }
5950 drag.Free();
5951 }
5952 selectionType = selChar;
5953 }
5954 } else {
5955 if (selectionType == selChar) {
5956 if (sel.Count() > 1) {
5957 sel.RangeMain() =
5958 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
5959 InvalidateSelection(sel.RangeMain(), true);
5960 } else {
5961 SetSelection(newPos, sel.RangeMain().anchor);
5962 }
5963 }
5964 sel.CommitTentative();
5965 }
5966 SetRectangularRange();
5967 lastClickTime = curTime;
5968 lastClick = pt;
5969 lastXChosen = pt.x;
5970 if (sel.selType == Selection::selStream) {
5971 SetLastXChosen();
5972 }
5973 inDragDrop = ddNone;
5974 EnsureCaretVisible(false);
5975 }
5976 }
5977
5978 // Called frequently to perform background UI including
5979 // caret blinking and automatic scrolling.
5980 void Editor::Tick() {
5981 if (HaveMouseCapture()) {
5982 // Auto scroll
5983 ButtonMove(ptMouseLast);
5984 }
5985 if (caret.period > 0) {
5986 timer.ticksToWait -= timer.tickSize;
5987 if (timer.ticksToWait <= 0) {
5988 caret.on = !caret.on;
5989 timer.ticksToWait = caret.period;
5990 if (caret.active) {
5991 InvalidateCaret();
5992 }
5993 }
5994 }
5995 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
5996 scrollWidth = lineWidthMaxSeen;
5997 SetScrollBars();
5998 }
5999 if ((dwellDelay < SC_TIME_FOREVER) &&
6000 (ticksToDwell > 0) &&
6001 (!HaveMouseCapture())) {
6002 ticksToDwell -= timer.tickSize;
6003 if (ticksToDwell <= 0) {
6004 dwelling = true;
6005 NotifyDwelling(ptMouseLast, dwelling);
6006 }
6007 }
6008 }
6009
6010 bool Editor::Idle() {
6011
6012 bool idleDone;
6013
6014 bool wrappingDone = wrapState == eWrapNone;
6015
6016 if (!wrappingDone) {
6017 // Wrap lines during idle.
6018 WrapLines(false, -1);
6019 // No more wrapping
6020 if (wrapStart == wrapEnd)
6021 wrappingDone = true;
6022 }
6023
6024 // Add more idle things to do here, but make sure idleDone is
6025 // set correctly before the function returns. returning
6026 // false will stop calling this idle funtion until SetIdle() is
6027 // called again.
6028
6029 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6030
6031 return !idleDone;
6032 }
6033
6034 void Editor::SetFocusState(bool focusState) {
6035 hasFocus = focusState;
6036 NotifyFocus(hasFocus);
6037 if (hasFocus) {
6038 ShowCaretAtCurrentPosition();
6039 } else {
6040 CancelModes();
6041 DropCaret();
6042 }
6043 }
6044
6045 bool Editor::PaintContains(PRectangle rc) {
6046 if (rc.Empty()) {
6047 return true;
6048 } else {
6049 return rcPaint.Contains(rc);
6050 }
6051 }
6052
6053 bool Editor::PaintContainsMargin() {
6054 PRectangle rcSelMargin = GetClientRectangle();
6055 rcSelMargin.right = vs.fixedColumnWidth;
6056 return PaintContains(rcSelMargin);
6057 }
6058
6059 void Editor::CheckForChangeOutsidePaint(Range r) {
6060 if (paintState == painting && !paintingAllText) {
6061 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6062 if (!r.Valid())
6063 return;
6064
6065 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6066 PRectangle rcText = GetTextRectangle();
6067 if (rcRange.top < rcText.top) {
6068 rcRange.top = rcText.top;
6069 }
6070 if (rcRange.bottom > rcText.bottom) {
6071 rcRange.bottom = rcText.bottom;
6072 }
6073
6074 if (!PaintContains(rcRange)) {
6075 AbandonPaint();
6076 }
6077 }
6078 }
6079
6080 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6081 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6082 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6083 CheckForChangeOutsidePaint(Range(braces[0]));
6084 CheckForChangeOutsidePaint(Range(pos0));
6085 braces[0] = pos0;
6086 }
6087 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6088 CheckForChangeOutsidePaint(Range(braces[1]));
6089 CheckForChangeOutsidePaint(Range(pos1));
6090 braces[1] = pos1;
6091 }
6092 bracesMatchStyle = matchStyle;
6093 if (paintState == notPainting) {
6094 Redraw();
6095 }
6096 }
6097 }
6098
6099 void Editor::SetAnnotationHeights(int start, int end) {
6100 if (vs.annotationVisible) {
6101 for (int line=start; line<end; line++) {
6102 cs.SetHeight(line, pdoc->AnnotationLines(line) + 1);
6103 }
6104 }
6105 }
6106
6107 void Editor::SetDocPointer(Document *document) {
6108 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6109 pdoc->RemoveWatcher(this, 0);
6110 pdoc->Release();
6111 if (document == NULL) {
6112 pdoc = new Document();
6113 } else {
6114 pdoc = document;
6115 }
6116 pdoc->AddRef();
6117
6118 // Ensure all positions within document
6119 sel.Clear();
6120 targetStart = 0;
6121 targetEnd = 0;
6122
6123 braces[0] = invalidPosition;
6124 braces[1] = invalidPosition;
6125
6126 // Reset the contraction state to fully shown.
6127 cs.Clear();
6128 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6129 SetAnnotationHeights(0, pdoc->LinesTotal());
6130 llc.Deallocate();
6131 NeedWrapping();
6132
6133 pdoc->AddWatcher(this, 0);
6134 SetScrollBars();
6135 Redraw();
6136 }
6137
6138 void Editor::SetAnnotationVisible(int visible) {
6139 if (vs.annotationVisible != visible) {
6140 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6141 vs.annotationVisible = visible;
6142 if (changedFromOrToHidden) {
6143 int dir = vs.annotationVisible ? 1 : -1;
6144 for (int line=0; line<pdoc->LinesTotal(); line++) {
6145 int annotationLines = pdoc->AnnotationLines(line);
6146 if (annotationLines > 0) {
6147 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6148 }
6149 }
6150 }
6151 }
6152 }
6153
6154 /**
6155 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6156 */
6157 void Editor::Expand(int &line, bool doExpand) {
6158 int lineMaxSubord = pdoc->GetLastChild(line);
6159 line++;
6160 while (line <= lineMaxSubord) {
6161 if (doExpand)
6162 cs.SetVisible(line, line, true);
6163 int level = pdoc->GetLevel(line);
6164 if (level & SC_FOLDLEVELHEADERFLAG) {
6165 if (doExpand && cs.GetExpanded(line)) {
6166 Expand(line, true);
6167 } else {
6168 Expand(line, false);
6169 }
6170 } else {
6171 line++;
6172 }
6173 }
6174 }
6175
6176 void Editor::ToggleContraction(int line) {
6177 if (line >= 0) {
6178 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6179 line = pdoc->GetFoldParent(line);
6180 if (line < 0)
6181 return;
6182 }
6183
6184 if (cs.GetExpanded(line)) {
6185 int lineMaxSubord = pdoc->GetLastChild(line);
6186 cs.SetExpanded(line, 0);
6187 if (lineMaxSubord > line) {
6188 cs.SetVisible(line + 1, lineMaxSubord, false);
6189
6190 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
6191 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
6192 // This does not re-expand the fold
6193 EnsureCaretVisible();
6194 }
6195
6196 SetScrollBars();
6197 Redraw();
6198 }
6199
6200 } else {
6201 if (!(cs.GetVisible(line))) {
6202 EnsureLineVisible(line, false);
6203 GoToLine(line);
6204 }
6205 cs.SetExpanded(line, 1);
6206 Expand(line, true);
6207 SetScrollBars();
6208 Redraw();
6209 }
6210 }
6211 }
6212
6213 /**
6214 * Recurse up from this line to find any folds that prevent this line from being visible
6215 * and unfold them all.
6216 */
6217 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
6218
6219 // In case in need of wrapping to ensure DisplayFromDoc works.
6220 WrapLines(true, -1);
6221
6222 if (!cs.GetVisible(lineDoc)) {
6223 int lineParent = pdoc->GetFoldParent(lineDoc);
6224 if (lineParent >= 0) {
6225 if (lineDoc != lineParent)
6226 EnsureLineVisible(lineParent, enforcePolicy);
6227 if (!cs.GetExpanded(lineParent)) {
6228 cs.SetExpanded(lineParent, 1);
6229 Expand(lineParent, true);
6230 }
6231 }
6232 SetScrollBars();
6233 Redraw();
6234 }
6235 if (enforcePolicy) {
6236 int lineDisplay = cs.DisplayFromDoc(lineDoc);
6237 if (visiblePolicy & VISIBLE_SLOP) {
6238 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
6239 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
6240 SetVerticalScrollPos();
6241 Redraw();
6242 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
6243 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
6244 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
6245 SetVerticalScrollPos();
6246 Redraw();
6247 }
6248 } else {
6249 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
6250 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
6251 SetVerticalScrollPos();
6252 Redraw();
6253 }
6254 }
6255 }
6256 }
6257
6258 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
6259 UndoGroup ug(pdoc);
6260 if (length == -1)
6261 length = istrlen(text);
6262 if (replacePatterns) {
6263 text = pdoc->SubstituteByPosition(text, &length);
6264 if (!text) {
6265 return 0;
6266 }
6267 }
6268 if (targetStart != targetEnd)
6269 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
6270 targetEnd = targetStart;
6271 pdoc->InsertString(targetStart, text, length);
6272 targetEnd = targetStart + length;
6273 return length;
6274 }
6275
6276 bool Editor::IsUnicodeMode() const {
6277 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
6278 }
6279
6280 int Editor::CodePage() const {
6281 if (pdoc)
6282 return pdoc->dbcsCodePage;
6283 else
6284 return 0;
6285 }
6286
6287 int Editor::WrapCount(int line) {
6288 AutoSurface surface(this);
6289 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6290
6291 if (surface && ll) {
6292 LayoutLine(line, surface, vs, ll, wrapWidth);
6293 return ll->lines;
6294 } else {
6295 return 1;
6296 }
6297 }
6298
6299 void Editor::AddStyledText(char *buffer, int appendLength) {
6300 // The buffer consists of alternating character bytes and style bytes
6301 size_t textLength = appendLength / 2;
6302 char *text = new char[textLength];
6303 size_t i;
6304 for (i = 0;i < textLength;i++) {
6305 text[i] = buffer[i*2];
6306 }
6307 pdoc->InsertString(CurrentPosition(), text, textLength);
6308 for (i = 0;i < textLength;i++) {
6309 text[i] = buffer[i*2+1];
6310 }
6311 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
6312 pdoc->SetStyles(textLength, text);
6313 delete []text;
6314 SetEmptySelection(sel.MainCaret() + textLength);
6315 }
6316
6317 static bool ValidMargin(unsigned long wParam) {
6318 return wParam < ViewStyle::margins;
6319 }
6320
6321 static char *CharPtrFromSPtr(sptr_t lParam) {
6322 return reinterpret_cast<char *>(lParam);
6323 }
6324
6325 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6326 vs.EnsureStyle(wParam);
6327 switch (iMessage) {
6328 case SCI_STYLESETFORE:
6329 vs.styles[wParam].fore.desired = ColourDesired(lParam);
6330 break;
6331 case SCI_STYLESETBACK:
6332 vs.styles[wParam].back.desired = ColourDesired(lParam);
6333 break;
6334 case SCI_STYLESETBOLD:
6335 vs.styles[wParam].bold = lParam != 0;
6336 break;
6337 case SCI_STYLESETITALIC:
6338 vs.styles[wParam].italic = lParam != 0;
6339 break;
6340 case SCI_STYLESETEOLFILLED:
6341 vs.styles[wParam].eolFilled = lParam != 0;
6342 break;
6343 case SCI_STYLESETSIZE:
6344 vs.styles[wParam].size = lParam;
6345 break;
6346 case SCI_STYLESETFONT:
6347 if (lParam != 0) {
6348 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
6349 }
6350 break;
6351 case SCI_STYLESETUNDERLINE:
6352 vs.styles[wParam].underline = lParam != 0;
6353 break;
6354 case SCI_STYLESETCASE:
6355 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
6356 break;
6357 case SCI_STYLESETCHARACTERSET:
6358 vs.styles[wParam].characterSet = lParam;
6359 break;
6360 case SCI_STYLESETVISIBLE:
6361 vs.styles[wParam].visible = lParam != 0;
6362 break;
6363 case SCI_STYLESETCHANGEABLE:
6364 vs.styles[wParam].changeable = lParam != 0;
6365 break;
6366 case SCI_STYLESETHOTSPOT:
6367 vs.styles[wParam].hotspot = lParam != 0;
6368 break;
6369 }
6370 InvalidateStyleRedraw();
6371 }
6372
6373 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6374 vs.EnsureStyle(wParam);
6375 switch (iMessage) {
6376 case SCI_STYLEGETFORE:
6377 return vs.styles[wParam].fore.desired.AsLong();
6378 case SCI_STYLEGETBACK:
6379 return vs.styles[wParam].back.desired.AsLong();
6380 case SCI_STYLEGETBOLD:
6381 return vs.styles[wParam].bold ? 1 : 0;
6382 case SCI_STYLEGETITALIC:
6383 return vs.styles[wParam].italic ? 1 : 0;
6384 case SCI_STYLEGETEOLFILLED:
6385 return vs.styles[wParam].eolFilled ? 1 : 0;
6386 case SCI_STYLEGETSIZE:
6387 return vs.styles[wParam].size;
6388 case SCI_STYLEGETFONT:
6389 if (!vs.styles[wParam].fontName)
6390 return 0;
6391 if (lParam != 0)
6392 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
6393 return strlen(vs.styles[wParam].fontName);
6394 case SCI_STYLEGETUNDERLINE:
6395 return vs.styles[wParam].underline ? 1 : 0;
6396 case SCI_STYLEGETCASE:
6397 return static_cast<int>(vs.styles[wParam].caseForce);
6398 case SCI_STYLEGETCHARACTERSET:
6399 return vs.styles[wParam].characterSet;
6400 case SCI_STYLEGETVISIBLE:
6401 return vs.styles[wParam].visible ? 1 : 0;
6402 case SCI_STYLEGETCHANGEABLE:
6403 return vs.styles[wParam].changeable ? 1 : 0;
6404 case SCI_STYLEGETHOTSPOT:
6405 return vs.styles[wParam].hotspot ? 1 : 0;
6406 }
6407 return 0;
6408 }
6409
6410 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
6411 const int n = strlen(val);
6412 if (lParam != 0) {
6413 char *ptr = reinterpret_cast<char *>(lParam);
6414 strcpy(ptr, val);
6415 }
6416 return n; // Not including NUL
6417 }
6418
6419 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
6420 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
6421
6422 // Optional macro recording hook
6423 if (recordingMacro)
6424 NotifyMacroRecord(iMessage, wParam, lParam);
6425
6426 switch (iMessage) {
6427
6428 case SCI_GETTEXT: {
6429 if (lParam == 0)
6430 return pdoc->Length() + 1;
6431 if (wParam == 0)
6432 return 0;
6433 char *ptr = CharPtrFromSPtr(lParam);
6434 unsigned int iChar = 0;
6435 for (; iChar < wParam - 1; iChar++)
6436 ptr[iChar] = pdoc->CharAt(iChar);
6437 ptr[iChar] = '\0';
6438 return iChar;
6439 }
6440
6441 case SCI_SETTEXT: {
6442 if (lParam == 0)
6443 return 0;
6444 UndoGroup ug(pdoc);
6445 pdoc->DeleteChars(0, pdoc->Length());
6446 SetEmptySelection(0);
6447 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
6448 return 1;
6449 }
6450
6451 case SCI_GETTEXTLENGTH:
6452 return pdoc->Length();
6453
6454 case SCI_CUT:
6455 Cut();
6456 SetLastXChosen();
6457 break;
6458
6459 case SCI_COPY:
6460 Copy();
6461 break;
6462
6463 case SCI_COPYALLOWLINE:
6464 CopyAllowLine();
6465 break;
6466
6467 case SCI_COPYRANGE:
6468 CopyRangeToClipboard(wParam, lParam);
6469 break;
6470
6471 case SCI_COPYTEXT:
6472 CopyText(wParam, CharPtrFromSPtr(lParam));
6473 break;
6474
6475 case SCI_PASTE:
6476 Paste();
6477 if (!caretSticky) {
6478 SetLastXChosen();
6479 }
6480 EnsureCaretVisible();
6481 break;
6482
6483 case SCI_CLEAR:
6484 Clear();
6485 SetLastXChosen();
6486 EnsureCaretVisible();
6487 break;
6488
6489 case SCI_UNDO:
6490 Undo();
6491 SetLastXChosen();
6492 break;
6493
6494 case SCI_CANUNDO:
6495 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
6496
6497 case SCI_EMPTYUNDOBUFFER:
6498 pdoc->DeleteUndoHistory();
6499 return 0;
6500
6501 case SCI_GETFIRSTVISIBLELINE:
6502 return topLine;
6503
6504 case SCI_SETFIRSTVISIBLELINE:
6505 ScrollTo(wParam);
6506 break;
6507
6508 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
6509 int lineStart = pdoc->LineStart(wParam);
6510 int lineEnd = pdoc->LineStart(wParam + 1);
6511 if (lParam == 0) {
6512 return lineEnd - lineStart;
6513 }
6514 char *ptr = CharPtrFromSPtr(lParam);
6515 int iPlace = 0;
6516 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
6517 ptr[iPlace++] = pdoc->CharAt(iChar);
6518 }
6519 return iPlace;
6520 }
6521
6522 case SCI_GETLINECOUNT:
6523 if (pdoc->LinesTotal() == 0)
6524 return 1;
6525 else
6526 return pdoc->LinesTotal();
6527
6528 case SCI_GETMODIFY:
6529 return !pdoc->IsSavePoint();
6530
6531 case SCI_SETSEL: {
6532 int nStart = static_cast<int>(wParam);
6533 int nEnd = static_cast<int>(lParam);
6534 if (nEnd < 0)
6535 nEnd = pdoc->Length();
6536 if (nStart < 0)
6537 nStart = nEnd; // Remove selection
6538 InvalidateSelection(SelectionRange(nStart, nEnd));
6539 sel.Clear();
6540 sel.selType = Selection::selStream;
6541 SetSelection(nEnd, nStart);
6542 EnsureCaretVisible();
6543 }
6544 break;
6545
6546 case SCI_GETSELTEXT: {
6547 SelectionText selectedText;
6548 CopySelectionRange(&selectedText);
6549 if (lParam == 0) {
6550 return selectedText.len ? selectedText.len : 1;
6551 } else {
6552 char *ptr = CharPtrFromSPtr(lParam);
6553 int iChar = 0;
6554 if (selectedText.len) {
6555 for (; iChar < selectedText.len; iChar++)
6556 ptr[iChar] = selectedText.s[iChar];
6557 } else {
6558 ptr[0] = '\0';
6559 }
6560 return iChar;
6561 }
6562 }
6563
6564 case SCI_LINEFROMPOSITION:
6565 if (static_cast<int>(wParam) < 0)
6566 return 0;
6567 return pdoc->LineFromPosition(wParam);
6568
6569 case SCI_POSITIONFROMLINE:
6570 if (static_cast<int>(wParam) < 0)
6571 wParam = pdoc->LineFromPosition(SelectionStart().Position());
6572 if (wParam == 0)
6573 return 0; // Even if there is no text, there is a first line that starts at 0
6574 if (static_cast<int>(wParam) > pdoc->LinesTotal())
6575 return -1;
6576 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6577 // return -1;
6578 return pdoc->LineStart(wParam);
6579
6580 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
6581 case SCI_LINELENGTH:
6582 if ((static_cast<int>(wParam) < 0) ||
6583 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
6584 return 0;
6585 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
6586
6587 case SCI_REPLACESEL: {
6588 if (lParam == 0)
6589 return 0;
6590 UndoGroup ug(pdoc);
6591 ClearSelection();
6592 char *replacement = CharPtrFromSPtr(lParam);
6593 pdoc->InsertCString(sel.MainCaret(), replacement);
6594 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
6595 EnsureCaretVisible();
6596 }
6597 break;
6598
6599 case SCI_SETTARGETSTART:
6600 targetStart = wParam;
6601 break;
6602
6603 case SCI_GETTARGETSTART:
6604 return targetStart;
6605
6606 case SCI_SETTARGETEND:
6607 targetEnd = wParam;
6608 break;
6609
6610 case SCI_GETTARGETEND:
6611 return targetEnd;
6612
6613 case SCI_TARGETFROMSELECTION:
6614 if (sel.MainCaret() < sel.MainAnchor()) {
6615 targetStart = sel.MainCaret();
6616 targetEnd = sel.MainAnchor();
6617 } else {
6618 targetStart = sel.MainAnchor();
6619 targetEnd = sel.MainCaret();
6620 }
6621 break;
6622
6623 case SCI_REPLACETARGET:
6624 PLATFORM_ASSERT(lParam);
6625 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
6626
6627 case SCI_REPLACETARGETRE:
6628 PLATFORM_ASSERT(lParam);
6629 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
6630
6631 case SCI_SEARCHINTARGET:
6632 PLATFORM_ASSERT(lParam);
6633 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
6634
6635 case SCI_SETSEARCHFLAGS:
6636 searchFlags = wParam;
6637 break;
6638
6639 case SCI_GETSEARCHFLAGS:
6640 return searchFlags;
6641
6642 case SCI_POSITIONBEFORE:
6643 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
6644
6645 case SCI_POSITIONAFTER:
6646 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
6647
6648 case SCI_LINESCROLL:
6649 ScrollTo(topLine + lParam);
6650 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
6651 return 1;
6652
6653 case SCI_SETXOFFSET:
6654 xOffset = wParam;
6655 SetHorizontalScrollPos();
6656 Redraw();
6657 break;
6658
6659 case SCI_GETXOFFSET:
6660 return xOffset;
6661
6662 case SCI_CHOOSECARETX:
6663 SetLastXChosen();
6664 break;
6665
6666 case SCI_SCROLLCARET:
6667 EnsureCaretVisible();
6668 break;
6669
6670 case SCI_SETREADONLY:
6671 pdoc->SetReadOnly(wParam != 0);
6672 return 1;
6673
6674 case SCI_GETREADONLY:
6675 return pdoc->IsReadOnly();
6676
6677 case SCI_CANPASTE:
6678 return CanPaste();
6679
6680 case SCI_POINTXFROMPOSITION:
6681 if (lParam < 0) {
6682 return 0;
6683 } else {
6684 Point pt = LocationFromPosition(lParam);
6685 return pt.x;
6686 }
6687
6688 case SCI_POINTYFROMPOSITION:
6689 if (lParam < 0) {
6690 return 0;
6691 } else {
6692 Point pt = LocationFromPosition(lParam);
6693 return pt.y;
6694 }
6695
6696 case SCI_FINDTEXT:
6697 return FindText(wParam, lParam);
6698
6699 case SCI_GETTEXTRANGE: {
6700 if (lParam == 0)
6701 return 0;
6702 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6703 int cpMax = tr->chrg.cpMax;
6704 if (cpMax == -1)
6705 cpMax = pdoc->Length();
6706 PLATFORM_ASSERT(cpMax <= pdoc->Length());
6707 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
6708 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
6709 // Spec says copied text is terminated with a NUL
6710 tr->lpstrText[len] = '\0';
6711 return len; // Not including NUL
6712 }
6713
6714 case SCI_HIDESELECTION:
6715 hideSelection = wParam != 0;
6716 Redraw();
6717 break;
6718
6719 case SCI_FORMATRANGE:
6720 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
6721
6722 case SCI_GETMARGINLEFT:
6723 return vs.leftMarginWidth;
6724
6725 case SCI_GETMARGINRIGHT:
6726 return vs.rightMarginWidth;
6727
6728 case SCI_SETMARGINLEFT:
6729 vs.leftMarginWidth = lParam;
6730 InvalidateStyleRedraw();
6731 break;
6732
6733 case SCI_SETMARGINRIGHT:
6734 vs.rightMarginWidth = lParam;
6735 InvalidateStyleRedraw();
6736 break;
6737
6738 // Control specific mesages
6739
6740 case SCI_ADDTEXT: {
6741 if (lParam == 0)
6742 return 0;
6743 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
6744 SetEmptySelection(sel.MainCaret() + wParam);
6745 return 0;
6746 }
6747
6748 case SCI_ADDSTYLEDTEXT:
6749 if (lParam)
6750 AddStyledText(CharPtrFromSPtr(lParam), wParam);
6751 return 0;
6752
6753 case SCI_INSERTTEXT: {
6754 if (lParam == 0)
6755 return 0;
6756 int insertPos = wParam;
6757 if (static_cast<int>(wParam) == -1)
6758 insertPos = CurrentPosition();
6759 int newCurrent = CurrentPosition();
6760 char *sz = CharPtrFromSPtr(lParam);
6761 pdoc->InsertCString(insertPos, sz);
6762 if (newCurrent > insertPos)
6763 newCurrent += istrlen(sz);
6764 SetEmptySelection(newCurrent);
6765 return 0;
6766 }
6767
6768 case SCI_APPENDTEXT:
6769 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
6770 return 0;
6771
6772 case SCI_CLEARALL:
6773 ClearAll();
6774 return 0;
6775
6776 case SCI_CLEARDOCUMENTSTYLE:
6777 ClearDocumentStyle();
6778 return 0;
6779
6780 case SCI_SETUNDOCOLLECTION:
6781 pdoc->SetUndoCollection(wParam != 0);
6782 return 0;
6783
6784 case SCI_GETUNDOCOLLECTION:
6785 return pdoc->IsCollectingUndo();
6786
6787 case SCI_BEGINUNDOACTION:
6788 pdoc->BeginUndoAction();
6789 return 0;
6790
6791 case SCI_ENDUNDOACTION:
6792 pdoc->EndUndoAction();
6793 return 0;
6794
6795 case SCI_GETCARETPERIOD:
6796 return caret.period;
6797
6798 case SCI_SETCARETPERIOD:
6799 caret.period = wParam;
6800 break;
6801
6802 case SCI_SETWORDCHARS: {
6803 pdoc->SetDefaultCharClasses(false);
6804 if (lParam == 0)
6805 return 0;
6806 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
6807 }
6808 break;
6809
6810 case SCI_SETWHITESPACECHARS: {
6811 if (lParam == 0)
6812 return 0;
6813 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
6814 }
6815 break;
6816
6817 case SCI_SETCHARSDEFAULT:
6818 pdoc->SetDefaultCharClasses(true);
6819 break;
6820
6821 case SCI_GETLENGTH:
6822 return pdoc->Length();
6823
6824 case SCI_ALLOCATE:
6825 pdoc->Allocate(wParam);
6826 break;
6827
6828 case SCI_GETCHARAT:
6829 return pdoc->CharAt(wParam);
6830
6831 case SCI_SETCURRENTPOS:
6832 if (sel.IsRectangular()) {
6833 sel.Rectangular().caret.SetPosition(wParam);
6834 SetRectangularRange();
6835 Redraw();
6836 } else {
6837 SetSelection(wParam, sel.MainAnchor());
6838 }
6839 break;
6840
6841 case SCI_GETCURRENTPOS:
6842 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
6843
6844 case SCI_SETANCHOR:
6845 if (sel.IsRectangular()) {
6846 sel.Rectangular().anchor.SetPosition(wParam);
6847 SetRectangularRange();
6848 Redraw();
6849 } else {
6850 SetSelection(sel.MainCaret(), wParam);
6851 }
6852 break;
6853
6854 case SCI_GETANCHOR:
6855 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
6856
6857 case SCI_SETSELECTIONSTART:
6858 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
6859 break;
6860
6861 case SCI_GETSELECTIONSTART:
6862 return Platform::Minimum(sel.MainAnchor(), sel.MainCaret());
6863
6864 case SCI_SETSELECTIONEND:
6865 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
6866 break;
6867
6868 case SCI_GETSELECTIONEND:
6869 return Platform::Maximum(sel.MainAnchor(), sel.MainCaret());
6870
6871 case SCI_SETPRINTMAGNIFICATION:
6872 printMagnification = wParam;
6873 break;
6874
6875 case SCI_GETPRINTMAGNIFICATION:
6876 return printMagnification;
6877
6878 case SCI_SETPRINTCOLOURMODE:
6879 printColourMode = wParam;
6880 break;
6881
6882 case SCI_GETPRINTCOLOURMODE:
6883 return printColourMode;
6884
6885 case SCI_SETPRINTWRAPMODE:
6886 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6887 break;
6888
6889 case SCI_GETPRINTWRAPMODE:
6890 return printWrapState;
6891
6892 case SCI_GETSTYLEAT:
6893 if (static_cast<int>(wParam) >= pdoc->Length())
6894 return 0;
6895 else
6896 return pdoc->StyleAt(wParam);
6897
6898 case SCI_REDO:
6899 Redo();
6900 break;
6901
6902 case SCI_SELECTALL:
6903 SelectAll();
6904 break;
6905
6906 case SCI_SETSAVEPOINT:
6907 pdoc->SetSavePoint();
6908 break;
6909
6910 case SCI_GETSTYLEDTEXT: {
6911 if (lParam == 0)
6912 return 0;
6913 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
6914 int iPlace = 0;
6915 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6916 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6917 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6918 }
6919 tr->lpstrText[iPlace] = '\0';
6920 tr->lpstrText[iPlace + 1] = '\0';
6921 return iPlace;
6922 }
6923
6924 case SCI_CANREDO:
6925 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6926
6927 case SCI_MARKERLINEFROMHANDLE:
6928 return pdoc->LineFromHandle(wParam);
6929
6930 case SCI_MARKERDELETEHANDLE:
6931 pdoc->DeleteMarkFromHandle(wParam);
6932 break;
6933
6934 case SCI_GETVIEWWS:
6935 return vs.viewWhitespace;
6936
6937 case SCI_SETVIEWWS:
6938 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6939 Redraw();
6940 break;
6941
6942 case SCI_GETWHITESPACESIZE:
6943 return vs.whitespaceSize;
6944
6945 case SCI_SETWHITESPACESIZE:
6946 vs.whitespaceSize = static_cast<int>(wParam);
6947 Redraw();
6948 break;
6949
6950 case SCI_POSITIONFROMPOINT:
6951 return PositionFromLocation(Point(wParam, lParam), false, false);
6952
6953 case SCI_POSITIONFROMPOINTCLOSE:
6954 return PositionFromLocation(Point(wParam, lParam), true, false);
6955
6956 case SCI_CHARPOSITIONFROMPOINT:
6957 return PositionFromLocation(Point(wParam, lParam), false, true);
6958
6959 case SCI_CHARPOSITIONFROMPOINTCLOSE:
6960 return PositionFromLocation(Point(wParam, lParam), true, true);
6961
6962 case SCI_GOTOLINE:
6963 GoToLine(wParam);
6964 break;
6965
6966 case SCI_GOTOPOS:
6967 SetEmptySelection(wParam);
6968 EnsureCaretVisible();
6969 Redraw();
6970 break;
6971
6972 case SCI_GETCURLINE: {
6973 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
6974 int lineStart = pdoc->LineStart(lineCurrentPos);
6975 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6976 if (lParam == 0) {
6977 return 1 + lineEnd - lineStart;
6978 }
6979 PLATFORM_ASSERT(wParam > 0);
6980 char *ptr = CharPtrFromSPtr(lParam);
6981 unsigned int iPlace = 0;
6982 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
6983 ptr[iPlace++] = pdoc->CharAt(iChar);
6984 }
6985 ptr[iPlace] = '\0';
6986 return sel.MainCaret() - lineStart;
6987 }
6988
6989 case SCI_GETENDSTYLED:
6990 return pdoc->GetEndStyled();
6991
6992 case SCI_GETEOLMODE:
6993 return pdoc->eolMode;
6994
6995 case SCI_SETEOLMODE:
6996 pdoc->eolMode = wParam;
6997 break;
6998
6999 case SCI_STARTSTYLING:
7000 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7001 break;
7002
7003 case SCI_SETSTYLING:
7004 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7005 break;
7006
7007 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7008 if (lParam == 0)
7009 return 0;
7010 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7011 break;
7012
7013 case SCI_SETBUFFEREDDRAW:
7014 bufferedDraw = wParam != 0;
7015 break;
7016
7017 case SCI_GETBUFFEREDDRAW:
7018 return bufferedDraw;
7019
7020 case SCI_GETTWOPHASEDRAW:
7021 return twoPhaseDraw;
7022
7023 case SCI_SETTWOPHASEDRAW:
7024 twoPhaseDraw = wParam != 0;
7025 InvalidateStyleRedraw();
7026 break;
7027
7028 case SCI_SETFONTQUALITY:
7029 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7030 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7031 InvalidateStyleRedraw();
7032 break;
7033
7034 case SCI_GETFONTQUALITY:
7035 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7036
7037 case SCI_SETTABWIDTH:
7038 if (wParam > 0) {
7039 pdoc->tabInChars = wParam;
7040 if (pdoc->indentInChars == 0)
7041 pdoc->actualIndentInChars = pdoc->tabInChars;
7042 }
7043 InvalidateStyleRedraw();
7044 break;
7045
7046 case SCI_GETTABWIDTH:
7047 return pdoc->tabInChars;
7048
7049 case SCI_SETINDENT:
7050 pdoc->indentInChars = wParam;
7051 if (pdoc->indentInChars != 0)
7052 pdoc->actualIndentInChars = pdoc->indentInChars;
7053 else
7054 pdoc->actualIndentInChars = pdoc->tabInChars;
7055 InvalidateStyleRedraw();
7056 break;
7057
7058 case SCI_GETINDENT:
7059 return pdoc->indentInChars;
7060
7061 case SCI_SETUSETABS:
7062 pdoc->useTabs = wParam != 0;
7063 InvalidateStyleRedraw();
7064 break;
7065
7066 case SCI_GETUSETABS:
7067 return pdoc->useTabs;
7068
7069 case SCI_SETLINEINDENTATION:
7070 pdoc->SetLineIndentation(wParam, lParam);
7071 break;
7072
7073 case SCI_GETLINEINDENTATION:
7074 return pdoc->GetLineIndentation(wParam);
7075
7076 case SCI_GETLINEINDENTPOSITION:
7077 return pdoc->GetLineIndentPosition(wParam);
7078
7079 case SCI_SETTABINDENTS:
7080 pdoc->tabIndents = wParam != 0;
7081 break;
7082
7083 case SCI_GETTABINDENTS:
7084 return pdoc->tabIndents;
7085
7086 case SCI_SETBACKSPACEUNINDENTS:
7087 pdoc->backspaceUnindents = wParam != 0;
7088 break;
7089
7090 case SCI_GETBACKSPACEUNINDENTS:
7091 return pdoc->backspaceUnindents;
7092
7093 case SCI_SETMOUSEDWELLTIME:
7094 dwellDelay = wParam;
7095 ticksToDwell = dwellDelay;
7096 break;
7097
7098 case SCI_GETMOUSEDWELLTIME:
7099 return dwellDelay;
7100
7101 case SCI_WORDSTARTPOSITION:
7102 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
7103
7104 case SCI_WORDENDPOSITION:
7105 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
7106
7107 case SCI_SETWRAPMODE:
7108 switch (wParam) {
7109 case SC_WRAP_WORD:
7110 wrapState = eWrapWord;
7111 break;
7112 case SC_WRAP_CHAR:
7113 wrapState = eWrapChar;
7114 break;
7115 default:
7116 wrapState = eWrapNone;
7117 break;
7118 }
7119 xOffset = 0;
7120 InvalidateStyleRedraw();
7121 ReconfigureScrollBars();
7122 break;
7123
7124 case SCI_GETWRAPMODE:
7125 return wrapState;
7126
7127 case SCI_SETWRAPVISUALFLAGS:
7128 wrapVisualFlags = wParam;
7129 InvalidateStyleRedraw();
7130 ReconfigureScrollBars();
7131 break;
7132
7133 case SCI_GETWRAPVISUALFLAGS:
7134 return wrapVisualFlags;
7135
7136 case SCI_SETWRAPVISUALFLAGSLOCATION:
7137 wrapVisualFlagsLocation = wParam;
7138 InvalidateStyleRedraw();
7139 break;
7140
7141 case SCI_GETWRAPVISUALFLAGSLOCATION:
7142 return wrapVisualFlagsLocation;
7143
7144 case SCI_SETWRAPSTARTINDENT:
7145 wrapVisualStartIndent = wParam;
7146 InvalidateStyleRedraw();
7147 ReconfigureScrollBars();
7148 break;
7149
7150 case SCI_GETWRAPSTARTINDENT:
7151 return wrapVisualStartIndent;
7152
7153 case SCI_SETWRAPINDENTMODE:
7154 wrapIndentMode = wParam;
7155 InvalidateStyleRedraw();
7156 ReconfigureScrollBars();
7157 break;
7158
7159 case SCI_GETWRAPINDENTMODE:
7160 return wrapIndentMode;
7161
7162 case SCI_SETLAYOUTCACHE:
7163 llc.SetLevel(wParam);
7164 break;
7165
7166 case SCI_GETLAYOUTCACHE:
7167 return llc.GetLevel();
7168
7169 case SCI_SETPOSITIONCACHE:
7170 posCache.SetSize(wParam);
7171 break;
7172
7173 case SCI_GETPOSITIONCACHE:
7174 return posCache.GetSize();
7175
7176 case SCI_SETSCROLLWIDTH:
7177 PLATFORM_ASSERT(wParam > 0);
7178 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
7179 lineWidthMaxSeen = 0;
7180 scrollWidth = wParam;
7181 SetScrollBars();
7182 }
7183 break;
7184
7185 case SCI_GETSCROLLWIDTH:
7186 return scrollWidth;
7187
7188 case SCI_SETSCROLLWIDTHTRACKING:
7189 trackLineWidth = wParam != 0;
7190 break;
7191
7192 case SCI_GETSCROLLWIDTHTRACKING:
7193 return trackLineWidth;
7194
7195 case SCI_LINESJOIN:
7196 LinesJoin();
7197 break;
7198
7199 case SCI_LINESSPLIT:
7200 LinesSplit(wParam);
7201 break;
7202
7203 case SCI_TEXTWIDTH:
7204 PLATFORM_ASSERT(wParam < vs.stylesSize);
7205 PLATFORM_ASSERT(lParam);
7206 return TextWidth(wParam, CharPtrFromSPtr(lParam));
7207
7208 case SCI_TEXTHEIGHT:
7209 return vs.lineHeight;
7210
7211 case SCI_SETENDATLASTLINE:
7212 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7213 if (endAtLastLine != (wParam != 0)) {
7214 endAtLastLine = wParam != 0;
7215 SetScrollBars();
7216 }
7217 break;
7218
7219 case SCI_GETENDATLASTLINE:
7220 return endAtLastLine;
7221
7222 case SCI_SETCARETSTICKY:
7223 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
7224 if (caretSticky != (wParam != 0)) {
7225 caretSticky = wParam != 0;
7226 }
7227 break;
7228
7229 case SCI_GETCARETSTICKY:
7230 return caretSticky;
7231
7232 case SCI_TOGGLECARETSTICKY:
7233 caretSticky = !caretSticky;
7234 break;
7235
7236 case SCI_GETCOLUMN:
7237 return pdoc->GetColumn(wParam);
7238
7239 case SCI_FINDCOLUMN:
7240 return pdoc->FindColumn(wParam, lParam);
7241
7242 case SCI_SETHSCROLLBAR :
7243 if (horizontalScrollBarVisible != (wParam != 0)) {
7244 horizontalScrollBarVisible = wParam != 0;
7245 SetScrollBars();
7246 ReconfigureScrollBars();
7247 }
7248 break;
7249
7250 case SCI_GETHSCROLLBAR:
7251 return horizontalScrollBarVisible;
7252
7253 case SCI_SETVSCROLLBAR:
7254 if (verticalScrollBarVisible != (wParam != 0)) {
7255 verticalScrollBarVisible = wParam != 0;
7256 SetScrollBars();
7257 ReconfigureScrollBars();
7258 }
7259 break;
7260
7261 case SCI_GETVSCROLLBAR:
7262 return verticalScrollBarVisible;
7263
7264 case SCI_SETINDENTATIONGUIDES:
7265 vs.viewIndentationGuides = IndentView(wParam);
7266 Redraw();
7267 break;
7268
7269 case SCI_GETINDENTATIONGUIDES:
7270 return vs.viewIndentationGuides;
7271
7272 case SCI_SETHIGHLIGHTGUIDE:
7273 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
7274 highlightGuideColumn = wParam;
7275 Redraw();
7276 }
7277 break;
7278
7279 case SCI_GETHIGHLIGHTGUIDE:
7280 return highlightGuideColumn;
7281
7282 case SCI_GETLINEENDPOSITION:
7283 return pdoc->LineEnd(wParam);
7284
7285 case SCI_SETCODEPAGE:
7286 if (ValidCodePage(wParam)) {
7287 pdoc->dbcsCodePage = wParam;
7288 InvalidateStyleRedraw();
7289 }
7290 break;
7291
7292 case SCI_GETCODEPAGE:
7293 return pdoc->dbcsCodePage;
7294
7295 case SCI_SETUSEPALETTE:
7296 palette.allowRealization = wParam != 0;
7297 InvalidateStyleRedraw();
7298 break;
7299
7300 case SCI_GETUSEPALETTE:
7301 return palette.allowRealization;
7302
7303 // Marker definition and setting
7304 case SCI_MARKERDEFINE:
7305 if (wParam <= MARKER_MAX)
7306 vs.markers[wParam].markType = lParam;
7307 InvalidateStyleData();
7308 RedrawSelMargin();
7309 break;
7310
7311 case SCI_MARKERSYMBOLDEFINED:
7312 if (wParam <= MARKER_MAX)
7313 return vs.markers[wParam].markType;
7314 else
7315 return 0;
7316
7317 case SCI_MARKERSETFORE:
7318 if (wParam <= MARKER_MAX)
7319 vs.markers[wParam].fore.desired = ColourDesired(lParam);
7320 InvalidateStyleData();
7321 RedrawSelMargin();
7322 break;
7323 case SCI_MARKERSETBACK:
7324 if (wParam <= MARKER_MAX)
7325 vs.markers[wParam].back.desired = ColourDesired(lParam);
7326 InvalidateStyleData();
7327 RedrawSelMargin();
7328 break;
7329 case SCI_MARKERSETALPHA:
7330 if (wParam <= MARKER_MAX)
7331 vs.markers[wParam].alpha = lParam;
7332 InvalidateStyleRedraw();
7333 break;
7334 case SCI_MARKERADD: {
7335 int markerID = pdoc->AddMark(wParam, lParam);
7336 return markerID;
7337 }
7338 case SCI_MARKERADDSET:
7339 if (lParam != 0)
7340 pdoc->AddMarkSet(wParam, lParam);
7341 break;
7342
7343 case SCI_MARKERDELETE:
7344 pdoc->DeleteMark(wParam, lParam);
7345 break;
7346
7347 case SCI_MARKERDELETEALL:
7348 pdoc->DeleteAllMarks(static_cast<int>(wParam));
7349 break;
7350
7351 case SCI_MARKERGET:
7352 return pdoc->GetMark(wParam);
7353
7354 case SCI_MARKERNEXT: {
7355 int lt = pdoc->LinesTotal();
7356 for (int iLine = wParam; iLine < lt; iLine++) {
7357 if ((pdoc->GetMark(iLine) & lParam) != 0)
7358 return iLine;
7359 }
7360 }
7361 return -1;
7362
7363 case SCI_MARKERPREVIOUS: {
7364 for (int iLine = wParam; iLine >= 0; iLine--) {
7365 if ((pdoc->GetMark(iLine) & lParam) != 0)
7366 return iLine;
7367 }
7368 }
7369 return -1;
7370
7371 case SCI_MARKERDEFINEPIXMAP:
7372 if (wParam <= MARKER_MAX) {
7373 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
7374 };
7375 InvalidateStyleData();
7376 RedrawSelMargin();
7377 break;
7378
7379 case SCI_SETMARGINTYPEN:
7380 if (ValidMargin(wParam)) {
7381 vs.ms[wParam].style = lParam;
7382 InvalidateStyleRedraw();
7383 }
7384 break;
7385
7386 case SCI_GETMARGINTYPEN:
7387 if (ValidMargin(wParam))
7388 return vs.ms[wParam].style;
7389 else
7390 return 0;
7391
7392 case SCI_SETMARGINWIDTHN:
7393 if (ValidMargin(wParam)) {
7394 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
7395 if (vs.ms[wParam].width != lParam) {
7396 vs.ms[wParam].width = lParam;
7397 InvalidateStyleRedraw();
7398 }
7399 }
7400 break;
7401
7402 case SCI_GETMARGINWIDTHN:
7403 if (ValidMargin(wParam))
7404 return vs.ms[wParam].width;
7405 else
7406 return 0;
7407
7408 case SCI_SETMARGINMASKN:
7409 if (ValidMargin(wParam)) {
7410 vs.ms[wParam].mask = lParam;
7411 InvalidateStyleRedraw();
7412 }
7413 break;
7414
7415 case SCI_GETMARGINMASKN:
7416 if (ValidMargin(wParam))
7417 return vs.ms[wParam].mask;
7418 else
7419 return 0;
7420
7421 case SCI_SETMARGINSENSITIVEN:
7422 if (ValidMargin(wParam)) {
7423 vs.ms[wParam].sensitive = lParam != 0;
7424 InvalidateStyleRedraw();
7425 }
7426 break;
7427
7428 case SCI_GETMARGINSENSITIVEN:
7429 if (ValidMargin(wParam))
7430 return vs.ms[wParam].sensitive ? 1 : 0;
7431 else
7432 return 0;
7433
7434 case SCI_STYLECLEARALL:
7435 vs.ClearStyles();
7436 InvalidateStyleRedraw();
7437 break;
7438
7439 case SCI_STYLESETFORE:
7440 case SCI_STYLESETBACK:
7441 case SCI_STYLESETBOLD:
7442 case SCI_STYLESETITALIC:
7443 case SCI_STYLESETEOLFILLED:
7444 case SCI_STYLESETSIZE:
7445 case SCI_STYLESETFONT:
7446 case SCI_STYLESETUNDERLINE:
7447 case SCI_STYLESETCASE:
7448 case SCI_STYLESETCHARACTERSET:
7449 case SCI_STYLESETVISIBLE:
7450 case SCI_STYLESETCHANGEABLE:
7451 case SCI_STYLESETHOTSPOT:
7452 StyleSetMessage(iMessage, wParam, lParam);
7453 break;
7454
7455 case SCI_STYLEGETFORE:
7456 case SCI_STYLEGETBACK:
7457 case SCI_STYLEGETBOLD:
7458 case SCI_STYLEGETITALIC:
7459 case SCI_STYLEGETEOLFILLED:
7460 case SCI_STYLEGETSIZE:
7461 case SCI_STYLEGETFONT:
7462 case SCI_STYLEGETUNDERLINE:
7463 case SCI_STYLEGETCASE:
7464 case SCI_STYLEGETCHARACTERSET:
7465 case SCI_STYLEGETVISIBLE:
7466 case SCI_STYLEGETCHANGEABLE:
7467 case SCI_STYLEGETHOTSPOT:
7468 return StyleGetMessage(iMessage, wParam, lParam);
7469
7470 case SCI_STYLERESETDEFAULT:
7471 vs.ResetDefaultStyle();
7472 InvalidateStyleRedraw();
7473 break;
7474 case SCI_SETSTYLEBITS:
7475 vs.EnsureStyle((1 << wParam) - 1);
7476 pdoc->SetStylingBits(wParam);
7477 break;
7478
7479 case SCI_GETSTYLEBITS:
7480 return pdoc->stylingBits;
7481
7482 case SCI_SETLINESTATE:
7483 return pdoc->SetLineState(wParam, lParam);
7484
7485 case SCI_GETLINESTATE:
7486 return pdoc->GetLineState(wParam);
7487
7488 case SCI_GETMAXLINESTATE:
7489 return pdoc->GetMaxLineState();
7490
7491 case SCI_GETCARETLINEVISIBLE:
7492 return vs.showCaretLineBackground;
7493 case SCI_SETCARETLINEVISIBLE:
7494 vs.showCaretLineBackground = wParam != 0;
7495 InvalidateStyleRedraw();
7496 break;
7497 case SCI_GETCARETLINEBACK:
7498 return vs.caretLineBackground.desired.AsLong();
7499 case SCI_SETCARETLINEBACK:
7500 vs.caretLineBackground.desired = wParam;
7501 InvalidateStyleRedraw();
7502 break;
7503 case SCI_GETCARETLINEBACKALPHA:
7504 return vs.caretLineAlpha;
7505 case SCI_SETCARETLINEBACKALPHA:
7506 vs.caretLineAlpha = wParam;
7507 InvalidateStyleRedraw();
7508 break;
7509
7510 // Folding messages
7511
7512 case SCI_VISIBLEFROMDOCLINE:
7513 return cs.DisplayFromDoc(wParam);
7514
7515 case SCI_DOCLINEFROMVISIBLE:
7516 return cs.DocFromDisplay(wParam);
7517
7518 case SCI_WRAPCOUNT:
7519 return WrapCount(wParam);
7520
7521 case SCI_SETFOLDLEVEL: {
7522 int prev = pdoc->SetLevel(wParam, lParam);
7523 if (prev != lParam)
7524 RedrawSelMargin();
7525 return prev;
7526 }
7527
7528 case SCI_GETFOLDLEVEL:
7529 return pdoc->GetLevel(wParam);
7530
7531 case SCI_GETLASTCHILD:
7532 return pdoc->GetLastChild(wParam, lParam);
7533
7534 case SCI_GETFOLDPARENT:
7535 return pdoc->GetFoldParent(wParam);
7536
7537 case SCI_SHOWLINES:
7538 cs.SetVisible(wParam, lParam, true);
7539 SetScrollBars();
7540 Redraw();
7541 break;
7542
7543 case SCI_HIDELINES:
7544 if (wParam > 0)
7545 cs.SetVisible(wParam, lParam, false);
7546 SetScrollBars();
7547 Redraw();
7548 break;
7549
7550 case SCI_GETLINEVISIBLE:
7551 return cs.GetVisible(wParam);
7552
7553 case SCI_SETFOLDEXPANDED:
7554 if (cs.SetExpanded(wParam, lParam != 0)) {
7555 RedrawSelMargin();
7556 }
7557 break;
7558
7559 case SCI_GETFOLDEXPANDED:
7560 return cs.GetExpanded(wParam);
7561
7562 case SCI_SETFOLDFLAGS:
7563 foldFlags = wParam;
7564 Redraw();
7565 break;
7566
7567 case SCI_TOGGLEFOLD:
7568 ToggleContraction(wParam);
7569 break;
7570
7571 case SCI_ENSUREVISIBLE:
7572 EnsureLineVisible(wParam, false);
7573 break;
7574
7575 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7576 EnsureLineVisible(wParam, true);
7577 break;
7578
7579 case SCI_SEARCHANCHOR:
7580 SearchAnchor();
7581 break;
7582
7583 case SCI_SEARCHNEXT:
7584 case SCI_SEARCHPREV:
7585 return SearchText(iMessage, wParam, lParam);
7586
7587 case SCI_SETXCARETPOLICY:
7588 caretXPolicy = wParam;
7589 caretXSlop = lParam;
7590 break;
7591
7592 case SCI_SETYCARETPOLICY:
7593 caretYPolicy = wParam;
7594 caretYSlop = lParam;
7595 break;
7596
7597 case SCI_SETVISIBLEPOLICY:
7598 visiblePolicy = wParam;
7599 visibleSlop = lParam;
7600 break;
7601
7602 case SCI_LINESONSCREEN:
7603 return LinesOnScreen();
7604
7605 case SCI_SETSELFORE:
7606 vs.selforeset = wParam != 0;
7607 vs.selforeground.desired = ColourDesired(lParam);
7608 vs.selAdditionalForeground.desired = ColourDesired(lParam);
7609 InvalidateStyleRedraw();
7610 break;
7611
7612 case SCI_SETSELBACK:
7613 vs.selbackset = wParam != 0;
7614 vs.selbackground.desired = ColourDesired(lParam);
7615 vs.selAdditionalBackground.desired = ColourDesired(lParam);
7616 InvalidateStyleRedraw();
7617 break;
7618
7619 case SCI_SETSELALPHA:
7620 vs.selAlpha = wParam;
7621 vs.selAdditionalAlpha = wParam;
7622 InvalidateStyleRedraw();
7623 break;
7624
7625 case SCI_GETSELALPHA:
7626 return vs.selAlpha;
7627
7628 case SCI_GETSELEOLFILLED:
7629 return vs.selEOLFilled;
7630
7631 case SCI_SETSELEOLFILLED:
7632 vs.selEOLFilled = wParam != 0;
7633 InvalidateStyleRedraw();
7634 break;
7635
7636 case SCI_SETWHITESPACEFORE:
7637 vs.whitespaceForegroundSet = wParam != 0;
7638 vs.whitespaceForeground.desired = ColourDesired(lParam);
7639 InvalidateStyleRedraw();
7640 break;
7641
7642 case SCI_SETWHITESPACEBACK:
7643 vs.whitespaceBackgroundSet = wParam != 0;
7644 vs.whitespaceBackground.desired = ColourDesired(lParam);
7645 InvalidateStyleRedraw();
7646 break;
7647
7648 case SCI_SETCARETFORE:
7649 vs.caretcolour.desired = ColourDesired(wParam);
7650 InvalidateStyleRedraw();
7651 break;
7652
7653 case SCI_GETCARETFORE:
7654 return vs.caretcolour.desired.AsLong();
7655
7656 case SCI_SETCARETSTYLE:
7657 if (wParam >= CARETSTYLE_INVISIBLE && wParam <= CARETSTYLE_BLOCK)
7658 vs.caretStyle = wParam;
7659 else
7660 /* Default to the line caret */
7661 vs.caretStyle = CARETSTYLE_LINE;
7662 InvalidateStyleRedraw();
7663 break;
7664
7665 case SCI_GETCARETSTYLE:
7666 return vs.caretStyle;
7667
7668 case SCI_SETCARETWIDTH:
7669 if (wParam <= 0)
7670 vs.caretWidth = 0;
7671 else if (wParam >= 3)
7672 vs.caretWidth = 3;
7673 else
7674 vs.caretWidth = wParam;
7675 InvalidateStyleRedraw();
7676 break;
7677
7678 case SCI_GETCARETWIDTH:
7679 return vs.caretWidth;
7680
7681 case SCI_ASSIGNCMDKEY:
7682 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7683 Platform::HighShortFromLong(wParam), lParam);
7684 break;
7685
7686 case SCI_CLEARCMDKEY:
7687 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7688 Platform::HighShortFromLong(wParam), SCI_NULL);
7689 break;
7690
7691 case SCI_CLEARALLCMDKEYS:
7692 kmap.Clear();
7693 break;
7694
7695 case SCI_INDICSETSTYLE:
7696 if (wParam <= INDIC_MAX) {
7697 vs.indicators[wParam].style = lParam;
7698 InvalidateStyleRedraw();
7699 }
7700 break;
7701
7702 case SCI_INDICGETSTYLE:
7703 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
7704
7705 case SCI_INDICSETFORE:
7706 if (wParam <= INDIC_MAX) {
7707 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
7708 InvalidateStyleRedraw();
7709 }
7710 break;
7711
7712 case SCI_INDICGETFORE:
7713 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
7714
7715 case SCI_INDICSETUNDER:
7716 if (wParam <= INDIC_MAX) {
7717 vs.indicators[wParam].under = lParam != 0;
7718 InvalidateStyleRedraw();
7719 }
7720 break;
7721
7722 case SCI_INDICGETUNDER:
7723 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
7724
7725 case SCI_INDICSETALPHA:
7726 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 100) {
7727 vs.indicators[wParam].fillAlpha = lParam;
7728 InvalidateStyleRedraw();
7729 }
7730 break;
7731
7732 case SCI_INDICGETALPHA:
7733 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
7734
7735 case SCI_SETINDICATORCURRENT:
7736 pdoc->decorations.SetCurrentIndicator(wParam);
7737 break;
7738 case SCI_GETINDICATORCURRENT:
7739 return pdoc->decorations.GetCurrentIndicator();
7740 case SCI_SETINDICATORVALUE:
7741 pdoc->decorations.SetCurrentValue(wParam);
7742 break;
7743 case SCI_GETINDICATORVALUE:
7744 return pdoc->decorations.GetCurrentValue();
7745
7746 case SCI_INDICATORFILLRANGE:
7747 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
7748 break;
7749
7750 case SCI_INDICATORCLEARRANGE:
7751 pdoc->DecorationFillRange(wParam, 0, lParam);
7752 break;
7753
7754 case SCI_INDICATORALLONFOR:
7755 return pdoc->decorations.AllOnFor(wParam);
7756
7757 case SCI_INDICATORVALUEAT:
7758 return pdoc->decorations.ValueAt(wParam, lParam);
7759
7760 case SCI_INDICATORSTART:
7761 return pdoc->decorations.Start(wParam, lParam);
7762
7763 case SCI_INDICATOREND:
7764 return pdoc->decorations.End(wParam, lParam);
7765
7766 case SCI_LINEDOWN:
7767 case SCI_LINEDOWNEXTEND:
7768 case SCI_PARADOWN:
7769 case SCI_PARADOWNEXTEND:
7770 case SCI_LINEUP:
7771 case SCI_LINEUPEXTEND:
7772 case SCI_PARAUP:
7773 case SCI_PARAUPEXTEND:
7774 case SCI_CHARLEFT:
7775 case SCI_CHARLEFTEXTEND:
7776 case SCI_CHARRIGHT:
7777 case SCI_CHARRIGHTEXTEND:
7778 case SCI_WORDLEFT:
7779 case SCI_WORDLEFTEXTEND:
7780 case SCI_WORDRIGHT:
7781 case SCI_WORDRIGHTEXTEND:
7782 case SCI_WORDLEFTEND:
7783 case SCI_WORDLEFTENDEXTEND:
7784 case SCI_WORDRIGHTEND:
7785 case SCI_WORDRIGHTENDEXTEND:
7786 case SCI_HOME:
7787 case SCI_HOMEEXTEND:
7788 case SCI_LINEEND:
7789 case SCI_LINEENDEXTEND:
7790 case SCI_HOMEWRAP:
7791 case SCI_HOMEWRAPEXTEND:
7792 case SCI_LINEENDWRAP:
7793 case SCI_LINEENDWRAPEXTEND:
7794 case SCI_DOCUMENTSTART:
7795 case SCI_DOCUMENTSTARTEXTEND:
7796 case SCI_DOCUMENTEND:
7797 case SCI_DOCUMENTENDEXTEND:
7798
7799 case SCI_STUTTEREDPAGEUP:
7800 case SCI_STUTTEREDPAGEUPEXTEND:
7801 case SCI_STUTTEREDPAGEDOWN:
7802 case SCI_STUTTEREDPAGEDOWNEXTEND:
7803
7804 case SCI_PAGEUP:
7805 case SCI_PAGEUPEXTEND:
7806 case SCI_PAGEDOWN:
7807 case SCI_PAGEDOWNEXTEND:
7808 case SCI_EDITTOGGLEOVERTYPE:
7809 case SCI_CANCEL:
7810 case SCI_DELETEBACK:
7811 case SCI_TAB:
7812 case SCI_BACKTAB:
7813 case SCI_NEWLINE:
7814 case SCI_FORMFEED:
7815 case SCI_VCHOME:
7816 case SCI_VCHOMEEXTEND:
7817 case SCI_VCHOMEWRAP:
7818 case SCI_VCHOMEWRAPEXTEND:
7819 case SCI_ZOOMIN:
7820 case SCI_ZOOMOUT:
7821 case SCI_DELWORDLEFT:
7822 case SCI_DELWORDRIGHT:
7823 case SCI_DELWORDRIGHTEND:
7824 case SCI_DELLINELEFT:
7825 case SCI_DELLINERIGHT:
7826 case SCI_LINECOPY:
7827 case SCI_LINECUT:
7828 case SCI_LINEDELETE:
7829 case SCI_LINETRANSPOSE:
7830 case SCI_LINEDUPLICATE:
7831 case SCI_LOWERCASE:
7832 case SCI_UPPERCASE:
7833 case SCI_LINESCROLLDOWN:
7834 case SCI_LINESCROLLUP:
7835 case SCI_WORDPARTLEFT:
7836 case SCI_WORDPARTLEFTEXTEND:
7837 case SCI_WORDPARTRIGHT:
7838 case SCI_WORDPARTRIGHTEXTEND:
7839 case SCI_DELETEBACKNOTLINE:
7840 case SCI_HOMEDISPLAY:
7841 case SCI_HOMEDISPLAYEXTEND:
7842 case SCI_LINEENDDISPLAY:
7843 case SCI_LINEENDDISPLAYEXTEND:
7844 case SCI_LINEDOWNRECTEXTEND:
7845 case SCI_LINEUPRECTEXTEND:
7846 case SCI_CHARLEFTRECTEXTEND:
7847 case SCI_CHARRIGHTRECTEXTEND:
7848 case SCI_HOMERECTEXTEND:
7849 case SCI_VCHOMERECTEXTEND:
7850 case SCI_LINEENDRECTEXTEND:
7851 case SCI_PAGEUPRECTEXTEND:
7852 case SCI_PAGEDOWNRECTEXTEND:
7853 case SCI_SELECTIONDUPLICATE:
7854 return KeyCommand(iMessage);
7855
7856 case SCI_BRACEHIGHLIGHT:
7857 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
7858 break;
7859
7860 case SCI_BRACEBADLIGHT:
7861 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7862 break;
7863
7864 case SCI_BRACEMATCH:
7865 // wParam is position of char to find brace for,
7866 // lParam is maximum amount of text to restyle to find it
7867 return pdoc->BraceMatch(wParam, lParam);
7868
7869 case SCI_GETVIEWEOL:
7870 return vs.viewEOL;
7871
7872 case SCI_SETVIEWEOL:
7873 vs.viewEOL = wParam != 0;
7874 InvalidateStyleRedraw();
7875 break;
7876
7877 case SCI_SETZOOM:
7878 vs.zoomLevel = wParam;
7879 InvalidateStyleRedraw();
7880 NotifyZoom();
7881 break;
7882
7883 case SCI_GETZOOM:
7884 return vs.zoomLevel;
7885
7886 case SCI_GETEDGECOLUMN:
7887 return theEdge;
7888
7889 case SCI_SETEDGECOLUMN:
7890 theEdge = wParam;
7891 InvalidateStyleRedraw();
7892 break;
7893
7894 case SCI_GETEDGEMODE:
7895 return vs.edgeState;
7896
7897 case SCI_SETEDGEMODE:
7898 vs.edgeState = wParam;
7899 InvalidateStyleRedraw();
7900 break;
7901
7902 case SCI_GETEDGECOLOUR:
7903 return vs.edgecolour.desired.AsLong();
7904
7905 case SCI_SETEDGECOLOUR:
7906 vs.edgecolour.desired = ColourDesired(wParam);
7907 InvalidateStyleRedraw();
7908 break;
7909
7910 case SCI_GETDOCPOINTER:
7911 return reinterpret_cast<sptr_t>(pdoc);
7912
7913 case SCI_SETDOCPOINTER:
7914 CancelModes();
7915 SetDocPointer(reinterpret_cast<Document *>(lParam));
7916 return 0;
7917
7918 case SCI_CREATEDOCUMENT: {
7919 Document *doc = new Document();
7920 if (doc) {
7921 doc->AddRef();
7922 }
7923 return reinterpret_cast<sptr_t>(doc);
7924 }
7925
7926 case SCI_ADDREFDOCUMENT:
7927 (reinterpret_cast<Document *>(lParam))->AddRef();
7928 break;
7929
7930 case SCI_RELEASEDOCUMENT:
7931 (reinterpret_cast<Document *>(lParam))->Release();
7932 break;
7933
7934 case SCI_SETMODEVENTMASK:
7935 modEventMask = wParam;
7936 return 0;
7937
7938 case SCI_GETMODEVENTMASK:
7939 return modEventMask;
7940
7941 case SCI_CONVERTEOLS:
7942 pdoc->ConvertLineEnds(wParam);
7943 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
7944 return 0;
7945
7946 case SCI_SETLENGTHFORENCODE:
7947 lengthForEncode = wParam;
7948 return 0;
7949
7950 case SCI_SELECTIONISRECTANGLE:
7951 return sel.selType == Selection::selRectangle ? 1 : 0;
7952
7953 case SCI_SETSELECTIONMODE: {
7954 switch (wParam) {
7955 case SC_SEL_STREAM:
7956 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7957 sel.selType = Selection::selStream;
7958 break;
7959 case SC_SEL_RECTANGLE:
7960 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
7961 sel.selType = Selection::selRectangle;
7962 break;
7963 case SC_SEL_LINES:
7964 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
7965 sel.selType = Selection::selLines;
7966 break;
7967 case SC_SEL_THIN:
7968 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
7969 sel.selType = Selection::selThin;
7970 break;
7971 default:
7972 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
7973 sel.selType = Selection::selStream;
7974 }
7975 InvalidateSelection(sel.RangeMain(), true);
7976 }
7977 case SCI_GETSELECTIONMODE:
7978 switch (sel.selType) {
7979 case Selection::selStream:
7980 return SC_SEL_STREAM;
7981 case Selection::selRectangle:
7982 return SC_SEL_RECTANGLE;
7983 case Selection::selLines:
7984 return SC_SEL_LINES;
7985 case Selection::selThin:
7986 return SC_SEL_THIN;
7987 default: // ?!
7988 return SC_SEL_STREAM;
7989 }
7990 case SCI_GETLINESELSTARTPOSITION:
7991 case SCI_GETLINESELENDPOSITION: {
7992 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
7993 SelectionPosition(pdoc->LineEnd(wParam)));
7994 for (size_t r=0; r<sel.Count(); r++) {
7995 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
7996 if (portion.start.IsValid()) {
7997 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
7998 }
7999 }
8000 return INVALID_POSITION;
8001 }
8002
8003 case SCI_SETOVERTYPE:
8004 inOverstrike = wParam != 0;
8005 break;
8006
8007 case SCI_GETOVERTYPE:
8008 return inOverstrike ? 1 : 0;
8009
8010 case SCI_SETFOCUS:
8011 SetFocusState(wParam != 0);
8012 break;
8013
8014 case SCI_GETFOCUS:
8015 return hasFocus;
8016
8017 case SCI_SETSTATUS:
8018 errorStatus = wParam;
8019 break;
8020
8021 case SCI_GETSTATUS:
8022 return errorStatus;
8023
8024 case SCI_SETMOUSEDOWNCAPTURES:
8025 mouseDownCaptures = wParam != 0;
8026 break;
8027
8028 case SCI_GETMOUSEDOWNCAPTURES:
8029 return mouseDownCaptures;
8030
8031 case SCI_SETCURSOR:
8032 cursorMode = wParam;
8033 DisplayCursor(Window::cursorText);
8034 break;
8035
8036 case SCI_GETCURSOR:
8037 return cursorMode;
8038
8039 case SCI_SETCONTROLCHARSYMBOL:
8040 controlCharSymbol = wParam;
8041 break;
8042
8043 case SCI_GETCONTROLCHARSYMBOL:
8044 return controlCharSymbol;
8045
8046 case SCI_STARTRECORD:
8047 recordingMacro = true;
8048 return 0;
8049
8050 case SCI_STOPRECORD:
8051 recordingMacro = false;
8052 return 0;
8053
8054 case SCI_MOVECARETINSIDEVIEW:
8055 MoveCaretInsideView();
8056 break;
8057
8058 case SCI_SETFOLDMARGINCOLOUR:
8059 vs.foldmarginColourSet = wParam != 0;
8060 vs.foldmarginColour.desired = ColourDesired(lParam);
8061 InvalidateStyleRedraw();
8062 break;
8063
8064 case SCI_SETFOLDMARGINHICOLOUR:
8065 vs.foldmarginHighlightColourSet = wParam != 0;
8066 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
8067 InvalidateStyleRedraw();
8068 break;
8069
8070 case SCI_SETHOTSPOTACTIVEFORE:
8071 vs.hotspotForegroundSet = wParam != 0;
8072 vs.hotspotForeground.desired = ColourDesired(lParam);
8073 InvalidateStyleRedraw();
8074 break;
8075
8076 case SCI_GETHOTSPOTACTIVEFORE:
8077 return vs.hotspotForeground.desired.AsLong();
8078
8079 case SCI_SETHOTSPOTACTIVEBACK:
8080 vs.hotspotBackgroundSet = wParam != 0;
8081 vs.hotspotBackground.desired = ColourDesired(lParam);
8082 InvalidateStyleRedraw();
8083 break;
8084
8085 case SCI_GETHOTSPOTACTIVEBACK:
8086 return vs.hotspotBackground.desired.AsLong();
8087
8088 case SCI_SETHOTSPOTACTIVEUNDERLINE:
8089 vs.hotspotUnderline = wParam != 0;
8090 InvalidateStyleRedraw();
8091 break;
8092
8093 case SCI_GETHOTSPOTACTIVEUNDERLINE:
8094 return vs.hotspotUnderline ? 1 : 0;
8095
8096 case SCI_SETHOTSPOTSINGLELINE:
8097 vs.hotspotSingleLine = wParam != 0;
8098 InvalidateStyleRedraw();
8099 break;
8100
8101 case SCI_GETHOTSPOTSINGLELINE:
8102 return vs.hotspotSingleLine ? 1 : 0;
8103
8104 case SCI_SETPASTECONVERTENDINGS:
8105 convertPastes = wParam != 0;
8106 break;
8107
8108 case SCI_GETPASTECONVERTENDINGS:
8109 return convertPastes ? 1 : 0;
8110
8111 case SCI_GETCHARACTERPOINTER:
8112 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
8113
8114 case SCI_SETEXTRAASCENT:
8115 vs.extraAscent = wParam;
8116 InvalidateStyleRedraw();
8117 break;
8118
8119 case SCI_GETEXTRAASCENT:
8120 return vs.extraAscent;
8121
8122 case SCI_SETEXTRADESCENT:
8123 vs.extraDescent = wParam;
8124 InvalidateStyleRedraw();
8125 break;
8126
8127 case SCI_GETEXTRADESCENT:
8128 return vs.extraDescent;
8129
8130 case SCI_MARGINSETSTYLEOFFSET:
8131 vs.marginStyleOffset = wParam;
8132 InvalidateStyleRedraw();
8133 break;
8134
8135 case SCI_MARGINGETSTYLEOFFSET:
8136 return vs.marginStyleOffset;
8137
8138 case SCI_MARGINSETTEXT:
8139 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
8140 break;
8141
8142 case SCI_MARGINGETTEXT: {
8143 const StyledText st = pdoc->MarginStyledText(wParam);
8144 if (lParam) {
8145 if (st.text)
8146 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8147 else
8148 strcpy(CharPtrFromSPtr(lParam), "");
8149 }
8150 return st.length;
8151 }
8152
8153 case SCI_MARGINSETSTYLE:
8154 pdoc->MarginSetStyle(wParam, lParam);
8155 break;
8156
8157 case SCI_MARGINGETSTYLE: {
8158 const StyledText st = pdoc->MarginStyledText(wParam);
8159 return st.style;
8160 }
8161
8162 case SCI_MARGINSETSTYLES:
8163 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8164 break;
8165
8166 case SCI_MARGINGETSTYLES: {
8167 const StyledText st = pdoc->MarginStyledText(wParam);
8168 if (lParam) {
8169 if (st.styles)
8170 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8171 else
8172 strcpy(CharPtrFromSPtr(lParam), "");
8173 }
8174 return st.styles ? st.length : 0;
8175 }
8176
8177 case SCI_MARGINTEXTCLEARALL:
8178 pdoc->MarginClearAll();
8179 break;
8180
8181 case SCI_ANNOTATIONSETTEXT:
8182 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
8183 break;
8184
8185 case SCI_ANNOTATIONGETTEXT: {
8186 const StyledText st = pdoc->AnnotationStyledText(wParam);
8187 if (lParam) {
8188 if (st.text)
8189 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
8190 else
8191 strcpy(CharPtrFromSPtr(lParam), "");
8192 }
8193 return st.length;
8194 }
8195
8196 case SCI_ANNOTATIONGETSTYLE: {
8197 const StyledText st = pdoc->AnnotationStyledText(wParam);
8198 return st.style;
8199 }
8200
8201 case SCI_ANNOTATIONSETSTYLE:
8202 pdoc->AnnotationSetStyle(wParam, lParam);
8203 break;
8204
8205 case SCI_ANNOTATIONSETSTYLES:
8206 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
8207 break;
8208
8209 case SCI_ANNOTATIONGETSTYLES: {
8210 const StyledText st = pdoc->AnnotationStyledText(wParam);
8211 if (lParam) {
8212 if (st.styles)
8213 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
8214 else
8215 strcpy(CharPtrFromSPtr(lParam), "");
8216 }
8217 return st.styles ? st.length : 0;
8218 }
8219
8220 case SCI_ANNOTATIONGETLINES:
8221 return pdoc->AnnotationLines(wParam);
8222
8223 case SCI_ANNOTATIONCLEARALL:
8224 pdoc->AnnotationClearAll();
8225 break;
8226
8227 case SCI_ANNOTATIONSETVISIBLE:
8228 SetAnnotationVisible(wParam);
8229 break;
8230
8231 case SCI_ANNOTATIONGETVISIBLE:
8232 return vs.annotationVisible;
8233
8234 case SCI_ANNOTATIONSETSTYLEOFFSET:
8235 vs.annotationStyleOffset = wParam;
8236 InvalidateStyleRedraw();
8237 break;
8238
8239 case SCI_ANNOTATIONGETSTYLEOFFSET:
8240 return vs.annotationStyleOffset;
8241
8242 case SCI_ADDUNDOACTION:
8243 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
8244 break;
8245
8246 case SCI_SETMULTIPLESELECTION:
8247 multipleSelection = wParam != 0;
8248 InvalidateCaret();
8249 break;
8250
8251 case SCI_GETMULTIPLESELECTION:
8252 return multipleSelection;
8253
8254 case SCI_SETADDITIONALSELECTIONTYPING:
8255 additionalSelectionTyping = wParam != 0;
8256 InvalidateCaret();
8257 break;
8258
8259 case SCI_GETADDITIONALSELECTIONTYPING:
8260 return additionalSelectionTyping;
8261
8262 case SCI_SETADDITIONALCARETSBLINK:
8263 additionalCaretsBlink = wParam != 0;
8264 InvalidateCaret();
8265 break;
8266
8267 case SCI_GETADDITIONALCARETSBLINK:
8268 return additionalCaretsBlink;
8269
8270 case SCI_SETADDITIONALCARETSVISIBLE:
8271 additionalCaretsVisible = wParam != 0;
8272 InvalidateCaret();
8273 break;
8274
8275 case SCI_GETADDITIONALCARETSVISIBLE:
8276 return additionalCaretsVisible;
8277
8278 case SCI_GETSELECTIONS:
8279 return sel.Count();
8280
8281 case SCI_CLEARSELECTIONS:
8282 sel.Clear();
8283 Redraw();
8284 break;
8285
8286 case SCI_SETSELECTION:
8287 sel.SetSelection(SelectionRange(wParam, lParam));
8288 Redraw();
8289 break;
8290
8291 case SCI_ADDSELECTION:
8292 sel.AddSelection(SelectionRange(wParam, lParam));
8293 Redraw();
8294 break;
8295
8296 case SCI_SETMAINSELECTION:
8297 sel.SetMain(wParam);
8298 Redraw();
8299 break;
8300
8301 case SCI_GETMAINSELECTION:
8302 return sel.Main();
8303
8304 case SCI_SETSELECTIONNCARET:
8305 sel.Range(wParam).caret.SetPosition(lParam);
8306 Redraw();
8307 break;
8308
8309 case SCI_GETSELECTIONNCARET:
8310 return sel.Range(wParam).caret.Position();
8311
8312 case SCI_SETSELECTIONNANCHOR:
8313 sel.Range(wParam).anchor.SetPosition(lParam);
8314 Redraw();
8315 break;
8316 case SCI_GETSELECTIONNANCHOR:
8317 return sel.Range(wParam).anchor.Position();
8318
8319 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
8320 sel.Range(wParam).caret.SetVirtualSpace(lParam);
8321 Redraw();
8322 break;
8323
8324 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
8325 return sel.Range(wParam).caret.VirtualSpace();
8326
8327 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
8328 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
8329 Redraw();
8330 break;
8331
8332 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
8333 return sel.Range(wParam).anchor.VirtualSpace();
8334
8335 case SCI_SETSELECTIONNSTART:
8336 sel.Range(wParam).anchor.SetPosition(lParam);
8337 Redraw();
8338 break;
8339
8340 case SCI_GETSELECTIONNSTART:
8341 return sel.Range(wParam).Start().Position();
8342
8343 case SCI_SETSELECTIONNEND:
8344 sel.Range(wParam).caret.SetPosition(lParam);
8345 Redraw();
8346 break;
8347
8348 case SCI_GETSELECTIONNEND:
8349 return sel.Range(wParam).End().Position();
8350
8351 case SCI_SETRECTANGULARSELECTIONCARET:
8352 if (!sel.IsRectangular())
8353 sel.Clear();
8354 sel.selType = Selection::selRectangle;
8355 sel.Rectangular().caret.SetPosition(wParam);
8356 SetRectangularRange();
8357 Redraw();
8358 break;
8359
8360 case SCI_GETRECTANGULARSELECTIONCARET:
8361 return sel.Rectangular().caret.Position();
8362
8363 case SCI_SETRECTANGULARSELECTIONANCHOR:
8364 if (!sel.IsRectangular())
8365 sel.Clear();
8366 sel.selType = Selection::selRectangle;
8367 sel.Rectangular().anchor.SetPosition(wParam);
8368 SetRectangularRange();
8369 Redraw();
8370 break;
8371
8372 case SCI_GETRECTANGULARSELECTIONANCHOR:
8373 return sel.Rectangular().anchor.Position();
8374
8375 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8376 if (!sel.IsRectangular())
8377 sel.Clear();
8378 sel.selType = Selection::selRectangle;
8379 sel.Rectangular().caret.SetVirtualSpace(wParam);
8380 SetRectangularRange();
8381 Redraw();
8382 break;
8383
8384 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
8385 return sel.Rectangular().caret.VirtualSpace();
8386
8387 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8388 if (!sel.IsRectangular())
8389 sel.Clear();
8390 sel.selType = Selection::selRectangle;
8391 sel.Rectangular().anchor.SetVirtualSpace(wParam);
8392 SetRectangularRange();
8393 Redraw();
8394 break;
8395
8396 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
8397 return sel.Rectangular().anchor.VirtualSpace();
8398
8399 case SCI_SETVIRTUALSPACEOPTIONS:
8400 virtualSpaceOptions = wParam;
8401 break;
8402
8403 case SCI_GETVIRTUALSPACEOPTIONS:
8404 return virtualSpaceOptions;
8405
8406 case SCI_SETADDITIONALSELFORE:
8407 vs.selAdditionalForeground.desired = ColourDesired(wParam);
8408 InvalidateStyleRedraw();
8409 break;
8410
8411 case SCI_SETADDITIONALSELBACK:
8412 vs.selAdditionalBackground.desired = ColourDesired(wParam);
8413 InvalidateStyleRedraw();
8414 break;
8415
8416 case SCI_SETADDITIONALSELALPHA:
8417 vs.selAdditionalAlpha = wParam;
8418 InvalidateStyleRedraw();
8419 break;
8420
8421 case SCI_GETADDITIONALSELALPHA:
8422 return vs.selAdditionalAlpha;
8423
8424 case SCI_SETADDITIONALCARETFORE:
8425 vs.additionalCaretColour.desired = ColourDesired(wParam);
8426 InvalidateStyleRedraw();
8427 break;
8428
8429 case SCI_GETADDITIONALCARETFORE:
8430 return vs.additionalCaretColour.desired.AsLong();
8431
8432 case SCI_ROTATESELECTION:
8433 sel.RotateMain();
8434 InvalidateSelection(sel.RangeMain(), true);
8435 break;
8436
8437 case SCI_SWAPMAINANCHORCARET:
8438 InvalidateSelection(sel.RangeMain());
8439 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
8440 break;
8441
8442 default:
8443 return DefWndProc(iMessage, wParam, lParam);
8444 }
8445 //Platform::DebugPrintf("end wnd proc\n");
8446 return 0l;
8447 }