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