V4.7 release fixes
[SCSI2SD.git] / software / scsi2sd-util / wxWidgets / 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-2011 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 #include <assert.h>
13
14 #include <cmath>
15 #include <string>
16 #include <vector>
17 #include <map>
18 #include <algorithm>
19 #include <memory>
20
21 #include "Platform.h"
22
23 #include "ILexer.h"
24 #include "Scintilla.h"
25
26 #include "SplitVector.h"
27 #include "Partitioning.h"
28 #include "RunStyles.h"
29 #include "ContractionState.h"
30 #include "CellBuffer.h"
31 #include "KeyMap.h"
32 #include "Indicator.h"
33 #include "XPM.h"
34 #include "LineMarker.h"
35 #include "Style.h"
36 #include "ViewStyle.h"
37 #include "CharClassify.h"
38 #include "Decoration.h"
39 #include "Document.h"
40 #include "UniConversion.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 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
95         for (unsigned int i = 0; i < len; i++) {
96                 // This is safe because IsSpaceOrTab() will return false for null terminators
97                 if (!IsSpaceOrTab(s[i]))
98                         return false;
99         }
100         return true;
101 }
102
103 Editor::Editor() {
104         ctrlID = 0;
105
106         stylesValid = false;
107         technology = SC_TECHNOLOGY_DEFAULT;
108         
109         printMagnification = 0;
110         printColourMode = SC_PRINT_NORMAL;
111         printWrapState = eWrapWord;
112         cursorMode = SC_CURSORNORMAL;
113         controlCharSymbol = 0;  /* Draw the control characters */
114
115         hasFocus = false;
116         hideSelection = false;
117         inOverstrike = false;
118         errorStatus = 0;
119         mouseDownCaptures = true;
120
121         bufferedDraw = true;
122         twoPhaseDraw = true;
123
124         lastClickTime = 0;
125         dwellDelay = SC_TIME_FOREVER;
126         ticksToDwell = SC_TIME_FOREVER;
127         dwelling = false;
128         ptMouseLast.x = 0;
129         ptMouseLast.y = 0;
130         inDragDrop = ddNone;
131         dropWentOutside = false;
132         posDrag = SelectionPosition(invalidPosition);
133         posDrop = SelectionPosition(invalidPosition);
134         hotSpotClickPos = INVALID_POSITION;
135         selectionType = selChar;
136
137         lastXChosen = 0;
138         lineAnchorPos = 0;
139         originalAnchorPos = 0;
140         wordSelectAnchorStartPos = 0;
141         wordSelectAnchorEndPos = 0;
142         wordSelectInitialCaretPos = -1;
143
144         primarySelection = true;
145
146         caretXPolicy = CARET_SLOP | CARET_EVEN;
147         caretXSlop = 50;
148
149         caretYPolicy = CARET_EVEN;
150         caretYSlop = 0;
151         
152         visiblePolicy = 0;
153         visibleSlop = 0;
154
155         searchAnchor = 0;
156
157         xOffset = 0;
158         xCaretMargin = 50;
159         horizontalScrollBarVisible = true;
160         scrollWidth = 2000;
161         trackLineWidth = false;
162         lineWidthMaxSeen = 0;
163         verticalScrollBarVisible = true;
164         endAtLastLine = true;
165         caretSticky = SC_CARETSTICKY_OFF;
166         marginOptions = SC_MARGINOPTION_NONE;
167         multipleSelection = false;
168         additionalSelectionTyping = false;
169         multiPasteMode = SC_MULTIPASTE_ONCE;
170         additionalCaretsBlink = true;
171         additionalCaretsVisible = true;
172         virtualSpaceOptions = SCVS_NONE;
173
174         pixmapLine = 0;
175         pixmapSelMargin = 0;
176         pixmapSelPattern = 0;
177         pixmapIndentGuide = 0;
178         pixmapIndentGuideHighlight = 0;
179
180         targetStart = 0;
181         targetEnd = 0;
182         searchFlags = 0;
183
184         topLine = 0;
185         posTopLine = 0;
186
187         lengthForEncode = -1;
188
189         needUpdateUI = 0;
190         ContainerNeedsUpdate(SC_UPDATE_CONTENT);
191         braces[0] = invalidPosition;
192         braces[1] = invalidPosition;
193         bracesMatchStyle = STYLE_BRACEBAD;
194         highlightGuideColumn = 0;
195
196         theEdge = 0;
197
198         paintState = notPainting;
199         willRedrawAll = false;
200
201         modEventMask = SC_MODEVENTMASKALL;
202
203         pdoc = new Document();
204         pdoc->AddRef();
205         pdoc->AddWatcher(this, 0);
206
207         recordingMacro = false;
208         foldFlags = 0;
209
210         wrapState = eWrapNone;
211         wrapWidth = LineLayout::wrapWidthInfinite;
212         wrapStart = wrapLineLarge;
213         wrapEnd = wrapLineLarge;
214         wrapVisualFlags = 0;
215         wrapVisualFlagsLocation = 0;
216         wrapVisualStartIndent = 0;
217         wrapIndentMode = SC_WRAPINDENT_FIXED;
218
219         convertPastes = true;
220
221         hsStart = -1;
222         hsEnd = -1;
223
224         llc.SetLevel(LineLayoutCache::llcCaret);
225         posCache.SetSize(0x400);
226 }
227
228 Editor::~Editor() {
229         pdoc->RemoveWatcher(this, 0);
230         pdoc->Release();
231         pdoc = 0;
232         DropGraphics(true);
233 }
234
235 void Editor::Finalise() {
236         SetIdle(false);
237         CancelModes();
238 }
239
240 void Editor::DropGraphics(bool freeObjects) {
241         if (freeObjects) {
242                 delete pixmapLine;
243                 pixmapLine = 0;
244                 delete pixmapSelMargin;
245                 pixmapSelMargin = 0;
246                 delete pixmapSelPattern;
247                 pixmapSelPattern = 0;
248                 delete pixmapIndentGuide;
249                 pixmapIndentGuide = 0;
250                 delete pixmapIndentGuideHighlight;
251                 pixmapIndentGuideHighlight = 0;
252         } else {
253                 if (pixmapLine)
254                         pixmapLine->Release();
255                 if (pixmapSelMargin)
256                         pixmapSelMargin->Release();
257                 if (pixmapSelPattern)
258                         pixmapSelPattern->Release();
259                 if (pixmapIndentGuide)
260                         pixmapIndentGuide->Release();
261                 if (pixmapIndentGuideHighlight)
262                         pixmapIndentGuideHighlight->Release();
263         }
264 }
265
266 void Editor::AllocateGraphics() {
267         if (!pixmapLine)
268                 pixmapLine = Surface::Allocate(technology);
269         if (!pixmapSelMargin)
270                 pixmapSelMargin = Surface::Allocate(technology);
271         if (!pixmapSelPattern)
272                 pixmapSelPattern = Surface::Allocate(technology);
273         if (!pixmapIndentGuide)
274                 pixmapIndentGuide = Surface::Allocate(technology);
275         if (!pixmapIndentGuideHighlight)
276                 pixmapIndentGuideHighlight = Surface::Allocate(technology);
277 }
278
279 void Editor::InvalidateStyleData() {
280         stylesValid = false;
281         vs.technology = technology;
282         DropGraphics(false);
283         AllocateGraphics();
284         llc.Invalidate(LineLayout::llInvalid);
285         posCache.Clear();
286 }
287
288 void Editor::InvalidateStyleRedraw() {
289         NeedWrapping();
290         InvalidateStyleData();
291         Redraw();
292 }
293
294 void Editor::RefreshStyleData() {
295         if (!stylesValid) {
296                 stylesValid = true;
297                 AutoSurface surface(this);
298                 if (surface) {
299                         vs.Refresh(*surface);
300                 }
301                 SetScrollBars();
302                 SetRectangularRange();
303         }
304 }
305
306 PRectangle Editor::GetClientRectangle() {
307         return wMain.GetClientPosition();
308 }
309
310 PRectangle Editor::GetTextRectangle() {
311         PRectangle rc = GetClientRectangle();
312         rc.left += vs.fixedColumnWidth;
313         rc.right -= vs.rightMarginWidth;
314         return rc;
315 }
316
317 int Editor::LinesOnScreen() {
318         PRectangle rcClient = GetClientRectangle();
319         int htClient = rcClient.bottom - rcClient.top;
320         //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
321         return htClient / vs.lineHeight;
322 }
323
324 int Editor::LinesToScroll() {
325         int retVal = LinesOnScreen() - 1;
326         if (retVal < 1)
327                 return 1;
328         else
329                 return retVal;
330 }
331
332 int Editor::MaxScrollPos() {
333         //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
334         //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
335         int retVal = cs.LinesDisplayed();
336         if (endAtLastLine) {
337                 retVal -= LinesOnScreen();
338         } else {
339                 retVal--;
340         }
341         if (retVal < 0) {
342                 return 0;
343         } else {
344                 return retVal;
345         }
346 }
347
348 const char *ControlCharacterString(unsigned char ch) {
349         const char *reps[] = {
350                 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
351                 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
352                 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
353                 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
354         };
355         if (ch < (sizeof(reps) / sizeof(reps[0]))) {
356                 return reps[ch];
357         } else {
358                 return "BAD";
359         }
360 }
361
362 /**
363  * Convenience class to ensure LineLayout objects are always disposed.
364  */
365 class AutoLineLayout {
366         LineLayoutCache &llc;
367         LineLayout *ll;
368         AutoLineLayout &operator=(const AutoLineLayout &);
369 public:
370         AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
371         ~AutoLineLayout() {
372                 llc.Dispose(ll);
373                 ll = 0;
374         }
375         LineLayout *operator->() const {
376                 return ll;
377         }
378         operator LineLayout *() const {
379                 return ll;
380         }
381         void Set(LineLayout *ll_) {
382                 llc.Dispose(ll);
383                 ll = ll_;
384         }
385 };
386
387 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
388         if (sp.Position() < 0) {
389                 return SelectionPosition(0);
390         } else if (sp.Position() > pdoc->Length()) {
391                 return SelectionPosition(pdoc->Length());
392         } else {
393                 // If not at end of line then set offset to 0
394                 if (!pdoc->IsLineEndPosition(sp.Position()))
395                         sp.SetVirtualSpace(0);
396                 return sp;
397         }
398 }
399
400 Point Editor::LocationFromPosition(SelectionPosition pos) {
401         Point pt;
402         RefreshStyleData();
403         if (pos.Position() == INVALID_POSITION)
404                 return pt;
405         int line = pdoc->LineFromPosition(pos.Position());
406         int lineVisible = cs.DisplayFromDoc(line);
407         //Platform::DebugPrintf("line=%d\n", line);
408         AutoSurface surface(this);
409         AutoLineLayout ll(llc, RetrieveLineLayout(line));
410         if (surface && ll) {
411                 // -1 because of adding in for visible lines in following loop.
412                 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
413                 pt.x = 0;
414                 unsigned int posLineStart = pdoc->LineStart(line);
415                 LayoutLine(line, surface, vs, ll, wrapWidth);
416                 int posInLine = pos.Position() - posLineStart;
417                 // In case of very long line put x at arbitrary large position
418                 if (posInLine > ll->maxLineLength) {
419                         pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
420                 }
421
422                 for (int subLine = 0; subLine < ll->lines; subLine++) {
423                         if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
424                                 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
425                                 if (ll->wrapIndent != 0) {
426                                         int lineStart = ll->LineStart(subLine);
427                                         if (lineStart != 0)     // Wrapped
428                                                 pt.x += ll->wrapIndent;
429                                 }
430                         }
431                         if (posInLine >= ll->LineStart(subLine)) {
432                                 pt.y += vs.lineHeight;
433                         }
434                 }
435                 pt.x += vs.fixedColumnWidth - xOffset;
436         }
437         pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
438         return pt;
439 }
440
441 Point Editor::LocationFromPosition(int pos) {
442         return LocationFromPosition(SelectionPosition(pos));
443 }
444
445 int Editor::XFromPosition(int pos) {
446         Point pt = LocationFromPosition(pos);
447         return pt.x - vs.fixedColumnWidth + xOffset;
448 }
449
450 int Editor::XFromPosition(SelectionPosition sp) {
451         Point pt = LocationFromPosition(sp);
452         return pt.x - vs.fixedColumnWidth + xOffset;
453 }
454
455 int Editor::LineFromLocation(Point pt) {
456         return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
457 }
458
459 void Editor::SetTopLine(int topLineNew) {
460         if (topLine != topLineNew) {
461                 topLine = topLineNew;
462                 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
463         }
464         posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
465 }
466
467 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
468         RefreshStyleData();
469         if (canReturnInvalid) {
470                 PRectangle rcClient = GetTextRectangle();
471                 if (!rcClient.Contains(pt))
472                         return SelectionPosition(INVALID_POSITION);
473                 if (pt.x < vs.fixedColumnWidth)
474                         return SelectionPosition(INVALID_POSITION);
475                 if (pt.y < 0)
476                         return SelectionPosition(INVALID_POSITION);
477         }
478         pt.x = pt.x - vs.fixedColumnWidth + xOffset;
479         int visibleLine = pt.y / vs.lineHeight + topLine;
480         if (pt.y < 0) { // Division rounds towards 0
481                 visibleLine = (static_cast<int>(pt.y) - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
482         }
483         if (!canReturnInvalid && (visibleLine < 0))
484                 visibleLine = 0;
485         int lineDoc = cs.DocFromDisplay(visibleLine);
486         if (canReturnInvalid && (lineDoc < 0))
487                 return SelectionPosition(INVALID_POSITION);
488         if (lineDoc >= pdoc->LinesTotal())
489                 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
490         unsigned int posLineStart = pdoc->LineStart(lineDoc);
491         SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
492         AutoSurface surface(this);
493         AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
494         if (surface && ll) {
495                 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
496                 int lineStartSet = cs.DisplayFromDoc(lineDoc);
497                 int subLine = visibleLine - lineStartSet;
498                 if (subLine < ll->lines) {
499                         int lineStart = ll->LineStart(subLine);
500                         int lineEnd = ll->LineLastVisible(subLine);
501                         XYPOSITION subLineStart = ll->positions[lineStart];
502
503                         if (ll->wrapIndent != 0) {
504                                 if (lineStart != 0)     // Wrapped
505                                         pt.x -= ll->wrapIndent;
506                         }
507                         int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
508                         while (i < lineEnd) {
509                                 if (charPosition) {
510                                         if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
511                                                 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
512                                         }
513                                 } else {
514                                         if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
515                                                 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
516                                         }
517                                 }
518                                 i++;
519                         }
520                         if (virtualSpace) {
521                                 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
522                                 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
523                                         spaceWidth;
524                                 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
525                         } else if (canReturnInvalid) {
526                                 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
527                                         return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
528                                 }
529                         } else {
530                                 return SelectionPosition(lineEnd + posLineStart);
531                         }
532                 }
533                 if (!canReturnInvalid)
534                         return SelectionPosition(ll->numCharsInLine + posLineStart);
535         }
536         return retVal;
537 }
538
539 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
540         return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
541 }
542
543 /**
544  * Find the document position corresponding to an x coordinate on a particular document line.
545  * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
546  */
547 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
548         RefreshStyleData();
549         if (lineDoc >= pdoc->LinesTotal())
550                 return SelectionPosition(pdoc->Length());
551         //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
552         AutoSurface surface(this);
553         AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
554         int retVal = 0;
555         if (surface && ll) {
556                 unsigned int posLineStart = pdoc->LineStart(lineDoc);
557                 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
558                 int subLine = 0;
559                 int lineStart = ll->LineStart(subLine);
560                 int lineEnd = ll->LineLastVisible(subLine);
561                 XYPOSITION subLineStart = ll->positions[lineStart];
562                 XYPOSITION newX = x;
563
564                 if (ll->wrapIndent != 0) {
565                         if (lineStart != 0)     // Wrapped
566                                 newX -= ll->wrapIndent;
567                 }
568                 int i = ll->FindBefore(newX + subLineStart, lineStart, lineEnd);
569                 while (i < lineEnd) {
570                         if ((newX + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
571                                 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
572                                 return SelectionPosition(retVal);
573                         }
574                         i++;
575                 }
576                 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
577                 int spaceOffset = (newX + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
578                 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
579         }
580         return SelectionPosition(retVal);
581 }
582
583 int Editor::PositionFromLineX(int lineDoc, int x) {
584         return SPositionFromLineX(lineDoc, x).Position();
585 }
586
587 /**
588  * If painting then abandon the painting because a wider redraw is needed.
589  * @return true if calling code should stop drawing.
590  */
591 bool Editor::AbandonPaint() {
592         if ((paintState == painting) && !paintingAllText) {
593                 paintState = paintAbandoned;
594         }
595         return paintState == paintAbandoned;
596 }
597
598 void Editor::RedrawRect(PRectangle rc) {
599         //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
600
601         // Clip the redraw rectangle into the client area
602         PRectangle rcClient = GetClientRectangle();
603         if (rc.top < rcClient.top)
604                 rc.top = rcClient.top;
605         if (rc.bottom > rcClient.bottom)
606                 rc.bottom = rcClient.bottom;
607         if (rc.left < rcClient.left)
608                 rc.left = rcClient.left;
609         if (rc.right > rcClient.right)
610                 rc.right = rcClient.right;
611
612         if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
613                 wMain.InvalidateRectangle(rc);
614         }
615 }
616
617 void Editor::Redraw() {
618         //Platform::DebugPrintf("Redraw all\n");
619         PRectangle rcClient = GetClientRectangle();
620         wMain.InvalidateRectangle(rcClient);
621         //wMain.InvalidateAll();
622 }
623
624 void Editor::RedrawSelMargin(int line, bool allAfter) {
625         if (!AbandonPaint()) {
626                 if (vs.maskInLine) {
627                         Redraw();
628                 } else {
629                         PRectangle rcSelMargin = GetClientRectangle();
630                         rcSelMargin.right = vs.fixedColumnWidth;
631                         if (line != -1) {
632                                 int position = pdoc->LineStart(line);
633                                 PRectangle rcLine = RectangleFromRange(position, position);
634
635                                 // Inflate line rectangle if there are image markers with height larger than line height
636                                 if (vs.largestMarkerHeight > vs.lineHeight) {
637                                         int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
638                                         rcLine.top -= delta;
639                                         rcLine.bottom += delta;
640                                         if (rcLine.top < rcSelMargin.top)
641                                                 rcLine.top = rcSelMargin.top;
642                                         if (rcLine.bottom > rcSelMargin.bottom)
643                                                 rcLine.bottom = rcSelMargin.bottom;
644                                 }
645
646                                 rcSelMargin.top = rcLine.top;
647                                 if (!allAfter)
648                                         rcSelMargin.bottom = rcLine.bottom;
649                         }
650                         wMain.InvalidateRectangle(rcSelMargin);
651                 }
652         }
653 }
654
655 PRectangle Editor::RectangleFromRange(int start, int end) {
656         int minPos = start;
657         if (minPos > end)
658                 minPos = end;
659         int maxPos = start;
660         if (maxPos < end)
661                 maxPos = end;
662         int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
663         int lineDocMax = pdoc->LineFromPosition(maxPos);
664         int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
665         PRectangle rcClient = GetTextRectangle();
666         PRectangle rc;
667         const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
668         rc.left = vs.fixedColumnWidth - leftTextOverlap;
669         rc.top = (minLine - topLine) * vs.lineHeight;
670         if (rc.top < 0)
671                 rc.top = 0;
672         rc.right = rcClient.right;
673         rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
674         // Ensure PRectangle is within 16 bit space
675         rc.top = Platform::Clamp(rc.top, -32000, 32000);
676         rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
677
678         return rc;
679 }
680
681 void Editor::InvalidateRange(int start, int end) {
682         RedrawRect(RectangleFromRange(start, end));
683 }
684
685 int Editor::CurrentPosition() {
686         return sel.MainCaret();
687 }
688
689 bool Editor::SelectionEmpty() {
690         return sel.Empty();
691 }
692
693 SelectionPosition Editor::SelectionStart() {
694         return sel.RangeMain().Start();
695 }
696
697 SelectionPosition Editor::SelectionEnd() {
698         return sel.RangeMain().End();
699 }
700
701 void Editor::SetRectangularRange() {
702         if (sel.IsRectangular()) {
703                 int xAnchor = XFromPosition(sel.Rectangular().anchor);
704                 int xCaret = XFromPosition(sel.Rectangular().caret);
705                 if (sel.selType == Selection::selThin) {
706                         xCaret = xAnchor;
707                 }
708                 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
709                 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
710                 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
711                 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
712                         SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
713                         if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
714                                 range.ClearVirtualSpace();
715                         if (line == lineAnchorRect)
716                                 sel.SetSelection(range);
717                         else
718                                 sel.AddSelectionWithoutTrim(range);
719                 }
720         }
721 }
722
723 void Editor::ThinRectangularRange() {
724         if (sel.IsRectangular()) {
725                 sel.selType = Selection::selThin;
726                 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
727                         sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
728                 } else {
729                         sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
730                 }
731                 SetRectangularRange();
732         }
733 }
734
735 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
736         if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
737                 invalidateWholeSelection = true;
738         }
739         int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
740         // +1 for lastAffected ensures caret repainted
741         int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
742         lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
743         if (invalidateWholeSelection) {
744                 for (size_t r=0; r<sel.Count(); r++) {
745                         firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
746                         firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
747                         lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
748                         lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
749                 }
750         }
751         ContainerNeedsUpdate(SC_UPDATE_SELECTION);
752         InvalidateRange(firstAffected, lastAffected);
753 }
754
755 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
756         currentPos_ = ClampPositionIntoDocument(currentPos_);
757         anchor_ = ClampPositionIntoDocument(anchor_);
758         int currentLine = pdoc->LineFromPosition(currentPos_.Position());
759         /* For Line selection - ensure the anchor and caret are always
760            at the beginning and end of the region lines. */
761         if (sel.selType == Selection::selLines) {
762                 if (currentPos_ > anchor_) {
763                         anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
764                         currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
765                 } else {
766                         currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
767                         anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
768                 }
769         }
770         SelectionRange rangeNew(currentPos_, anchor_);
771         if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
772                 InvalidateSelection(rangeNew);
773         }
774         sel.RangeMain() = rangeNew;
775         SetRectangularRange();
776         ClaimSelection();
777
778         if (highlightDelimiter.NeedsDrawing(currentLine)) {
779                 RedrawSelMargin();
780         }
781 }
782
783 void Editor::SetSelection(int currentPos_, int anchor_) {
784         SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
785 }
786
787 // Just move the caret on the main selection
788 void Editor::SetSelection(SelectionPosition currentPos_) {
789         currentPos_ = ClampPositionIntoDocument(currentPos_);
790         int currentLine = pdoc->LineFromPosition(currentPos_.Position());
791         if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
792                 InvalidateSelection(SelectionRange(currentPos_));
793         }
794         if (sel.IsRectangular()) {
795                 sel.Rectangular() =
796                         SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
797                 SetRectangularRange();
798         } else {
799                 sel.RangeMain() =
800                         SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
801         }
802         ClaimSelection();
803
804         if (highlightDelimiter.NeedsDrawing(currentLine)) {
805                 RedrawSelMargin();
806         }
807 }
808
809 void Editor::SetSelection(int currentPos_) {
810         SetSelection(SelectionPosition(currentPos_));
811 }
812
813 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
814         int currentLine = pdoc->LineFromPosition(currentPos_.Position());
815         SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
816         if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
817                 InvalidateSelection(rangeNew);
818         }
819         sel.Clear();
820         sel.RangeMain() = rangeNew;
821         SetRectangularRange();
822         ClaimSelection();
823
824         if (highlightDelimiter.NeedsDrawing(currentLine)) {
825                 RedrawSelMargin();
826         }
827 }
828
829 void Editor::SetEmptySelection(int currentPos_) {
830         SetEmptySelection(SelectionPosition(currentPos_));
831 }
832
833 bool Editor::RangeContainsProtected(int start, int end) const {
834         if (vs.ProtectionActive()) {
835                 if (start > end) {
836                         int t = start;
837                         start = end;
838                         end = t;
839                 }
840                 int mask = pdoc->stylingBitsMask;
841                 for (int pos = start; pos < end; pos++) {
842                         if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
843                                 return true;
844                 }
845         }
846         return false;
847 }
848
849 bool Editor::SelectionContainsProtected() {
850         for (size_t r=0; r<sel.Count(); r++) {
851                 if (RangeContainsProtected(sel.Range(r).Start().Position(),
852                         sel.Range(r).End().Position())) {
853                         return true;
854                 }
855         }
856         return false;
857 }
858
859 /**
860  * Asks document to find a good position and then moves out of any invisible positions.
861  */
862 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
863         return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
864 }
865
866 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
867         int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
868         if (posMoved != pos.Position())
869                 pos.SetPosition(posMoved);
870         if (vs.ProtectionActive()) {
871                 int mask = pdoc->stylingBitsMask;
872                 if (moveDir > 0) {
873                         if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
874                                 while ((pos.Position() < pdoc->Length()) &&
875                                         (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
876                                         pos.Add(1);
877                         }
878                 } else if (moveDir < 0) {
879                         if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
880                                 while ((pos.Position() > 0) &&
881                                         (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
882                                         pos.Add(-1);
883                         }
884                 }
885         }
886         return pos;
887 }
888
889 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
890         bool simpleCaret = (sel.Count() == 1) && sel.Empty();
891         SelectionPosition spCaret = sel.Last();
892
893         int delta = newPos.Position() - sel.MainCaret();
894         newPos = ClampPositionIntoDocument(newPos);
895         newPos = MovePositionOutsideChar(newPos, delta);
896         if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
897                 // Can't turn into multiple selection so clear additional selections
898                 InvalidateSelection(SelectionRange(newPos), true);
899                 SelectionRange rangeMain = sel.RangeMain();
900                 sel.SetSelection(rangeMain);
901         }
902         if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
903                 // Switching to rectangular
904                 SelectionRange rangeMain = sel.RangeMain();
905                 sel.Clear();
906                 sel.Rectangular() = rangeMain;
907         }
908         if (selt != Selection::noSel) {
909                 sel.selType = selt;
910         }
911         if (selt != Selection::noSel || sel.MoveExtends()) {
912                 SetSelection(newPos);
913         } else {
914                 SetEmptySelection(newPos);
915         }
916         ShowCaretAtCurrentPosition();
917
918         int currentLine = pdoc->LineFromPosition(newPos.Position());
919         if (ensureVisible) {
920                 // In case in need of wrapping to ensure DisplayFromDoc works.
921                 if (currentLine >= wrapStart)
922                         WrapLines(true, -1);
923                 XYScrollPosition newXY = XYScrollToMakeVisible(true, true, true);
924                 if (simpleCaret && (newXY.xOffset == xOffset)) {
925                         // simple vertical scroll then invalidate
926                         ScrollTo(newXY.topLine);
927                         InvalidateSelection(SelectionRange(spCaret), true);
928                 } else {
929                         SetXYScroll(newXY);
930                 }
931         }
932
933         if (highlightDelimiter.NeedsDrawing(currentLine)) {
934                 RedrawSelMargin();
935         }
936         return 0;
937 }
938
939 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
940         return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
941 }
942
943 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
944         pos = ClampPositionIntoDocument(pos);
945         pos = MovePositionOutsideChar(pos, moveDir);
946         int lineDoc = pdoc->LineFromPosition(pos.Position());
947         if (cs.GetVisible(lineDoc)) {
948                 return pos;
949         } else {
950                 int lineDisplay = cs.DisplayFromDoc(lineDoc);
951                 if (moveDir > 0) {
952                         // lineDisplay is already line before fold as lines in fold use display line of line after fold
953                         lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
954                         return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
955                 } else {
956                         lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
957                         return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
958                 }
959         }
960 }
961
962 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
963         return MovePositionSoVisible(SelectionPosition(pos), moveDir);
964 }
965
966 Point Editor::PointMainCaret() {
967         return LocationFromPosition(sel.Range(sel.Main()).caret);
968 }
969
970 /**
971  * Choose the x position that the caret will try to stick to
972  * as it moves up and down.
973  */
974 void Editor::SetLastXChosen() {
975         Point pt = PointMainCaret();
976         lastXChosen = pt.x + xOffset;
977 }
978
979 void Editor::ScrollTo(int line, bool moveThumb) {
980         int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
981         if (topLineNew != topLine) {
982                 // Try to optimise small scrolls
983 #ifndef UNDER_CE
984                 int linesToMove = topLine - topLineNew;
985                 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
986                 willRedrawAll = !performBlit;
987 #endif
988                 SetTopLine(topLineNew);
989                 // Optimize by styling the view as this will invalidate any needed area
990                 // which could abort the initial paint if discovered later.
991                 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
992 #ifndef UNDER_CE
993                 // Perform redraw rather than scroll if many lines would be redrawn anyway.
994                 if (performBlit) {
995                         ScrollText(linesToMove);
996                 } else {
997                         Redraw();
998                 }
999                 willRedrawAll = false;
1000 #else
1001                 Redraw();
1002 #endif
1003                 if (moveThumb) {
1004                         SetVerticalScrollPos();
1005                 }
1006         }
1007 }
1008
1009 void Editor::ScrollText(int /* linesToMove */) {
1010         //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1011         Redraw();
1012 }
1013
1014 void Editor::HorizontalScrollTo(int xPos) {
1015         //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1016         if (xPos < 0)
1017                 xPos = 0;
1018         if ((wrapState == eWrapNone) && (xOffset != xPos)) {
1019                 xOffset = xPos;
1020                 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1021                 SetHorizontalScrollPos();
1022                 RedrawRect(GetClientRectangle());
1023         }
1024 }
1025
1026 void Editor::VerticalCentreCaret() {
1027         int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1028         int lineDisplay = cs.DisplayFromDoc(lineDoc);
1029         int newTop = lineDisplay - (LinesOnScreen() / 2);
1030         if (topLine != newTop) {
1031                 SetTopLine(newTop > 0 ? newTop : 0);
1032                 RedrawRect(GetClientRectangle());
1033         }
1034 }
1035
1036 // Avoid 64 bit compiler warnings.
1037 // Scintilla does not support text buffers larger than 2**31
1038 static int istrlen(const char *s) {
1039         return static_cast<int>(strlen(s));
1040 }
1041
1042 void Editor::MoveSelectedLines(int lineDelta) {
1043
1044         // if selection doesn't start at the beginning of the line, set the new start
1045         int selectionStart = SelectionStart().Position();
1046         int startLine = pdoc->LineFromPosition(selectionStart);
1047         int beginningOfStartLine = pdoc->LineStart(startLine);
1048         selectionStart = beginningOfStartLine;
1049
1050         // if selection doesn't end at the beginning of a line greater than that of the start,
1051         // then set it at the beginning of the next one
1052         int selectionEnd = SelectionEnd().Position();
1053         int endLine = pdoc->LineFromPosition(selectionEnd);
1054         int beginningOfEndLine = pdoc->LineStart(endLine);
1055         bool appendEol = false;
1056         if (selectionEnd > beginningOfEndLine
1057                 || selectionStart == selectionEnd) {
1058                 selectionEnd = pdoc->LineStart(endLine + 1);
1059                 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
1060         }
1061
1062         // if there's nowhere for the selection to move
1063         // (i.e. at the beginning going up or at the end going down),
1064         // stop it right there!
1065         if ((selectionStart == 0 && lineDelta < 0)
1066                 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1067                 || selectionStart == selectionEnd) {
1068                 return;
1069         }
1070
1071         UndoGroup ug(pdoc);
1072
1073         if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1074                 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1075                 ClearSelection();
1076                 selectionEnd = CurrentPosition();
1077         }
1078         SetSelection(selectionStart, selectionEnd);
1079
1080         SelectionText selectedText;
1081         CopySelectionRange(&selectedText);
1082
1083         int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1084         Point currentLocation = LocationFromPosition(CurrentPosition());
1085         int currentLine = LineFromLocation(currentLocation);
1086
1087         if (appendEol)
1088                 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1089         ClearSelection();
1090
1091         const char *eol = StringFromEOLMode(pdoc->eolMode);
1092         if (currentLine + lineDelta >= pdoc->LinesTotal())
1093                 pdoc->InsertCString(pdoc->Length(), eol);
1094         GoToLine(currentLine + lineDelta);
1095
1096         pdoc->InsertCString(CurrentPosition(), selectedText.s);
1097         if (appendEol) {
1098                 pdoc->InsertCString(CurrentPosition() + selectionLength, eol);
1099                 selectionLength += istrlen(eol);
1100         }
1101         SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1102 }
1103
1104 void Editor::MoveSelectedLinesUp() {
1105         MoveSelectedLines(-1);
1106 }
1107
1108 void Editor::MoveSelectedLinesDown() {
1109         MoveSelectedLines(1);
1110 }
1111
1112 void Editor::MoveCaretInsideView(bool ensureVisible) {
1113         PRectangle rcClient = GetTextRectangle();
1114         Point pt = PointMainCaret();
1115         if (pt.y < rcClient.top) {
1116                 MovePositionTo(SPositionFromLocation(
1117                             Point(lastXChosen - xOffset, rcClient.top),
1118                                         false, false, UserVirtualSpace()),
1119                                         Selection::noSel, ensureVisible);
1120         } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1121                 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
1122                 MovePositionTo(SPositionFromLocation(
1123                             Point(lastXChosen - xOffset, rcClient.top + yOfLastLineFullyDisplayed),
1124                                         false, false, UserVirtualSpace()),
1125                         Selection::noSel, ensureVisible);
1126         }
1127 }
1128
1129 int Editor::DisplayFromPosition(int pos) {
1130         int lineDoc = pdoc->LineFromPosition(pos);
1131         int lineDisplay = cs.DisplayFromDoc(lineDoc);
1132         AutoSurface surface(this);
1133         AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1134         if (surface && ll) {
1135                 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1136                 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1137                 int posInLine = pos - posLineStart;
1138                 lineDisplay--; // To make up for first increment ahead.
1139                 for (int subLine = 0; subLine < ll->lines; subLine++) {
1140                         if (posInLine >= ll->LineStart(subLine)) {
1141                                 lineDisplay++;
1142                         }
1143                 }
1144         }
1145         return lineDisplay;
1146 }
1147
1148 /**
1149  * Ensure the caret is reasonably visible in context.
1150  *
1151 Caret policy in SciTE
1152
1153 If slop is set, we can define a slop value.
1154 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1155 This zone is defined as a number of pixels near the vertical margins,
1156 and as a number of lines near the horizontal margins.
1157 By keeping the caret away from the edges, it is seen within its context,
1158 so it is likely that the identifier that the caret is on can be completely seen,
1159 and that the current line is seen with some of the lines following it which are
1160 often dependent on that line.
1161
1162 If strict is set, the policy is enforced... strictly.
1163 The caret is centred on the display if slop is not set,
1164 and cannot go in the UZ if slop is set.
1165
1166 If jumps is set, the display is moved more energetically
1167 so the caret can move in the same direction longer before the policy is applied again.
1168 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1169
1170 If even is not set, instead of having symmetrical UZs,
1171 the left and bottom UZs are extended up to right and top UZs respectively.
1172 This way, we favour the displaying of useful information: the begining of lines,
1173 where most code reside, and the lines after the caret, eg. the body of a function.
1174
1175      |        |       |      |                                            |
1176 slop | strict | jumps | even | Caret can go to the margin                 | When reaching limit (caret going out of
1177      |        |       |      |                                            | visibility or going into the UZ) display is...
1178 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1179   0  |   0    |   0   |   0  | Yes                                        | moved to put caret on top/on right
1180   0  |   0    |   0   |   1  | Yes                                        | moved by one position
1181   0  |   0    |   1   |   0  | Yes                                        | moved to put caret on top/on right
1182   0  |   0    |   1   |   1  | Yes                                        | centred on the caret
1183   0  |   1    |   -   |   0  | Caret is always on top/on right of display | -
1184   0  |   1    |   -   |   1  | No, caret is always centred                | -
1185   1  |   0    |   0   |   0  | Yes                                        | moved to put caret out of the asymmetrical UZ
1186   1  |   0    |   0   |   1  | Yes                                        | moved to put caret out of the UZ
1187   1  |   0    |   1   |   0  | Yes                                        | moved to put caret at 3UZ of the top or right margin
1188   1  |   0    |   1   |   1  | Yes                                        | moved to put caret at 3UZ of the margin
1189   1  |   1    |   -   |   0  | Caret is always at UZ of top/right margin  | -
1190   1  |   1    |   0   |   1  | No, kept out of UZ                         | moved by one position
1191   1  |   1    |   1   |   1  | No, kept out of UZ                         | moved to put caret at 3UZ of the margin
1192 */
1193
1194 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const bool useMargin, const bool vert, const bool horiz) {
1195         PRectangle rcClient = GetTextRectangle();
1196         const SelectionPosition posCaret = posDrag.IsValid() ? posDrag : sel.RangeMain().caret;
1197         const Point pt = LocationFromPosition(posCaret);
1198         const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1199         const int lineCaret = DisplayFromPosition(posCaret.Position());
1200
1201         XYScrollPosition newXY(xOffset, topLine);
1202
1203         // Vertical positioning
1204         if (vert && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1205                 const int linesOnScreen = LinesOnScreen();
1206                 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1207                 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1208                 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1209                 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1210                 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1211
1212                 // It should be possible to scroll the window to show the caret,
1213                 // but this fails to remove the caret on GTK+
1214                 if (bSlop) {    // A margin is defined
1215                         int yMoveT, yMoveB;
1216                         if (bStrict) {
1217                                 int yMarginT, yMarginB;
1218                                 if (!useMargin) {
1219                                         // In drag mode, avoid moves
1220                                         // otherwise, a double click will select several lines.
1221                                         yMarginT = yMarginB = 0;
1222                                 } else {
1223                                         // yMarginT must equal to caretYSlop, with a minimum of 1 and
1224                                         // a maximum of slightly less than half the heigth of the text area.
1225                                         yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1226                                         if (bEven) {
1227                                                 yMarginB = yMarginT;
1228                                         } else {
1229                                                 yMarginB = linesOnScreen - yMarginT - 1;
1230                                         }
1231                                 }
1232                                 yMoveT = yMarginT;
1233                                 if (bEven) {
1234                                         if (bJump) {
1235                                                 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1236                                         }
1237                                         yMoveB = yMoveT;
1238                                 } else {
1239                                         yMoveB = linesOnScreen - yMoveT - 1;
1240                                 }
1241                                 if (lineCaret < topLine + yMarginT) {
1242                                         // Caret goes too high
1243                                         newXY.topLine = lineCaret - yMoveT;
1244                                 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1245                                         // Caret goes too low
1246                                         newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1247                                 }
1248                         } else {        // Not strict
1249                                 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1250                                 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1251                                 if (bEven) {
1252                                         yMoveB = yMoveT;
1253                                 } else {
1254                                         yMoveB = linesOnScreen - yMoveT - 1;
1255                                 }
1256                                 if (lineCaret < topLine) {
1257                                         // Caret goes too high
1258                                         newXY.topLine = lineCaret - yMoveT;
1259                                 } else if (lineCaret > topLine + linesOnScreen - 1) {
1260                                         // Caret goes too low
1261                                         newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1262                                 }
1263                         }
1264                 } else {        // No slop
1265                         if (!bStrict && !bJump) {
1266                                 // Minimal move
1267                                 if (lineCaret < topLine) {
1268                                         // Caret goes too high
1269                                         newXY.topLine = lineCaret;
1270                                 } else if (lineCaret > topLine + linesOnScreen - 1) {
1271                                         // Caret goes too low
1272                                         if (bEven) {
1273                                                 newXY.topLine = lineCaret - linesOnScreen + 1;
1274                                         } else {
1275                                                 newXY.topLine = lineCaret;
1276                                         }
1277                                 }
1278                         } else {        // Strict or going out of display
1279                                 if (bEven) {
1280                                         // Always center caret
1281                                         newXY.topLine = lineCaret - halfScreen;
1282                                 } else {
1283                                         // Always put caret on top of display
1284                                         newXY.topLine = lineCaret;
1285                                 }
1286                         }
1287                 }
1288                 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1289         }
1290
1291         // Horizontal positioning
1292         if (horiz && (wrapState == eWrapNone)) {
1293                 const int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1294                 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1295                 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1296                 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1297                 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1298
1299                 if (bSlop) {    // A margin is defined
1300                         int xMoveL, xMoveR;
1301                         if (bStrict) {
1302                                 int xMarginL, xMarginR;
1303                                 if (!useMargin) {
1304                                         // In drag mode, avoid moves unless very near of the margin
1305                                         // otherwise, a simple click will select text.
1306                                         xMarginL = xMarginR = 2;
1307                                 } else {
1308                                         // xMargin must equal to caretXSlop, with a minimum of 2 and
1309                                         // a maximum of slightly less than half the width of the text area.
1310                                         xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1311                                         if (bEven) {
1312                                                 xMarginL = xMarginR;
1313                                         } else {
1314                                                 xMarginL = rcClient.Width() - xMarginR - 4;
1315                                         }
1316                                 }
1317                                 if (bJump && bEven) {
1318                                         // Jump is used only in even mode
1319                                         xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1320                                 } else {
1321                                         xMoveL = xMoveR = 0;    // Not used, avoid a warning
1322                                 }
1323                                 if (pt.x < rcClient.left + xMarginL) {
1324                                         // Caret is on the left of the display
1325                                         if (bJump && bEven) {
1326                                                 newXY.xOffset -= xMoveL;
1327                                         } else {
1328                                                 // Move just enough to allow to display the caret
1329                                                 newXY.xOffset -= (rcClient.left + xMarginL) - pt.x;
1330                                         }
1331                                 } else if (pt.x >= rcClient.right - xMarginR) {
1332                                         // Caret is on the right of the display
1333                                         if (bJump && bEven) {
1334                                                 newXY.xOffset += xMoveR;
1335                                         } else {
1336                                                 // Move just enough to allow to display the caret
1337                                                 newXY.xOffset += pt.x - (rcClient.right - xMarginR) + 1;
1338                                         }
1339                                 }
1340                         } else {        // Not strict
1341                                 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1342                                 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1343                                 if (bEven) {
1344                                         xMoveL = xMoveR;
1345                                 } else {
1346                                         xMoveL = rcClient.Width() - xMoveR - 4;
1347                                 }
1348                                 if (pt.x < rcClient.left) {
1349                                         // Caret is on the left of the display
1350                                         newXY.xOffset -= xMoveL;
1351                                 } else if (pt.x >= rcClient.right) {
1352                                         // Caret is on the right of the display
1353                                         newXY.xOffset += xMoveR;
1354                                 }
1355                         }
1356                 } else {        // No slop
1357                         if (bStrict ||
1358                                 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1359                                 // Strict or going out of display
1360                                 if (bEven) {
1361                                         // Center caret
1362                                         newXY.xOffset += pt.x - rcClient.left - halfScreen;
1363                                 } else {
1364                                         // Put caret on right
1365                                         newXY.xOffset += pt.x - rcClient.right + 1;
1366                                 }
1367                         } else {
1368                                 // Move just enough to allow to display the caret
1369                                 if (pt.x < rcClient.left) {
1370                                         // Caret is on the left of the display
1371                                         if (bEven) {
1372                                                 newXY.xOffset -= rcClient.left - pt.x;
1373                                         } else {
1374                                                 newXY.xOffset += pt.x - rcClient.right + 1;
1375                                         }
1376                                 } else if (pt.x >= rcClient.right) {
1377                                         // Caret is on the right of the display
1378                                         newXY.xOffset += pt.x - rcClient.right + 1;
1379                                 }
1380                         }
1381                 }
1382                 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1383                 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1384                         newXY.xOffset = pt.x + xOffset - rcClient.left;
1385                 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1386                         newXY.xOffset = pt.x + xOffset - rcClient.right + 1;
1387                         if (vs.caretStyle == CARETSTYLE_BLOCK) {
1388                                 // Ensure we can see a good portion of the block caret
1389                                 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1390                         }
1391                 }
1392                 if (newXY.xOffset < 0) {
1393                         newXY.xOffset = 0;
1394                 }
1395         }
1396
1397         return newXY;
1398 }
1399
1400 void Editor::SetXYScroll(XYScrollPosition newXY) {
1401         if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1402                 if (newXY.topLine != topLine) {
1403                         SetTopLine(newXY.topLine);
1404                         SetVerticalScrollPos();
1405                 }
1406                 if (newXY.xOffset != xOffset) {
1407                         xOffset = newXY.xOffset;
1408                         ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1409                         if (newXY.xOffset > 0) {
1410                                 PRectangle rcText = GetTextRectangle();
1411                                 if (horizontalScrollBarVisible &&
1412                                         rcText.Width() + xOffset > scrollWidth) {
1413                                         scrollWidth = xOffset + rcText.Width();
1414                                         SetScrollBars();
1415                                 }
1416                         }
1417                         SetHorizontalScrollPos();
1418                 }
1419                 Redraw();
1420                 UpdateSystemCaret();
1421         }
1422 }
1423
1424 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1425         SetXYScroll(XYScrollToMakeVisible(useMargin, vert, horiz));
1426 }
1427
1428 void Editor::ShowCaretAtCurrentPosition() {
1429         if (hasFocus) {
1430                 caret.active = true;
1431                 caret.on = true;
1432                 SetTicking(true);
1433         } else {
1434                 caret.active = false;
1435                 caret.on = false;
1436         }
1437         InvalidateCaret();
1438 }
1439
1440 void Editor::DropCaret() {
1441         caret.active = false;
1442         InvalidateCaret();
1443 }
1444
1445 void Editor::InvalidateCaret() {
1446         if (posDrag.IsValid()) {
1447                 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1448         } else {
1449                 for (size_t r=0; r<sel.Count(); r++) {
1450                         InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1451                 }
1452         }
1453         UpdateSystemCaret();
1454 }
1455
1456 void Editor::UpdateSystemCaret() {
1457 }
1458
1459 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1460         docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1461         if (wrapStart > docLineStart) {
1462                 wrapStart = docLineStart;
1463                 llc.Invalidate(LineLayout::llPositions);
1464         }
1465         if (wrapEnd < docLineEnd) {
1466                 wrapEnd = docLineEnd;
1467         }
1468         wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1469         // Wrap lines during idle.
1470         if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1471                 SetIdle(true);
1472         }
1473 }
1474
1475 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1476         AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1477         int linesWrapped = 1;
1478         if (ll) {
1479                 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1480                 linesWrapped = ll->lines;
1481         }
1482         return cs.SetHeight(lineToWrap, linesWrapped +
1483                 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1484 }
1485
1486 // Check if wrapping needed and perform any needed wrapping.
1487 // fullwrap: if true, all lines which need wrapping will be done,
1488 //           in this single call.
1489 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1490 //           here to 1 page + 100 lines past will be wrapped (even if there are
1491 //           more lines under wrapping process in idle).
1492 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1493 // wrapped, if there are any wrapping going on in idle. (Generally this
1494 // condition is called only from idler).
1495 // Return true if wrapping occurred.
1496 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1497         // If there are any pending wraps, do them during idle if possible.
1498         int linesInOneCall = LinesOnScreen() + 100;
1499         if (priorityWrapLineStart >= 0) {
1500                 // Using DocFromDisplay() here may result in chicken and egg problem in certain corner cases,
1501                 // which will hopefully be handled by added 100 lines. If some lines are still missed, idle wrapping will catch on.
1502                 int docLinesInOneCall = cs.DocFromDisplay(topLine + LinesOnScreen() + 100) - cs.DocFromDisplay(topLine);
1503                 linesInOneCall = Platform::Maximum(linesInOneCall, docLinesInOneCall);
1504         }
1505         if (wrapState != eWrapNone) {
1506                 if (wrapStart < wrapEnd) {
1507                         if (!SetIdle(true)) {
1508                                 // Idle processing not supported so full wrap required.
1509                                 fullWrap = true;
1510                         }
1511                 }
1512                 if (!fullWrap && priorityWrapLineStart >= 0 &&
1513                         // .. and if the paint window is outside pending wraps
1514                         (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1515                          (priorityWrapLineStart > wrapEnd))) {
1516                         // No priority wrap pending
1517                         return false;
1518                 }
1519         }
1520         int goodTopLine = topLine;
1521         bool wrapOccurred = false;
1522         if (wrapStart <= pdoc->LinesTotal()) {
1523                 if (wrapState == eWrapNone) {
1524                         if (wrapWidth != LineLayout::wrapWidthInfinite) {
1525                                 wrapWidth = LineLayout::wrapWidthInfinite;
1526                                 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1527                                         cs.SetHeight(lineDoc, 1 +
1528                                                 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1529                                 }
1530                                 wrapOccurred = true;
1531                         }
1532                         wrapStart = wrapLineLarge;
1533                         wrapEnd = wrapLineLarge;
1534                 } else {
1535                         if (wrapEnd >= pdoc->LinesTotal())
1536                                 wrapEnd = pdoc->LinesTotal();
1537                         //ElapsedTime et;
1538                         int lineDocTop = cs.DocFromDisplay(topLine);
1539                         int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1540                         PRectangle rcTextArea = GetClientRectangle();
1541                         rcTextArea.left = vs.fixedColumnWidth;
1542                         rcTextArea.right -= vs.rightMarginWidth;
1543                         wrapWidth = rcTextArea.Width();
1544                         RefreshStyleData();
1545                         AutoSurface surface(this);
1546                         if (surface) {
1547                                 bool priorityWrap = false;
1548                                 int lastLineToWrap = wrapEnd;
1549                                 int lineToWrap = wrapStart;
1550                                 if (!fullWrap) {
1551                                         if (priorityWrapLineStart >= 0) {
1552                                                 // This is a priority wrap.
1553                                                 lineToWrap = priorityWrapLineStart;
1554                                                 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1555                                                 priorityWrap = true;
1556                                         } else {
1557                                                 // This is idle wrap.
1558                                                 lastLineToWrap = wrapStart + linesInOneCall;
1559                                         }
1560                                         if (lastLineToWrap >= wrapEnd)
1561                                                 lastLineToWrap = wrapEnd;
1562                                 } // else do a fullWrap.
1563
1564                                 // Ensure all lines being wrapped are styled.
1565                                 pdoc->EnsureStyledTo(pdoc->LineEnd(lastLineToWrap));
1566
1567                                 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1568                                 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1569                                 while (lineToWrap < lastLineToWrap) {
1570                                         if (WrapOneLine(surface, lineToWrap)) {
1571                                                 wrapOccurred = true;
1572                                         }
1573                                         lineToWrap++;
1574                                 }
1575                                 if (!priorityWrap)
1576                                         wrapStart = lineToWrap;
1577                                 // If wrapping is done, bring it to resting position
1578                                 if (wrapStart >= wrapEnd) {
1579                                         wrapStart = wrapLineLarge;
1580                                         wrapEnd = wrapLineLarge;
1581                                 }
1582                         }
1583                         goodTopLine = cs.DisplayFromDoc(lineDocTop);
1584                         if (subLineTop < cs.GetHeight(lineDocTop))
1585                                 goodTopLine += subLineTop;
1586                         else
1587                                 goodTopLine += cs.GetHeight(lineDocTop);
1588                         //double durWrap = et.Duration(true);
1589                         //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1590                 }
1591         }
1592         if (wrapOccurred) {
1593                 SetScrollBars();
1594                 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1595                 SetVerticalScrollPos();
1596         }
1597         return wrapOccurred;
1598 }
1599
1600 void Editor::LinesJoin() {
1601         if (!RangeContainsProtected(targetStart, targetEnd)) {
1602                 UndoGroup ug(pdoc);
1603                 bool prevNonWS = true;
1604                 for (int pos = targetStart; pos < targetEnd; pos++) {
1605                         if (IsEOLChar(pdoc->CharAt(pos))) {
1606                                 targetEnd -= pdoc->LenChar(pos);
1607                                 pdoc->DelChar(pos);
1608                                 if (prevNonWS) {
1609                                         // Ensure at least one space separating previous lines
1610                                         pdoc->InsertChar(pos, ' ');
1611                                         targetEnd++;
1612                                 }
1613                         } else {
1614                                 prevNonWS = pdoc->CharAt(pos) != ' ';
1615                         }
1616                 }
1617         }
1618 }
1619
1620 const char *Editor::StringFromEOLMode(int eolMode) {
1621         if (eolMode == SC_EOL_CRLF) {
1622                 return "\r\n";
1623         } else if (eolMode == SC_EOL_CR) {
1624                 return "\r";
1625         } else {
1626                 return "\n";
1627         }
1628 }
1629
1630 void Editor::LinesSplit(int pixelWidth) {
1631         if (!RangeContainsProtected(targetStart, targetEnd)) {
1632                 if (pixelWidth == 0) {
1633                         PRectangle rcText = GetTextRectangle();
1634                         pixelWidth = rcText.Width();
1635                 }
1636                 int lineStart = pdoc->LineFromPosition(targetStart);
1637                 int lineEnd = pdoc->LineFromPosition(targetEnd);
1638                 const char *eol = StringFromEOLMode(pdoc->eolMode);
1639                 UndoGroup ug(pdoc);
1640                 for (int line = lineStart; line <= lineEnd; line++) {
1641                         AutoSurface surface(this);
1642                         AutoLineLayout ll(llc, RetrieveLineLayout(line));
1643                         if (surface && ll) {
1644                                 unsigned int posLineStart = pdoc->LineStart(line);
1645                                 LayoutLine(line, surface, vs, ll, pixelWidth);
1646                                 for (int subLine = 1; subLine < ll->lines; subLine++) {
1647                                         pdoc->InsertCString(
1648                                                 static_cast<int>(posLineStart + (subLine - 1) * strlen(eol) +
1649                                                         ll->LineStart(subLine)),
1650                                                 eol);
1651                                         targetEnd += static_cast<int>(strlen(eol));
1652                                 }
1653                         }
1654                         lineEnd = pdoc->LineFromPosition(targetEnd);
1655                 }
1656         }
1657 }
1658
1659 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1660         if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1661                 return markerDefault;
1662         return markerCheck;
1663 }
1664
1665 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1666         if (st.multipleStyles) {
1667                 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1668                         if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1669                                 return false;
1670                 }
1671         } else {
1672                 if (!vs.ValidStyle(styleOffset + st.style))
1673                         return false;
1674         }
1675         return true;
1676 }
1677
1678 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1679         const char *text, const unsigned char *styles, size_t len) {
1680         int width = 0;
1681         size_t start = 0;
1682         while (start < len) {
1683                 size_t style = styles[start];
1684                 size_t endSegment = start;
1685                 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1686                         endSegment++;
1687                 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start, 
1688                         static_cast<int>(endSegment - start + 1));
1689                 start = endSegment + 1;
1690         }
1691         return width;
1692 }
1693
1694 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1695         int widthMax = 0;
1696         size_t start = 0;
1697         while (start < st.length) {
1698                 size_t lenLine = st.LineLength(start);
1699                 int widthSubLine;
1700                 if (st.multipleStyles) {
1701                         widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1702                 } else {
1703                         widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font,
1704                                 st.text + start, static_cast<int>(lenLine));
1705                 }
1706                 if (widthSubLine > widthMax)
1707                         widthMax = widthSubLine;
1708                 start += lenLine + 1;
1709         }
1710         return widthMax;
1711 }
1712
1713 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1714         const StyledText &st, size_t start, size_t length) {
1715
1716         if (st.multipleStyles) {
1717                 int x = rcText.left;
1718                 size_t i = 0;
1719                 while (i < length) {
1720                         size_t end = i;
1721                         int style = st.styles[i + start];
1722                         while (end < length-1 && st.styles[start+end+1] == style)
1723                                 end++;
1724                         style += styleOffset;
1725                         int width = surface->WidthText(vs.styles[style].font,
1726                                 st.text + start + i, static_cast<int>(end - i + 1));
1727                         PRectangle rcSegment = rcText;
1728                         rcSegment.left = x;
1729                         rcSegment.right = x + width + 1;
1730                         surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1731                                         ascent, st.text + start + i,
1732                                         static_cast<int>(end - i + 1),
1733                                         vs.styles[style].fore,
1734                                         vs.styles[style].back);
1735                         x += width;
1736                         i = end + 1;
1737                 }
1738         } else {
1739                 size_t style = st.style + styleOffset;
1740                 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1741                                 rcText.top + vs.maxAscent, st.text + start,
1742                                 static_cast<int>(length),
1743                                 vs.styles[style].fore,
1744                                 vs.styles[style].back);
1745         }
1746 }
1747
1748 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1749         if (vs.fixedColumnWidth == 0)
1750                 return;
1751
1752         PRectangle rcMargin = GetClientRectangle();
1753         rcMargin.right = vs.fixedColumnWidth;
1754
1755         if (!rc.Intersects(rcMargin))
1756                 return;
1757
1758         Surface *surface;
1759         if (bufferedDraw) {
1760                 surface = pixmapSelMargin;
1761         } else {
1762                 surface = surfWindow;
1763         }
1764
1765         // Clip vertically to paint area to avoid drawing line numbers
1766         if (rcMargin.bottom > rc.bottom)
1767                 rcMargin.bottom = rc.bottom;
1768         if (rcMargin.top < rc.top)
1769                 rcMargin.top = rc.top;
1770
1771         PRectangle rcSelMargin = rcMargin;
1772         rcSelMargin.right = rcMargin.left;
1773
1774         for (int margin = 0; margin < vs.margins; margin++) {
1775                 if (vs.ms[margin].width > 0) {
1776
1777                         rcSelMargin.left = rcSelMargin.right;
1778                         rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1779
1780                         if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1781                                 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1782                                         // Required because of special way brush is created for selection margin
1783                                         surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1784                                 else {
1785                                         ColourDesired colour;
1786                                         switch (vs.ms[margin].style) {
1787                                         case SC_MARGIN_BACK:
1788                                                 colour = vs.styles[STYLE_DEFAULT].back;
1789                                                 break;
1790                                         case SC_MARGIN_FORE:
1791                                                 colour = vs.styles[STYLE_DEFAULT].fore;
1792                                                 break;
1793                                         default:
1794                                                 colour = vs.styles[STYLE_LINENUMBER].back;
1795                                                 break;
1796                                         }
1797                                         surface->FillRectangle(rcSelMargin, colour);
1798                                 }
1799                         } else {
1800                                 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
1801                         }
1802
1803                         const int lineStartPaint = rcMargin.top / vs.lineHeight;
1804                         int visibleLine = topLine + lineStartPaint;
1805                         int yposScreen = lineStartPaint * vs.lineHeight;
1806                         // Work out whether the top line is whitespace located after a
1807                         // lessening of fold level which implies a 'fold tail' but which should not
1808                         // be displayed until the last of a sequence of whitespace.
1809                         bool needWhiteClosure = false;
1810                         if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1811                                 int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine));
1812                                 if (level & SC_FOLDLEVELWHITEFLAG) {
1813                                         int lineBack = cs.DocFromDisplay(visibleLine);
1814                                         int levelPrev = level;
1815                                         while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1816                                                 lineBack--;
1817                                                 levelPrev = pdoc->GetLevel(lineBack);
1818                                         }
1819                                         if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1820                                                 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1821                                                         needWhiteClosure = true;
1822                                         }
1823                                 }
1824                                 if (highlightDelimiter.isEnabled) {
1825                                         int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
1826                                         pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
1827                                 }
1828                         }
1829
1830                         // Old code does not know about new markers needed to distinguish all cases
1831                         int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1832                                 SC_MARKNUM_FOLDEROPEN);
1833                         int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1834                                 SC_MARKNUM_FOLDER);
1835
1836                         while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1837
1838                                 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1839                                 int lineDoc = cs.DocFromDisplay(visibleLine);
1840                                 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1841                                 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1842                                 bool lastSubLine = visibleLine == (cs.DisplayFromDoc(lineDoc + 1) - 1);
1843
1844                                 int marks = pdoc->GetMark(lineDoc);
1845                                 if (!firstSubLine)
1846                                         marks = 0;
1847
1848                                 bool headWithTail = false;
1849
1850                                 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1851                                         // Decide which fold indicator should be displayed
1852                                         int level = pdoc->GetLevel(lineDoc);
1853                                         int levelNext = pdoc->GetLevel(lineDoc + 1);
1854                                         int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1855                                         int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1856                                         if (level & SC_FOLDLEVELHEADERFLAG) {
1857                                                 if (firstSubLine) {
1858                                                         if (levelNum < levelNextNum) {
1859                                                                 if (cs.GetExpanded(lineDoc)) {
1860                                                                         if (levelNum == SC_FOLDLEVELBASE)
1861                                                                                 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1862                                                                         else
1863                                                                                 marks |= 1 << folderOpenMid;
1864                                                                 } else {
1865                                                                         if (levelNum == SC_FOLDLEVELBASE)
1866                                                                                 marks |= 1 << SC_MARKNUM_FOLDER;
1867                                                                         else
1868                                                                                 marks |= 1 << folderEnd;
1869                                                                 }
1870                                                         } else if (levelNum > SC_FOLDLEVELBASE) {
1871                                                                 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1872                                                         }
1873                                                 } else {
1874                                                         if (levelNum < levelNextNum) {
1875                                                                 if (cs.GetExpanded(lineDoc)) {
1876                                                                         marks |= 1 << SC_MARKNUM_FOLDERSUB;
1877                                                                 } else if (levelNum > SC_FOLDLEVELBASE) {
1878                                                                         marks |= 1 << SC_MARKNUM_FOLDERSUB;
1879                                                                 }
1880                                                         } else if (levelNum > SC_FOLDLEVELBASE) {
1881                                                                 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1882                                                         }
1883                                                 }
1884                                                 needWhiteClosure = false;
1885                                                 int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1886                                                 int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1887                                                 int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
1888                                                 if (!cs.GetExpanded(lineDoc)) {
1889                                                         if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
1890                                                                 (levelNum > secondFollowupLineLevelNum))
1891                                                                 needWhiteClosure = true;
1892
1893                                                         if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
1894                                                                 headWithTail = true;
1895                                                 }
1896                                         } else if (level & SC_FOLDLEVELWHITEFLAG) {
1897                                                 if (needWhiteClosure) {
1898                                                         if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1899                                                                 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1900                                                         } else if (levelNextNum > SC_FOLDLEVELBASE) {
1901                                                                 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1902                                                                 needWhiteClosure = false;
1903                                                         } else {
1904                                                                 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1905                                                                 needWhiteClosure = false;
1906                                                         }
1907                                                 } else if (levelNum > SC_FOLDLEVELBASE) {
1908                                                         if (levelNextNum < levelNum) {
1909                                                                 if (levelNextNum > SC_FOLDLEVELBASE) {
1910                                                                         marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1911                                                                 } else {
1912                                                                         marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1913                                                                 }
1914                                                         } else {
1915                                                                 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1916                                                         }
1917                                                 }
1918                                         } else if (levelNum > SC_FOLDLEVELBASE) {
1919                                                 if (levelNextNum < levelNum) {
1920                                                         needWhiteClosure = false;
1921                                                         if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1922                                                                 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1923                                                                 needWhiteClosure = true;
1924                                                         } else if (lastSubLine) {
1925                                                                 if (levelNextNum > SC_FOLDLEVELBASE) {
1926                                                                         marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1927                                                                 } else {
1928                                                                         marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1929                                                                 }
1930                                                         } else {
1931                                                                 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1932                                                         }
1933                                                 } else {
1934                                                         marks |= 1 << SC_MARKNUM_FOLDERSUB;
1935                                                 }
1936                                         }
1937                                 }
1938
1939                                 marks &= vs.ms[margin].mask;
1940
1941                                 PRectangle rcMarker = rcSelMargin;
1942                                 rcMarker.top = yposScreen;
1943                                 rcMarker.bottom = yposScreen + vs.lineHeight;
1944                                 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1945                                         if (firstSubLine) {
1946                                                 char number[100];
1947                                                 sprintf(number, "%d", lineDoc + 1);
1948                                                 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1949                                                         int lev = pdoc->GetLevel(lineDoc);
1950                                                         sprintf(number, "%c%c %03X %03X",
1951                                                                         (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1952                                                                         (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1953                                                                         lev & SC_FOLDLEVELNUMBERMASK,
1954                                                                         lev >> 16
1955                                                                    );
1956                                                 }
1957                                                 PRectangle rcNumber = rcMarker;
1958                                                 // Right justify
1959                                                 XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1960                                                 XYPOSITION xpos = rcNumber.right - width - 3;
1961                                                 rcNumber.left = xpos;
1962                                                 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1963                                                                 rcNumber.top + vs.maxAscent, number, istrlen(number),
1964                                                                 vs.styles[STYLE_LINENUMBER].fore,
1965                                                                 vs.styles[STYLE_LINENUMBER].back);
1966                                         } else if (wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
1967                                                 PRectangle rcWrapMarker = rcMarker;
1968                                                 rcWrapMarker.right -= 3;
1969                                                 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
1970                                                 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
1971                                         }
1972                                 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
1973                                         if (firstSubLine) {
1974                                                 const StyledText stMargin  = pdoc->MarginStyledText(lineDoc);
1975                                                 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
1976                                                         surface->FillRectangle(rcMarker,
1977                                                                 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
1978                                                         if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
1979                                                                 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
1980                                                                 rcMarker.left = rcMarker.right - width - 3;
1981                                                         }
1982                                                         DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
1983                                                                 stMargin, 0, stMargin.length);
1984                                                 }
1985                                         }
1986                                 }
1987
1988                                 if (marks) {
1989                                         for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1990                                                 if (marks & 1) {
1991                                                         LineMarker::typeOfFold tFold = LineMarker::undefined;
1992                                                         if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
1993                                                                 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
1994                                                                         tFold = LineMarker::body;
1995                                                                 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
1996                                                                         if (firstSubLine) {
1997                                                                                 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
1998                                                                         } else {
1999                                                                                 if (cs.GetExpanded(lineDoc) || headWithTail) {
2000                                                                                         tFold = LineMarker::body;
2001                                                                                 } else {
2002                                                                                         tFold = LineMarker::undefined;
2003                                                                                 }
2004                                                                         }
2005                                                                 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
2006                                                                         tFold = LineMarker::tail;
2007                                                                 }
2008                                                         }
2009                                                         vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
2010                                                 }
2011                                                 marks >>= 1;
2012                                         }
2013                                 }
2014
2015                                 visibleLine++;
2016                                 yposScreen += vs.lineHeight;
2017                         }
2018                 }
2019         }
2020
2021         PRectangle rcBlankMargin = rcMargin;
2022         rcBlankMargin.left = rcSelMargin.right;
2023         surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
2024
2025         if (bufferedDraw) {
2026                 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
2027         }
2028 }
2029
2030 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
2031         int ydiff = (rcTab.bottom - rcTab.top) / 2;
2032         int xhead = rcTab.right - 1 - ydiff;
2033         if (xhead <= rcTab.left) {
2034                 ydiff -= rcTab.left - xhead - 1;
2035                 xhead = rcTab.left - 1;
2036         }
2037         if ((rcTab.left + 2) < (rcTab.right - 1))
2038                 surface->MoveTo(rcTab.left + 2, ymid);
2039         else
2040                 surface->MoveTo(rcTab.right - 1, ymid);
2041         surface->LineTo(rcTab.right - 1, ymid);
2042         surface->LineTo(xhead, ymid - ydiff);
2043         surface->MoveTo(rcTab.right - 1, ymid);
2044         surface->LineTo(xhead, ymid + ydiff);
2045 }
2046
2047 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2048         int posLineStart = pdoc->LineStart(lineNumber);
2049         int posLineEnd = pdoc->LineStart(lineNumber + 1);
2050         PLATFORM_ASSERT(posLineEnd >= posLineStart);
2051         int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2052         return llc.Retrieve(lineNumber, lineCaret,
2053                 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2054                 LinesOnScreen() + 1, pdoc->LinesTotal());
2055 }
2056
2057 bool BadUTF(const char *s, int len, int &trailBytes) {
2058         // For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
2059         if (trailBytes) {
2060                 trailBytes--;
2061                 return false;
2062         }
2063         int utf8status = UTF8Classify(reinterpret_cast<const unsigned char *>(s), len);
2064         if (utf8status & UTF8MaskInvalid) {
2065                 return true;
2066         } else {
2067                 trailBytes = (utf8status & UTF8MaskWidth) - 1;
2068                 return false;
2069         }
2070 }
2071
2072 /**
2073  * Fill in the LineLayout data for the given line.
2074  * Copy the given @a line and its styles from the document into local arrays.
2075  * Also determine the x position at which each character starts.
2076  */
2077 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2078         if (!ll)
2079                 return;
2080
2081         PLATFORM_ASSERT(line < pdoc->LinesTotal());
2082         PLATFORM_ASSERT(ll->chars != NULL);
2083         int posLineStart = pdoc->LineStart(line);
2084         int posLineEnd = pdoc->LineStart(line + 1);
2085         // If the line is very long, limit the treatment to a length that should fit in the viewport
2086         if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2087                 posLineEnd = posLineStart + ll->maxLineLength;
2088         }
2089         if (ll->validity == LineLayout::llCheckTextAndStyle) {
2090                 int lineLength = posLineEnd - posLineStart;
2091                 if (!vstyle.viewEOL) {
2092                         lineLength = pdoc->LineEnd(line) - posLineStart;
2093                 }
2094                 if (lineLength == ll->numCharsInLine) {
2095                         // See if chars, styles, indicators, are all the same
2096                         bool allSame = true;
2097                         const int styleMask = pdoc->stylingBitsMask;
2098                         // Check base line layout
2099                         char styleByte = 0;
2100                         int numCharsInLine = 0;
2101                         while (numCharsInLine < lineLength) {
2102                                 int charInDoc = numCharsInLine + posLineStart;
2103                                 char chDoc = pdoc->CharAt(charInDoc);
2104                                 styleByte = pdoc->StyleAt(charInDoc);
2105                                 allSame = allSame &&
2106                                         (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2107                                 allSame = allSame &&
2108                                         (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2109                                 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2110                                         allSame = allSame &&
2111                                                 (ll->chars[numCharsInLine] == chDoc);
2112                                 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2113                                         allSame = allSame &&
2114                                                 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2115                                 else    // Style::caseUpper
2116                                         allSame = allSame &&
2117                                                 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2118                                 numCharsInLine++;
2119                         }
2120                         allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2121                         if (allSame) {
2122                                 ll->validity = LineLayout::llPositions;
2123                         } else {
2124                                 ll->validity = LineLayout::llInvalid;
2125                         }
2126                 } else {
2127                         ll->validity = LineLayout::llInvalid;
2128                 }
2129         }
2130         if (ll->validity == LineLayout::llInvalid) {
2131                 ll->widthLine = LineLayout::wrapWidthInfinite;
2132                 ll->lines = 1;
2133                 if (vstyle.edgeState == EDGE_BACKGROUND) {
2134                         ll->edgeColumn = pdoc->FindColumn(line, theEdge);
2135                         if (ll->edgeColumn >= posLineStart) {
2136                                 ll->edgeColumn -= posLineStart;
2137                         }
2138                 } else {
2139                         ll->edgeColumn = -1;
2140                 }
2141
2142                 char styleByte;
2143                 const int styleMask = pdoc->stylingBitsMask;
2144                 ll->styleBitsSet = 0;
2145                 // Fill base line layout
2146                 const int lineLength = posLineEnd - posLineStart;
2147                 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2148                 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2149                 int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
2150                 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2151                 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2152                         styleByte = ll->styles[styleInLine];
2153                         ll->styleBitsSet |= styleByte;
2154                         ll->styles[styleInLine] = static_cast<char>(styleByte & styleMask);
2155                         ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2156                 }
2157                 styleByte = static_cast<char>(((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask);
2158                 if (vstyle.someStylesForceCase) {
2159                         for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2160                                 char chDoc = ll->chars[charInLine];
2161                                 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2162                                         ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2163                                 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2164                                         ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2165                         }
2166                 }
2167                 ll->xHighlightGuide = 0;
2168                 // Extra element at the end of the line to hold end x position and act as
2169                 ll->chars[numCharsInLine] = 0;   // Also triggers processing in the loops as this is a control character
2170                 ll->styles[numCharsInLine] = styleByte; // For eolFilled
2171                 ll->indicators[numCharsInLine] = 0;
2172
2173                 // Layout the line, determining the position of each character,
2174                 // with an extra element at the end for the end of the line.
2175                 int startseg = 0;       // Start of the current segment, in char. number
2176                 XYACCUMULATOR startsegx = 0;    // Start of the current segment, in pixels
2177                 ll->positions[0] = 0;
2178                 XYPOSITION tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
2179                 bool lastSegItalics = false;
2180                 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
2181
2182                 XYPOSITION ctrlCharWidth[32] = {0};
2183                 bool isControlNext = IsControlCharacter(ll->chars[0]);
2184                 int trailBytes = 0;
2185                 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
2186                 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
2187                         bool isControl = isControlNext;
2188                         isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
2189                         bool isBadUTF = isBadUTFNext;
2190                         isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
2191                         if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
2192                                 isControl || isControlNext || isBadUTF || isBadUTFNext) {
2193                                 ll->positions[startseg] = 0;
2194                                 if (vstyle.styles[ll->styles[charInLine]].visible) {
2195                                         if (isControl) {
2196                                                 if (ll->chars[charInLine] == '\t') {
2197                                                         ll->positions[charInLine + 1] = 
2198                                                                 ((static_cast<int>((startsegx + 2) / tabWidth) + 1) * tabWidth) - startsegx;
2199                                                 } else if (controlCharSymbol < 32) {
2200                                                         if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2201                                                                 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2202                                                                 // +3 For a blank on front and rounded edge each side:
2203                                                                 ctrlCharWidth[ll->chars[charInLine]] =
2204                                                                     surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
2205                                                         }
2206                                                         ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2207                                                 } else {
2208                                                         char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2209                                                         surface->MeasureWidths(ctrlCharsFont, cc, 1,
2210                                                                 ll->positions + startseg + 1);
2211                                                 }
2212                                                 lastSegItalics = false;
2213                                         } else if (isBadUTF) {
2214                                                 char hexits[4];
2215                                                 sprintf(hexits, "x%2X", ll->chars[charInLine] & 0xff);
2216                                                 ll->positions[charInLine + 1] =
2217                                                     surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2218                                         } else {        // Regular character
2219                                                 int lenSeg = charInLine - startseg + 1;
2220                                                 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2221                                                         lastSegItalics = false;
2222                                                         // Over half the segments are single characters and of these about half are space characters.
2223                                                         ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2224                                                 } else {
2225                                                         lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2226                                                         posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2227                                                                 lenSeg, ll->positions + startseg + 1, pdoc);
2228                                                 }
2229                                         }
2230                                 } else {    // invisible
2231                                         for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2232                                                 ll->positions[posToZero] = 0;
2233                                         }
2234                                 }
2235                                 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2236                                         ll->positions[posToIncrease] += startsegx;
2237                                 }
2238                                 startsegx = ll->positions[charInLine + 1];
2239                                 startseg = charInLine + 1;
2240                         }
2241                 }
2242                 // Small hack to make lines that end with italics not cut off the edge of the last character
2243                 if ((startseg > 0) && lastSegItalics) {
2244                         ll->positions[startseg] += 2;
2245                 }
2246                 ll->numCharsInLine = numCharsInLine;
2247                 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2248                 ll->validity = LineLayout::llPositions;
2249         }
2250         // Hard to cope when too narrow, so just assume there is space
2251         if (width < 20) {
2252                 width = 20;
2253         }
2254         if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2255                 ll->widthLine = width;
2256                 if (width == LineLayout::wrapWidthInfinite) {
2257                         ll->lines = 1;
2258                 } else if (width > ll->positions[ll->numCharsInLine]) {
2259                         // Simple common case where line does not need wrapping.
2260                         ll->lines = 1;
2261                 } else {
2262                         if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2263                                 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
2264                         }
2265                         XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
2266                         if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
2267                                 wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
2268                         } else if (wrapIndentMode == SC_WRAPINDENT_FIXED) {
2269                                 wrapAddIndent = wrapVisualStartIndent * vstyle.aveCharWidth;
2270                         }
2271                         ll->wrapIndent = wrapAddIndent;
2272                         if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2273                                 for (int i = 0; i < ll->numCharsInLine; i++) {
2274                                         if (!IsSpaceOrTab(ll->chars[i])) {
2275                                                 ll->wrapIndent += ll->positions[i]; // Add line indent
2276                                                 break;
2277                                         }
2278                                 }
2279                         // Check for text width minimum
2280                         if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2281                                 ll->wrapIndent = wrapAddIndent;
2282                         // Check for wrapIndent minimum
2283                         if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
2284                                 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2285                         ll->lines = 0;
2286                         // Calculate line start positions based upon width.
2287                         int lastGoodBreak = 0;
2288                         int lastLineStart = 0;
2289                         XYACCUMULATOR startOffset = 0;
2290                         int p = 0;
2291                         while (p < ll->numCharsInLine) {
2292                                 if ((ll->positions[p + 1] - startOffset) >= width) {
2293                                         if (lastGoodBreak == lastLineStart) {
2294                                                 // Try moving to start of last character
2295                                                 if (p > 0) {
2296                                                         lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2297                                                                 - posLineStart;
2298                                                 }
2299                                                 if (lastGoodBreak == lastLineStart) {
2300                                                         // Ensure at least one character on line.
2301                                                         lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2302                                                                 - posLineStart;
2303                                                 }
2304                                         }
2305                                         lastLineStart = lastGoodBreak;
2306                                         ll->lines++;
2307                                         ll->SetLineStart(ll->lines, lastGoodBreak);
2308                                         startOffset = ll->positions[lastGoodBreak];
2309                                         // take into account the space for start wrap mark and indent
2310                                         startOffset -= ll->wrapIndent;
2311                                         p = lastGoodBreak + 1;
2312                                         continue;
2313                                 }
2314                                 if (p > 0) {
2315                                         if (wrapState == eWrapChar) {
2316                                                 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2317                                                         - posLineStart;
2318                                                 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2319                                                 continue;
2320                                         } else if (ll->styles[p] != ll->styles[p - 1]) {
2321                                                 lastGoodBreak = p;
2322                                         } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2323                                                 lastGoodBreak = p;
2324                                         }
2325                                 }
2326                                 p++;
2327                         }
2328                         ll->lines++;
2329                 }
2330                 ll->validity = LineLayout::llLines;
2331         }
2332 }
2333
2334 ColourDesired Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2335         return main ?
2336                 (primarySelection ? vsDraw.selbackground : vsDraw.selbackground2) :
2337                 vsDraw.selAdditionalBackground;
2338 }
2339
2340 ColourDesired Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2341         ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2342         if (inSelection == 1) {
2343                 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2344                         return SelectionBackground(vsDraw, true);
2345                 }
2346         } else if (inSelection == 2) {
2347                 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2348                         return SelectionBackground(vsDraw, false);
2349                 }
2350         } else {
2351                 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2352                         (i >= ll->edgeColumn) &&
2353                         (i < ll->numCharsBeforeEOL))
2354                         return vsDraw.edgecolour;
2355                 if (inHotspot && vsDraw.hotspotBackgroundSet)
2356                         return vsDraw.hotspotBackground;
2357         }
2358         if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2359                 return background;
2360         } else {
2361                 return vsDraw.styles[styleMain].back;
2362         }
2363 }
2364
2365 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2366         Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2367         PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2368         surface->Copy(rcCopyArea, from,
2369                 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2370 }
2371
2372 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2373         bool isEndMarker, ColourDesired wrapColour) {
2374         surface->PenColour(wrapColour);
2375
2376         enum { xa = 1 }; // gap before start
2377         int w = rcPlace.right - rcPlace.left - xa - 1;
2378
2379         bool xStraight = isEndMarker;  // x-mirrored symbol for start marker
2380         bool yStraight = true;
2381         //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2382
2383         int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2384         int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2385
2386         int dy = (rcPlace.bottom - rcPlace.top) / 5;
2387         int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2388
2389         struct Relative {
2390                 Surface *surface;
2391                 int xBase;
2392                 int xDir;
2393                 int yBase;
2394                 int yDir;
2395                 void MoveTo(int xRelative, int yRelative) {
2396                         surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2397                 }
2398                 void LineTo(int xRelative, int yRelative) {
2399                         surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2400                 }
2401         };
2402         Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2403
2404         // arrow head
2405         rel.MoveTo(xa, y);
2406         rel.LineTo(xa + 2*w / 3, y - dy);
2407         rel.MoveTo(xa, y);
2408         rel.LineTo(xa + 2*w / 3, y + dy);
2409
2410         // arrow body
2411         rel.MoveTo(xa, y);
2412         rel.LineTo(xa + w, y);
2413         rel.LineTo(xa + w, y - 2 * dy);
2414         rel.LineTo(xa - 1,   // on windows lineto is exclusive endpoint, perhaps GTK not...
2415                 y - 2 * dy);
2416 }
2417
2418 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
2419         if (alpha != SC_ALPHA_NOALPHA) {
2420                 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2421         }
2422 }
2423
2424 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2425                                   const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
2426         if (!twoPhaseDraw) {
2427                 surface->FillRectangle(rcSegment, textBack);
2428         }
2429         Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2430         int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2431                 surface->InternalLeading(ctrlCharsFont);
2432         PRectangle rcCChar = rcSegment;
2433         rcCChar.left = rcCChar.left + 1;
2434         rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2435         rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2436         PRectangle rcCentral = rcCChar;
2437         rcCentral.top++;
2438         rcCentral.bottom--;
2439         surface->FillRectangle(rcCentral, textFore);
2440         PRectangle rcChar = rcCChar;
2441         rcChar.left++;
2442         rcChar.right--;
2443         surface->DrawTextClipped(rcChar, ctrlCharsFont,
2444                 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2445                 textBack, textFore);
2446 }
2447
2448 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2449         int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
2450         bool overrideBackground, ColourDesired background,
2451         bool drawWrapMarkEnd, ColourDesired wrapColour) {
2452
2453         const int posLineStart = pdoc->LineStart(line);
2454         const int styleMask = pdoc->stylingBitsMask;
2455         PRectangle rcSegment = rcLine;
2456
2457         const bool lastSubLine = subLine == (ll->lines - 1);
2458         XYPOSITION virtualSpace = 0;
2459         if (lastSubLine) {
2460                 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2461                 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2462         }
2463         XYPOSITION xEol = ll->positions[lineEnd] - subLineStart;
2464
2465         // Fill the virtual space and show selections within it