Add an UI to enable/disable specific overlay handlers.
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blob19cb7c97059d3b3f54f1f5c004dd212d8a28242c
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
13 #include "Platform.h"
15 #ifndef PLAT_QT
16 #define INCLUDE_DEPRECATED_FEATURES
17 #endif
18 #include "Scintilla.h"
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"
37 #ifdef SCI_NAMESPACE
38 using namespace Scintilla;
39 #endif
42 return whether this modification represents an operation that
43 may reasonably be deferred (not done now OR [possibly] at all)
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
55 static bool CanEliminate(const DocModification& mh) {
56 return
57 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
61 return whether this modification represents the FINAL step
62 in a [possibly lengthy] multi-step Undo/Redo sequence
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;
72 Caret::Caret() :
73 active(false), on(false), period(500) {}
75 Timer::Timer() :
76 ticking(false), ticksToWait(0), tickerID(0) {}
78 Idler::Idler() :
79 state(false), idlerID(0) {}
81 static inline bool IsControlCharacter(int ch) {
82 // iscntrl returns true for lots of chars > 127 which are displayable
83 return ch >= 0 && ch < ' ';
86 Editor::Editor() {
87 ctrlID = 0;
89 stylesValid = false;
91 printMagnification = 0;
92 printColourMode = SC_PRINT_NORMAL;
93 printWrapState = eWrapWord;
94 cursorMode = SC_CURSORNORMAL;
95 controlCharSymbol = 0; /* Draw the control characters */
97 hasFocus = false;
98 hideSelection = false;
99 inOverstrike = false;
100 errorStatus = 0;
101 mouseDownCaptures = true;
103 bufferedDraw = true;
104 twoPhaseDraw = true;
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;
118 lastXChosen = 0;
119 lineAnchor = 0;
120 originalAnchorPos = 0;
122 selType = selStream;
123 moveExtendsSelection = false;
124 xStartSelect = 0;
125 xEndSelect = 0;
126 primarySelection = true;
128 caretXPolicy = CARET_SLOP | CARET_EVEN;
129 caretXSlop = 50;
131 caretYPolicy = CARET_EVEN;
132 caretYSlop = 0;
134 searchAnchor = 0;
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;
146 pixmapLine = Surface::Allocate();
147 pixmapSelMargin = Surface::Allocate();
148 pixmapSelPattern = Surface::Allocate();
149 pixmapIndentGuide = Surface::Allocate();
150 pixmapIndentGuideHighlight = Surface::Allocate();
152 currentPos = 0;
153 anchor = 0;
155 targetStart = 0;
156 targetEnd = 0;
157 searchFlags = 0;
159 topLine = 0;
160 posTopLine = 0;
162 lengthForEncode = -1;
164 needUpdateUI = true;
165 braces[0] = invalidPosition;
166 braces[1] = invalidPosition;
167 bracesMatchStyle = STYLE_BRACEBAD;
168 highlightGuideColumn = 0;
170 theEdge = 0;
172 paintState = notPainting;
174 modEventMask = SC_MODEVENTMASKALL;
176 pdoc = new Document();
177 pdoc->AddRef();
178 pdoc->AddWatcher(this, 0);
180 recordingMacro = false;
181 foldFlags = 0;
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;
192 convertPastes = true;
194 hsStart = -1;
195 hsEnd = -1;
197 llc.SetLevel(LineLayoutCache::llcCaret);
198 posCache.SetSize(0x400);
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;
213 void Editor::Finalise() {
214 SetIdle(false);
215 CancelModes();
218 void Editor::DropGraphics() {
219 pixmapLine->Release();
220 pixmapSelMargin->Release();
221 pixmapSelPattern->Release();
222 pixmapIndentGuide->Release();
223 pixmapIndentGuideHighlight->Release();
226 void Editor::InvalidateStyleData() {
227 stylesValid = false;
228 DropGraphics();
229 palette.Release();
230 llc.Invalidate(LineLayout::llInvalid);
231 posCache.Clear();
232 if (selType == selRectangle) {
233 xStartSelect = XFromPosition(anchor);
234 xEndSelect = XFromPosition(currentPos);
238 void Editor::InvalidateStyleRedraw() {
239 NeedWrapping();
240 InvalidateStyleData();
241 Redraw();
244 void Editor::RefreshColourPalette(Palette &pal, bool want) {
245 vs.RefreshColourPalette(pal, want);
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);
258 SetScrollBars();
262 PRectangle Editor::GetClientRectangle() {
263 return wMain.GetClientPosition();
266 PRectangle Editor::GetTextRectangle() {
267 PRectangle rc = GetClientRectangle();
268 rc.left += vs.fixedColumnWidth;
269 rc.right -= vs.rightMarginWidth;
270 return rc;
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;
280 int Editor::LinesToScroll() {
281 int retVal = LinesOnScreen() - 1;
282 if (retVal < 1)
283 return 1;
284 else
285 return retVal;
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--;
297 if (retVal < 0) {
298 return 0;
299 } else {
300 return retVal;
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"
311 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
312 return reps[ch];
313 } else {
314 return "BAD";
319 * Convenience class to ensure LineLayout objects are always disposed.
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;
331 LineLayout *operator->() const {
332 return ll;
334 operator LineLayout *() const {
335 return ll;
337 void Set(LineLayout *ll_) {
338 llc.Dispose(ll);
339 ll = ll_;
343 #ifdef SCI_NAMESPACE
344 namespace Scintilla {
345 #endif
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.
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.
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.
365 void Reset() {
366 if (forward) {
367 line = lineStart;
368 } else {
369 line = lineEnd;
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();
386 ~SelectionLineIterator() {}
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);
406 if (line == lineEnd) {
407 endPos = selEnd;
408 } else {
409 endPos = ed->pdoc->LineStart(line + 1);
414 bool Iterate() {
415 SetAt(line);
416 if (forward) {
417 line++;
418 } else {
419 line--;
421 return startPos != INVALID_POSITION;
425 #ifdef SCI_NAMESPACE
427 #endif
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)];
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;
460 if (posInLine >= ll->LineStart(subLine)) {
461 pt.y += vs.lineHeight;
464 pt.x += vs.fixedColumnWidth - xOffset;
466 return pt;
469 int Editor::XFromPosition(int pos) {
470 Point pt = LocationFromPosition(pos);
471 return pt.x - vs.fixedColumnWidth + xOffset;
474 int Editor::LineFromLocation(Point pt) {
475 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
478 void Editor::SetTopLine(int topLineNew) {
479 topLine = topLineNew;
480 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
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;
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];
508 if (actualWrapVisualStartIndent != 0) {
509 if (lineStart != 0) // Wrapped
510 pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth;
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);
517 i++;
519 return lineEnd + posLineStart;
521 retVal = ll->numCharsInLine + posLineStart;
523 return retVal;
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;
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];
558 if (actualWrapVisualStartIndent != 0) {
559 if (lineStart != 0) // Wrapped
560 pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth;
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);
567 i++;
569 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
570 return pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1);
575 return INVALID_POSITION;
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.
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];
599 if (actualWrapVisualStartIndent != 0) {
600 if (lineStart != 0) // Wrapped
601 x -= actualWrapVisualStartIndent * vs.aveCharWidth;
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;
609 i++;
612 return retVal;
616 * If painting then abandon the painting because a wider redraw is needed.
617 * @return true if calling code should stop drawing.
619 bool Editor::AbandonPaint() {
620 if ((paintState == painting) && !paintingAllText) {
621 paintState = paintAbandoned;
623 return paintState == paintAbandoned;
626 void Editor::RedrawRect(PRectangle rc) {
627 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
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;
640 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
641 wMain.InvalidateRectangle(rc);
645 void Editor::Redraw() {
646 //Platform::DebugPrintf("Redraw all\n");
647 PRectangle rcClient = GetClientRectangle();
648 wMain.InvalidateRectangle(rcClient);
649 //wMain.InvalidateAll();
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;
665 wMain.InvalidateRectangle(rcSelMargin);
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);
692 return rc;
695 void Editor::InvalidateRange(int start, int end) {
696 RedrawRect(RectangleFromRange(start, end));
699 int Editor::CurrentPosition() {
700 return currentPos;
703 bool Editor::SelectionEmpty() {
704 return anchor == currentPos;
707 int Editor::SelectionStart() {
708 return Platform::Minimum(currentPos, anchor);
711 int Editor::SelectionEnd() {
712 return Platform::Maximum(currentPos, anchor);
715 void Editor::SetRectangularRange() {
716 if (selType == selRectangle) {
717 xStartSelect = XFromPosition(anchor);
718 xEndSelect = XFromPosition(currentPos);
722 void Editor::InvalidateSelection(int currentPos_, int anchor_, bool invalidateWholeSelection) {
723 if (anchor != anchor_ || selType == selRectangle) {
724 invalidateWholeSelection = true;
726 int firstAffected = currentPos;
727 if (invalidateWholeSelection) {
728 if (firstAffected > anchor)
729 firstAffected = anchor;
730 if (firstAffected > anchor_)
731 firstAffected = anchor_;
733 if (firstAffected > currentPos_)
734 firstAffected = currentPos_;
735 int lastAffected = currentPos;
736 if (invalidateWholeSelection) {
737 if (lastAffected < anchor)
738 lastAffected = anchor;
739 if (lastAffected < anchor_)
740 lastAffected = anchor_;
742 if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
743 lastAffected = (currentPos_ + 1);
744 needUpdateUI = true;
745 InvalidateRange(firstAffected, lastAffected);
748 void Editor::SetSelection(int currentPos_, int anchor_) {
749 currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
750 anchor_ = pdoc->ClampPositionIntoDocument(anchor_);
751 if ((currentPos != currentPos_) || (anchor != anchor_)) {
752 InvalidateSelection(currentPos_, anchor_, true);
753 currentPos = currentPos_;
754 anchor = anchor_;
756 SetRectangularRange();
757 ClaimSelection();
760 void Editor::SetSelection(int currentPos_) {
761 currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
762 if (currentPos != currentPos_) {
763 InvalidateSelection(currentPos_, anchor, false);
764 currentPos = currentPos_;
766 SetRectangularRange();
767 ClaimSelection();
770 void Editor::SetEmptySelection(int currentPos_) {
771 selType = selStream;
772 moveExtendsSelection = false;
773 SetSelection(currentPos_, currentPos_);
776 bool Editor::RangeContainsProtected(int start, int end) const {
777 if (vs.ProtectionActive()) {
778 if (start > end) {
779 int t = start;
780 start = end;
781 end = t;
783 int mask = pdoc->stylingBitsMask;
784 for (int pos = start; pos < end; pos++) {
785 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
786 return true;
789 return false;
792 bool Editor::SelectionContainsProtected() {
793 // DONE, but untested...: make support rectangular selection
794 bool scp = false;
795 if (selType == selStream) {
796 scp = RangeContainsProtected(anchor, currentPos);
797 } else {
798 SelectionLineIterator lineIterator(this);
799 while (lineIterator.Iterate()) {
800 if (RangeContainsProtected(lineIterator.startPos, lineIterator.endPos)) {
801 scp = true;
802 break;
806 return scp;
810 * Asks document to find a good position and then moves out of any invisible positions.
812 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
813 pos = pdoc->MovePositionOutsideChar(pos, moveDir, checkLineEnd);
814 if (vs.ProtectionActive()) {
815 int mask = pdoc->stylingBitsMask;
816 if (moveDir > 0) {
817 if ((pos > 0) && vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()) {
818 while ((pos < pdoc->Length()) &&
819 (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()))
820 pos++;
822 } else if (moveDir < 0) {
823 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()) {
824 while ((pos > 0) &&
825 (vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()))
826 pos--;
830 return pos;
833 int Editor::MovePositionTo(int newPos, selTypes sel, bool ensureVisible) {
834 int delta = newPos - currentPos;
835 newPos = pdoc->ClampPositionIntoDocument(newPos);
836 newPos = MovePositionOutsideChar(newPos, delta);
837 if (sel != noSel) {
838 selType = sel;
840 if (sel != noSel || moveExtendsSelection) {
841 SetSelection(newPos);
842 } else {
843 SetEmptySelection(newPos);
845 ShowCaretAtCurrentPosition();
846 if (ensureVisible) {
847 EnsureCaretVisible();
849 NotifyMove(newPos);
850 return 0;
853 int Editor::MovePositionSoVisible(int pos, int moveDir) {
854 pos = pdoc->ClampPositionIntoDocument(pos);
855 pos = MovePositionOutsideChar(pos, moveDir);
856 int lineDoc = pdoc->LineFromPosition(pos);
857 if (cs.GetVisible(lineDoc)) {
858 return pos;
859 } else {
860 int lineDisplay = cs.DisplayFromDoc(lineDoc);
861 if (moveDir > 0) {
862 // lineDisplay is already line before fold as lines in fold use display line of line after fold
863 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
864 return pdoc->LineStart(cs.DocFromDisplay(lineDisplay));
865 } else {
866 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
867 return pdoc->LineEnd(cs.DocFromDisplay(lineDisplay));
873 * Choose the x position that the caret will try to stick to
874 * as it moves up and down.
876 void Editor::SetLastXChosen() {
877 Point pt = LocationFromPosition(currentPos);
878 lastXChosen = pt.x;
881 void Editor::ScrollTo(int line, bool moveThumb) {
882 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
883 if (topLineNew != topLine) {
884 // Try to optimise small scrolls
885 int linesToMove = topLine - topLineNew;
886 SetTopLine(topLineNew);
887 ShowCaretAtCurrentPosition();
888 // Perform redraw rather than scroll if many lines would be redrawn anyway.
889 #ifndef UNDER_CE
890 if ((abs(linesToMove) <= 10) && (paintState == notPainting)) {
891 ScrollText(linesToMove);
892 } else {
893 Redraw();
895 #else
896 Redraw();
897 #endif
898 if (moveThumb) {
899 SetVerticalScrollPos();
904 void Editor::ScrollText(int /* linesToMove */) {
905 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
906 Redraw();
909 void Editor::HorizontalScrollTo(int xPos) {
910 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
911 if (xPos < 0)
912 xPos = 0;
913 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
914 xOffset = xPos;
915 SetHorizontalScrollPos();
916 RedrawRect(GetClientRectangle());
920 void Editor::MoveCaretInsideView(bool ensureVisible) {
921 PRectangle rcClient = GetTextRectangle();
922 Point pt = LocationFromPosition(currentPos);
923 if (pt.y < rcClient.top) {
924 MovePositionTo(PositionFromLocation(
925 Point(lastXChosen, rcClient.top)),
926 noSel, ensureVisible);
927 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
928 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
929 MovePositionTo(PositionFromLocation(
930 Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
931 noSel, ensureVisible);
935 int Editor::DisplayFromPosition(int pos) {
936 int lineDoc = pdoc->LineFromPosition(pos);
937 int lineDisplay = cs.DisplayFromDoc(lineDoc);
938 AutoSurface surface(this);
939 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
940 if (surface && ll) {
941 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
942 unsigned int posLineStart = pdoc->LineStart(lineDoc);
943 int posInLine = pos - posLineStart;
944 lineDisplay--; // To make up for first increment ahead.
945 for (int subLine = 0; subLine < ll->lines; subLine++) {
946 if (posInLine >= ll->LineStart(subLine)) {
947 lineDisplay++;
951 return lineDisplay;
955 * Ensure the caret is reasonably visible in context.
957 Caret policy in SciTE
959 If slop is set, we can define a slop value.
960 This value defines an unwanted zone (UZ) where the caret is... unwanted.
961 This zone is defined as a number of pixels near the vertical margins,
962 and as a number of lines near the horizontal margins.
963 By keeping the caret away from the edges, it is seen within its context,
964 so it is likely that the identifier that the caret is on can be completely seen,
965 and that the current line is seen with some of the lines following it which are
966 often dependent on that line.
968 If strict is set, the policy is enforced... strictly.
969 The caret is centred on the display if slop is not set,
970 and cannot go in the UZ if slop is set.
972 If jumps is set, the display is moved more energetically
973 so the caret can move in the same direction longer before the policy is applied again.
974 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
976 If even is not set, instead of having symmetrical UZs,
977 the left and bottom UZs are extended up to right and top UZs respectively.
978 This way, we favour the displaying of useful information: the begining of lines,
979 where most code reside, and the lines after the caret, eg. the body of a function.
981 | | | | |
982 slop | strict | jumps | even | Caret can go to the margin | When reaching limitÝ(caret going out of
983 | | | | | visibility or going into the UZ) display is...
984 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
985 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
986 0 | 0 | 0 | 1 | Yes | moved by one position
987 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
988 0 | 0 | 1 | 1 | Yes | centred on the caret
989 0 | 1 | - | 0 | Caret is always on top/on right of display | -
990 0 | 1 | - | 1 | No, caret is always centred | -
991 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
992 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
993 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
994 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
995 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
996 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
997 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
999 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1000 //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
1001 PRectangle rcClient = GetTextRectangle();
1002 //int rcClientFullWidth = rcClient.Width();
1003 int posCaret = currentPos;
1004 if (posDrag >= 0) {
1005 posCaret = posDrag;
1007 Point pt = LocationFromPosition(posCaret);
1008 Point ptBottomCaret = pt;
1009 ptBottomCaret.y += vs.lineHeight - 1;
1010 int lineCaret = DisplayFromPosition(posCaret);
1011 bool bSlop, bStrict, bJump, bEven;
1013 // Vertical positioning
1014 if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1015 int linesOnScreen = LinesOnScreen();
1016 int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1017 int newTopLine = topLine;
1018 bSlop = (caretYPolicy & CARET_SLOP) != 0;
1019 bStrict = (caretYPolicy & CARET_STRICT) != 0;
1020 bJump = (caretYPolicy & CARET_JUMPS) != 0;
1021 bEven = (caretYPolicy & CARET_EVEN) != 0;
1023 // It should be possible to scroll the window to show the caret,
1024 // but this fails to remove the caret on GTK+
1025 if (bSlop) { // A margin is defined
1026 int yMoveT, yMoveB;
1027 if (bStrict) {
1028 int yMarginT, yMarginB;
1029 if (!useMargin) {
1030 // In drag mode, avoid moves
1031 // otherwise, a double click will select several lines.
1032 yMarginT = yMarginB = 0;
1033 } else {
1034 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1035 // a maximum of slightly less than half the heigth of the text area.
1036 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1037 if (bEven) {
1038 yMarginB = yMarginT;
1039 } else {
1040 yMarginB = linesOnScreen - yMarginT - 1;
1043 yMoveT = yMarginT;
1044 if (bEven) {
1045 if (bJump) {
1046 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1048 yMoveB = yMoveT;
1049 } else {
1050 yMoveB = linesOnScreen - yMoveT - 1;
1052 if (lineCaret < topLine + yMarginT) {
1053 // Caret goes too high
1054 newTopLine = lineCaret - yMoveT;
1055 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1056 // Caret goes too low
1057 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1059 } else { // Not strict
1060 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1061 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1062 if (bEven) {
1063 yMoveB = yMoveT;
1064 } else {
1065 yMoveB = linesOnScreen - yMoveT - 1;
1067 if (lineCaret < topLine) {
1068 // Caret goes too high
1069 newTopLine = lineCaret - yMoveT;
1070 } else if (lineCaret > topLine + linesOnScreen - 1) {
1071 // Caret goes too low
1072 newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
1075 } else { // No slop
1076 if (!bStrict && !bJump) {
1077 // Minimal move
1078 if (lineCaret < topLine) {
1079 // Caret goes too high
1080 newTopLine = lineCaret;
1081 } else if (lineCaret > topLine + linesOnScreen - 1) {
1082 // Caret goes too low
1083 if (bEven) {
1084 newTopLine = lineCaret - linesOnScreen + 1;
1085 } else {
1086 newTopLine = lineCaret;
1089 } else { // Strict or going out of display
1090 if (bEven) {
1091 // Always center caret
1092 newTopLine = lineCaret - halfScreen;
1093 } else {
1094 // Always put caret on top of display
1095 newTopLine = lineCaret;
1099 newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos());
1100 if (newTopLine != topLine) {
1101 Redraw();
1102 SetTopLine(newTopLine);
1103 SetVerticalScrollPos();
1107 // Horizontal positioning
1108 if (horiz && (wrapState == eWrapNone)) {
1109 int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1110 int xOffsetNew = xOffset;
1111 bSlop = (caretXPolicy & CARET_SLOP) != 0;
1112 bStrict = (caretXPolicy & CARET_STRICT) != 0;
1113 bJump = (caretXPolicy & CARET_JUMPS) != 0;
1114 bEven = (caretXPolicy & CARET_EVEN) != 0;
1116 if (bSlop) { // A margin is defined
1117 int xMoveL, xMoveR;
1118 if (bStrict) {
1119 int xMarginL, xMarginR;
1120 if (!useMargin) {
1121 // In drag mode, avoid moves unless very near of the margin
1122 // otherwise, a simple click will select text.
1123 xMarginL = xMarginR = 2;
1124 } else {
1125 // xMargin must equal to caretXSlop, with a minimum of 2 and
1126 // a maximum of slightly less than half the width of the text area.
1127 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1128 if (bEven) {
1129 xMarginL = xMarginR;
1130 } else {
1131 xMarginL = rcClient.Width() - xMarginR - 4;
1134 if (bJump && bEven) {
1135 // Jump is used only in even mode
1136 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1137 } else {
1138 xMoveL = xMoveR = 0; // Not used, avoid a warning
1140 if (pt.x < rcClient.left + xMarginL) {
1141 // Caret is on the left of the display
1142 if (bJump && bEven) {
1143 xOffsetNew -= xMoveL;
1144 } else {
1145 // Move just enough to allow to display the caret
1146 xOffsetNew -= (rcClient.left + xMarginL) - pt.x;
1148 } else if (pt.x >= rcClient.right - xMarginR) {
1149 // Caret is on the right of the display
1150 if (bJump && bEven) {
1151 xOffsetNew += xMoveR;
1152 } else {
1153 // Move just enough to allow to display the caret
1154 xOffsetNew += pt.x - (rcClient.right - xMarginR) + 1;
1157 } else { // Not strict
1158 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1159 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1160 if (bEven) {
1161 xMoveL = xMoveR;
1162 } else {
1163 xMoveL = rcClient.Width() - xMoveR - 4;
1165 if (pt.x < rcClient.left) {
1166 // Caret is on the left of the display
1167 xOffsetNew -= xMoveL;
1168 } else if (pt.x >= rcClient.right) {
1169 // Caret is on the right of the display
1170 xOffsetNew += xMoveR;
1173 } else { // No slop
1174 if (bStrict ||
1175 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1176 // Strict or going out of display
1177 if (bEven) {
1178 // Center caret
1179 xOffsetNew += pt.x - rcClient.left - halfScreen;
1180 } else {
1181 // Put caret on right
1182 xOffsetNew += pt.x - rcClient.right + 1;
1184 } else {
1185 // Move just enough to allow to display the caret
1186 if (pt.x < rcClient.left) {
1187 // Caret is on the left of the display
1188 if (bEven) {
1189 xOffsetNew -= rcClient.left - pt.x;
1190 } else {
1191 xOffsetNew += pt.x - rcClient.right + 1;
1193 } else if (pt.x >= rcClient.right) {
1194 // Caret is on the right of the display
1195 xOffsetNew += pt.x - rcClient.right + 1;
1199 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1200 if (pt.x + xOffset < rcClient.left + xOffsetNew) {
1201 xOffsetNew = pt.x + xOffset - rcClient.left;
1202 } else if (pt.x + xOffset >= rcClient.right + xOffsetNew) {
1203 xOffsetNew = pt.x + xOffset - rcClient.right + 1;
1204 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1205 // Ensure we can see a good portion of the block caret
1206 xOffsetNew += vs.aveCharWidth;
1209 if (xOffsetNew < 0) {
1210 xOffsetNew = 0;
1212 if (xOffset != xOffsetNew) {
1213 xOffset = xOffsetNew;
1214 if (xOffsetNew > 0) {
1215 PRectangle rcText = GetTextRectangle();
1216 if (horizontalScrollBarVisible &&
1217 rcText.Width() + xOffset > scrollWidth) {
1218 scrollWidth = xOffset + rcText.Width();
1219 SetScrollBars();
1222 SetHorizontalScrollPos();
1223 Redraw();
1226 UpdateSystemCaret();
1229 void Editor::ShowCaretAtCurrentPosition() {
1230 if (hasFocus) {
1231 caret.active = true;
1232 caret.on = true;
1233 SetTicking(true);
1234 } else {
1235 caret.active = false;
1236 caret.on = false;
1238 InvalidateCaret();
1241 void Editor::DropCaret() {
1242 caret.active = false;
1243 InvalidateCaret();
1246 void Editor::InvalidateCaret() {
1247 if (posDrag >= 0)
1248 InvalidateRange(posDrag, posDrag + 1);
1249 else
1250 InvalidateRange(currentPos, currentPos + 1);
1251 UpdateSystemCaret();
1254 void Editor::UpdateSystemCaret() {
1257 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1258 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1259 if (wrapStart > docLineStart) {
1260 wrapStart = docLineStart;
1261 llc.Invalidate(LineLayout::llPositions);
1263 if (wrapEnd < docLineEnd) {
1264 wrapEnd = docLineEnd;
1266 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1267 // Wrap lines during idle.
1268 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1269 SetIdle(true);
1273 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1274 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1275 int linesWrapped = 1;
1276 if (ll) {
1277 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1278 linesWrapped = ll->lines;
1280 return cs.SetHeight(lineToWrap, linesWrapped);
1283 // Check if wrapping needed and perform any needed wrapping.
1284 // fullwrap: if true, all lines which need wrapping will be done,
1285 // in this single call.
1286 // priorityWrapLineStart: If greater than zero, all lines starting from
1287 // here to 1 page + 100 lines past will be wrapped (even if there are
1288 // more lines under wrapping process in idle).
1289 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1290 // wrapped, if there are any wrapping going on in idle. (Generally this
1291 // condition is called only from idler).
1292 // Return true if wrapping occurred.
1293 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1294 // If there are any pending wraps, do them during idle if possible.
1295 int linesInOneCall = LinesOnScreen() + 100;
1296 if (wrapState != eWrapNone) {
1297 if (wrapStart < wrapEnd) {
1298 if (!SetIdle(true)) {
1299 // Idle processing not supported so full wrap required.
1300 fullWrap = true;
1303 if (!fullWrap && priorityWrapLineStart >= 0 &&
1304 // .. and if the paint window is outside pending wraps
1305 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1306 (priorityWrapLineStart > wrapEnd))) {
1307 // No priority wrap pending
1308 return false;
1311 int goodTopLine = topLine;
1312 bool wrapOccurred = false;
1313 if (wrapStart <= pdoc->LinesTotal()) {
1314 if (wrapState == eWrapNone) {
1315 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1316 wrapWidth = LineLayout::wrapWidthInfinite;
1317 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1318 cs.SetHeight(lineDoc, 1);
1320 wrapOccurred = true;
1322 wrapStart = wrapLineLarge;
1323 wrapEnd = wrapLineLarge;
1324 } else {
1325 if (wrapEnd >= pdoc->LinesTotal())
1326 wrapEnd = pdoc->LinesTotal();
1327 //ElapsedTime et;
1328 int lineDocTop = cs.DocFromDisplay(topLine);
1329 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1330 PRectangle rcTextArea = GetClientRectangle();
1331 rcTextArea.left = vs.fixedColumnWidth;
1332 rcTextArea.right -= vs.rightMarginWidth;
1333 wrapWidth = rcTextArea.Width();
1334 // Ensure all of the document is styled.
1335 pdoc->EnsureStyledTo(pdoc->Length());
1336 RefreshStyleData();
1337 AutoSurface surface(this);
1338 if (surface) {
1339 bool priorityWrap = false;
1340 int lastLineToWrap = wrapEnd;
1341 int lineToWrap = wrapStart;
1342 if (!fullWrap) {
1343 if (priorityWrapLineStart >= 0) {
1344 // This is a priority wrap.
1345 lineToWrap = priorityWrapLineStart;
1346 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1347 priorityWrap = true;
1348 } else {
1349 // This is idle wrap.
1350 lastLineToWrap = wrapStart + linesInOneCall;
1352 if (lastLineToWrap >= wrapEnd)
1353 lastLineToWrap = wrapEnd;
1354 } // else do a fullWrap.
1356 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1357 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1358 while (lineToWrap < lastLineToWrap) {
1359 if (WrapOneLine(surface, lineToWrap)) {
1360 wrapOccurred = true;
1362 lineToWrap++;
1364 if (!priorityWrap)
1365 wrapStart = lineToWrap;
1366 // If wrapping is done, bring it to resting position
1367 if (wrapStart >= wrapEnd) {
1368 wrapStart = wrapLineLarge;
1369 wrapEnd = wrapLineLarge;
1372 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1373 if (subLineTop < cs.GetHeight(lineDocTop))
1374 goodTopLine += subLineTop;
1375 else
1376 goodTopLine += cs.GetHeight(lineDocTop);
1377 //double durWrap = et.Duration(true);
1378 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1381 if (wrapOccurred) {
1382 SetScrollBars();
1383 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1384 SetVerticalScrollPos();
1386 return wrapOccurred;
1389 void Editor::LinesJoin() {
1390 if (!RangeContainsProtected(targetStart, targetEnd)) {
1391 pdoc->BeginUndoAction();
1392 bool prevNonWS = true;
1393 for (int pos = targetStart; pos < targetEnd; pos++) {
1394 if (IsEOLChar(pdoc->CharAt(pos))) {
1395 targetEnd -= pdoc->LenChar(pos);
1396 pdoc->DelChar(pos);
1397 if (prevNonWS) {
1398 // Ensure at least one space separating previous lines
1399 pdoc->InsertChar(pos, ' ');
1400 targetEnd++;
1402 } else {
1403 prevNonWS = pdoc->CharAt(pos) != ' ';
1406 pdoc->EndUndoAction();
1410 const char *Editor::StringFromEOLMode(int eolMode) {
1411 if (eolMode == SC_EOL_CRLF) {
1412 return "\r\n";
1413 } else if (eolMode == SC_EOL_CR) {
1414 return "\r";
1415 } else {
1416 return "\n";
1420 void Editor::LinesSplit(int pixelWidth) {
1421 if (!RangeContainsProtected(targetStart, targetEnd)) {
1422 if (pixelWidth == 0) {
1423 PRectangle rcText = GetTextRectangle();
1424 pixelWidth = rcText.Width();
1426 int lineStart = pdoc->LineFromPosition(targetStart);
1427 int lineEnd = pdoc->LineFromPosition(targetEnd);
1428 const char *eol = StringFromEOLMode(pdoc->eolMode);
1429 pdoc->BeginUndoAction();
1430 for (int line = lineStart; line <= lineEnd; line++) {
1431 AutoSurface surface(this);
1432 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1433 if (surface && ll) {
1434 unsigned int posLineStart = pdoc->LineStart(line);
1435 LayoutLine(line, surface, vs, ll, pixelWidth);
1436 for (int subLine = 1; subLine < ll->lines; subLine++) {
1437 pdoc->InsertCString(posLineStart + (subLine - 1) * strlen(eol) +
1438 ll->LineStart(subLine), eol);
1439 targetEnd += static_cast<int>(strlen(eol));
1442 lineEnd = pdoc->LineFromPosition(targetEnd);
1444 pdoc->EndUndoAction();
1448 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1449 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1450 return markerDefault;
1451 return markerCheck;
1454 // Avoid 64 bit compiler warnings.
1455 // Scintilla does not support text buffers larger than 2**31
1456 static int istrlen(const char *s) {
1457 return static_cast<int>(strlen(s));
1460 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1461 if (vs.fixedColumnWidth == 0)
1462 return;
1464 PRectangle rcMargin = GetClientRectangle();
1465 rcMargin.right = vs.fixedColumnWidth;
1467 if (!rc.Intersects(rcMargin))
1468 return;
1470 Surface *surface;
1471 if (bufferedDraw) {
1472 surface = pixmapSelMargin;
1473 } else {
1474 surface = surfWindow;
1477 PRectangle rcSelMargin = rcMargin;
1478 rcSelMargin.right = rcMargin.left;
1480 for (int margin = 0; margin < vs.margins; margin++) {
1481 if (vs.ms[margin].width > 0) {
1483 rcSelMargin.left = rcSelMargin.right;
1484 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1486 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1487 /* alternate scheme:
1488 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1489 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1490 else
1491 // Required because of special way brush is created for selection margin
1492 surface->FillRectangle(rcSelMargin, pixmapSelPattern);
1494 if (vs.ms[margin].mask & SC_MASK_FOLDERS)
1495 // Required because of special way brush is created for selection margin
1496 surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
1497 else {
1498 ColourAllocated colour;
1499 switch (vs.ms[margin].style) {
1500 case SC_MARGIN_BACK:
1501 colour = vs.styles[STYLE_DEFAULT].back.allocated;
1502 break;
1503 case SC_MARGIN_FORE:
1504 colour = vs.styles[STYLE_DEFAULT].fore.allocated;
1505 break;
1506 default:
1507 colour = vs.styles[STYLE_LINENUMBER].back.allocated;
1508 break;
1510 surface->FillRectangle(rcSelMargin, colour);
1512 } else {
1513 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
1516 int visibleLine = topLine;
1517 int yposScreen = 0;
1519 // Work out whether the top line is whitespace located after a
1520 // lessening of fold level which implies a 'fold tail' but which should not
1521 // be displayed until the last of a sequence of whitespace.
1522 bool needWhiteClosure = false;
1523 int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
1524 if (level & SC_FOLDLEVELWHITEFLAG) {
1525 int lineBack = cs.DocFromDisplay(topLine);
1526 int levelPrev = level;
1527 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1528 lineBack--;
1529 levelPrev = pdoc->GetLevel(lineBack);
1531 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1532 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1533 needWhiteClosure = true;
1537 // Old code does not know about new markers needed to distinguish all cases
1538 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1539 SC_MARKNUM_FOLDEROPEN);
1540 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1541 SC_MARKNUM_FOLDER);
1543 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
1545 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1547 int lineDoc = cs.DocFromDisplay(visibleLine);
1548 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1549 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1551 // Decide which fold indicator should be displayed
1552 level = pdoc->GetLevel(lineDoc);
1553 int levelNext = pdoc->GetLevel(lineDoc + 1);
1554 int marks = pdoc->GetMark(lineDoc);
1555 if (!firstSubLine)
1556 marks = 0;
1557 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1558 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1559 if (level & SC_FOLDLEVELHEADERFLAG) {
1560 if (firstSubLine) {
1561 if (cs.GetExpanded(lineDoc)) {
1562 if (levelNum == SC_FOLDLEVELBASE)
1563 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1564 else
1565 marks |= 1 << folderOpenMid;
1566 } else {
1567 if (levelNum == SC_FOLDLEVELBASE)
1568 marks |= 1 << SC_MARKNUM_FOLDER;
1569 else
1570 marks |= 1 << folderEnd;
1572 } else {
1573 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1575 needWhiteClosure = false;
1576 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1577 if (needWhiteClosure) {
1578 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1579 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1580 } else if (levelNum > SC_FOLDLEVELBASE) {
1581 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1582 needWhiteClosure = false;
1583 } else {
1584 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1585 needWhiteClosure = false;
1587 } else if (levelNum > SC_FOLDLEVELBASE) {
1588 if (levelNextNum < levelNum) {
1589 if (levelNextNum > SC_FOLDLEVELBASE) {
1590 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1591 } else {
1592 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1594 } else {
1595 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1598 } else if (levelNum > SC_FOLDLEVELBASE) {
1599 if (levelNextNum < levelNum) {
1600 needWhiteClosure = false;
1601 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1602 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1603 needWhiteClosure = true;
1604 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1605 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1606 } else {
1607 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1609 } else {
1610 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1614 marks &= vs.ms[margin].mask;
1615 PRectangle rcMarker = rcSelMargin;
1616 rcMarker.top = yposScreen;
1617 rcMarker.bottom = yposScreen + vs.lineHeight;
1618 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
1619 char number[100];
1620 number[0] = '\0';
1621 if (firstSubLine)
1622 sprintf(number, "%d", lineDoc + 1);
1623 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
1624 int lev = pdoc->GetLevel(lineDoc);
1625 sprintf(number, "%c%c %03X %03X",
1626 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
1627 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
1628 lev & SC_FOLDLEVELNUMBERMASK,
1629 lev >> 16
1632 PRectangle rcNumber = rcMarker;
1633 // Right justify
1634 int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
1635 int xpos = rcNumber.right - width - 3;
1636 rcNumber.left = xpos;
1637 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
1638 rcNumber.top + vs.maxAscent, number, istrlen(number),
1639 vs.styles[STYLE_LINENUMBER].fore.allocated,
1640 vs.styles[STYLE_LINENUMBER].back.allocated);
1643 if (marks) {
1644 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
1645 if (marks & 1) {
1646 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font);
1648 marks >>= 1;
1652 visibleLine++;
1653 yposScreen += vs.lineHeight;
1658 PRectangle rcBlankMargin = rcMargin;
1659 rcBlankMargin.left = rcSelMargin.right;
1660 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
1662 if (bufferedDraw) {
1663 surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
1667 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
1668 int ydiff = (rcTab.bottom - rcTab.top) / 2;
1669 int xhead = rcTab.right - 1 - ydiff;
1670 if (xhead <= rcTab.left) {
1671 ydiff -= rcTab.left - xhead - 1;
1672 xhead = rcTab.left - 1;
1674 if ((rcTab.left + 2) < (rcTab.right - 1))
1675 surface->MoveTo(rcTab.left + 2, ymid);
1676 else
1677 surface->MoveTo(rcTab.right - 1, ymid);
1678 surface->LineTo(rcTab.right - 1, ymid);
1679 surface->LineTo(xhead, ymid - ydiff);
1680 surface->MoveTo(rcTab.right - 1, ymid);
1681 surface->LineTo(xhead, ymid + ydiff);
1684 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
1685 int posLineStart = pdoc->LineStart(lineNumber);
1686 int posLineEnd = pdoc->LineStart(lineNumber + 1);
1687 PLATFORM_ASSERT(posLineEnd >= posLineStart);
1688 int lineCaret = pdoc->LineFromPosition(currentPos);
1689 return llc.Retrieve(lineNumber, lineCaret,
1690 posLineEnd - posLineStart, pdoc->GetStyleClock(),
1691 LinesOnScreen() + 1, pdoc->LinesTotal());
1694 static bool GoodTrailByte(int v) {
1695 return (v >= 0x80) && (v < 0xc0);
1698 bool BadUTF(const char *s, int len, int &trailBytes) {
1699 if (trailBytes) {
1700 trailBytes--;
1701 return false;
1703 const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
1704 if (*us < 0x80) {
1705 // Single bytes easy
1706 return false;
1707 } else if (*us > 0xF4) {
1708 // Characters longer than 4 bytes not possible in current UTF-8
1709 return true;
1710 } else if (*us >= 0xF0) {
1711 // 4 bytes
1712 if (len < 4)
1713 return true;
1714 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2]) && GoodTrailByte(us[3])) {
1715 trailBytes = 3;
1716 return false;
1717 } else {
1718 return true;
1720 } else if (*us >= 0xE0) {
1721 // 3 bytes
1722 if (len < 3)
1723 return true;
1724 if (GoodTrailByte(us[1]) && GoodTrailByte(us[2])) {
1725 trailBytes = 2;
1726 return false;
1727 } else {
1728 return true;
1730 } else if (*us >= 0xC2) {
1731 // 2 bytes
1732 if (len < 2)
1733 return true;
1734 if (GoodTrailByte(us[1])) {
1735 trailBytes = 1;
1736 return false;
1737 } else {
1738 return true;
1740 } else if (*us >= 0xC0) {
1741 // Overlong encoding
1742 return true;
1743 } else {
1744 // Trail byte
1745 return true;
1750 * Fill in the LineLayout data for the given line.
1751 * Copy the given @a line and its styles from the document into local arrays.
1752 * Also determine the x position at which each character starts.
1754 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
1755 if (!ll)
1756 return;
1758 PLATFORM_ASSERT(line < pdoc->LinesTotal());
1759 PLATFORM_ASSERT(ll->chars != NULL);
1760 int posLineStart = pdoc->LineStart(line);
1761 int posLineEnd = pdoc->LineStart(line + 1);
1762 // If the line is very long, limit the treatment to a length that should fit in the viewport
1763 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
1764 posLineEnd = posLineStart + ll->maxLineLength;
1766 if (ll->validity == LineLayout::llCheckTextAndStyle) {
1767 int lineLength = posLineEnd - posLineStart;
1768 if (!vstyle.viewEOL) {
1769 int cid = posLineEnd - 1;
1770 while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
1771 cid--;
1772 lineLength--;
1775 if (lineLength == ll->numCharsInLine) {
1776 // See if chars, styles, indicators, are all the same
1777 bool allSame = true;
1778 const int styleMask = pdoc->stylingBitsMask;
1779 // Check base line layout
1780 char styleByte = 0;
1781 int numCharsInLine = 0;
1782 while (numCharsInLine < lineLength) {
1783 int charInDoc = numCharsInLine + posLineStart;
1784 char chDoc = pdoc->CharAt(charInDoc);
1785 styleByte = pdoc->StyleAt(charInDoc);
1786 allSame = allSame &&
1787 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
1788 allSame = allSame &&
1789 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
1790 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
1791 allSame = allSame &&
1792 (ll->chars[numCharsInLine] == chDoc);
1793 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
1794 allSame = allSame &&
1795 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
1796 else // Style::caseUpper
1797 allSame = allSame &&
1798 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
1799 numCharsInLine++;
1801 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
1802 if (allSame) {
1803 ll->validity = LineLayout::llPositions;
1804 } else {
1805 ll->validity = LineLayout::llInvalid;
1807 } else {
1808 ll->validity = LineLayout::llInvalid;
1811 if (ll->validity == LineLayout::llInvalid) {
1812 ll->widthLine = LineLayout::wrapWidthInfinite;
1813 ll->lines = 1;
1814 int numCharsInLine = 0;
1815 if (vstyle.edgeState == EDGE_BACKGROUND) {
1816 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
1817 if (ll->edgeColumn >= posLineStart) {
1818 ll->edgeColumn -= posLineStart;
1820 } else {
1821 ll->edgeColumn = -1;
1824 char styleByte = 0;
1825 int styleMask = pdoc->stylingBitsMask;
1826 ll->styleBitsSet = 0;
1827 // Fill base line layout
1828 for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
1829 char chDoc = pdoc->CharAt(charInDoc);
1830 styleByte = pdoc->StyleAt(charInDoc);
1831 ll->styleBitsSet |= styleByte;
1832 if (vstyle.viewEOL || (!IsEOLChar(chDoc))) {
1833 ll->chars[numCharsInLine] = chDoc;
1834 ll->styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
1835 ll->indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
1836 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
1837 ll->chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
1838 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
1839 ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
1840 numCharsInLine++;
1843 ll->xHighlightGuide = 0;
1844 // Extra element at the end of the line to hold end x position and act as
1845 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
1846 ll->styles[numCharsInLine] = styleByte; // For eolFilled
1847 ll->indicators[numCharsInLine] = 0;
1849 // Layout the line, determining the position of each character,
1850 // with an extra element at the end for the end of the line.
1851 int startseg = 0; // Start of the current segment, in char. number
1852 int startsegx = 0; // Start of the current segment, in pixels
1853 ll->positions[0] = 0;
1854 unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
1855 bool lastSegItalics = false;
1856 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
1858 int ctrlCharWidth[32] = {0};
1859 bool isControlNext = IsControlCharacter(ll->chars[0]);
1860 int trailBytes = 0;
1861 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
1862 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
1863 bool isControl = isControlNext;
1864 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
1865 bool isBadUTF = isBadUTFNext;
1866 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
1867 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
1868 isControl || isControlNext || isBadUTF || isBadUTFNext) {
1869 ll->positions[startseg] = 0;
1870 if (vstyle.styles[ll->styles[charInLine]].visible) {
1871 if (isControl) {
1872 if (ll->chars[charInLine] == '\t') {
1873 ll->positions[charInLine + 1] = ((((startsegx + 2) /
1874 tabWidth) + 1) * tabWidth) - startsegx;
1875 } else if (controlCharSymbol < 32) {
1876 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
1877 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
1878 // +3 For a blank on front and rounded edge each side:
1879 ctrlCharWidth[ll->chars[charInLine]] =
1880 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
1882 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
1883 } else {
1884 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
1885 surface->MeasureWidths(ctrlCharsFont, cc, 1,
1886 ll->positions + startseg + 1);
1888 lastSegItalics = false;
1889 } else if (isBadUTF) {
1890 char hexits[3];
1891 sprintf(hexits, "%2X", ll->chars[charInLine] & 0xff);
1892 ll->positions[charInLine + 1] =
1893 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
1894 } else { // Regular character
1895 int lenSeg = charInLine - startseg + 1;
1896 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
1897 lastSegItalics = false;
1898 // Over half the segments are single characters and of these about half are space characters.
1899 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
1900 } else {
1901 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
1902 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
1903 lenSeg, ll->positions + startseg + 1);
1906 } else { // invisible
1907 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
1908 ll->positions[posToZero] = 0;
1911 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
1912 ll->positions[posToIncrease] += startsegx;
1914 startsegx = ll->positions[charInLine + 1];
1915 startseg = charInLine + 1;
1918 // Small hack to make lines that end with italics not cut off the edge of the last character
1919 if ((startseg > 0) && lastSegItalics) {
1920 ll->positions[startseg] += 2;
1922 ll->numCharsInLine = numCharsInLine;
1923 ll->validity = LineLayout::llPositions;
1925 // Hard to cope when too narrow, so just assume there is space
1926 if (width < 20) {
1927 width = 20;
1929 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
1930 ll->widthLine = width;
1931 if (width == LineLayout::wrapWidthInfinite) {
1932 ll->lines = 1;
1933 } else if (width > ll->positions[ll->numCharsInLine]) {
1934 // Simple common case where line does not need wrapping.
1935 ll->lines = 1;
1936 } else {
1937 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
1938 width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
1940 ll->lines = 0;
1941 // Calculate line start positions based upon width.
1942 int lastGoodBreak = 0;
1943 int lastLineStart = 0;
1944 int startOffset = 0;
1945 int p = 0;
1946 while (p < ll->numCharsInLine) {
1947 if ((ll->positions[p + 1] - startOffset) >= width) {
1948 if (lastGoodBreak == lastLineStart) {
1949 // Try moving to start of last character
1950 if (p > 0) {
1951 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
1952 - posLineStart;
1954 if (lastGoodBreak == lastLineStart) {
1955 // Ensure at least one character on line.
1956 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
1957 - posLineStart;
1960 lastLineStart = lastGoodBreak;
1961 ll->lines++;
1962 ll->SetLineStart(ll->lines, lastGoodBreak);
1963 startOffset = ll->positions[lastGoodBreak];
1964 // take into account the space for start wrap mark and indent
1965 startOffset -= actualWrapVisualStartIndent * vstyle.aveCharWidth;
1966 p = lastGoodBreak + 1;
1967 continue;
1969 if (p > 0) {
1970 if (wrapState == eWrapChar) {
1971 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
1972 - posLineStart;
1973 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
1974 continue;
1975 } else if (ll->styles[p] != ll->styles[p - 1]) {
1976 lastGoodBreak = p;
1977 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
1978 lastGoodBreak = p;
1981 p++;
1983 ll->lines++;
1985 ll->validity = LineLayout::llLines;
1989 ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw) {
1990 return primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated;
1993 ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
1994 ColourAllocated background, bool inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
1995 if (inSelection) {
1996 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
1997 return SelectionBackground(vsDraw);
1999 } else {
2000 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2001 (i >= ll->edgeColumn) &&
2002 !IsEOLChar(ll->chars[i]))
2003 return vsDraw.edgecolour.allocated;
2004 if (inHotspot && vsDraw.hotspotBackgroundSet)
2005 return vsDraw.hotspotBackground.allocated;
2006 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD))
2007 return background;
2009 return vsDraw.styles[styleMain].back.allocated;
2012 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2013 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2014 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2015 surface->Copy(rcCopyArea, from,
2016 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2019 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2020 bool isEndMarker, ColourAllocated wrapColour) {
2021 surface->PenColour(wrapColour);
2023 enum { xa = 1 }; // gap before start
2024 int w = rcPlace.right - rcPlace.left - xa - 1;
2026 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2027 bool yStraight = true;
2028 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2030 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2031 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2033 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2034 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2036 struct Relative {
2037 Surface *surface;
2038 int xBase;
2039 int xDir;
2040 int yBase;
2041 int yDir;
2042 void MoveTo(int xRelative, int yRelative) {
2043 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2045 void LineTo(int xRelative, int yRelative) {
2046 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2049 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2051 // arrow head
2052 rel.MoveTo(xa, y);
2053 rel.LineTo(xa + 2*w / 3, y - dy);
2054 rel.MoveTo(xa, y);
2055 rel.LineTo(xa + 2*w / 3, y + dy);
2057 // arrow body
2058 rel.MoveTo(xa, y);
2059 rel.LineTo(xa + w, y);
2060 rel.LineTo(xa + w, y - 2 * dy);
2061 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2062 y - 2 * dy);
2065 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) {
2066 if (alpha != SC_ALPHA_NOALPHA) {
2067 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2071 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2072 int line, int lineEnd, int xStart, int subLine, int subLineStart,
2073 bool overrideBackground, ColourAllocated background,
2074 bool drawWrapMarkEnd, ColourAllocated wrapColour) {
2076 int styleMask = pdoc->stylingBitsMask;
2077 PRectangle rcSegment = rcLine;
2079 // Fill in a PRectangle representing the end of line characters
2080 int xEol = ll->positions[lineEnd] - subLineStart;
2081 rcSegment.left = xEol + xStart;
2082 rcSegment.right = xEol + vsDraw.aveCharWidth + xStart;
2083 int posLineEnd = pdoc->LineStart(line + 1);
2084 bool eolInSelection = (subLine == (ll->lines - 1)) &&
2085 (posLineEnd > ll->selStart) && (posLineEnd <= ll->selEnd) && (ll->selStart != ll->selEnd);
2087 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2088 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw));
2089 } else {
2090 if (overrideBackground) {
2091 surface->FillRectangle(rcSegment, background);
2092 } else {
2093 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
2095 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) {
2096 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
2100 rcSegment.left = xEol + vsDraw.aveCharWidth + xStart;
2101 rcSegment.right = rcLine.right;
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);
2113 if (vsDraw.selEOLFilled && eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) {
2114 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
2118 if (drawWrapMarkEnd) {
2119 PRectangle rcPlace = rcSegment;
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;
2129 DrawWrapMarker(surface, rcPlace, true, wrapColour);
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;
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;
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;
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;
2174 mask <<= 1;
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);
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);
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);
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);
2224 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2225 PRectangle rcLine, LineLayout *ll, int subLine) {
2227 PRectangle rcSegment = rcLine;
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;
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;
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;
2254 marks >>= 1;
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;
2267 marksMasked >>= 1;
2273 SCNotification scn = {0};
2274 scn.nmhdr.code = SCN_GETBKCOLOR;
2275 scn.line = line;
2276 scn.lParam = -1;
2277 NotifyParent(&scn);
2278 if (scn.lParam != -1)
2280 background = scn.lParam;
2281 overrideBackground = true;
2284 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2285 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2287 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2288 int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2290 int posLineStart = pdoc->LineStart(line);
2292 int startseg = ll->LineStart(subLine);
2293 int subLineStart = ll->positions[startseg];
2294 int lineStart = 0;
2295 int lineEnd = 0;
2296 if (subLine < ll->lines) {
2297 lineStart = ll->LineStart(subLine);
2298 lineEnd = ll->LineStart(subLine + 1);
2301 ColourAllocated wrapColour = vsDraw.styles[STYLE_DEFAULT].fore.allocated;
2302 if (vsDraw.whitespaceForegroundSet)
2303 wrapColour = vsDraw.whitespaceForeground.allocated;
2305 bool drawWrapMarkEnd = false;
2307 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2308 if (subLine + 1 < ll->lines) {
2309 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2313 if (actualWrapVisualStartIndent != 0) {
2315 bool continuedWrapLine = false;
2316 if (subLine < ll->lines) {
2317 continuedWrapLine = ll->LineStart(subLine) != 0;
2320 if (continuedWrapLine) {
2321 // draw continuation rect
2322 PRectangle rcPlace = rcSegment;
2324 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2325 rcPlace.right = rcPlace.left + actualWrapVisualStartIndent * vsDraw.aveCharWidth;
2327 // default bgnd here..
2328 surface->FillRectangle(rcSegment, overrideBackground ? background :
2329 vsDraw.styles[STYLE_DEFAULT].back.allocated);
2331 // main line style would be below but this would be inconsistent with end markers
2332 // also would possibly not be the style at wrap point
2333 //int styleMain = ll->styles[lineStart];
2334 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
2336 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2338 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2339 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2340 else
2341 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2343 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2346 xStart += actualWrapVisualStartIndent * vsDraw.aveCharWidth;
2350 // Does not take margin into account but not significant
2351 int xStartVisible = subLineStart - xStart;
2353 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible);
2354 int next = bfBack.First();
2356 // Background drawing loop
2357 while (twoPhaseDraw && (next < lineEnd)) {
2359 startseg = next;
2360 next = bfBack.Next();
2361 int i = next - 1;
2362 int iDoc = i + posLineStart;
2364 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2365 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2366 // Only try to draw if really visible - enhances performance by not calling environment to
2367 // draw strings that are completely past the right side of the window.
2368 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2369 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2370 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2371 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2373 int styleMain = ll->styles[i];
2374 bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
2375 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2376 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2377 if (ll->chars[i] == '\t') {
2378 // Tab display
2379 if (drawWhitespaceBackground &&
2380 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2381 textBack = vsDraw.whitespaceBackground.allocated;
2382 surface->FillRectangle(rcSegment, textBack);
2383 } else if (IsControlCharacter(ll->chars[i])) {
2384 // Control character display
2385 inIndentation = false;
2386 surface->FillRectangle(rcSegment, textBack);
2387 } else {
2388 // Normal text display
2389 surface->FillRectangle(rcSegment, textBack);
2390 if (vsDraw.viewWhitespace != wsInvisible ||
2391 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
2392 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2393 if (ll->chars[cpos + startseg] == ' ') {
2394 if (drawWhitespaceBackground &&
2395 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2396 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2397 rcSegment.top,
2398 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2399 rcSegment.bottom);
2400 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
2402 } else {
2403 inIndentation = false;
2408 } else if (rcSegment.left > rcLine.right) {
2409 break;
2413 if (twoPhaseDraw) {
2414 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2415 xStart, subLine, subLineStart, overrideBackground, background,
2416 drawWrapMarkEnd, wrapColour);
2419 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
2421 if (vsDraw.edgeState == EDGE_LINE) {
2422 int edgeX = theEdge * vsDraw.spaceWidth;
2423 rcSegment.left = edgeX + xStart;
2424 rcSegment.right = rcSegment.left + 1;
2425 surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
2428 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2429 // Foreground drawing loop
2430 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, IsUnicodeMode(), xStartVisible);
2431 next = bfFore.First();
2433 while (next < lineEnd) {
2435 startseg = next;
2436 next = bfFore.Next();
2437 int i = next - 1;
2439 int iDoc = i + posLineStart;
2441 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2442 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2443 // Only try to draw if really visible - enhances performance by not calling environment to
2444 // draw strings that are completely past the right side of the window.
2445 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2446 int styleMain = ll->styles[i];
2447 ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
2448 Font &textFont = vsDraw.styles[styleMain].font;
2449 //hotspot foreground
2450 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
2451 if (vsDraw.hotspotForegroundSet)
2452 textFore = vsDraw.hotspotForeground.allocated;
2454 bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
2455 if (inSelection && (vsDraw.selforeset)) {
2456 textFore = vsDraw.selforeground.allocated;
2458 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2459 ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2460 if (ll->chars[i] == '\t') {
2461 // Tab display
2462 if (!twoPhaseDraw) {
2463 if (drawWhitespaceBackground &&
2464 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2465 textBack = vsDraw.whitespaceBackground.allocated;
2466 surface->FillRectangle(rcSegment, textBack);
2468 if ((vsDraw.viewWhitespace != wsInvisible) ||
2469 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2470 if (vsDraw.whitespaceForegroundSet)
2471 textFore = vsDraw.whitespaceForeground.allocated;
2472 surface->PenColour(textFore);
2474 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2475 for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
2476 if (xIG >= ll->positions[i] && xIG > 0) {
2477 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
2478 (ll->xHighlightGuide == xIG));
2482 if (vsDraw.viewWhitespace != wsInvisible) {
2483 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2484 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
2485 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
2486 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
2489 } else if (IsControlCharacter(ll->chars[i])) {
2490 // Control character display
2491 inIndentation = false;
2492 if (controlCharSymbol < 32) {
2493 // Draw the character
2494 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
2495 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2496 } else {
2497 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2498 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
2499 rcSegment.top + vsDraw.maxAscent,
2500 cc, 1, textBack, textFore);
2502 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
2503 char hexits[3];
2504 sprintf(hexits, "%2X", ll->chars[i] & 0xff);
2505 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
2506 } else {
2507 // Normal text display
2508 if (vsDraw.styles[styleMain].visible) {
2509 if (twoPhaseDraw) {
2510 surface->DrawTextTransparent(rcSegment, textFont,
2511 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2512 i - startseg + 1, textFore);
2513 } else {
2514 surface->DrawTextNoClip(rcSegment, textFont,
2515 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
2516 i - startseg + 1, textFore, textBack);
2519 if (vsDraw.viewWhitespace != wsInvisible ||
2520 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
2521 for (int cpos = 0; cpos <= i - startseg; cpos++) {
2522 if (ll->chars[cpos + startseg] == ' ') {
2523 if (vsDraw.viewWhitespace != wsInvisible) {
2524 if (vsDraw.whitespaceForegroundSet)
2525 textFore = vsDraw.whitespaceForeground.allocated;
2526 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
2527 int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
2528 if (!twoPhaseDraw && drawWhitespaceBackground &&
2529 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
2530 textBack = vsDraw.whitespaceBackground.allocated;
2531 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
2532 rcSegment.top,
2533 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
2534 rcSegment.bottom);
2535 surface->FillRectangle(rcSpace, textBack);
2537 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
2538 rcDot.right = rcDot.left + 1;
2539 rcDot.bottom = rcDot.top + 1;
2540 surface->FillRectangle(rcDot, textFore);
2543 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
2544 int startSpace = ll->positions[cpos + startseg];
2545 if (startSpace > 0 && (startSpace % indentWidth == 0)) {
2546 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
2547 (ll->xHighlightGuide == ll->positions[cpos + startseg]));
2550 } else {
2551 inIndentation = false;
2556 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) {
2557 PRectangle rcUL = rcSegment;
2558 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2559 rcUL.bottom = rcUL.top + 1;
2560 if (vsDraw.hotspotForegroundSet)
2561 surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
2562 else
2563 surface->FillRectangle(rcUL, textFore);
2564 } else if (vsDraw.styles[styleMain].underline) {
2565 PRectangle rcUL = rcSegment;
2566 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
2567 rcUL.bottom = rcUL.top + 1;
2568 surface->FillRectangle(rcUL, textFore);
2570 } else if (rcSegment.left > rcLine.right) {
2571 break;
2574 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
2575 && (subLine == 0)) {
2576 int indentSpace = pdoc->GetLineIndentation(line);
2577 // Find the most recent line with some text
2579 int lineLastWithText = line;
2580 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
2581 lineLastWithText--;
2583 if (lineLastWithText < line) {
2584 // This line is empty, so use indentation of last line with text
2585 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
2586 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
2587 if (isFoldHeader) {
2588 // Level is one more level than parent
2589 indentLastWithText += pdoc->IndentSize();
2591 if (vsDraw.viewIndentationGuides == ivLookForward) {
2592 // In viLookForward mode, previous line only used if it is a fold header
2593 if (isFoldHeader) {
2594 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
2596 } else { // viLookBoth
2597 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
2601 int lineNextWithText = line;
2602 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
2603 lineNextWithText++;
2605 if (lineNextWithText > line) {
2606 // This line is empty, so use indentation of last line with text
2607 indentSpace = Platform::Maximum(indentSpace,
2608 pdoc->GetLineIndentation(lineNextWithText));
2611 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
2612 int xIndent = indentPos * vsDraw.spaceWidth;
2613 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
2614 (ll->xHighlightGuide == xIndent));
2618 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
2620 // End of the drawing of the current line
2621 if (!twoPhaseDraw) {
2622 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
2623 xStart, subLine, subLineStart, overrideBackground, background,
2624 drawWrapMarkEnd, wrapColour);
2626 if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) && (ll->selStart >= 0) && (ll->selEnd >= 0)) {
2627 int startPosSel = (ll->selStart < posLineStart) ? posLineStart : ll->selStart;
2628 int endPosSel = (ll->selEnd < (lineEnd + posLineStart)) ? ll->selEnd : (lineEnd + posLineStart);
2629 if (startPosSel < endPosSel) {
2630 rcSegment.left = xStart + ll->positions[startPosSel - posLineStart] - subLineStart;
2631 rcSegment.right = xStart + ll->positions[endPosSel - posLineStart] - subLineStart;
2632 rcSegment.left = Platform::Maximum(rcSegment.left, rcLine.left);
2633 rcSegment.right = Platform::Minimum(rcSegment.right, rcLine.right);
2634 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
2638 // Draw any translucent whole line states
2639 rcSegment.left = xStart;
2640 rcSegment.right = rcLine.right - 1;
2641 if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
2642 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
2644 int marks = pdoc->GetMark(line);
2645 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2646 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
2647 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2649 marks >>= 1;
2651 if (vsDraw.maskInLine) {
2652 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2653 if (marksMasked) {
2654 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2655 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
2656 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
2658 marksMasked >>= 1;
2664 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine, int xStart, int offset, int posCaret, PRectangle rcCaret) {
2666 int lineStart = ll->LineStart(subLine);
2667 int posBefore = posCaret;
2668 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
2669 int numCharsToDraw = posAfter - posCaret;
2671 // Work out where the starting and ending offsets are. We need to
2672 // see if the previous character shares horizontal space, such as a
2673 // glyph / combining character. If so we'll need to draw that too.
2674 int offsetFirstChar = offset;
2675 int offsetLastChar = offset + (posAfter - posCaret);
2676 while ((offsetLastChar - numCharsToDraw) >= lineStart) {
2677 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
2678 // The char does not share horizontal space
2679 break;
2681 // Char shares horizontal space, update the numChars to draw
2682 // Update posBefore to point to the prev char
2683 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
2684 numCharsToDraw = posAfter - posBefore;
2685 offsetFirstChar = offset - (posCaret - posBefore);
2688 // See if the next character shares horizontal space, if so we'll
2689 // need to draw that too.
2690 numCharsToDraw = offsetLastChar - offsetFirstChar;
2691 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
2692 // Update posAfter to point to the 2nd next char, this is where
2693 // the next character ends, and 2nd next begins. We'll need
2694 // to compare these two
2695 posBefore = posAfter;
2696 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
2697 offsetLastChar = offset + (posAfter - posCaret);
2698 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
2699 // The char does not share horizontal space
2700 break;
2702 // Char shares horizontal space, update the numChars to draw
2703 numCharsToDraw = offsetLastChar - offsetFirstChar;
2706 // We now know what to draw, update the caret drawing rectangle
2707 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[ll->LineStart(subLine)] + xStart;
2708 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[ll->LineStart(subLine)] + xStart;
2710 // This character is where the caret block is, we override the colours
2711 // (inversed) for drawing the caret here.
2712 int styleMain = ll->styles[offsetFirstChar];
2713 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
2714 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
2715 numCharsToDraw, vsDraw.styles[styleMain].back.allocated,
2716 vsDraw.caretcolour.allocated);
2719 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
2720 if (!pixmapSelPattern->Initialised()) {
2721 const int patternSize = 8;
2722 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
2723 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
2724 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
2725 // way between the chrome colour and the chrome highlight colour making a nice transition
2726 // between the window chrome and the content area. And it works in low colour depths.
2727 PRectangle rcPattern(0, 0, patternSize, patternSize);
2729 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
2730 ColourAllocated colourFMFill = vs.selbar.allocated;
2731 ColourAllocated colourFMStripes = vs.selbarlight.allocated;
2733 if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
2734 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
2735 // (Typically, the highlight colour is white.)
2736 colourFMFill = vs.selbarlight.allocated;
2739 if (vs.foldmarginColourSet) {
2740 // override default fold margin colour
2741 colourFMFill = vs.foldmarginColour.allocated;
2743 if (vs.foldmarginHighlightColourSet) {
2744 // override default fold margin highlight colour
2745 colourFMStripes = vs.foldmarginHighlightColour.allocated;
2748 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
2749 pixmapSelPattern->PenColour(colourFMStripes);
2750 for (int stripe = 0; stripe < patternSize; stripe++) {
2751 // Alternating 1 pixel stripes is same as checkerboard.
2752 pixmapSelPattern->MoveTo(0, stripe * 2);
2753 pixmapSelPattern->LineTo(patternSize, stripe * 2 - patternSize);
2757 if (!pixmapIndentGuide->Initialised()) {
2758 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
2759 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
2760 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
2761 PRectangle rcIG(0, 0, 1, vs.lineHeight);
2762 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
2763 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
2764 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
2765 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
2766 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
2767 pixmapIndentGuide->MoveTo(0, stripe);
2768 pixmapIndentGuide->LineTo(2, stripe);
2769 pixmapIndentGuideHighlight->MoveTo(0, stripe);
2770 pixmapIndentGuideHighlight->LineTo(2, stripe);
2774 if (bufferedDraw) {
2775 if (!pixmapLine->Initialised()) {
2776 PRectangle rcClient = GetClientRectangle();
2777 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
2778 surfaceWindow, wMain.GetID());
2779 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
2780 rcClient.Height(), surfaceWindow, wMain.GetID());
2785 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
2786 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
2787 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
2789 pixmapLine->Release();
2790 RefreshStyleData();
2791 RefreshPixMaps(surfaceWindow);
2793 PRectangle rcClient = GetClientRectangle();
2794 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
2795 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
2797 surfaceWindow->SetPalette(&palette, true);
2798 pixmapLine->SetPalette(&palette, !hasFocus);
2800 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
2801 // The area to be painted plus one extra line is styled.
2802 // The extra line is to determine when a style change, such as starting a comment flows on to other lines.
2803 int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
2804 //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
2805 int endPosPaint = pdoc->Length();
2806 if (lineStyleLast < cs.LinesDisplayed())
2807 endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast + 1));
2809 int xStart = vs.fixedColumnWidth - xOffset;
2810 int ypos = 0;
2811 if (!bufferedDraw)
2812 ypos += screenLinePaintFirst * vs.lineHeight;
2813 int yposScreen = screenLinePaintFirst * vs.lineHeight;
2815 // Ensure we are styled as far as we are painting.
2816 pdoc->EnsureStyledTo(endPosPaint);
2817 bool paintAbandonedByStyling = paintState == paintAbandoned;
2818 if (needUpdateUI) {
2819 // Deselect palette by selecting a temporary palette
2820 Palette palTemp;
2821 surfaceWindow->SetPalette(&palTemp, true);
2823 NotifyUpdateUI();
2824 needUpdateUI = false;
2826 RefreshStyleData();
2827 RefreshPixMaps(surfaceWindow);
2828 surfaceWindow->SetPalette(&palette, true);
2829 pixmapLine->SetPalette(&palette, !hasFocus);
2832 // Call priority lines wrap on a window of lines which are likely
2833 // to rendered with the following paint (that is wrap the visible
2834 // lines first).
2835 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
2836 if (startLineToWrap < 0)
2837 startLineToWrap = -1;
2838 if (WrapLines(false, startLineToWrap)) {
2839 // The wrapping process has changed the height of some lines so
2840 // abandon this paint for a complete repaint.
2841 if (AbandonPaint()) {
2842 return;
2844 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
2846 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
2848 PaintSelMargin(surfaceWindow, rcArea);
2850 PRectangle rcRightMargin = rcClient;
2851 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
2852 if (rcArea.Intersects(rcRightMargin)) {
2853 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
2856 if (paintState == paintAbandoned) {
2857 // Either styling or NotifyUpdateUI noticed that painting is needed
2858 // outside the current painting rectangle
2859 //Platform::DebugPrintf("Abandoning paint\n");
2860 if (wrapState != eWrapNone) {
2861 if (paintAbandonedByStyling) {
2862 // Styling has spilled over a line end, such as occurs by starting a multiline
2863 // comment. The width of subsequent text may have changed, so rewrap.
2864 NeedWrapping(cs.DocFromDisplay(topLine));
2867 return;
2869 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
2871 // Do the painting
2872 if (rcArea.right > vs.fixedColumnWidth) {
2874 Surface *surface = surfaceWindow;
2875 if (bufferedDraw) {
2876 surface = pixmapLine;
2877 PLATFORM_ASSERT(pixmapLine->Initialised());
2879 surface->SetUnicodeMode(IsUnicodeMode());
2880 surface->SetDBCSMode(CodePage());
2882 int visibleLine = topLine + screenLinePaintFirst;
2884 int posCaret = currentPos;
2885 if (posDrag >= 0)
2886 posCaret = posDrag;
2887 int lineCaret = pdoc->LineFromPosition(posCaret);
2889 // Remove selection margin from drawing area so text will not be drawn
2890 // on it in unbuffered mode.
2891 PRectangle rcTextArea = rcClient;
2892 rcTextArea.left = vs.fixedColumnWidth;
2893 rcTextArea.right -= vs.rightMarginWidth;
2894 surfaceWindow->SetClip(rcTextArea);
2896 // Loop on visible lines
2897 //double durLayout = 0.0;
2898 //double durPaint = 0.0;
2899 //double durCopy = 0.0;
2900 //ElapsedTime etWhole;
2901 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
2902 AutoLineLayout ll(llc, 0);
2903 SelectionLineIterator lineIterator(this);
2904 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
2906 int lineDoc = cs.DocFromDisplay(visibleLine);
2907 // Only visible lines should be handled by the code within the loop
2908 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
2909 int lineStartSet = cs.DisplayFromDoc(lineDoc);
2910 int subLine = visibleLine - lineStartSet;
2912 // Copy this line and its styles from the document into local arrays
2913 // and determine the x position at which each character starts.
2914 //ElapsedTime et;
2915 if (lineDoc != lineDocPrevious) {
2916 ll.Set(0);
2917 // For rectangular selection this accesses the layout cache so should be after layout returned.
2918 lineIterator.SetAt(lineDoc);
2919 ll.Set(RetrieveLineLayout(lineDoc));
2920 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
2921 lineDocPrevious = lineDoc;
2923 //durLayout += et.Duration(true);
2925 if (ll) {
2926 if (selType == selStream) {
2927 ll->selStart = SelectionStart();
2928 ll->selEnd = SelectionEnd();
2929 } else {
2930 ll->selStart = lineIterator.startPos;
2931 ll->selEnd = lineIterator.endPos;
2933 ll->containsCaret = lineDoc == lineCaret;
2934 if (hideSelection) {
2935 ll->selStart = -1;
2936 ll->selEnd = -1;
2937 ll->containsCaret = false;
2940 GetHotSpotRange(ll->hsStart, ll->hsEnd);
2942 PRectangle rcLine = rcClient;
2943 rcLine.top = ypos;
2944 rcLine.bottom = ypos + vs.lineHeight;
2946 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
2947 // Highlight the current braces if any
2948 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
2949 highlightGuideColumn * vs.spaceWidth);
2951 // Draw the line
2952 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
2953 //durPaint += et.Duration(true);
2955 // Restore the previous styles for the brace highlights in case layout is in cache.
2956 ll->RestoreBracesHighlight(rangeLine, braces);
2958 bool expanded = cs.GetExpanded(lineDoc);
2959 if ((foldFlags & SC_FOLDFLAG_BOX) == 0) {
2960 // Paint the line above the fold
2961 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
2963 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
2964 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
2965 PRectangle rcFoldLine = rcLine;
2966 rcFoldLine.bottom = rcFoldLine.top + 1;
2967 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
2970 // Paint the line below the fold
2971 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
2973 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
2974 if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
2975 PRectangle rcFoldLine = rcLine;
2976 rcFoldLine.top = rcFoldLine.bottom - 1;
2977 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
2980 } else {
2981 int FoldLevelCurr = (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE;
2982 int FoldLevelPrev = (pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE;
2983 int FoldLevelFlags = (pdoc->GetLevel(lineDoc) & ~SC_FOLDLEVELNUMBERMASK) & ~(0xFFF0000);
2984 int indentationStep = pdoc->IndentSize();
2985 // Draw line above fold
2986 if ((FoldLevelPrev < FoldLevelCurr)
2988 (FoldLevelFlags & SC_FOLDLEVELBOXHEADERFLAG
2990 (pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELBOXFOOTERFLAG) == 0)) {
2991 PRectangle rcFoldLine = rcLine;
2992 rcFoldLine.bottom = rcFoldLine.top + 1;
2993 rcFoldLine.left += xStart + FoldLevelCurr * vs.spaceWidth * indentationStep - 1;
2994 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
2997 // Line below the fold (or below a contracted fold)
2998 if (FoldLevelFlags & SC_FOLDLEVELBOXFOOTERFLAG
3000 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3001 PRectangle rcFoldLine = rcLine;
3002 rcFoldLine.top = rcFoldLine.bottom - 1;
3003 rcFoldLine.left += xStart + (FoldLevelCurr) * vs.spaceWidth * indentationStep - 1;
3004 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3007 PRectangle rcBoxLine = rcLine;
3008 // Draw vertical line for every fold level
3009 for (int i = 0; i <= FoldLevelCurr; i++) {
3010 rcBoxLine.left = xStart + i * vs.spaceWidth * indentationStep - 1;
3011 rcBoxLine.right = rcBoxLine.left + 1;
3012 surface->FillRectangle(rcBoxLine, vs.styles[STYLE_DEFAULT].fore.allocated);
3016 // Draw the Caret
3017 if (lineDoc == lineCaret) {
3018 int offset = Platform::Minimum(posCaret - rangeLine.start, ll->maxLineLength);
3019 if (ll->InLine(offset, subLine)) {
3020 int xposCaret = ll->positions[offset] - ll->positions[ll->LineStart(subLine)] + xStart;
3022 if (actualWrapVisualStartIndent != 0) {
3023 int lineStart = ll->LineStart(subLine);
3024 if (lineStart != 0) // Wrapped
3025 xposCaret += actualWrapVisualStartIndent * vs.aveCharWidth;
3027 if ((xposCaret >= 0) && (vs.caretWidth > 0) && (vs.caretStyle != CARETSTYLE_INVISIBLE) &&
3028 ((posDrag >= 0) || (caret.active && caret.on))) {
3029 bool caretAtEOF = false;
3030 bool caretAtEOL = false;
3031 bool drawBlockCaret = false;
3032 int widthOverstrikeCaret;
3033 int caretWidthOffset = 0;
3034 PRectangle rcCaret = rcLine;
3036 if (posCaret == pdoc->Length()) { // At end of document
3037 caretAtEOF = true;
3038 widthOverstrikeCaret = vs.aveCharWidth;
3039 } else if ((posCaret - rangeLine.start) >= ll->numCharsInLine) { // At end of line
3040 caretAtEOL = true;
3041 widthOverstrikeCaret = vs.aveCharWidth;
3042 } else {
3043 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3045 if (widthOverstrikeCaret < 3) // Make sure its visible
3046 widthOverstrikeCaret = 3;
3048 if (offset > ll->LineStart(subLine))
3049 caretWidthOffset = 1; // Move back so overlaps both character cells.
3050 if (posDrag >= 0) {
3051 /* Dragging text, use a line caret */
3052 rcCaret.left = xposCaret - caretWidthOffset;
3053 rcCaret.right = rcCaret.left + vs.caretWidth;
3054 } else if (inOverstrike) {
3055 /* Overstrike (insert mode), use a modified bar caret */
3056 rcCaret.top = rcCaret.bottom - 2;
3057 rcCaret.left = xposCaret + 1;
3058 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3059 } else if (vs.caretStyle == CARETSTYLE_BLOCK) {
3060 /* Block caret */
3061 rcCaret.left = xposCaret;
3062 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3063 drawBlockCaret = true;
3064 rcCaret.right = xposCaret + widthOverstrikeCaret;
3065 } else {
3066 rcCaret.right = xposCaret + vs.aveCharWidth;
3068 } else {
3069 /* Line caret */
3070 rcCaret.left = xposCaret - caretWidthOffset;
3071 rcCaret.right = rcCaret.left + vs.caretWidth;
3073 if (drawBlockCaret) {
3074 DrawBlockCaret(surface, vs, ll, subLine, xStart, offset, posCaret, rcCaret);
3075 } else {
3076 surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
3082 if (bufferedDraw) {
3083 Point from(vs.fixedColumnWidth, 0);
3084 PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
3085 rcClient.right, yposScreen + vs.lineHeight);
3086 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3088 //durCopy += et.Duration(true);
3091 if (!bufferedDraw) {
3092 ypos += vs.lineHeight;
3095 yposScreen += vs.lineHeight;
3096 visibleLine++;
3098 lineWidthMaxSeen = Platform::Maximum(
3099 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3100 //gdk_flush();
3102 ll.Set(0);
3103 //if (durPaint < 0.00000001)
3104 // durPaint = 0.00000001;
3106 // Right column limit indicator
3107 PRectangle rcBeyondEOF = rcClient;
3108 rcBeyondEOF.left = vs.fixedColumnWidth;
3109 rcBeyondEOF.right = rcBeyondEOF.right;
3110 rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
3111 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3112 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
3113 if (vs.edgeState == EDGE_LINE) {
3114 int edgeX = theEdge * vs.spaceWidth;
3115 rcBeyondEOF.left = edgeX + xStart;
3116 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3117 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
3120 //Platform::DebugPrintf(
3121 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3122 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3123 NotifyPainted();
3127 // Space (3 space characters) between line numbers and text when printing.
3128 #define lineNumberPrintSpace " "
3130 ColourDesired InvertedLight(ColourDesired orig) {
3131 unsigned int r = orig.GetRed();
3132 unsigned int g = orig.GetGreen();
3133 unsigned int b = orig.GetBlue();
3134 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3135 unsigned int il = 0xff - l;
3136 if (l == 0)
3137 return ColourDesired(0xff, 0xff, 0xff);
3138 r = r * il / l;
3139 g = g * il / l;
3140 b = b * il / l;
3141 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3144 // This is mostly copied from the Paint method but with some things omitted
3145 // such as the margin markers, line numbers, selection and caret
3146 // Should be merged back into a combined Draw method.
3147 long Editor::FormatRange(bool draw, RangeToFormat *pfr) {
3148 if (!pfr)
3149 return 0;
3151 AutoSurface surface(pfr->hdc, this);
3152 if (!surface)
3153 return 0;
3154 AutoSurface surfaceMeasure(pfr->hdcTarget, this);
3155 if (!surfaceMeasure) {
3156 return 0;
3159 // Can't use measurements cached for screen
3160 posCache.Clear();
3162 ViewStyle vsPrint(vs);
3164 // Modify the view style for printing as do not normally want any of the transient features to be printed
3165 // Printing supports only the line number margin.
3166 int lineNumberIndex = -1;
3167 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3168 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3169 lineNumberIndex = margin;
3170 } else {
3171 vsPrint.ms[margin].width = 0;
3174 vsPrint.showMarkedLines = false;
3175 vsPrint.fixedColumnWidth = 0;
3176 vsPrint.zoomLevel = printMagnification;
3177 vsPrint.viewIndentationGuides = ivNone;
3178 // Don't show the selection when printing
3179 vsPrint.selbackset = false;
3180 vsPrint.selforeset = false;
3181 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3182 vsPrint.whitespaceBackgroundSet = false;
3183 vsPrint.whitespaceForegroundSet = false;
3184 vsPrint.showCaretLineBackground = false;
3186 // Set colours for printing according to users settings
3187 for (size_t sty = 0;sty < vsPrint.stylesSize;sty++) {
3188 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3189 vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
3190 vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
3191 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3192 vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
3193 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3194 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3195 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3196 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3197 if (sty <= STYLE_DEFAULT) {
3198 vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
3202 // White background for the line numbers
3203 vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
3205 vsPrint.Refresh(*surfaceMeasure);
3206 // Determining width must hapen after fonts have been realised in Refresh
3207 int lineNumberWidth = 0;
3208 if (lineNumberIndex >= 0) {
3209 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3210 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3211 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3212 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3214 // Ensure colours are set up
3215 vsPrint.RefreshColourPalette(palette, true);
3216 vsPrint.RefreshColourPalette(palette, false);
3218 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3219 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3220 if (linePrintLast < linePrintStart)
3221 linePrintLast = linePrintStart;
3222 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3223 if (linePrintLast > linePrintMax)
3224 linePrintLast = linePrintMax;
3225 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3226 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3227 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3228 int endPosPrint = pdoc->Length();
3229 if (linePrintLast < pdoc->LinesTotal())
3230 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3232 // Ensure we are styled to where we are formatting.
3233 pdoc->EnsureStyledTo(endPosPrint);
3235 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3236 int ypos = pfr->rc.top;
3238 int lineDoc = linePrintStart;
3240 int nPrintPos = pfr->chrg.cpMin;
3241 int visibleLine = 0;
3242 int widthPrint = pfr->rc.Width() - vsPrint.fixedColumnWidth;
3243 if (printWrapState == eWrapNone)
3244 widthPrint = LineLayout::wrapWidthInfinite;
3246 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3248 // When printing, the hdc and hdcTarget may be the same, so
3249 // changing the state of surfaceMeasure may change the underlying
3250 // state of surface. Therefore, any cached state is discarded before
3251 // using each surface.
3252 surfaceMeasure->FlushCachedState();
3254 // Copy this line and its styles from the document into local arrays
3255 // and determine the x position at which each character starts.
3256 LineLayout ll(8000);
3257 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3259 ll.selStart = -1;
3260 ll.selEnd = -1;
3261 ll.containsCaret = false;
3263 PRectangle rcLine;
3264 rcLine.left = pfr->rc.left;
3265 rcLine.top = ypos;
3266 rcLine.right = pfr->rc.right - 1;
3267 rcLine.bottom = ypos + vsPrint.lineHeight;
3269 // When document line is wrapped over multiple display lines, find where
3270 // to start printing from to ensure a particular position is on the first
3271 // line of the page.
3272 if (visibleLine == 0) {
3273 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3274 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3275 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3276 visibleLine = -iwl;
3280 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3281 visibleLine = -(ll.lines - 1);
3285 if (draw && lineNumberWidth &&
3286 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3287 (visibleLine >= 0)) {
3288 char number[100];
3289 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3290 PRectangle rcNumber = rcLine;
3291 rcNumber.right = rcNumber.left + lineNumberWidth;
3292 // Right justify
3293 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3294 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3295 surface->FlushCachedState();
3296 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3297 ypos + vsPrint.maxAscent, number, istrlen(number),
3298 vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
3299 vsPrint.styles[STYLE_LINENUMBER].back.allocated);
3302 // Draw the line
3303 surface->FlushCachedState();
3305 for (int iwl = 0; iwl < ll.lines; iwl++) {
3306 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3307 if (visibleLine >= 0) {
3308 if (draw) {
3309 rcLine.top = ypos;
3310 rcLine.bottom = ypos + vsPrint.lineHeight;
3311 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3313 ypos += vsPrint.lineHeight;
3315 visibleLine++;
3316 if (iwl == ll.lines - 1)
3317 nPrintPos = pdoc->LineStart(lineDoc + 1);
3318 else
3319 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3323 ++lineDoc;
3326 // Clear cache so measurements are not used for screen
3327 posCache.Clear();
3329 return nPrintPos;
3332 int Editor::TextWidth(int style, const char *text) {
3333 RefreshStyleData();
3334 AutoSurface surface(this);
3335 if (surface) {
3336 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
3337 } else {
3338 return 1;
3342 // Empty method is overridden on GTK+ to show / hide scrollbars
3343 void Editor::ReconfigureScrollBars() {}
3345 void Editor::SetScrollBars() {
3346 RefreshStyleData();
3348 int nMax = MaxScrollPos();
3349 int nPage = LinesOnScreen();
3350 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
3351 if (modified) {
3352 DwellEnd(true);
3355 // TODO: ensure always showing as many lines as possible
3356 // May not be, if, for example, window made larger
3357 if (topLine > MaxScrollPos()) {
3358 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
3359 SetVerticalScrollPos();
3360 Redraw();
3362 if (modified) {
3363 if (!AbandonPaint())
3364 Redraw();
3366 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
3369 void Editor::ChangeSize() {
3370 DropGraphics();
3371 SetScrollBars();
3372 if (wrapState != eWrapNone) {
3373 PRectangle rcTextArea = GetClientRectangle();
3374 rcTextArea.left = vs.fixedColumnWidth;
3375 rcTextArea.right -= vs.rightMarginWidth;
3376 if (wrapWidth != rcTextArea.Width()) {
3377 NeedWrapping();
3378 Redraw();
3383 void Editor::AddChar(char ch) {
3384 char s[2];
3385 s[0] = ch;
3386 s[1] = '\0';
3387 AddCharUTF(s, 1);
3390 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
3391 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
3392 bool wasSelection = currentPos != anchor;
3393 ClearSelection();
3394 bool charReplaceAction = false;
3395 if (inOverstrike && !wasSelection && !RangeContainsProtected(currentPos, currentPos + 1)) {
3396 if (currentPos < (pdoc->Length())) {
3397 if (!IsEOLChar(pdoc->CharAt(currentPos))) {
3398 charReplaceAction = true;
3399 pdoc->BeginUndoAction();
3400 pdoc->DelChar(currentPos);
3404 if (pdoc->InsertString(currentPos, s, len)) {
3405 SetEmptySelection(currentPos + len);
3407 if (charReplaceAction) {
3408 pdoc->EndUndoAction();
3410 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
3411 if (wrapState != eWrapNone) {
3412 AutoSurface surface(this);
3413 if (surface) {
3414 WrapOneLine(surface, pdoc->LineFromPosition(currentPos));
3416 SetScrollBars();
3418 EnsureCaretVisible();
3419 // Avoid blinking during rapid typing:
3420 ShowCaretAtCurrentPosition();
3421 if (!caretSticky) {
3422 SetLastXChosen();
3425 if (treatAsDBCS) {
3426 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
3427 static_cast<unsigned char>(s[1]));
3428 } else {
3429 int byte = static_cast<unsigned char>(s[0]);
3430 if ((byte < 0xC0) || (1 == len)) {
3431 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
3432 // characters when not in UTF-8 mode.
3433 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
3434 // characters representing themselves.
3435 } else {
3436 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
3437 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
3438 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
3439 if (byte < 0xE0) {
3440 int byte2 = static_cast<unsigned char>(s[1]);
3441 if ((byte2 & 0xC0) == 0x80) {
3442 // Two-byte-character lead-byte followed by a trail-byte.
3443 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
3445 // A two-byte-character lead-byte not followed by trail-byte
3446 // represents itself.
3447 } else if (byte < 0xF0) {
3448 int byte2 = static_cast<unsigned char>(s[1]);
3449 int byte3 = static_cast<unsigned char>(s[2]);
3450 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
3451 // Three-byte-character lead byte followed by two trail bytes.
3452 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
3453 (byte3 & 0x3F));
3455 // A three-byte-character lead-byte not followed by two trail-bytes
3456 // represents itself.
3459 NotifyChar(byte);
3463 void Editor::ClearSelection() {
3464 if (!SelectionContainsProtected()) {
3465 int startPos = SelectionStart();
3466 if (selType == selStream) {
3467 unsigned int chars = SelectionEnd() - startPos;
3468 if (0 != chars) {
3469 pdoc->BeginUndoAction();
3470 pdoc->DeleteChars(startPos, chars);
3471 pdoc->EndUndoAction();
3473 } else {
3474 pdoc->BeginUndoAction();
3475 SelectionLineIterator lineIterator(this, false);
3476 while (lineIterator.Iterate()) {
3477 startPos = lineIterator.startPos;
3478 unsigned int chars = lineIterator.endPos - startPos;
3479 if (0 != chars) {
3480 pdoc->DeleteChars(startPos, chars);
3483 pdoc->EndUndoAction();
3484 selType = selStream;
3486 SetEmptySelection(startPos);
3490 void Editor::ClearAll() {
3491 pdoc->BeginUndoAction();
3492 if (0 != pdoc->Length()) {
3493 pdoc->DeleteChars(0, pdoc->Length());
3495 if (!pdoc->IsReadOnly()) {
3496 cs.Clear();
3498 pdoc->EndUndoAction();
3499 anchor = 0;
3500 currentPos = 0;
3501 SetTopLine(0);
3502 SetVerticalScrollPos();
3503 InvalidateStyleRedraw();
3506 void Editor::ClearDocumentStyle() {
3507 Decoration *deco = pdoc->decorations.root;
3508 while (deco) {
3509 // Save next in case deco deleted
3510 Decoration *decoNext = deco->next;
3511 if (deco->indicator < INDIC_CONTAINER) {
3512 pdoc->decorations.SetCurrentIndicator(deco->indicator);
3513 pdoc->DecorationFillRange(0, 0, pdoc->Length());
3515 deco = decoNext;
3517 pdoc->StartStyling(0, '\377');
3518 pdoc->SetStyleFor(pdoc->Length(), 0);
3519 cs.ShowAll();
3520 pdoc->ClearLevels();
3523 void Editor::CopyAllowLine() {
3524 SelectionText selectedText;
3525 CopySelectionRange(&selectedText, true);
3526 CopyToClipboard(selectedText);
3529 void Editor::Cut() {
3530 pdoc->CheckReadOnly();
3531 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
3532 Copy();
3533 ClearSelection();
3537 void Editor::PasteRectangular(int pos, const char *ptr, int len) {
3538 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
3539 return;
3541 currentPos = pos;
3542 int xInsert = XFromPosition(currentPos);
3543 int line = pdoc->LineFromPosition(currentPos);
3544 bool prevCr = false;
3545 pdoc->BeginUndoAction();
3546 for (int i = 0; i < len; i++) {
3547 if (IsEOLChar(ptr[i])) {
3548 if ((ptr[i] == '\r') || (!prevCr))
3549 line++;
3550 if (line >= pdoc->LinesTotal()) {
3551 if (pdoc->eolMode != SC_EOL_LF)
3552 pdoc->InsertChar(pdoc->Length(), '\r');
3553 if (pdoc->eolMode != SC_EOL_CR)
3554 pdoc->InsertChar(pdoc->Length(), '\n');
3556 // Pad the end of lines with spaces if required
3557 currentPos = PositionFromLineX(line, xInsert);
3558 if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) {
3559 for (int i = 0; i < xInsert - XFromPosition(currentPos); i++) {
3560 pdoc->InsertChar(currentPos, ' ');
3561 currentPos++;
3564 prevCr = ptr[i] == '\r';
3565 } else {
3566 pdoc->InsertString(currentPos, ptr + i, 1);
3567 currentPos++;
3568 prevCr = false;
3571 pdoc->EndUndoAction();
3572 SetEmptySelection(pos);
3575 bool Editor::CanPaste() {
3576 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
3579 void Editor::Clear() {
3580 if (currentPos == anchor) {
3581 if (!RangeContainsProtected(currentPos, currentPos + 1)) {
3582 DelChar();
3584 } else {
3585 ClearSelection();
3587 SetEmptySelection(currentPos);
3590 void Editor::SelectAll() {
3591 SetSelection(0, pdoc->Length());
3592 Redraw();
3595 void Editor::Undo() {
3596 if (pdoc->CanUndo()) {
3597 InvalidateCaret();
3598 int newPos = pdoc->Undo();
3599 if (newPos >= 0)
3600 SetEmptySelection(newPos);
3601 EnsureCaretVisible();
3605 void Editor::Redo() {
3606 if (pdoc->CanRedo()) {
3607 int newPos = pdoc->Redo();
3608 if (newPos >= 0)
3609 SetEmptySelection(newPos);
3610 EnsureCaretVisible();
3614 void Editor::DelChar() {
3615 if (!RangeContainsProtected(currentPos, currentPos + 1)) {
3616 pdoc->DelChar(currentPos);
3618 // Avoid blinking during rapid typing:
3619 ShowCaretAtCurrentPosition();
3622 void Editor::DelCharBack(bool allowLineStartDeletion) {
3623 if (currentPos == anchor) {
3624 if (!RangeContainsProtected(currentPos - 1, currentPos)) {
3625 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
3626 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != currentPos)) {
3627 if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
3628 pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) {
3629 pdoc->BeginUndoAction();
3630 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
3631 int indentationStep = pdoc->IndentSize();
3632 if (indentation % indentationStep == 0) {
3633 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
3634 } else {
3635 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
3637 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
3638 pdoc->EndUndoAction();
3639 } else {
3640 pdoc->DelCharBack(currentPos);
3644 } else {
3645 ClearSelection();
3646 SetEmptySelection(currentPos);
3648 // Avoid blinking during rapid typing:
3649 ShowCaretAtCurrentPosition();
3652 void Editor::NotifyFocus(bool) {}
3654 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
3655 SCNotification scn = {0};
3656 scn.nmhdr.code = SCN_STYLENEEDED;
3657 scn.position = endStyleNeeded;
3658 NotifyParent(scn);
3661 void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
3662 NotifyStyleToNeeded(endStyleNeeded);
3665 void Editor::NotifyChar(int ch) {
3666 SCNotification scn = {0};
3667 scn.nmhdr.code = SCN_CHARADDED;
3668 scn.ch = ch;
3669 NotifyParent(scn);
3670 if (recordingMacro) {
3671 char txt[2];
3672 txt[0] = static_cast<char>(ch);
3673 txt[1] = '\0';
3674 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
3678 void Editor::NotifySavePoint(bool isSavePoint) {
3679 SCNotification scn = {0};
3680 if (isSavePoint) {
3681 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
3682 } else {
3683 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
3685 NotifyParent(scn);
3688 void Editor::NotifyModifyAttempt() {
3689 SCNotification scn = {0};
3690 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
3691 NotifyParent(scn);
3694 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
3695 SCNotification scn = {0};
3696 scn.nmhdr.code = SCN_DOUBLECLICK;
3697 scn.line = LineFromLocation(pt);
3698 scn.position = PositionFromLocationClose(pt);
3699 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
3700 (alt ? SCI_ALT : 0);
3701 NotifyParent(scn);
3704 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
3705 SCNotification scn = {0};
3706 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
3707 scn.position = position;
3708 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
3709 (alt ? SCI_ALT : 0);
3710 NotifyParent(scn);
3713 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
3714 SCNotification scn = {0};
3715 scn.nmhdr.code = SCN_HOTSPOTCLICK;
3716 scn.position = position;
3717 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
3718 (alt ? SCI_ALT : 0);
3719 NotifyParent(scn);
3722 void Editor::NotifyUpdateUI() {
3723 SCNotification scn = {0};
3724 scn.nmhdr.code = SCN_UPDATEUI;
3725 NotifyParent(scn);
3728 void Editor::NotifyPainted() {
3729 SCNotification scn = {0};
3730 scn.nmhdr.code = SCN_PAINTED;
3731 NotifyParent(scn);
3734 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
3735 int mask = pdoc->decorations.AllOnFor(position);
3736 if ((click && mask) || pdoc->decorations.clickNotified) {
3737 SCNotification scn = {0};
3738 pdoc->decorations.clickNotified = click;
3739 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
3740 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
3741 scn.position = position;
3742 NotifyParent(scn);
3746 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
3747 int marginClicked = -1;
3748 int x = 0;
3749 for (int margin = 0; margin < ViewStyle::margins; margin++) {
3750 if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
3751 marginClicked = margin;
3752 x += vs.ms[margin].width;
3754 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
3755 SCNotification scn = {0};
3756 scn.nmhdr.code = SCN_MARGINCLICK;
3757 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
3758 (alt ? SCI_ALT : 0);
3759 scn.position = pdoc->LineStart(LineFromLocation(pt));
3760 scn.margin = marginClicked;
3761 NotifyParent(scn);
3762 return true;
3763 } else {
3764 return false;
3768 void Editor::NotifyNeedShown(int pos, int len) {
3769 SCNotification scn = {0};
3770 scn.nmhdr.code = SCN_NEEDSHOWN;
3771 scn.position = pos;
3772 scn.length = len;
3773 NotifyParent(scn);
3776 void Editor::NotifyDwelling(Point pt, bool state) {
3777 SCNotification scn = {0};
3778 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
3779 scn.position = PositionFromLocationClose(pt);
3780 scn.x = pt.x;
3781 scn.y = pt.y;
3782 NotifyParent(scn);
3785 void Editor::NotifyZoom() {
3786 SCNotification scn = {0};
3787 scn.nmhdr.code = SCN_ZOOM;
3788 NotifyParent(scn);
3791 // Notifications from document
3792 void Editor::NotifyModifyAttempt(Document*, void *) {
3793 //Platform::DebugPrintf("** Modify Attempt\n");
3794 NotifyModifyAttempt();
3797 void Editor::NotifyMove(int position) {
3798 SCNotification scn = {0};
3799 scn.nmhdr.code = SCN_POSCHANGED;
3800 scn.position = position;
3801 NotifyParent(scn);
3804 void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
3805 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
3806 NotifySavePoint(atSavePoint);
3809 void Editor::CheckModificationForWrap(DocModification mh) {
3810 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
3811 llc.Invalidate(LineLayout::llCheckTextAndStyle);
3812 if (wrapState != eWrapNone) {
3813 int lineDoc = pdoc->LineFromPosition(mh.position);
3814 int lines = Platform::Maximum(0, mh.linesAdded);
3815 NeedWrapping(lineDoc, lineDoc + lines + 1);
3820 // Move a position so it is still after the same character as before the insertion.
3821 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
3822 if (position > startInsertion) {
3823 return position + length;
3825 return position;
3828 // Move a position so it is still after the same character as before the deletion if that
3829 // character is still present else after the previous surviving character.
3830 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
3831 if (position > startDeletion) {
3832 int endDeletion = startDeletion + length;
3833 if (position > endDeletion) {
3834 return position - length;
3835 } else {
3836 return startDeletion;
3838 } else {
3839 return position;
3843 void Editor::NotifyModified(Document*, DocModification mh, void *) {
3844 needUpdateUI = true;
3845 if (paintState == painting) {
3846 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
3848 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
3849 if (paintState == painting) {
3850 CheckForChangeOutsidePaint(
3851 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
3852 } else {
3853 // Could check that change is before last visible line.
3854 Redraw();
3857 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
3858 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
3859 pdoc->IncrementStyleClock();
3861 if (paintState == notPainting) {
3862 if (mh.position < pdoc->LineStart(topLine)) {
3863 // Styling performed before this view
3864 Redraw();
3865 } else {
3866 InvalidateRange(mh.position, mh.position + mh.length);
3869 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
3870 llc.Invalidate(LineLayout::llCheckTextAndStyle);
3872 } else {
3873 // Move selection and brace highlights
3874 if (mh.modificationType & SC_MOD_INSERTTEXT) {
3875 currentPos = MovePositionForInsertion(currentPos, mh.position, mh.length);
3876 anchor = MovePositionForInsertion(anchor, mh.position, mh.length);
3877 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
3878 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
3879 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
3880 currentPos = MovePositionForDeletion(currentPos, mh.position, mh.length);
3881 anchor = MovePositionForDeletion(anchor, mh.position, mh.length);
3882 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
3883 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
3885 if (cs.LinesDisplayed() < cs.LinesInDoc()) {
3886 // Some lines are hidden so may need shown.
3887 // TODO: check if the modified area is hidden.
3888 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
3889 NotifyNeedShown(mh.position, 0);
3890 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
3891 NotifyNeedShown(mh.position, mh.length);
3894 if (mh.linesAdded != 0) {
3895 // Update contraction state for inserted and removed lines
3896 // lineOfPos should be calculated in context of state before modification, shouldn't it
3897 int lineOfPos = pdoc->LineFromPosition(mh.position);
3898 if (mh.linesAdded > 0) {
3899 cs.InsertLines(lineOfPos, mh.linesAdded);
3900 } else {
3901 cs.DeleteLines(lineOfPos, -mh.linesAdded);
3904 CheckModificationForWrap(mh);
3905 if (mh.linesAdded != 0) {
3906 // Avoid scrolling of display if change before current display
3907 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
3908 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
3909 if (newTop != topLine) {
3910 SetTopLine(newTop);
3911 SetVerticalScrollPos();
3915 //Platform::DebugPrintf("** %x Doc Changed\n", this);
3916 // TODO: could invalidate from mh.startModification to end of screen
3917 //InvalidateRange(mh.position, mh.position + mh.length);
3918 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
3919 Redraw();
3921 } else {
3922 //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
3923 // mh.position, mh.position + mh.length);
3924 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
3925 InvalidateRange(mh.position, mh.position + mh.length);
3930 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
3931 SetScrollBars();
3934 if (mh.modificationType & SC_MOD_CHANGEMARKER) {
3935 if ((paintState == notPainting) || !PaintContainsMargin()) {
3936 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
3937 // Fold changes can affect the drawing of following lines so redraw whole margin
3938 RedrawSelMargin();
3939 } else {
3940 RedrawSelMargin(mh.line);
3945 // NOW pay the piper WRT "deferred" visual updates
3946 if (IsLastStep(mh)) {
3947 SetScrollBars();
3948 Redraw();
3951 // If client wants to see this modification
3952 if (mh.modificationType & modEventMask) {
3953 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
3954 // Real modification made to text of document.
3955 NotifyChange(); // Send EN_CHANGE
3958 SCNotification scn = {0};
3959 scn.nmhdr.code = SCN_MODIFIED;
3960 scn.position = mh.position;
3961 scn.modificationType = mh.modificationType;
3962 scn.text = mh.text;
3963 scn.length = mh.length;
3964 scn.linesAdded = mh.linesAdded;
3965 scn.line = mh.line;
3966 scn.foldLevelNow = mh.foldLevelNow;
3967 scn.foldLevelPrev = mh.foldLevelPrev;
3968 NotifyParent(scn);
3972 void Editor::NotifyDeleted(Document *, void *) {
3973 /* Do nothing */
3976 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
3978 // Enumerates all macroable messages
3979 switch (iMessage) {
3980 case SCI_CUT:
3981 case SCI_COPY:
3982 case SCI_PASTE:
3983 case SCI_CLEAR:
3984 case SCI_REPLACESEL:
3985 case SCI_ADDTEXT:
3986 case SCI_INSERTTEXT:
3987 case SCI_APPENDTEXT:
3988 case SCI_CLEARALL:
3989 case SCI_SELECTALL:
3990 case SCI_GOTOLINE:
3991 case SCI_GOTOPOS:
3992 case SCI_SEARCHANCHOR:
3993 case SCI_SEARCHNEXT:
3994 case SCI_SEARCHPREV:
3995 case SCI_LINEDOWN:
3996 case SCI_LINEDOWNEXTEND:
3997 case SCI_PARADOWN:
3998 case SCI_PARADOWNEXTEND:
3999 case SCI_LINEUP:
4000 case SCI_LINEUPEXTEND:
4001 case SCI_PARAUP:
4002 case SCI_PARAUPEXTEND:
4003 case SCI_CHARLEFT:
4004 case SCI_CHARLEFTEXTEND:
4005 case SCI_CHARRIGHT:
4006 case SCI_CHARRIGHTEXTEND:
4007 case SCI_WORDLEFT:
4008 case SCI_WORDLEFTEXTEND:
4009 case SCI_WORDRIGHT:
4010 case SCI_WORDRIGHTEXTEND:
4011 case SCI_WORDPARTLEFT:
4012 case SCI_WORDPARTLEFTEXTEND:
4013 case SCI_WORDPARTRIGHT:
4014 case SCI_WORDPARTRIGHTEXTEND:
4015 case SCI_WORDLEFTEND:
4016 case SCI_WORDLEFTENDEXTEND:
4017 case SCI_WORDRIGHTEND:
4018 case SCI_WORDRIGHTENDEXTEND:
4019 case SCI_HOME:
4020 case SCI_HOMEEXTEND:
4021 case SCI_LINEEND:
4022 case SCI_LINEENDEXTEND:
4023 case SCI_HOMEWRAP:
4024 case SCI_HOMEWRAPEXTEND:
4025 case SCI_LINEENDWRAP:
4026 case SCI_LINEENDWRAPEXTEND:
4027 case SCI_DOCUMENTSTART:
4028 case SCI_DOCUMENTSTARTEXTEND:
4029 case SCI_DOCUMENTEND:
4030 case SCI_DOCUMENTENDEXTEND:
4031 case SCI_STUTTEREDPAGEUP:
4032 case SCI_STUTTEREDPAGEUPEXTEND:
4033 case SCI_STUTTEREDPAGEDOWN:
4034 case SCI_STUTTEREDPAGEDOWNEXTEND:
4035 case SCI_PAGEUP:
4036 case SCI_PAGEUPEXTEND:
4037 case SCI_PAGEDOWN:
4038 case SCI_PAGEDOWNEXTEND:
4039 case SCI_EDITTOGGLEOVERTYPE:
4040 case SCI_CANCEL:
4041 case SCI_DELETEBACK:
4042 case SCI_TAB:
4043 case SCI_BACKTAB:
4044 case SCI_FORMFEED:
4045 case SCI_VCHOME:
4046 case SCI_VCHOMEEXTEND:
4047 case SCI_VCHOMEWRAP:
4048 case SCI_VCHOMEWRAPEXTEND:
4049 case SCI_DELWORDLEFT:
4050 case SCI_DELWORDRIGHT:
4051 case SCI_DELWORDRIGHTEND:
4052 case SCI_DELLINELEFT:
4053 case SCI_DELLINERIGHT:
4054 case SCI_LINECOPY:
4055 case SCI_LINECUT:
4056 case SCI_LINEDELETE:
4057 case SCI_LINETRANSPOSE:
4058 case SCI_LINEDUPLICATE:
4059 case SCI_LOWERCASE:
4060 case SCI_UPPERCASE:
4061 case SCI_LINESCROLLDOWN:
4062 case SCI_LINESCROLLUP:
4063 case SCI_DELETEBACKNOTLINE:
4064 case SCI_HOMEDISPLAY:
4065 case SCI_HOMEDISPLAYEXTEND:
4066 case SCI_LINEENDDISPLAY:
4067 case SCI_LINEENDDISPLAYEXTEND:
4068 case SCI_SETSELECTIONMODE:
4069 case SCI_LINEDOWNRECTEXTEND:
4070 case SCI_LINEUPRECTEXTEND:
4071 case SCI_CHARLEFTRECTEXTEND:
4072 case SCI_CHARRIGHTRECTEXTEND:
4073 case SCI_HOMERECTEXTEND:
4074 case SCI_VCHOMERECTEXTEND:
4075 case SCI_LINEENDRECTEXTEND:
4076 case SCI_PAGEUPRECTEXTEND:
4077 case SCI_PAGEDOWNRECTEXTEND:
4078 case SCI_SELECTIONDUPLICATE:
4079 case SCI_COPYALLOWLINE:
4080 break;
4082 // Filter out all others like display changes. Also, newlines are redundant
4083 // with char insert messages.
4084 case SCI_NEWLINE:
4085 default:
4086 // printf("Filtered out %ld of macro recording\n", iMessage);
4087 return ;
4090 // Send notification
4091 SCNotification scn = {0};
4092 scn.nmhdr.code = SCN_MACRORECORD;
4093 scn.message = iMessage;
4094 scn.wParam = wParam;
4095 scn.lParam = lParam;
4096 NotifyParent(scn);
4100 * Force scroll and keep position relative to top of window.
4102 * If stuttered = true and not already at first/last row, move to first/last row of window.
4103 * If stuttered = true and already at first/last row, scroll as normal.
4105 void Editor::PageMove(int direction, selTypes sel, bool stuttered) {
4106 int topLineNew, newPos;
4108 // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
4109 int currentLine = pdoc->LineFromPosition(currentPos);
4110 int topStutterLine = topLine + caretYSlop;
4111 int bottomStutterLine =
4112 pdoc->LineFromPosition(PositionFromLocation(
4113 Point(lastXChosen, direction * vs.lineHeight * LinesToScroll())))
4114 - caretYSlop - 1;
4116 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4117 topLineNew = topLine;
4118 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop));
4120 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4121 topLineNew = topLine;
4122 newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
4124 } else {
4125 Point pt = LocationFromPosition(currentPos);
4127 topLineNew = Platform::Clamp(
4128 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4129 newPos = PositionFromLocation(
4130 Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
4133 if (topLineNew != topLine) {
4134 SetTopLine(topLineNew);
4135 MovePositionTo(newPos, sel);
4136 Redraw();
4137 SetVerticalScrollPos();
4138 } else {
4139 MovePositionTo(newPos, sel);
4143 void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
4144 pdoc->BeginUndoAction();
4145 int startCurrent = currentPos;
4146 int startAnchor = anchor;
4147 if (selType == selStream) {
4148 pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()),
4149 makeUpperCase);
4150 SetSelection(startCurrent, startAnchor);
4151 } else {
4152 SelectionLineIterator lineIterator(this, false);
4153 while (lineIterator.Iterate()) {
4154 pdoc->ChangeCase(
4155 Range(lineIterator.startPos, lineIterator.endPos),
4156 makeUpperCase);
4158 // Would be nicer to keep the rectangular selection but this is complex
4159 SetEmptySelection(startCurrent);
4161 pdoc->EndUndoAction();
4164 void Editor::LineTranspose() {
4165 int line = pdoc->LineFromPosition(currentPos);
4166 if (line > 0) {
4167 pdoc->BeginUndoAction();
4168 int startPrev = pdoc->LineStart(line - 1);
4169 int endPrev = pdoc->LineEnd(line - 1);
4170 int start = pdoc->LineStart(line);
4171 int end = pdoc->LineEnd(line);
4172 char *line1 = CopyRange(startPrev, endPrev);
4173 int len1 = endPrev - startPrev;
4174 char *line2 = CopyRange(start, end);
4175 int len2 = end - start;
4176 pdoc->DeleteChars(start, len2);
4177 pdoc->DeleteChars(startPrev, len1);
4178 pdoc->InsertString(startPrev, line2, len2);
4179 pdoc->InsertString(start - len1 + len2, line1, len1);
4180 MovePositionTo(start - len1 + len2);
4181 delete []line1;
4182 delete []line2;
4183 pdoc->EndUndoAction();
4187 void Editor::Duplicate(bool forLine) {
4188 int start = SelectionStart();
4189 int end = SelectionEnd();
4190 if (start == end) {
4191 forLine = true;
4193 if (forLine) {
4194 int line = pdoc->LineFromPosition(currentPos);
4195 start = pdoc->LineStart(line);
4196 end = pdoc->LineEnd(line);
4198 char *text = CopyRange(start, end);
4199 if (forLine) {
4200 const char *eol = StringFromEOLMode(pdoc->eolMode);
4201 pdoc->InsertCString(end, eol);
4202 pdoc->InsertString(end + istrlen(eol), text, end - start);
4203 } else {
4204 pdoc->InsertString(end, text, end - start);
4206 delete []text;
4209 void Editor::CancelModes() {
4210 moveExtendsSelection = false;
4213 void Editor::NewLine() {
4214 ClearSelection();
4215 const char *eol = "\n";
4216 if (pdoc->eolMode == SC_EOL_CRLF) {
4217 eol = "\r\n";
4218 } else if (pdoc->eolMode == SC_EOL_CR) {
4219 eol = "\r";
4220 } // else SC_EOL_LF -> "\n" already set
4221 if (pdoc->InsertCString(currentPos, eol)) {
4222 SetEmptySelection(currentPos + istrlen(eol));
4223 while (*eol) {
4224 NotifyChar(*eol);
4225 eol++;
4228 SetLastXChosen();
4229 SetScrollBars();
4230 EnsureCaretVisible();
4231 // Avoid blinking during rapid typing:
4232 ShowCaretAtCurrentPosition();
4235 void Editor::CursorUpOrDown(int direction, selTypes sel) {
4236 Point pt = LocationFromPosition(currentPos);
4237 int posNew = PositionFromLocation(
4238 Point(lastXChosen, pt.y + direction * vs.lineHeight));
4239 if (direction < 0) {
4240 // Line wrapping may lead to a location on the same line, so
4241 // seek back if that is the case.
4242 // There is an equivalent case when moving down which skips
4243 // over a line but as that does not trap the user it is fine.
4244 Point ptNew = LocationFromPosition(posNew);
4245 while ((posNew > 0) && (pt.y == ptNew.y)) {
4246 posNew--;
4247 ptNew = LocationFromPosition(posNew);
4250 MovePositionTo(posNew, sel);
4253 void Editor::ParaUpOrDown(int direction, selTypes sel) {
4254 int lineDoc, savedPos = currentPos;
4255 do {
4256 MovePositionTo(direction > 0 ? pdoc->ParaDown(currentPos) : pdoc->ParaUp(currentPos), sel);
4257 lineDoc = pdoc->LineFromPosition(currentPos);
4258 if (direction > 0) {
4259 if (currentPos >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
4260 if (sel == noSel) {
4261 MovePositionTo(pdoc->LineEndPosition(savedPos));
4263 break;
4266 } while (!cs.GetVisible(lineDoc));
4269 int Editor::StartEndDisplayLine(int pos, bool start) {
4270 RefreshStyleData();
4271 int line = pdoc->LineFromPosition(pos);
4272 AutoSurface surface(this);
4273 AutoLineLayout ll(llc, RetrieveLineLayout(line));
4274 int posRet = INVALID_POSITION;
4275 if (surface && ll) {
4276 unsigned int posLineStart = pdoc->LineStart(line);
4277 LayoutLine(line, surface, vs, ll, wrapWidth);
4278 int posInLine = pos - posLineStart;
4279 if (posInLine <= ll->maxLineLength) {
4280 for (int subLine = 0; subLine < ll->lines; subLine++) {
4281 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
4282 if (start) {
4283 posRet = ll->LineStart(subLine) + posLineStart;
4284 } else {
4285 if (subLine == ll->lines - 1)
4286 posRet = ll->LineStart(subLine + 1) + posLineStart;
4287 else
4288 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
4294 if (posRet == INVALID_POSITION) {
4295 return pos;
4296 } else {
4297 return posRet;
4301 int Editor::KeyCommand(unsigned int iMessage) {
4302 switch (iMessage) {
4303 case SCI_LINEDOWN:
4304 CursorUpOrDown(1);
4305 break;
4306 case SCI_LINEDOWNEXTEND:
4307 CursorUpOrDown(1, selStream);
4308 break;
4309 case SCI_LINEDOWNRECTEXTEND:
4310 CursorUpOrDown(1, selRectangle);
4311 break;
4312 case SCI_PARADOWN:
4313 ParaUpOrDown(1);
4314 break;
4315 case SCI_PARADOWNEXTEND:
4316 ParaUpOrDown(1, selStream);
4317 break;
4318 case SCI_LINESCROLLDOWN:
4319 ScrollTo(topLine + 1);
4320 MoveCaretInsideView(false);
4321 break;
4322 case SCI_LINEUP:
4323 CursorUpOrDown(-1);
4324 break;
4325 case SCI_LINEUPEXTEND:
4326 CursorUpOrDown(-1, selStream);
4327 break;
4328 case SCI_LINEUPRECTEXTEND:
4329 CursorUpOrDown(-1, selRectangle);
4330 break;
4331 case SCI_PARAUP:
4332 ParaUpOrDown(-1);
4333 break;
4334 case SCI_PARAUPEXTEND:
4335 ParaUpOrDown(-1, selStream);
4336 break;
4337 case SCI_LINESCROLLUP:
4338 ScrollTo(topLine - 1);
4339 MoveCaretInsideView(false);
4340 break;
4341 case SCI_CHARLEFT:
4342 if (SelectionEmpty() || moveExtendsSelection) {
4343 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1));
4344 } else {
4345 MovePositionTo(SelectionStart());
4347 SetLastXChosen();
4348 break;
4349 case SCI_CHARLEFTEXTEND:
4350 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selStream);
4351 SetLastXChosen();
4352 break;
4353 case SCI_CHARLEFTRECTEXTEND:
4354 MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selRectangle);
4355 SetLastXChosen();
4356 break;
4357 case SCI_CHARRIGHT:
4358 if (SelectionEmpty() || moveExtendsSelection) {
4359 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1));
4360 } else {
4361 MovePositionTo(SelectionEnd());
4363 SetLastXChosen();
4364 break;
4365 case SCI_CHARRIGHTEXTEND:
4366 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selStream);
4367 SetLastXChosen();
4368 break;
4369 case SCI_CHARRIGHTRECTEXTEND:
4370 MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selRectangle);
4371 SetLastXChosen();
4372 break;
4373 case SCI_WORDLEFT:
4374 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1));
4375 SetLastXChosen();
4376 break;
4377 case SCI_WORDLEFTEXTEND:
4378 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), selStream);
4379 SetLastXChosen();
4380 break;
4381 case SCI_WORDRIGHT:
4382 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1));
4383 SetLastXChosen();
4384 break;
4385 case SCI_WORDRIGHTEXTEND:
4386 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), selStream);
4387 SetLastXChosen();
4388 break;
4390 case SCI_WORDLEFTEND:
4391 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1));
4392 SetLastXChosen();
4393 break;
4394 case SCI_WORDLEFTENDEXTEND:
4395 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1), selStream);
4396 SetLastXChosen();
4397 break;
4398 case SCI_WORDRIGHTEND:
4399 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1));
4400 SetLastXChosen();
4401 break;
4402 case SCI_WORDRIGHTENDEXTEND:
4403 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1), selStream);
4404 SetLastXChosen();
4405 break;
4407 case SCI_HOME:
4408 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)));
4409 SetLastXChosen();
4410 break;
4411 case SCI_HOMEEXTEND:
4412 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selStream);
4413 SetLastXChosen();
4414 break;
4415 case SCI_HOMERECTEXTEND:
4416 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selRectangle);
4417 SetLastXChosen();
4418 break;
4419 case SCI_LINEEND:
4420 MovePositionTo(pdoc->LineEndPosition(currentPos));
4421 SetLastXChosen();
4422 break;
4423 case SCI_LINEENDEXTEND:
4424 MovePositionTo(pdoc->LineEndPosition(currentPos), selStream);
4425 SetLastXChosen();
4426 break;
4427 case SCI_LINEENDRECTEXTEND:
4428 MovePositionTo(pdoc->LineEndPosition(currentPos), selRectangle);
4429 SetLastXChosen();
4430 break;
4431 case SCI_HOMEWRAP: {
4432 int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4433 if (currentPos <= homePos)
4434 homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
4435 MovePositionTo(homePos);
4436 SetLastXChosen();
4438 break;
4439 case SCI_HOMEWRAPEXTEND: {
4440 int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4441 if (currentPos <= homePos)
4442 homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
4443 MovePositionTo(homePos, selStream);
4444 SetLastXChosen();
4446 break;
4447 case SCI_LINEENDWRAP: {
4448 int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
4449 int realEndPos = pdoc->LineEndPosition(currentPos);
4450 if (endPos > realEndPos // if moved past visible EOLs
4451 || currentPos >= endPos) // if at end of display line already
4452 endPos = realEndPos;
4453 MovePositionTo(endPos);
4454 SetLastXChosen();
4456 break;
4457 case SCI_LINEENDWRAPEXTEND: {
4458 int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
4459 int realEndPos = pdoc->LineEndPosition(currentPos);
4460 if (endPos > realEndPos // if moved past visible EOLs
4461 || currentPos >= endPos) // if at end of display line already
4462 endPos = realEndPos;
4463 MovePositionTo(endPos, selStream);
4464 SetLastXChosen();
4466 break;
4467 case SCI_DOCUMENTSTART:
4468 MovePositionTo(0);
4469 SetLastXChosen();
4470 break;
4471 case SCI_DOCUMENTSTARTEXTEND:
4472 MovePositionTo(0, selStream);
4473 SetLastXChosen();
4474 break;
4475 case SCI_DOCUMENTEND:
4476 MovePositionTo(pdoc->Length());
4477 SetLastXChosen();
4478 break;
4479 case SCI_DOCUMENTENDEXTEND:
4480 MovePositionTo(pdoc->Length(), selStream);
4481 SetLastXChosen();
4482 break;
4483 case SCI_STUTTEREDPAGEUP:
4484 PageMove(-1, noSel, true);
4485 break;
4486 case SCI_STUTTEREDPAGEUPEXTEND:
4487 PageMove(-1, selStream, true);
4488 break;
4489 case SCI_STUTTEREDPAGEDOWN:
4490 PageMove(1, noSel, true);
4491 break;
4492 case SCI_STUTTEREDPAGEDOWNEXTEND:
4493 PageMove(1, selStream, true);
4494 break;
4495 case SCI_PAGEUP:
4496 PageMove(-1);
4497 break;
4498 case SCI_PAGEUPEXTEND:
4499 PageMove(-1, selStream);
4500 break;
4501 case SCI_PAGEUPRECTEXTEND:
4502 PageMove(-1, selRectangle);
4503 break;
4504 case SCI_PAGEDOWN:
4505 PageMove(1);
4506 break;
4507 case SCI_PAGEDOWNEXTEND:
4508 PageMove(1, selStream);
4509 break;
4510 case SCI_PAGEDOWNRECTEXTEND:
4511 PageMove(1, selRectangle);
4512 break;
4513 case SCI_EDITTOGGLEOVERTYPE:
4514 inOverstrike = !inOverstrike;
4515 DropCaret();
4516 ShowCaretAtCurrentPosition();
4517 NotifyUpdateUI();
4518 break;
4519 case SCI_CANCEL: // Cancel any modes - handled in subclass
4520 // Also unselect text
4521 CancelModes();
4522 break;
4523 case SCI_DELETEBACK:
4524 DelCharBack(true);
4525 if (!caretSticky) {
4526 SetLastXChosen();
4528 EnsureCaretVisible();
4529 break;
4530 case SCI_DELETEBACKNOTLINE:
4531 DelCharBack(false);
4532 if (!caretSticky) {
4533 SetLastXChosen();
4535 EnsureCaretVisible();
4536 break;
4537 case SCI_TAB:
4538 Indent(true);
4539 if (!caretSticky) {
4540 SetLastXChosen();
4542 EnsureCaretVisible();
4543 break;
4544 case SCI_BACKTAB:
4545 Indent(false);
4546 if (!caretSticky) {
4547 SetLastXChosen();
4549 EnsureCaretVisible();
4550 break;
4551 case SCI_NEWLINE:
4552 NewLine();
4553 break;
4554 case SCI_FORMFEED:
4555 AddChar('\f');
4556 break;
4557 case SCI_VCHOME:
4558 MovePositionTo(pdoc->VCHomePosition(currentPos));
4559 SetLastXChosen();
4560 break;
4561 case SCI_VCHOMEEXTEND:
4562 MovePositionTo(pdoc->VCHomePosition(currentPos), selStream);
4563 SetLastXChosen();
4564 break;
4565 case SCI_VCHOMERECTEXTEND:
4566 MovePositionTo(pdoc->VCHomePosition(currentPos), selRectangle);
4567 SetLastXChosen();
4568 break;
4569 case SCI_VCHOMEWRAP: {
4570 int homePos = pdoc->VCHomePosition(currentPos);
4571 int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4572 if ((viewLineStart < currentPos) && (viewLineStart > homePos))
4573 homePos = viewLineStart;
4575 MovePositionTo(homePos);
4576 SetLastXChosen();
4578 break;
4579 case SCI_VCHOMEWRAPEXTEND: {
4580 int homePos = pdoc->VCHomePosition(currentPos);
4581 int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
4582 if ((viewLineStart < currentPos) && (viewLineStart > homePos))
4583 homePos = viewLineStart;
4585 MovePositionTo(homePos, selStream);
4586 SetLastXChosen();
4588 break;
4589 case SCI_ZOOMIN:
4590 if (vs.zoomLevel < 20) {
4591 vs.zoomLevel++;
4592 InvalidateStyleRedraw();
4593 NotifyZoom();
4595 break;
4596 case SCI_ZOOMOUT:
4597 if (vs.zoomLevel > -10) {
4598 vs.zoomLevel--;
4599 InvalidateStyleRedraw();
4600 NotifyZoom();
4602 break;
4603 case SCI_DELWORDLEFT: {
4604 int startWord = pdoc->NextWordStart(currentPos, -1);
4605 pdoc->DeleteChars(startWord, currentPos - startWord);
4606 SetLastXChosen();
4608 break;
4609 case SCI_DELWORDRIGHT: {
4610 int endWord = pdoc->NextWordStart(currentPos, 1);
4611 pdoc->DeleteChars(currentPos, endWord - currentPos);
4613 break;
4614 case SCI_DELWORDRIGHTEND: {
4615 int endWord = pdoc->NextWordEnd(currentPos, 1);
4616 pdoc->DeleteChars(currentPos, endWord - currentPos);
4618 break;
4619 case SCI_DELLINELEFT: {
4620 int line = pdoc->LineFromPosition(currentPos);
4621 int start = pdoc->LineStart(line);
4622 pdoc->DeleteChars(start, currentPos - start);
4623 SetLastXChosen();
4625 break;
4626 case SCI_DELLINERIGHT: {
4627 int line = pdoc->LineFromPosition(currentPos);
4628 int end = pdoc->LineEnd(line);
4629 pdoc->DeleteChars(currentPos, end - currentPos);
4631 break;
4632 case SCI_LINECOPY: {
4633 int lineStart = pdoc->LineFromPosition(SelectionStart());
4634 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
4635 CopyRangeToClipboard(pdoc->LineStart(lineStart),
4636 pdoc->LineStart(lineEnd + 1));
4638 break;
4639 case SCI_LINECUT: {
4640 int lineStart = pdoc->LineFromPosition(SelectionStart());
4641 int lineEnd = pdoc->LineFromPosition(SelectionEnd());
4642 int start = pdoc->LineStart(lineStart);
4643 int end = pdoc->LineStart(lineEnd + 1);
4644 SetSelection(start, end);
4645 Cut();
4646 SetLastXChosen();
4648 break;
4649 case SCI_LINEDELETE: {
4650 int line = pdoc->LineFromPosition(currentPos);
4651 int start = pdoc->LineStart(line);
4652 int end = pdoc->LineStart(line + 1);
4653 pdoc->DeleteChars(start, end - start);
4655 break;
4656 case SCI_LINETRANSPOSE:
4657 LineTranspose();
4658 break;
4659 case SCI_LINEDUPLICATE:
4660 Duplicate(true);
4661 break;
4662 case SCI_SELECTIONDUPLICATE:
4663 Duplicate(false);
4664 break;
4665 case SCI_LOWERCASE:
4666 ChangeCaseOfSelection(false);
4667 break;
4668 case SCI_UPPERCASE:
4669 ChangeCaseOfSelection(true);
4670 break;
4671 case SCI_WORDPARTLEFT:
4672 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1));
4673 SetLastXChosen();
4674 break;
4675 case SCI_WORDPARTLEFTEXTEND:
4676 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1), selStream);
4677 SetLastXChosen();
4678 break;
4679 case SCI_WORDPARTRIGHT:
4680 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1));
4681 SetLastXChosen();
4682 break;
4683 case SCI_WORDPARTRIGHTEXTEND:
4684 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1), selStream);
4685 SetLastXChosen();
4686 break;
4687 case SCI_HOMEDISPLAY:
4688 MovePositionTo(MovePositionSoVisible(
4689 StartEndDisplayLine(currentPos, true), -1));
4690 SetLastXChosen();
4691 break;
4692 case SCI_HOMEDISPLAYEXTEND:
4693 MovePositionTo(MovePositionSoVisible(
4694 StartEndDisplayLine(currentPos, true), -1), selStream);
4695 SetLastXChosen();
4696 break;
4697 case SCI_LINEENDDISPLAY:
4698 MovePositionTo(MovePositionSoVisible(
4699 StartEndDisplayLine(currentPos, false), 1));
4700 SetLastXChosen();
4701 break;
4702 case SCI_LINEENDDISPLAYEXTEND:
4703 MovePositionTo(MovePositionSoVisible(
4704 StartEndDisplayLine(currentPos, false), 1), selStream);
4705 SetLastXChosen();
4706 break;
4708 return 0;
4711 int Editor::KeyDefault(int, int) {
4712 return 0;
4715 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
4716 DwellEnd(false);
4717 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4718 (alt ? SCI_ALT : 0);
4719 int msg = kmap.Find(key, modifiers);
4720 if (msg) {
4721 if (consumed)
4722 *consumed = true;
4723 return WndProc(msg, 0, 0);
4724 } else {
4725 if (consumed)
4726 *consumed = false;
4727 return KeyDefault(key, modifiers);
4731 void Editor::SetWhitespaceVisible(int view) {
4732 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
4735 int Editor::GetWhitespaceVisible() {
4736 return vs.viewWhitespace;
4739 void Editor::Indent(bool forwards) {
4740 //Platform::DebugPrintf("INdent %d\n", forwards);
4741 int lineOfAnchor = pdoc->LineFromPosition(anchor);
4742 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
4743 if (lineOfAnchor == lineCurrentPos) {
4744 if (forwards) {
4745 pdoc->BeginUndoAction();
4746 ClearSelection();
4747 if (pdoc->GetColumn(currentPos) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
4748 pdoc->tabIndents) {
4749 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4750 int indentationStep = pdoc->IndentSize();
4751 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
4752 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
4753 } else {
4754 if (pdoc->useTabs) {
4755 pdoc->InsertChar(currentPos, '\t');
4756 SetEmptySelection(currentPos + 1);
4757 } else {
4758 int numSpaces = (pdoc->tabInChars) -
4759 (pdoc->GetColumn(currentPos) % (pdoc->tabInChars));
4760 if (numSpaces < 1)
4761 numSpaces = pdoc->tabInChars;
4762 for (int i = 0; i < numSpaces; i++) {
4763 pdoc->InsertChar(currentPos + i, ' ');
4765 SetEmptySelection(currentPos + numSpaces);
4768 pdoc->EndUndoAction();
4769 } else {
4770 if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4771 pdoc->tabIndents) {
4772 pdoc->BeginUndoAction();
4773 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4774 int indentationStep = pdoc->IndentSize();
4775 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4776 SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
4777 pdoc->EndUndoAction();
4778 } else {
4779 int newColumn = ((pdoc->GetColumn(currentPos) - 1) / pdoc->tabInChars) *
4780 pdoc->tabInChars;
4781 if (newColumn < 0)
4782 newColumn = 0;
4783 int newPos = currentPos;
4784 while (pdoc->GetColumn(newPos) > newColumn)
4785 newPos--;
4786 SetEmptySelection(newPos);
4789 } else {
4790 int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor);
4791 int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos);
4792 // Multiple lines selected so indent / dedent
4793 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
4794 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
4795 if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos)
4796 lineBottomSel--; // If not selecting any characters on a line, do not indent
4797 pdoc->BeginUndoAction();
4798 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
4799 pdoc->EndUndoAction();
4800 if (lineOfAnchor < lineCurrentPos) {
4801 if (currentPosPosOnLine == 0)
4802 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
4803 else
4804 SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
4805 } else {
4806 if (anchorPosOnLine == 0)
4807 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
4808 else
4809 SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
4815 * Search of a text in the document, in the given range.
4816 * @return The position of the found text, -1 if not found.
4818 long Editor::FindText(
4819 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4820 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4821 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
4823 TextToFind *ft = reinterpret_cast<TextToFind *>(lParam);
4824 int lengthFound = istrlen(ft->lpstrText);
4825 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
4826 (wParam & SCFIND_MATCHCASE) != 0,
4827 (wParam & SCFIND_WHOLEWORD) != 0,
4828 (wParam & SCFIND_WORDSTART) != 0,
4829 (wParam & SCFIND_REGEXP) != 0,
4830 wParam,
4831 &lengthFound);
4832 if (pos != -1) {
4833 ft->chrgText.cpMin = pos;
4834 ft->chrgText.cpMax = pos + lengthFound;
4836 return pos;
4840 * Relocatable search support : Searches relative to current selection
4841 * point and sets the selection to the found text range with
4842 * each search.
4845 * Anchor following searches at current selection start: This allows
4846 * multiple incremental interactive searches to be macro recorded
4847 * while still setting the selection to found text so the find/select
4848 * operation is self-contained.
4850 void Editor::SearchAnchor() {
4851 searchAnchor = SelectionStart();
4855 * Find text from current search anchor: Must call @c SearchAnchor first.
4856 * Used for next text and previous text requests.
4857 * @return The position of the found text, -1 if not found.
4859 long Editor::SearchText(
4860 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
4861 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
4862 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
4863 sptr_t lParam) { ///< The text to search for.
4865 const char *txt = reinterpret_cast<char *>(lParam);
4866 int pos;
4867 int lengthFound = istrlen(txt);
4868 if (iMessage == SCI_SEARCHNEXT) {
4869 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
4870 (wParam & SCFIND_MATCHCASE) != 0,
4871 (wParam & SCFIND_WHOLEWORD) != 0,
4872 (wParam & SCFIND_WORDSTART) != 0,
4873 (wParam & SCFIND_REGEXP) != 0,
4874 wParam,
4875 &lengthFound);
4876 } else {
4877 pos = pdoc->FindText(searchAnchor, 0, txt,
4878 (wParam & SCFIND_MATCHCASE) != 0,
4879 (wParam & SCFIND_WHOLEWORD) != 0,
4880 (wParam & SCFIND_WORDSTART) != 0,
4881 (wParam & SCFIND_REGEXP) != 0,
4882 wParam,
4883 &lengthFound);
4886 if (pos != -1) {
4887 SetSelection(pos, pos + lengthFound);
4890 return pos;
4894 * Search for text in the target range of the document.
4895 * @return The position of the found text, -1 if not found.
4897 long Editor::SearchInTarget(const char *text, int length) {
4898 int lengthFound = length;
4899 int pos = pdoc->FindText(targetStart, targetEnd, text,
4900 (searchFlags & SCFIND_MATCHCASE) != 0,
4901 (searchFlags & SCFIND_WHOLEWORD) != 0,
4902 (searchFlags & SCFIND_WORDSTART) != 0,
4903 (searchFlags & SCFIND_REGEXP) != 0,
4904 searchFlags,
4905 &lengthFound);
4906 if (pos != -1) {
4907 targetStart = pos;
4908 targetEnd = pos + lengthFound;
4910 return pos;
4913 void Editor::GoToLine(int lineNo) {
4914 if (lineNo > pdoc->LinesTotal())
4915 lineNo = pdoc->LinesTotal();
4916 if (lineNo < 0)
4917 lineNo = 0;
4918 SetEmptySelection(pdoc->LineStart(lineNo));
4919 ShowCaretAtCurrentPosition();
4920 EnsureCaretVisible();
4923 static bool Close(Point pt1, Point pt2) {
4924 if (abs(pt1.x - pt2.x) > 3)
4925 return false;
4926 if (abs(pt1.y - pt2.y) > 3)
4927 return false;
4928 return true;
4931 char *Editor::CopyRange(int start, int end) {
4932 char *text = 0;
4933 if (start < end) {
4934 int len = end - start;
4935 text = new char[len + 1];
4936 if (text) {
4937 for (int i = 0; i < len; i++) {
4938 text[i] = pdoc->CharAt(start + i);
4940 text[len] = '\0';
4943 return text;
4946 void Editor::CopySelectionFromRange(SelectionText *ss, bool allowLineCopy, int start, int end) {
4947 bool isLine = allowLineCopy && (start == end);
4948 if (isLine) {
4949 int currentLine = pdoc->LineFromPosition(currentPos);
4950 start = pdoc->LineStart(currentLine);
4951 end = pdoc->LineEnd(currentLine);
4953 char *text = CopyRange(start, end);
4954 int textLen = text ? strlen(text) : 0;
4955 // include room for \r\n\0
4956 textLen += 3;
4957 char *textWithEndl = new char[textLen];
4958 textWithEndl[0] = '\0';
4959 if (text)
4960 strncat(textWithEndl, text, textLen);
4961 if (pdoc->eolMode != SC_EOL_LF)
4962 strncat(textWithEndl, "\r", textLen);
4963 if (pdoc->eolMode != SC_EOL_CR)
4964 strncat(textWithEndl, "\n", textLen);
4965 ss->Set(textWithEndl, strlen(textWithEndl),
4966 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
4967 delete []text;
4968 } else {
4969 ss->Set(CopyRange(start, end), end - start + 1,
4970 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
4974 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
4975 if (selType == selStream) {
4976 CopySelectionFromRange(ss, allowLineCopy, SelectionStart(), SelectionEnd());
4977 } else {
4978 char *text = 0;
4979 int size = 0;
4980 SelectionLineIterator lineIterator(this);
4981 while (lineIterator.Iterate()) {
4982 size += lineIterator.endPos - lineIterator.startPos;
4983 if (selType != selLines) {
4984 size++;
4985 if (pdoc->eolMode == SC_EOL_CRLF) {
4986 size++;
4990 if (size > 0) {
4991 text = new char[size + 1];
4992 if (text) {
4993 int j = 0;
4994 lineIterator.Reset();
4995 while (lineIterator.Iterate()) {
4996 for (int i = lineIterator.startPos;
4997 i < lineIterator.endPos;
4998 i++) {
4999 text[j++] = pdoc->CharAt(i);
5001 if (selType != selLines) {
5002 if (pdoc->eolMode != SC_EOL_LF) {
5003 text[j++] = '\r';
5005 if (pdoc->eolMode != SC_EOL_CR) {
5006 text[j++] = '\n';
5010 text[size] = '\0';
5013 ss->Set(text, size + 1, pdoc->dbcsCodePage,
5014 vs.styles[STYLE_DEFAULT].characterSet, selType == selRectangle, selType == selLines);
5018 void Editor::CopyRangeToClipboard(int start, int end) {
5019 start = pdoc->ClampPositionIntoDocument(start);
5020 end = pdoc->ClampPositionIntoDocument(end);
5021 SelectionText selectedText;
5022 selectedText.Set(CopyRange(start, end), end - start + 1,
5023 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5024 CopyToClipboard(selectedText);
5027 void Editor::CopyText(int length, const char *text) {
5028 SelectionText selectedText;
5029 selectedText.Copy(text, length + 1,
5030 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
5031 CopyToClipboard(selectedText);
5034 void Editor::SetDragPosition(int newPos) {
5035 if (newPos >= 0) {
5036 newPos = MovePositionOutsideChar(newPos, 1);
5037 posDrop = newPos;
5039 if (posDrag != newPos) {
5040 caret.on = true;
5041 SetTicking(true);
5042 InvalidateCaret();
5043 posDrag = newPos;
5044 InvalidateCaret();
5048 void Editor::DisplayCursor(Window::Cursor c) {
5049 if (cursorMode == SC_CURSORNORMAL)
5050 wMain.SetCursor(c);
5051 else
5052 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
5055 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
5056 int xMove = ptStart.x - ptNow.x;
5057 int yMove = ptStart.y - ptNow.y;
5058 int distanceSquared = xMove * xMove + yMove * yMove;
5059 return distanceSquared > 16;
5062 void Editor::StartDrag() {
5063 // Always handled by subclasses
5064 //SetMouseCapture(true);
5065 //DisplayCursor(Window::cursorArrow);
5068 void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) {
5069 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
5070 if (inDragDrop == ddDragging)
5071 dropWentOutside = false;
5073 int positionWasInSelection = PositionInSelection(position);
5075 bool positionOnEdgeOfSelection =
5076 (position == SelectionStart()) || (position == SelectionEnd());
5078 if ((inDragDrop != ddDragging) || !(0 == positionWasInSelection) ||
5079 (positionOnEdgeOfSelection && !moving)) {
5081 int selStart = SelectionStart();
5082 int selEnd = SelectionEnd();
5084 pdoc->BeginUndoAction();
5086 int positionAfterDeletion = position;
5087 if ((inDragDrop == ddDragging) && moving) {
5088 // Remove dragged out text
5089 if (rectangular || selType == selLines) {
5090 SelectionLineIterator lineIterator(this);
5091 while (lineIterator.Iterate()) {
5092 if (position >= lineIterator.startPos) {
5093 if (position > lineIterator.endPos) {
5094 positionAfterDeletion -= lineIterator.endPos - lineIterator.startPos;
5095 } else {
5096 positionAfterDeletion -= position - lineIterator.startPos;
5100 } else {
5101 if (position > selStart) {
5102 positionAfterDeletion -= selEnd - selStart;
5105 ClearSelection();
5107 position = positionAfterDeletion;
5109 if (rectangular) {
5110 PasteRectangular(position, value, istrlen(value));
5111 pdoc->EndUndoAction();
5112 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
5113 SetEmptySelection(position);
5114 } else {
5115 position = MovePositionOutsideChar(position, currentPos - position);
5116 if (pdoc->InsertCString(position, value)) {
5117 SetSelection(position + istrlen(value), position);
5119 pdoc->EndUndoAction();
5121 } else if (inDragDrop == ddDragging) {
5122 SetEmptySelection(position);
5127 * @return -1 if given position is before the selection,
5128 * 1 if position is after the selection,
5129 * 0 if position is inside the selection,
5131 int Editor::PositionInSelection(int pos) {
5132 pos = MovePositionOutsideChar(pos, currentPos - pos);
5133 if (pos < SelectionStart()) {
5134 return -1;
5136 if (pos > SelectionEnd()) {
5137 return 1;
5139 if (selType == selStream) {
5140 return 0;
5141 } else {
5142 SelectionLineIterator lineIterator(this);
5143 lineIterator.SetAt(pdoc->LineFromPosition(pos));
5144 if (pos < lineIterator.startPos) {
5145 return -1;
5146 } else if (pos > lineIterator.endPos) {
5147 return 1;
5148 } else {
5149 return 0;
5154 bool Editor::PointInSelection(Point pt) {
5155 int pos = PositionFromLocation(pt);
5156 if (0 == PositionInSelection(pos)) {
5157 // Probably inside, but we must make a finer test
5158 int selStart, selEnd;
5159 if (selType == selStream) {
5160 selStart = SelectionStart();
5161 selEnd = SelectionEnd();
5162 } else {
5163 SelectionLineIterator lineIterator(this);
5164 lineIterator.SetAt(pdoc->LineFromPosition(pos));
5165 selStart = lineIterator.startPos;
5166 selEnd = lineIterator.endPos;
5168 if (pos == selStart) {
5169 // see if just before selection
5170 Point locStart = LocationFromPosition(pos);
5171 if (pt.x < locStart.x) {
5172 return false;
5175 if (pos == selEnd) {
5176 // see if just after selection
5177 Point locEnd = LocationFromPosition(pos);
5178 if (pt.x > locEnd.x) {
5179 return false;
5182 return true;
5184 return false;
5187 bool Editor::PointInSelMargin(Point pt) {
5188 // Really means: "Point in a margin"
5189 if (vs.fixedColumnWidth > 0) { // There is a margin
5190 PRectangle rcSelMargin = GetClientRectangle();
5191 rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
5192 return rcSelMargin.Contains(pt);
5193 } else {
5194 return false;
5198 void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
5199 if (lineAnchor_ < lineCurrent_) {
5200 SetSelection(pdoc->LineStart(lineCurrent_ + 1),
5201 pdoc->LineStart(lineAnchor_));
5202 } else if (lineAnchor_ > lineCurrent_) {
5203 SetSelection(pdoc->LineStart(lineCurrent_),
5204 pdoc->LineStart(lineAnchor_ + 1));
5205 } else { // Same line, select it
5206 SetSelection(pdoc->LineStart(lineAnchor_ + 1),
5207 pdoc->LineStart(lineAnchor_));
5211 void Editor::DwellEnd(bool mouseMoved) {
5212 if (mouseMoved)
5213 ticksToDwell = dwellDelay;
5214 else
5215 ticksToDwell = SC_TIME_FOREVER;
5216 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
5217 dwelling = false;
5218 NotifyDwelling(ptMouseLast, dwelling);
5222 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
5223 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
5224 ptMouseLast = pt;
5225 int newPos = PositionFromLocation(pt);
5226 newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
5227 inDragDrop = ddNone;
5228 moveExtendsSelection = false;
5230 bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
5231 if (processed)
5232 return;
5234 NotifyIndicatorClick(true, newPos, shift, ctrl, alt);
5236 bool inSelMargin = PointInSelMargin(pt);
5237 if (shift & !inSelMargin) {
5238 SetSelection(newPos);
5240 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
5241 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
5242 SetMouseCapture(true);
5243 SetEmptySelection(newPos);
5244 bool doubleClick = false;
5245 // Stop mouse button bounce changing selection type
5246 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
5247 if (selectionType == selChar) {
5248 selectionType = selWord;
5249 doubleClick = true;
5250 } else if (selectionType == selWord) {
5251 selectionType = selLine;
5252 } else {
5253 selectionType = selChar;
5254 originalAnchorPos = currentPos;
5258 if (selectionType == selWord) {
5259 if (currentPos >= originalAnchorPos) { // Moved forward
5260 SetSelection(pdoc->ExtendWordSelect(currentPos, 1),
5261 pdoc->ExtendWordSelect(originalAnchorPos, -1));
5262 } else { // Moved backward
5263 SetSelection(pdoc->ExtendWordSelect(currentPos, -1),
5264 pdoc->ExtendWordSelect(originalAnchorPos, 1));
5266 } else if (selectionType == selLine) {
5267 lineAnchor = LineFromLocation(pt);
5268 SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
5269 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
5270 } else {
5271 SetEmptySelection(currentPos);
5273 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
5274 if (doubleClick) {
5275 NotifyDoubleClick(pt, shift, ctrl, alt);
5276 if (PositionIsHotspot(newPos))
5277 NotifyHotSpotDoubleClicked(newPos, shift, ctrl, alt);
5279 } else { // Single click
5280 if (inSelMargin) {
5281 selType = selStream;
5282 if (ctrl) {
5283 SelectAll();
5284 lastClickTime = curTime;
5285 return;
5287 if (!shift) {
5288 lineAnchor = LineFromLocation(pt);
5289 // Single click in margin: select whole line
5290 LineSelection(lineAnchor, lineAnchor);
5291 SetSelection(pdoc->LineStart(lineAnchor + 1),
5292 pdoc->LineStart(lineAnchor));
5293 } else {
5294 // Single shift+click in margin: select from line anchor to clicked line
5295 if (anchor > currentPos)
5296 lineAnchor = pdoc->LineFromPosition(anchor - 1);
5297 else
5298 lineAnchor = pdoc->LineFromPosition(anchor);
5299 int lineStart = LineFromLocation(pt);
5300 LineSelection(lineStart, lineAnchor);
5301 //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
5304 SetDragPosition(invalidPosition);
5305 SetMouseCapture(true);
5306 selectionType = selLine;
5307 } else {
5308 if (PointIsHotspot(pt)) {
5309 NotifyHotSpotClicked(newPos, shift, ctrl, alt);
5311 if (!shift) {
5312 if (PointInSelection(pt) && !SelectionEmpty())
5313 inDragDrop = ddInitial;
5314 else
5315 inDragDrop = ddNone;
5317 SetMouseCapture(true);
5318 if (inDragDrop != ddInitial) {
5319 SetDragPosition(invalidPosition);
5320 if (!shift) {
5321 SetEmptySelection(newPos);
5323 selType = alt ? selRectangle : selStream;
5324 selectionType = selChar;
5325 originalAnchorPos = currentPos;
5326 SetRectangularRange();
5330 lastClickTime = curTime;
5331 lastXChosen = pt.x;
5332 ShowCaretAtCurrentPosition();
5335 bool Editor::PositionIsHotspot(int position) {
5336 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
5339 bool Editor::PointIsHotspot(Point pt) {
5340 int pos = PositionFromLocationClose(pt);
5341 if (pos == INVALID_POSITION)
5342 return false;
5343 return PositionIsHotspot(pos);
5346 void Editor::SetHotSpotRange(Point *pt) {
5347 if (pt) {
5348 int pos = PositionFromLocation(*pt);
5350 // If we don't limit this to word characters then the
5351 // range can encompass more than the run range and then
5352 // the underline will not be drawn properly.
5353 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
5354 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
5356 // Only invalidate the range if the hotspot range has changed...
5357 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
5358 if (hsStart != -1) {
5359 InvalidateRange(hsStart, hsEnd);
5361 hsStart = hsStart_;
5362 hsEnd = hsEnd_;
5363 InvalidateRange(hsStart, hsEnd);
5365 } else {
5366 if (hsStart != -1) {
5367 int hsStart_ = hsStart;
5368 int hsEnd_ = hsEnd;
5369 hsStart = -1;
5370 hsEnd = -1;
5371 InvalidateRange(hsStart_, hsEnd_);
5372 } else {
5373 hsStart = -1;
5374 hsEnd = -1;
5379 void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) {
5380 hsStart_ = hsStart;
5381 hsEnd_ = hsEnd;
5384 void Editor::ButtonMove(Point pt) {
5385 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
5386 DwellEnd(true);
5389 int movePos = PositionFromLocation(pt);
5390 movePos = MovePositionOutsideChar(movePos, currentPos - movePos);
5392 if (inDragDrop == ddInitial) {
5393 if (DragThreshold(ptMouseLast, pt)) {
5394 SetMouseCapture(false);
5395 SetDragPosition(movePos);
5396 CopySelectionRange(&drag);
5397 StartDrag();
5399 return;
5402 ptMouseLast = pt;
5403 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
5404 if (HaveMouseCapture()) {
5406 // Slow down autoscrolling/selection
5407 autoScrollTimer.ticksToWait -= timer.tickSize;
5408 if (autoScrollTimer.ticksToWait > 0)
5409 return;
5410 autoScrollTimer.ticksToWait = autoScrollDelay;
5412 // Adjust selection
5413 if (posDrag >= 0) {
5414 SetDragPosition(movePos);
5415 } else {
5416 if (selectionType == selChar) {
5417 SetSelection(movePos);
5418 } else if (selectionType == selWord) {
5419 // Continue selecting by word
5420 if (movePos == originalAnchorPos) { // Didn't move
5421 // No need to do anything. Previously this case was lumped
5422 // in with "Moved forward", but that can be harmful in this
5423 // case: a handler for the NotifyDoubleClick re-adjusts
5424 // the selection for a fancier definition of "word" (for
5425 // example, in Perl it is useful to include the leading
5426 // '$', '%' or '@' on variables for word selection). In this
5427 // the ButtonMove() called via Tick() for auto-scrolling
5428 // could result in the fancier word selection adjustment
5429 // being unmade.
5430 } else if (movePos > originalAnchorPos) { // Moved forward
5431 SetSelection(pdoc->ExtendWordSelect(movePos, 1),
5432 pdoc->ExtendWordSelect(originalAnchorPos, -1));
5433 } else { // Moved backward
5434 SetSelection(pdoc->ExtendWordSelect(movePos, -1),
5435 pdoc->ExtendWordSelect(originalAnchorPos, 1));
5437 } else {
5438 // Continue selecting by line
5439 int lineMove = LineFromLocation(pt);
5440 LineSelection(lineMove, lineAnchor);
5443 // While dragging to make rectangular selection, we don't want the current
5444 // position to jump to the end of smaller or empty lines.
5445 //xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
5446 xEndSelect = XFromPosition(movePos);
5448 // Autoscroll
5449 PRectangle rcClient = GetClientRectangle();
5450 if (pt.y > rcClient.bottom) {
5451 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5452 if (lineMove < 0) {
5453 lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
5455 ScrollTo(lineMove - LinesOnScreen() + 1);
5456 Redraw();
5457 } else if (pt.y < rcClient.top) {
5458 int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
5459 ScrollTo(lineMove - 1);
5460 Redraw();
5462 EnsureCaretVisible(false, false, true);
5464 if (hsStart != -1 && !PositionIsHotspot(movePos))
5465 SetHotSpotRange(NULL);
5467 } else {
5468 if (vs.fixedColumnWidth > 0) { // There is a margin
5469 if (PointInSelMargin(pt)) {
5470 DisplayCursor(Window::cursorReverseArrow);
5471 return; // No need to test for selection
5474 // Display regular (drag) cursor over selection
5475 if (PointInSelection(pt) && !SelectionEmpty()) {
5476 DisplayCursor(Window::cursorArrow);
5477 } else if (PointIsHotspot(pt)) {
5478 DisplayCursor(Window::cursorHand);
5479 SetHotSpotRange(&pt);
5480 } else {
5481 DisplayCursor(Window::cursorText);
5482 SetHotSpotRange(NULL);
5487 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
5488 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
5489 int newPos = PositionFromLocation(pt);
5490 newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
5491 if (inDragDrop == ddInitial) {
5492 inDragDrop = ddNone;
5493 SetEmptySelection(newPos);
5495 if (HaveMouseCapture()) {
5496 if (PointInSelMargin(pt)) {
5497 DisplayCursor(Window::cursorReverseArrow);
5498 } else {
5499 DisplayCursor(Window::cursorText);
5500 SetHotSpotRange(NULL);
5502 ptMouseLast = pt;
5503 SetMouseCapture(false);
5504 int newPos = PositionFromLocation(pt);
5505 newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
5506 NotifyIndicatorClick(false, newPos, false, false, false);
5507 if (inDragDrop == ddDragging) {
5508 int selStart = SelectionStart();
5509 int selEnd = SelectionEnd();
5510 if (selStart < selEnd) {
5511 if (drag.len) {
5512 if (ctrl) {
5513 if (pdoc->InsertString(newPos, drag.s, drag.len)) {
5514 SetSelection(newPos, newPos + drag.len);
5516 } else if (newPos < selStart) {
5517 pdoc->DeleteChars(selStart, drag.len);
5518 if (pdoc->InsertString(newPos, drag.s, drag.len)) {
5519 SetSelection(newPos, newPos + drag.len);
5521 } else if (newPos > selEnd) {
5522 pdoc->DeleteChars(selStart, drag.len);
5523 newPos -= drag.len;
5524 if (pdoc->InsertString(newPos, drag.s, drag.len)) {
5525 SetSelection(newPos, newPos + drag.len);
5527 } else {
5528 SetEmptySelection(newPos);
5530 drag.Free();
5532 selectionType = selChar;
5534 } else {
5535 if (selectionType == selChar) {
5536 SetSelection(newPos);
5539 SetRectangularRange();
5540 lastClickTime = curTime;
5541 lastClick = pt;
5542 lastXChosen = pt.x;
5543 if (selType == selStream) {
5544 SetLastXChosen();
5546 inDragDrop = ddNone;
5547 EnsureCaretVisible(false);
5551 // Called frequently to perform background UI including
5552 // caret blinking and automatic scrolling.
5553 void Editor::Tick() {
5554 if (HaveMouseCapture()) {
5555 // Auto scroll
5556 ButtonMove(ptMouseLast);
5558 if (caret.period > 0) {
5559 timer.ticksToWait -= timer.tickSize;
5560 if (timer.ticksToWait <= 0) {
5561 caret.on = !caret.on;
5562 timer.ticksToWait = caret.period;
5563 if (caret.active) {
5564 InvalidateCaret();
5568 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
5569 scrollWidth = lineWidthMaxSeen;
5570 SetScrollBars();
5572 if ((dwellDelay < SC_TIME_FOREVER) &&
5573 (ticksToDwell > 0) &&
5574 (!HaveMouseCapture())) {
5575 ticksToDwell -= timer.tickSize;
5576 if (ticksToDwell <= 0) {
5577 dwelling = true;
5578 NotifyDwelling(ptMouseLast, dwelling);
5583 bool Editor::Idle() {
5585 bool idleDone;
5587 bool wrappingDone = wrapState == eWrapNone;
5589 if (!wrappingDone) {
5590 // Wrap lines during idle.
5591 WrapLines(false, -1);
5592 // No more wrapping
5593 if (wrapStart == wrapEnd)
5594 wrappingDone = true;
5597 // Add more idle things to do here, but make sure idleDone is
5598 // set correctly before the function returns. returning
5599 // false will stop calling this idle funtion until SetIdle() is
5600 // called again.
5602 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
5604 return !idleDone;
5607 void Editor::SetFocusState(bool focusState) {
5608 hasFocus = focusState;
5609 NotifyFocus(hasFocus);
5610 if (hasFocus) {
5611 ShowCaretAtCurrentPosition();
5612 } else {
5613 CancelModes();
5614 DropCaret();
5618 bool Editor::PaintContains(PRectangle rc) {
5619 if (rc.Empty()) {
5620 return true;
5621 } else {
5622 return rcPaint.Contains(rc);
5626 bool Editor::PaintContainsMargin() {
5627 PRectangle rcSelMargin = GetClientRectangle();
5628 rcSelMargin.right = vs.fixedColumnWidth;
5629 return PaintContains(rcSelMargin);
5632 void Editor::CheckForChangeOutsidePaint(Range r) {
5633 if (paintState == painting && !paintingAllText) {
5634 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
5635 if (!r.Valid())
5636 return;
5638 PRectangle rcRange = RectangleFromRange(r.start, r.end);
5639 PRectangle rcText = GetTextRectangle();
5640 if (rcRange.top < rcText.top) {
5641 rcRange.top = rcText.top;
5643 if (rcRange.bottom > rcText.bottom) {
5644 rcRange.bottom = rcText.bottom;
5647 if (!PaintContains(rcRange)) {
5648 AbandonPaint();
5653 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
5654 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
5655 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
5656 CheckForChangeOutsidePaint(Range(braces[0]));
5657 CheckForChangeOutsidePaint(Range(pos0));
5658 braces[0] = pos0;
5660 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
5661 CheckForChangeOutsidePaint(Range(braces[1]));
5662 CheckForChangeOutsidePaint(Range(pos1));
5663 braces[1] = pos1;
5665 bracesMatchStyle = matchStyle;
5666 if (paintState == notPainting) {
5667 Redraw();
5672 void Editor::SetDocPointer(Document *document) {
5673 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
5674 pdoc->RemoveWatcher(this, 0);
5675 pdoc->Release();
5676 if (document == NULL) {
5677 pdoc = new Document();
5678 } else {
5679 pdoc = document;
5681 pdoc->AddRef();
5683 // Ensure all positions within document
5684 selType = selStream;
5685 currentPos = 0;
5686 anchor = 0;
5687 targetStart = 0;
5688 targetEnd = 0;
5690 braces[0] = invalidPosition;
5691 braces[1] = invalidPosition;
5693 // Reset the contraction state to fully shown.
5694 cs.Clear();
5695 cs.InsertLines(0, pdoc->LinesTotal() - 1);
5696 llc.Deallocate();
5697 NeedWrapping();
5699 pdoc->AddWatcher(this, 0);
5700 SetScrollBars();
5701 Redraw();
5705 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
5707 void Editor::Expand(int &line, bool doExpand) {
5708 int lineMaxSubord = pdoc->GetLastChild(line);
5709 line++;
5710 while (line <= lineMaxSubord) {
5711 if (doExpand)
5712 cs.SetVisible(line, line, true);
5713 int level = pdoc->GetLevel(line);
5714 if (level & SC_FOLDLEVELHEADERFLAG) {
5715 if (doExpand && cs.GetExpanded(line)) {
5716 Expand(line, true);
5717 } else {
5718 Expand(line, false);
5720 } else {
5721 line++;
5726 void Editor::ToggleContraction(int line) {
5727 if (line >= 0) {
5728 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
5729 line = pdoc->GetFoldParent(line);
5730 if (line < 0)
5731 return;
5734 if (cs.GetExpanded(line)) {
5735 int lineMaxSubord = pdoc->GetLastChild(line);
5736 cs.SetExpanded(line, 0);
5737 if (lineMaxSubord > line) {
5738 cs.SetVisible(line + 1, lineMaxSubord, false);
5740 int lineCurrent = pdoc->LineFromPosition(currentPos);
5741 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
5742 // This does not re-expand the fold
5743 EnsureCaretVisible();
5746 SetScrollBars();
5747 Redraw();
5750 } else {
5751 if (!(cs.GetVisible(line))) {
5752 EnsureLineVisible(line, false);
5753 GoToLine(line);
5755 cs.SetExpanded(line, 1);
5756 Expand(line, true);
5757 SetScrollBars();
5758 Redraw();
5764 * Recurse up from this line to find any folds that prevent this line from being visible
5765 * and unfold them all.
5767 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
5769 // In case in need of wrapping to ensure DisplayFromDoc works.
5770 WrapLines(true, -1);
5772 if (!cs.GetVisible(lineDoc)) {
5773 int lineParent = pdoc->GetFoldParent(lineDoc);
5774 if (lineParent >= 0) {
5775 if (lineDoc != lineParent)
5776 EnsureLineVisible(lineParent, enforcePolicy);
5777 if (!cs.GetExpanded(lineParent)) {
5778 cs.SetExpanded(lineParent, 1);
5779 Expand(lineParent, true);
5782 SetScrollBars();
5783 Redraw();
5785 if (enforcePolicy) {
5786 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5787 if (visiblePolicy & VISIBLE_SLOP) {
5788 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
5789 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
5790 SetVerticalScrollPos();
5791 Redraw();
5792 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
5793 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
5794 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
5795 SetVerticalScrollPos();
5796 Redraw();
5798 } else {
5799 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
5800 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
5801 SetVerticalScrollPos();
5802 Redraw();
5808 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
5809 pdoc->BeginUndoAction();
5810 if (length == -1)
5811 length = istrlen(text);
5812 if (replacePatterns) {
5813 text = pdoc->SubstituteByPosition(text, &length);
5814 if (!text) {
5815 pdoc->EndUndoAction();
5816 return 0;
5819 if (targetStart != targetEnd)
5820 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
5821 targetEnd = targetStart;
5822 pdoc->InsertString(targetStart, text, length);
5823 targetEnd = targetStart + length;
5824 pdoc->EndUndoAction();
5825 return length;
5828 bool Editor::IsUnicodeMode() const {
5829 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
5832 int Editor::CodePage() const {
5833 if (pdoc)
5834 return pdoc->dbcsCodePage;
5835 else
5836 return 0;
5839 int Editor::WrapCount(int line) {
5840 AutoSurface surface(this);
5841 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5843 if (surface && ll) {
5844 LayoutLine(line, surface, vs, ll, wrapWidth);
5845 return ll->lines;
5846 } else {
5847 return 1;
5851 void Editor::AddStyledText(char *buffer, int appendLength) {
5852 // The buffer consists of alternating character bytes and style bytes
5853 size_t textLength = appendLength / 2;
5854 char *text = new char[textLength];
5855 if (text) {
5856 size_t i;
5857 for (i = 0;i < textLength;i++) {
5858 text[i] = buffer[i*2];
5860 pdoc->InsertString(CurrentPosition(), text, textLength);
5861 for (i = 0;i < textLength;i++) {
5862 text[i] = buffer[i*2+1];
5864 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
5865 pdoc->SetStyles(textLength, text);
5866 delete []text;
5868 SetEmptySelection(currentPos + textLength);
5871 static bool ValidMargin(unsigned long wParam) {
5872 return wParam < ViewStyle::margins;
5875 static char *CharPtrFromSPtr(sptr_t lParam) {
5876 return reinterpret_cast<char *>(lParam);
5879 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5880 vs.EnsureStyle(wParam);
5881 switch (iMessage) {
5882 case SCI_STYLESETFORE:
5883 vs.styles[wParam].fore.desired = ColourDesired(lParam);
5884 break;
5885 case SCI_STYLESETBACK:
5886 vs.styles[wParam].back.desired = ColourDesired(lParam);
5887 break;
5888 case SCI_STYLESETBOLD:
5889 vs.styles[wParam].bold = lParam != 0;
5890 break;
5891 case SCI_STYLESETITALIC:
5892 vs.styles[wParam].italic = lParam != 0;
5893 break;
5894 case SCI_STYLESETEOLFILLED:
5895 vs.styles[wParam].eolFilled = lParam != 0;
5896 break;
5897 case SCI_STYLESETSIZE:
5898 vs.styles[wParam].size = lParam;
5899 break;
5900 case SCI_STYLESETFONT:
5901 if (lParam != 0) {
5902 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
5904 break;
5905 case SCI_STYLESETUNDERLINE:
5906 vs.styles[wParam].underline = lParam != 0;
5907 break;
5908 case SCI_STYLESETCASE:
5909 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
5910 break;
5911 case SCI_STYLESETCHARACTERSET:
5912 vs.styles[wParam].characterSet = lParam;
5913 break;
5914 case SCI_STYLESETVISIBLE:
5915 vs.styles[wParam].visible = lParam != 0;
5916 break;
5917 case SCI_STYLESETCHANGEABLE:
5918 vs.styles[wParam].changeable = lParam != 0;
5919 break;
5920 case SCI_STYLESETHOTSPOT:
5921 vs.styles[wParam].hotspot = lParam != 0;
5922 break;
5924 InvalidateStyleRedraw();
5927 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5928 vs.EnsureStyle(wParam);
5929 switch (iMessage) {
5930 case SCI_STYLEGETFORE:
5931 return vs.styles[wParam].fore.desired.AsLong();
5932 case SCI_STYLEGETBACK:
5933 return vs.styles[wParam].back.desired.AsLong();
5934 case SCI_STYLEGETBOLD:
5935 return vs.styles[wParam].bold ? 1 : 0;
5936 case SCI_STYLEGETITALIC:
5937 return vs.styles[wParam].italic ? 1 : 0;
5938 case SCI_STYLEGETEOLFILLED:
5939 return vs.styles[wParam].eolFilled ? 1 : 0;
5940 case SCI_STYLEGETSIZE:
5941 return vs.styles[wParam].size;
5942 case SCI_STYLEGETFONT:
5943 if (lParam != 0)
5944 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
5945 return strlen(vs.styles[wParam].fontName);
5946 case SCI_STYLEGETUNDERLINE:
5947 return vs.styles[wParam].underline ? 1 : 0;
5948 case SCI_STYLEGETCASE:
5949 return static_cast<int>(vs.styles[wParam].caseForce);
5950 case SCI_STYLEGETCHARACTERSET:
5951 return vs.styles[wParam].characterSet;
5952 case SCI_STYLEGETVISIBLE:
5953 return vs.styles[wParam].visible ? 1 : 0;
5954 case SCI_STYLEGETCHANGEABLE:
5955 return vs.styles[wParam].changeable ? 1 : 0;
5956 case SCI_STYLEGETHOTSPOT:
5957 return vs.styles[wParam].hotspot ? 1 : 0;
5959 return 0;
5962 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
5963 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
5965 // Optional macro recording hook
5966 if (recordingMacro)
5967 NotifyMacroRecord(iMessage, wParam, lParam);
5969 switch (iMessage) {
5971 case SCI_GETTEXT: {
5972 if (lParam == 0)
5973 return pdoc->Length() + 1;
5974 if (wParam == 0)
5975 return 0;
5976 char *ptr = CharPtrFromSPtr(lParam);
5977 unsigned int iChar = 0;
5978 for (; iChar < wParam - 1; iChar++)
5979 ptr[iChar] = pdoc->CharAt(iChar);
5980 ptr[iChar] = '\0';
5981 return iChar;
5984 case SCI_SETTEXT: {
5985 if (lParam == 0)
5986 return 0;
5987 pdoc->BeginUndoAction();
5988 pdoc->DeleteChars(0, pdoc->Length());
5989 SetEmptySelection(0);
5990 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
5991 pdoc->EndUndoAction();
5992 return 1;
5995 case SCI_GETTEXTLENGTH:
5996 return pdoc->Length();
5998 case SCI_CUT:
5999 Cut();
6000 SetLastXChosen();
6001 break;
6003 case SCI_COPY:
6004 Copy();
6005 break;
6007 case SCI_COPYALLOWLINE:
6008 CopyAllowLine();
6009 break;
6011 case SCI_COPYRANGE:
6012 CopyRangeToClipboard(wParam, lParam);
6013 break;
6015 case SCI_COPYTEXT:
6016 CopyText(wParam, CharPtrFromSPtr(lParam));
6017 break;
6019 case SCI_PASTE:
6020 Paste();
6021 if (!caretSticky) {
6022 SetLastXChosen();
6024 EnsureCaretVisible();
6025 break;
6027 case SCI_CLEAR:
6028 Clear();
6029 SetLastXChosen();
6030 EnsureCaretVisible();
6031 break;
6033 case SCI_UNDO:
6034 Undo();
6035 SetLastXChosen();
6036 break;
6038 case SCI_CANUNDO:
6039 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
6041 case SCI_EMPTYUNDOBUFFER:
6042 pdoc->DeleteUndoHistory();
6043 return 0;
6045 case SCI_GETFIRSTVISIBLELINE:
6046 return topLine;
6048 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
6049 int lineStart = pdoc->LineStart(wParam);
6050 int lineEnd = pdoc->LineStart(wParam + 1);
6051 if (lParam == 0) {
6052 return lineEnd - lineStart;
6054 char *ptr = CharPtrFromSPtr(lParam);
6055 int iPlace = 0;
6056 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
6057 ptr[iPlace++] = pdoc->CharAt(iChar);
6059 return iPlace;
6062 case SCI_GETLINECOUNT:
6063 if (pdoc->LinesTotal() == 0)
6064 return 1;
6065 else
6066 return pdoc->LinesTotal();
6068 case SCI_GETMODIFY:
6069 return !pdoc->IsSavePoint();
6071 case SCI_SETSEL: {
6072 int nStart = static_cast<int>(wParam);
6073 int nEnd = static_cast<int>(lParam);
6074 if (nEnd < 0)
6075 nEnd = pdoc->Length();
6076 if (nStart < 0)
6077 nStart = nEnd; // Remove selection
6078 selType = selStream;
6079 SetSelection(nEnd, nStart);
6080 EnsureCaretVisible();
6082 break;
6084 case SCI_GETSELTEXT: {
6085 if (lParam == 0) {
6086 if (selType == selStream) {
6087 return 1 + SelectionEnd() - SelectionStart();
6088 } else {
6089 // TODO: why is selLines handled the slow way?
6090 int size = 0;
6091 int extraCharsPerLine = 0;
6092 if (selType != selLines)
6093 extraCharsPerLine = (pdoc->eolMode == SC_EOL_CRLF) ? 2 : 1;
6094 SelectionLineIterator lineIterator(this);
6095 while (lineIterator.Iterate()) {
6096 size += lineIterator.endPos + extraCharsPerLine - lineIterator.startPos;
6099 return 1 + size;
6102 SelectionText selectedText;
6103 CopySelectionRange(&selectedText);
6104 char *ptr = CharPtrFromSPtr(lParam);
6105 int iChar = 0;
6106 if (selectedText.len) {
6107 for (; iChar < selectedText.len; iChar++)
6108 ptr[iChar] = selectedText.s[iChar];
6109 } else {
6110 ptr[0] = '\0';
6112 return iChar;
6115 case SCI_LINEFROMPOSITION:
6116 if (static_cast<int>(wParam) < 0)
6117 return 0;
6118 return pdoc->LineFromPosition(wParam);
6120 case SCI_POSITIONFROMLINE:
6121 if (static_cast<int>(wParam) < 0)
6122 wParam = pdoc->LineFromPosition(SelectionStart());
6123 if (wParam == 0)
6124 return 0; // Even if there is no text, there is a first line that starts at 0
6125 if (static_cast<int>(wParam) > pdoc->LinesTotal())
6126 return -1;
6127 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
6128 // return -1;
6129 return pdoc->LineStart(wParam);
6131 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
6132 case SCI_LINELENGTH:
6133 if ((static_cast<int>(wParam) < 0) ||
6134 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
6135 return 0;
6136 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
6138 case SCI_REPLACESEL: {
6139 if (lParam == 0)
6140 return 0;
6141 pdoc->BeginUndoAction();
6142 ClearSelection();
6143 char *replacement = CharPtrFromSPtr(lParam);
6144 pdoc->InsertCString(currentPos, replacement);
6145 pdoc->EndUndoAction();
6146 SetEmptySelection(currentPos + istrlen(replacement));
6147 EnsureCaretVisible();
6149 break;
6151 case SCI_SETTARGETSTART:
6152 targetStart = wParam;
6153 break;
6155 case SCI_GETTARGETSTART:
6156 return targetStart;
6158 case SCI_SETTARGETEND:
6159 targetEnd = wParam;
6160 break;
6162 case SCI_GETTARGETEND:
6163 return targetEnd;
6165 case SCI_TARGETFROMSELECTION:
6166 if (currentPos < anchor) {
6167 targetStart = currentPos;
6168 targetEnd = anchor;
6169 } else {
6170 targetStart = anchor;
6171 targetEnd = currentPos;
6173 break;
6175 case SCI_REPLACETARGET:
6176 PLATFORM_ASSERT(lParam);
6177 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
6179 case SCI_REPLACETARGETRE:
6180 PLATFORM_ASSERT(lParam);
6181 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
6183 case SCI_SEARCHINTARGET:
6184 PLATFORM_ASSERT(lParam);
6185 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
6187 case SCI_SETSEARCHFLAGS:
6188 searchFlags = wParam;
6189 break;
6191 case SCI_GETSEARCHFLAGS:
6192 return searchFlags;
6194 case SCI_POSITIONBEFORE:
6195 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
6197 case SCI_POSITIONAFTER:
6198 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
6200 case SCI_LINESCROLL:
6201 ScrollTo(topLine + lParam);
6202 HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
6203 return 1;
6205 case SCI_SETXOFFSET:
6206 xOffset = wParam;
6207 SetHorizontalScrollPos();
6208 Redraw();
6209 break;
6211 case SCI_GETXOFFSET:
6212 return xOffset;
6214 case SCI_CHOOSECARETX:
6215 SetLastXChosen();
6216 break;
6218 case SCI_SCROLLCARET:
6219 EnsureCaretVisible();
6220 break;
6222 case SCI_SETREADONLY:
6223 pdoc->SetReadOnly(wParam != 0);
6224 return 1;
6226 case SCI_GETREADONLY:
6227 return pdoc->IsReadOnly();
6229 case SCI_CANPASTE:
6230 return CanPaste();
6232 case SCI_POINTXFROMPOSITION:
6233 if (lParam < 0) {
6234 return 0;
6235 } else {
6236 Point pt = LocationFromPosition(lParam);
6237 return pt.x;
6240 case SCI_POINTYFROMPOSITION:
6241 if (lParam < 0) {
6242 return 0;
6243 } else {
6244 Point pt = LocationFromPosition(lParam);
6245 return pt.y;
6248 case SCI_FINDTEXT:
6249 return FindText(wParam, lParam);
6251 case SCI_GETTEXTRANGE: {
6252 if (lParam == 0)
6253 return 0;
6254 TextRange *tr = reinterpret_cast<TextRange *>(lParam);
6255 int cpMax = tr->chrg.cpMax;
6256 if (cpMax == -1)
6257 cpMax = pdoc->Length();
6258 PLATFORM_ASSERT(cpMax <= pdoc->Length());
6259 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
6260 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
6261 // Spec says copied text is terminated with a NUL
6262 tr->lpstrText[len] = '\0';
6263 return len; // Not including NUL
6266 case SCI_HIDESELECTION:
6267 hideSelection = wParam != 0;
6268 Redraw();
6269 break;
6271 case SCI_FORMATRANGE:
6272 return FormatRange(wParam != 0, reinterpret_cast<RangeToFormat *>(lParam));
6274 case SCI_GETMARGINLEFT:
6275 return vs.leftMarginWidth;
6277 case SCI_GETMARGINRIGHT:
6278 return vs.rightMarginWidth;
6280 case SCI_SETMARGINLEFT:
6281 vs.leftMarginWidth = lParam;
6282 InvalidateStyleRedraw();
6283 break;
6285 case SCI_SETMARGINRIGHT:
6286 vs.rightMarginWidth = lParam;
6287 InvalidateStyleRedraw();
6288 break;
6290 // Control specific mesages
6292 case SCI_ADDTEXT: {
6293 if (lParam == 0)
6294 return 0;
6295 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
6296 SetEmptySelection(currentPos + wParam);
6297 return 0;
6300 case SCI_ADDSTYLEDTEXT:
6301 if (lParam)
6302 AddStyledText(CharPtrFromSPtr(lParam), wParam);
6303 return 0;
6305 case SCI_INSERTTEXT: {
6306 if (lParam == 0)
6307 return 0;
6308 int insertPos = wParam;
6309 if (static_cast<int>(wParam) == -1)
6310 insertPos = CurrentPosition();
6311 int newCurrent = CurrentPosition();
6312 char *sz = CharPtrFromSPtr(lParam);
6313 pdoc->InsertCString(insertPos, sz);
6314 if (newCurrent > insertPos)
6315 newCurrent += istrlen(sz);
6316 SetEmptySelection(newCurrent);
6317 return 0;
6320 case SCI_APPENDTEXT:
6321 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
6322 return 0;
6324 case SCI_CLEARALL:
6325 ClearAll();
6326 return 0;
6328 case SCI_CLEARDOCUMENTSTYLE:
6329 ClearDocumentStyle();
6330 return 0;
6332 case SCI_SETUNDOCOLLECTION:
6333 pdoc->SetUndoCollection(wParam != 0);
6334 return 0;
6336 case SCI_GETUNDOCOLLECTION:
6337 return pdoc->IsCollectingUndo();
6339 case SCI_BEGINUNDOACTION:
6340 pdoc->BeginUndoAction();
6341 return 0;
6343 case SCI_ENDUNDOACTION:
6344 pdoc->EndUndoAction();
6345 return 0;
6347 case SCI_GETCARETPERIOD:
6348 return caret.period;
6350 case SCI_SETCARETPERIOD:
6351 caret.period = wParam;
6352 break;
6354 case SCI_SETWORDCHARS: {
6355 pdoc->SetDefaultCharClasses(false);
6356 if (lParam == 0)
6357 return 0;
6358 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
6360 break;
6362 case SCI_SETWHITESPACECHARS: {
6363 if (lParam == 0)
6364 return 0;
6365 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
6367 break;
6369 case SCI_SETCHARSDEFAULT:
6370 pdoc->SetDefaultCharClasses(true);
6371 break;
6373 case SCI_GETLENGTH:
6374 return pdoc->Length();
6376 case SCI_ALLOCATE:
6377 pdoc->Allocate(wParam);
6378 break;
6380 case SCI_GETCHARAT:
6381 return pdoc->CharAt(wParam);
6383 case SCI_SETCURRENTPOS:
6384 SetSelection(wParam, anchor);
6385 break;
6387 case SCI_GETCURRENTPOS:
6388 return currentPos;
6390 case SCI_SETANCHOR:
6391 SetSelection(currentPos, wParam);
6392 break;
6394 case SCI_GETANCHOR:
6395 return anchor;
6397 case SCI_SETSELECTIONSTART:
6398 SetSelection(Platform::Maximum(currentPos, wParam), wParam);
6399 break;
6401 case SCI_GETSELECTIONSTART:
6402 return Platform::Minimum(anchor, currentPos);
6404 case SCI_SETSELECTIONEND:
6405 SetSelection(wParam, Platform::Minimum(anchor, wParam));
6406 break;
6408 case SCI_GETSELECTIONEND:
6409 return Platform::Maximum(anchor, currentPos);
6411 case SCI_SETPRINTMAGNIFICATION:
6412 printMagnification = wParam;
6413 break;
6415 case SCI_GETPRINTMAGNIFICATION:
6416 return printMagnification;
6418 case SCI_SETPRINTCOLOURMODE:
6419 printColourMode = wParam;
6420 break;
6422 case SCI_GETPRINTCOLOURMODE:
6423 return printColourMode;
6425 case SCI_SETPRINTWRAPMODE:
6426 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
6427 break;
6429 case SCI_GETPRINTWRAPMODE:
6430 return printWrapState;
6432 case SCI_GETSTYLEAT:
6433 if (static_cast<int>(wParam) >= pdoc->Length())
6434 return 0;
6435 else
6436 return pdoc->StyleAt(wParam);
6438 case SCI_REDO:
6439 Redo();
6440 break;
6442 case SCI_SELECTALL:
6443 SelectAll();
6444 break;
6446 case SCI_SETSAVEPOINT:
6447 pdoc->SetSavePoint();
6448 break;
6450 case SCI_GETSTYLEDTEXT: {
6451 if (lParam == 0)
6452 return 0;
6453 TextRange *tr = reinterpret_cast<TextRange *>(lParam);
6454 int iPlace = 0;
6455 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
6456 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
6457 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
6459 tr->lpstrText[iPlace] = '\0';
6460 tr->lpstrText[iPlace + 1] = '\0';
6461 return iPlace;
6464 case SCI_CANREDO:
6465 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
6467 case SCI_MARKERLINEFROMHANDLE:
6468 return pdoc->LineFromHandle(wParam);
6470 case SCI_MARKERDELETEHANDLE:
6471 pdoc->DeleteMarkFromHandle(wParam);
6472 break;
6474 case SCI_GETVIEWWS:
6475 return vs.viewWhitespace;
6477 case SCI_SETVIEWWS:
6478 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
6479 Redraw();
6480 break;
6482 case SCI_POSITIONFROMPOINT:
6483 return PositionFromLocation(Point(wParam, lParam));
6485 case SCI_POSITIONFROMPOINTCLOSE:
6486 return PositionFromLocationClose(Point(wParam, lParam));
6488 case SCI_GOTOLINE:
6489 GoToLine(wParam);
6490 break;
6492 case SCI_GOTOPOS:
6493 SetEmptySelection(wParam);
6494 EnsureCaretVisible();
6495 Redraw();
6496 break;
6498 case SCI_GETCURLINE: {
6499 int lineCurrentPos = pdoc->LineFromPosition(currentPos);
6500 int lineStart = pdoc->LineStart(lineCurrentPos);
6501 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
6502 if (lParam == 0) {
6503 return 1 + lineEnd - lineStart;
6505 PLATFORM_ASSERT(wParam > 0);
6506 char *ptr = CharPtrFromSPtr(lParam);
6507 unsigned int iPlace = 0;
6508 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
6509 ptr[iPlace++] = pdoc->CharAt(iChar);
6511 ptr[iPlace] = '\0';
6512 return currentPos - lineStart;
6515 case SCI_GETENDSTYLED:
6516 return pdoc->GetEndStyled();
6518 case SCI_GETEOLMODE:
6519 return pdoc->eolMode;
6521 case SCI_SETEOLMODE:
6522 pdoc->eolMode = wParam;
6523 break;
6525 case SCI_STARTSTYLING:
6526 pdoc->StartStyling(wParam, static_cast<char>(lParam));
6527 break;
6529 case SCI_SETSTYLING:
6530 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
6531 break;
6533 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
6534 if (lParam == 0)
6535 return 0;
6536 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
6537 break;
6539 case SCI_SETBUFFEREDDRAW:
6540 bufferedDraw = wParam != 0;
6541 break;
6543 case SCI_GETBUFFEREDDRAW:
6544 return bufferedDraw;
6546 case SCI_GETTWOPHASEDRAW:
6547 return twoPhaseDraw;
6549 case SCI_SETTWOPHASEDRAW:
6550 twoPhaseDraw = wParam != 0;
6551 InvalidateStyleRedraw();
6552 break;
6554 case SCI_SETTABWIDTH:
6555 if (wParam > 0) {
6556 pdoc->tabInChars = wParam;
6557 if (pdoc->indentInChars == 0)
6558 pdoc->actualIndentInChars = pdoc->tabInChars;
6560 InvalidateStyleRedraw();
6561 break;
6563 case SCI_GETTABWIDTH:
6564 return pdoc->tabInChars;
6566 case SCI_SETINDENT:
6567 pdoc->indentInChars = wParam;
6568 if (pdoc->indentInChars != 0)
6569 pdoc->actualIndentInChars = pdoc->indentInChars;
6570 else
6571 pdoc->actualIndentInChars = pdoc->tabInChars;
6572 InvalidateStyleRedraw();
6573 break;
6575 case SCI_GETINDENT:
6576 return pdoc->indentInChars;
6578 case SCI_SETUSETABS:
6579 pdoc->useTabs = wParam != 0;
6580 InvalidateStyleRedraw();
6581 break;
6583 case SCI_GETUSETABS:
6584 return pdoc->useTabs;
6586 case SCI_SETLINEINDENTATION:
6587 pdoc->SetLineIndentation(wParam, lParam);
6588 break;
6590 case SCI_GETLINEINDENTATION:
6591 return pdoc->GetLineIndentation(wParam);
6593 case SCI_GETLINEINDENTPOSITION:
6594 return pdoc->GetLineIndentPosition(wParam);
6596 case SCI_SETTABINDENTS:
6597 pdoc->tabIndents = wParam != 0;
6598 break;
6600 case SCI_GETTABINDENTS:
6601 return pdoc->tabIndents;
6603 case SCI_SETBACKSPACEUNINDENTS:
6604 pdoc->backspaceUnindents = wParam != 0;
6605 break;
6607 case SCI_GETBACKSPACEUNINDENTS:
6608 return pdoc->backspaceUnindents;
6610 case SCI_SETMOUSEDWELLTIME:
6611 dwellDelay = wParam;
6612 ticksToDwell = dwellDelay;
6613 break;
6615 case SCI_GETMOUSEDWELLTIME:
6616 return dwellDelay;
6618 case SCI_WORDSTARTPOSITION:
6619 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
6621 case SCI_WORDENDPOSITION:
6622 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
6624 case SCI_SETWRAPMODE:
6625 switch (wParam) {
6626 case SC_WRAP_WORD:
6627 wrapState = eWrapWord;
6628 break;
6629 case SC_WRAP_CHAR:
6630 wrapState = eWrapChar;
6631 break;
6632 default:
6633 wrapState = eWrapNone;
6634 break;
6636 xOffset = 0;
6637 InvalidateStyleRedraw();
6638 ReconfigureScrollBars();
6639 break;
6641 case SCI_GETWRAPMODE:
6642 return wrapState;
6644 case SCI_SETWRAPVISUALFLAGS:
6645 wrapVisualFlags = wParam;
6646 actualWrapVisualStartIndent = wrapVisualStartIndent;
6647 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
6648 actualWrapVisualStartIndent = 1; // must indent to show start visual
6649 InvalidateStyleRedraw();
6650 ReconfigureScrollBars();
6651 break;
6653 case SCI_GETWRAPVISUALFLAGS:
6654 return wrapVisualFlags;
6656 case SCI_SETWRAPVISUALFLAGSLOCATION:
6657 wrapVisualFlagsLocation = wParam;
6658 InvalidateStyleRedraw();
6659 break;
6661 case SCI_GETWRAPVISUALFLAGSLOCATION:
6662 return wrapVisualFlagsLocation;
6664 case SCI_SETWRAPSTARTINDENT:
6665 wrapVisualStartIndent = wParam;
6666 actualWrapVisualStartIndent = wrapVisualStartIndent;
6667 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
6668 actualWrapVisualStartIndent = 1; // must indent to show start visual
6669 InvalidateStyleRedraw();
6670 ReconfigureScrollBars();
6671 break;
6673 case SCI_GETWRAPSTARTINDENT:
6674 return wrapVisualStartIndent;
6676 case SCI_SETLAYOUTCACHE:
6677 llc.SetLevel(wParam);
6678 break;
6680 case SCI_GETLAYOUTCACHE:
6681 return llc.GetLevel();
6683 case SCI_SETPOSITIONCACHE:
6684 posCache.SetSize(wParam);
6685 break;
6687 case SCI_GETPOSITIONCACHE:
6688 return posCache.GetSize();
6690 case SCI_SETSCROLLWIDTH:
6691 PLATFORM_ASSERT(wParam > 0);
6692 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
6693 lineWidthMaxSeen = 0;
6694 scrollWidth = wParam;
6695 SetScrollBars();
6697 break;
6699 case SCI_GETSCROLLWIDTH:
6700 return scrollWidth;
6702 case SCI_SETSCROLLWIDTHTRACKING:
6703 trackLineWidth = wParam != 0;
6704 break;
6706 case SCI_GETSCROLLWIDTHTRACKING:
6707 return trackLineWidth;
6709 case SCI_LINESJOIN:
6710 LinesJoin();
6711 break;
6713 case SCI_LINESSPLIT:
6714 LinesSplit(wParam);
6715 break;
6717 case SCI_TEXTWIDTH:
6718 PLATFORM_ASSERT(wParam < vs.stylesSize);
6719 PLATFORM_ASSERT(lParam);
6720 return TextWidth(wParam, CharPtrFromSPtr(lParam));
6722 case SCI_TEXTHEIGHT:
6723 return vs.lineHeight;
6725 case SCI_SETENDATLASTLINE:
6726 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6727 if (endAtLastLine != (wParam != 0)) {
6728 endAtLastLine = wParam != 0;
6729 SetScrollBars();
6731 break;
6733 case SCI_GETENDATLASTLINE:
6734 return endAtLastLine;
6736 case SCI_SETCARETSTICKY:
6737 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
6738 if (caretSticky != (wParam != 0)) {
6739 caretSticky = wParam != 0;
6741 break;
6743 case SCI_GETCARETSTICKY:
6744 return caretSticky;
6746 case SCI_TOGGLECARETSTICKY:
6747 caretSticky = !caretSticky;
6748 break;
6750 case SCI_GETCOLUMN:
6751 return pdoc->GetColumn(wParam);
6753 case SCI_FINDCOLUMN:
6754 return pdoc->FindColumn(wParam, lParam);
6756 case SCI_SETHSCROLLBAR :
6757 if (horizontalScrollBarVisible != (wParam != 0)) {
6758 horizontalScrollBarVisible = wParam != 0;
6759 SetScrollBars();
6760 ReconfigureScrollBars();
6762 break;
6764 case SCI_GETHSCROLLBAR:
6765 return horizontalScrollBarVisible;
6767 case SCI_SETVSCROLLBAR:
6768 if (verticalScrollBarVisible != (wParam != 0)) {
6769 verticalScrollBarVisible = wParam != 0;
6770 SetScrollBars();
6771 ReconfigureScrollBars();
6773 break;
6775 case SCI_GETVSCROLLBAR:
6776 return verticalScrollBarVisible;
6778 case SCI_SETINDENTATIONGUIDES:
6779 vs.viewIndentationGuides = IndentView(wParam);
6780 Redraw();
6781 break;
6783 case SCI_GETINDENTATIONGUIDES:
6784 return vs.viewIndentationGuides;
6786 case SCI_SETHIGHLIGHTGUIDE:
6787 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
6788 highlightGuideColumn = wParam;
6789 Redraw();
6791 break;
6793 case SCI_GETHIGHLIGHTGUIDE:
6794 return highlightGuideColumn;
6796 case SCI_GETLINEENDPOSITION:
6797 return pdoc->LineEnd(wParam);
6799 case SCI_SETCODEPAGE:
6800 if (ValidCodePage(wParam)) {
6801 pdoc->dbcsCodePage = wParam;
6802 InvalidateStyleRedraw();
6804 break;
6806 case SCI_GETCODEPAGE:
6807 return pdoc->dbcsCodePage;
6809 case SCI_SETUSEPALETTE:
6810 palette.allowRealization = wParam != 0;
6811 InvalidateStyleRedraw();
6812 break;
6814 case SCI_GETUSEPALETTE:
6815 return palette.allowRealization;
6817 // Marker definition and setting
6818 case SCI_MARKERDEFINE:
6819 if (wParam <= MARKER_MAX)
6820 vs.markers[wParam].markType = lParam;
6821 InvalidateStyleData();
6822 RedrawSelMargin();
6823 break;
6824 case SCI_MARKERSETFORE:
6825 if (wParam <= MARKER_MAX)
6826 vs.markers[wParam].fore.desired = ColourDesired(lParam);
6827 InvalidateStyleData();
6828 RedrawSelMargin();
6829 break;
6830 case SCI_MARKERSETBACK:
6831 if (wParam <= MARKER_MAX)
6832 vs.markers[wParam].back.desired = ColourDesired(lParam);
6833 InvalidateStyleData();
6834 RedrawSelMargin();
6835 break;
6836 case SCI_MARKERSETALPHA:
6837 if (wParam <= MARKER_MAX)
6838 vs.markers[wParam].alpha = lParam;
6839 InvalidateStyleRedraw();
6840 break;
6841 case SCI_MARKERADD: {
6842 int markerID = pdoc->AddMark(wParam, lParam);
6843 return markerID;
6845 case SCI_MARKERADDSET:
6846 if (lParam != 0)
6847 pdoc->AddMarkSet(wParam, lParam);
6848 break;
6850 case SCI_MARKERDELETE:
6851 pdoc->DeleteMark(wParam, lParam);
6852 break;
6854 case SCI_MARKERDELETEALL:
6855 pdoc->DeleteAllMarks(static_cast<int>(wParam));
6856 break;
6858 case SCI_MARKERGET:
6859 return pdoc->GetMark(wParam);
6861 case SCI_MARKERNEXT: {
6862 int lt = pdoc->LinesTotal();
6863 for (int iLine = wParam; iLine < lt; iLine++) {
6864 if ((pdoc->GetMark(iLine) & lParam) != 0)
6865 return iLine;
6868 return -1;
6870 case SCI_MARKERPREVIOUS: {
6871 for (int iLine = wParam; iLine >= 0; iLine--) {
6872 if ((pdoc->GetMark(iLine) & lParam) != 0)
6873 return iLine;
6876 return -1;
6878 case SCI_MARKERDEFINEPIXMAP:
6879 if (wParam <= MARKER_MAX) {
6880 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
6882 InvalidateStyleData();
6883 RedrawSelMargin();
6884 break;
6886 case SCI_SETMARGINTYPEN:
6887 if (ValidMargin(wParam)) {
6888 vs.ms[wParam].style = lParam;
6889 InvalidateStyleRedraw();
6891 break;
6893 case SCI_GETMARGINTYPEN:
6894 if (ValidMargin(wParam))
6895 return vs.ms[wParam].style;
6896 else
6897 return 0;
6899 case SCI_SETMARGINWIDTHN:
6900 if (ValidMargin(wParam)) {
6901 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
6902 if (vs.ms[wParam].width != lParam) {
6903 vs.ms[wParam].width = lParam;
6904 InvalidateStyleRedraw();
6907 break;
6909 case SCI_GETMARGINWIDTHN:
6910 if (ValidMargin(wParam))
6911 return vs.ms[wParam].width;
6912 else
6913 return 0;
6915 case SCI_SETMARGINMASKN:
6916 if (ValidMargin(wParam)) {
6917 vs.ms[wParam].mask = lParam;
6918 InvalidateStyleRedraw();
6920 break;
6922 case SCI_GETMARGINMASKN:
6923 if (ValidMargin(wParam))
6924 return vs.ms[wParam].mask;
6925 else
6926 return 0;
6928 case SCI_SETMARGINSENSITIVEN:
6929 if (ValidMargin(wParam)) {
6930 vs.ms[wParam].sensitive = lParam != 0;
6931 InvalidateStyleRedraw();
6933 break;
6935 case SCI_GETMARGINSENSITIVEN:
6936 if (ValidMargin(wParam))
6937 return vs.ms[wParam].sensitive ? 1 : 0;
6938 else
6939 return 0;
6941 case SCI_STYLECLEARALL:
6942 vs.ClearStyles();
6943 InvalidateStyleRedraw();
6944 break;
6946 case SCI_STYLESETFORE:
6947 case SCI_STYLESETBACK:
6948 case SCI_STYLESETBOLD:
6949 case SCI_STYLESETITALIC:
6950 case SCI_STYLESETEOLFILLED:
6951 case SCI_STYLESETSIZE:
6952 case SCI_STYLESETFONT:
6953 case SCI_STYLESETUNDERLINE:
6954 case SCI_STYLESETCASE:
6955 case SCI_STYLESETCHARACTERSET:
6956 case SCI_STYLESETVISIBLE:
6957 case SCI_STYLESETCHANGEABLE:
6958 case SCI_STYLESETHOTSPOT:
6959 StyleSetMessage(iMessage, wParam, lParam);
6960 break;
6962 case SCI_STYLEGETFORE:
6963 case SCI_STYLEGETBACK:
6964 case SCI_STYLEGETBOLD:
6965 case SCI_STYLEGETITALIC:
6966 case SCI_STYLEGETEOLFILLED:
6967 case SCI_STYLEGETSIZE:
6968 case SCI_STYLEGETFONT:
6969 case SCI_STYLEGETUNDERLINE:
6970 case SCI_STYLEGETCASE:
6971 case SCI_STYLEGETCHARACTERSET:
6972 case SCI_STYLEGETVISIBLE:
6973 case SCI_STYLEGETCHANGEABLE:
6974 case SCI_STYLEGETHOTSPOT:
6975 return StyleGetMessage(iMessage, wParam, lParam);
6977 case SCI_STYLERESETDEFAULT:
6978 vs.ResetDefaultStyle();
6979 InvalidateStyleRedraw();
6980 break;
6981 case SCI_SETSTYLEBITS:
6982 vs.EnsureStyle((1 << wParam) - 1);
6983 pdoc->SetStylingBits(wParam);
6984 break;
6986 case SCI_GETSTYLEBITS:
6987 return pdoc->stylingBits;
6989 case SCI_SETLINESTATE:
6990 return pdoc->SetLineState(wParam, lParam);
6992 case SCI_GETLINESTATE:
6993 return pdoc->GetLineState(wParam);
6995 case SCI_GETMAXLINESTATE:
6996 return pdoc->GetMaxLineState();
6998 case SCI_GETCARETLINEVISIBLE:
6999 return vs.showCaretLineBackground;
7000 case SCI_SETCARETLINEVISIBLE:
7001 vs.showCaretLineBackground = wParam != 0;
7002 InvalidateStyleRedraw();
7003 break;
7004 case SCI_GETCARETLINEBACK:
7005 return vs.caretLineBackground.desired.AsLong();
7006 case SCI_SETCARETLINEBACK:
7007 vs.caretLineBackground.desired = wParam;
7008 InvalidateStyleRedraw();
7009 break;
7010 case SCI_GETCARETLINEBACKALPHA:
7011 return vs.caretLineAlpha;
7012 case SCI_SETCARETLINEBACKALPHA:
7013 vs.caretLineAlpha = wParam;
7014 InvalidateStyleRedraw();
7015 break;
7017 // Folding messages
7019 case SCI_VISIBLEFROMDOCLINE:
7020 return cs.DisplayFromDoc(wParam);
7022 case SCI_DOCLINEFROMVISIBLE:
7023 return cs.DocFromDisplay(wParam);
7025 case SCI_WRAPCOUNT:
7026 return WrapCount(wParam);
7028 case SCI_SETFOLDLEVEL: {
7029 int prev = pdoc->SetLevel(wParam, lParam);
7030 if (prev != lParam)
7031 RedrawSelMargin();
7032 return prev;
7035 case SCI_GETFOLDLEVEL:
7036 return pdoc->GetLevel(wParam);
7038 case SCI_GETLASTCHILD:
7039 return pdoc->GetLastChild(wParam, lParam);
7041 case SCI_GETFOLDPARENT:
7042 return pdoc->GetFoldParent(wParam);
7044 case SCI_SHOWLINES:
7045 cs.SetVisible(wParam, lParam, true);
7046 SetScrollBars();
7047 Redraw();
7048 break;
7050 case SCI_HIDELINES:
7051 if (wParam > 0)
7052 cs.SetVisible(wParam, lParam, false);
7053 SetScrollBars();
7054 Redraw();
7055 break;
7057 case SCI_GETLINEVISIBLE:
7058 return cs.GetVisible(wParam);
7060 case SCI_SETFOLDEXPANDED:
7061 if (cs.SetExpanded(wParam, lParam != 0)) {
7062 RedrawSelMargin();
7064 break;
7066 case SCI_GETFOLDEXPANDED:
7067 return cs.GetExpanded(wParam);
7069 case SCI_SETFOLDFLAGS:
7070 foldFlags = wParam;
7071 Redraw();
7072 break;
7074 case SCI_TOGGLEFOLD:
7075 ToggleContraction(wParam);
7076 break;
7078 case SCI_ENSUREVISIBLE:
7079 EnsureLineVisible(wParam, false);
7080 break;
7082 case SCI_ENSUREVISIBLEENFORCEPOLICY:
7083 EnsureLineVisible(wParam, true);
7084 break;
7086 case SCI_SEARCHANCHOR:
7087 SearchAnchor();
7088 break;
7090 case SCI_SEARCHNEXT:
7091 case SCI_SEARCHPREV:
7092 return SearchText(iMessage, wParam, lParam);
7094 #ifdef INCLUDE_DEPRECATED_FEATURES
7095 case SCI_SETCARETPOLICY: // Deprecated
7096 caretXPolicy = caretYPolicy = wParam;
7097 caretXSlop = caretYSlop = lParam;
7098 break;
7099 #endif
7101 case SCI_SETXCARETPOLICY:
7102 caretXPolicy = wParam;
7103 caretXSlop = lParam;
7104 break;
7106 case SCI_SETYCARETPOLICY:
7107 caretYPolicy = wParam;
7108 caretYSlop = lParam;
7109 break;
7111 case SCI_SETVISIBLEPOLICY:
7112 visiblePolicy = wParam;
7113 visibleSlop = lParam;
7114 break;
7116 case SCI_LINESONSCREEN:
7117 return LinesOnScreen();
7119 case SCI_SETSELFORE:
7120 vs.selforeset = wParam != 0;
7121 vs.selforeground.desired = ColourDesired(lParam);
7122 InvalidateStyleRedraw();
7123 break;
7125 case SCI_SETSELBACK:
7126 vs.selbackset = wParam != 0;
7127 vs.selbackground.desired = ColourDesired(lParam);
7128 InvalidateStyleRedraw();
7129 break;
7131 case SCI_SETSELALPHA:
7132 vs.selAlpha = wParam;
7133 InvalidateStyleRedraw();
7134 break;
7136 case SCI_GETSELALPHA:
7137 return vs.selAlpha;
7139 case SCI_GETSELEOLFILLED:
7140 return vs.selEOLFilled;
7142 case SCI_SETSELEOLFILLED:
7143 vs.selEOLFilled = wParam != 0;
7144 InvalidateStyleRedraw();
7145 break;
7147 case SCI_SETWHITESPACEFORE:
7148 vs.whitespaceForegroundSet = wParam != 0;
7149 vs.whitespaceForeground.desired = ColourDesired(lParam);
7150 InvalidateStyleRedraw();
7151 break;
7153 case SCI_SETWHITESPACEBACK:
7154 vs.whitespaceBackgroundSet = wParam != 0;
7155 vs.whitespaceBackground.desired = ColourDesired(lParam);
7156 InvalidateStyleRedraw();
7157 break;
7159 case SCI_SETCARETFORE:
7160 vs.caretcolour.desired = ColourDesired(wParam);
7161 InvalidateStyleRedraw();
7162 break;
7164 case SCI_GETCARETFORE:
7165 return vs.caretcolour.desired.AsLong();
7167 case SCI_SETCARETSTYLE:
7168 if (wParam >= CARETSTYLE_INVISIBLE && wParam <= CARETSTYLE_BLOCK)
7169 vs.caretStyle = wParam;
7170 else
7171 /* Default to the line caret */
7172 vs.caretStyle = CARETSTYLE_LINE;
7173 InvalidateStyleRedraw();
7174 break;
7176 case SCI_GETCARETSTYLE:
7177 return vs.caretStyle;
7179 case SCI_SETCARETWIDTH:
7180 if (wParam <= 0)
7181 vs.caretWidth = 0;
7182 else if (wParam >= 3)
7183 vs.caretWidth = 3;
7184 else
7185 vs.caretWidth = wParam;
7186 InvalidateStyleRedraw();
7187 break;
7189 case SCI_GETCARETWIDTH:
7190 return vs.caretWidth;
7192 case SCI_ASSIGNCMDKEY:
7193 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7194 Platform::HighShortFromLong(wParam), lParam);
7195 break;
7197 case SCI_CLEARCMDKEY:
7198 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
7199 Platform::HighShortFromLong(wParam), SCI_NULL);
7200 break;
7202 case SCI_CLEARALLCMDKEYS:
7203 kmap.Clear();
7204 break;
7206 case SCI_INDICSETSTYLE:
7207 if (wParam <= INDIC_MAX) {
7208 vs.indicators[wParam].style = lParam;
7209 InvalidateStyleRedraw();
7211 break;
7213 case SCI_INDICGETSTYLE:
7214 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
7216 case SCI_INDICSETFORE:
7217 if (wParam <= INDIC_MAX) {
7218 vs.indicators[wParam].fore.desired = ColourDesired(lParam);
7219 InvalidateStyleRedraw();
7221 break;
7223 case SCI_INDICGETFORE:
7224 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
7226 case SCI_INDICSETUNDER:
7227 if (wParam <= INDIC_MAX) {
7228 vs.indicators[wParam].under = lParam != 0;
7229 InvalidateStyleRedraw();
7231 break;
7233 case SCI_INDICGETUNDER:
7234 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
7236 case SCI_SETINDICATORCURRENT:
7237 pdoc->decorations.SetCurrentIndicator(wParam);
7238 break;
7239 case SCI_GETINDICATORCURRENT:
7240 return pdoc->decorations.GetCurrentIndicator();
7241 case SCI_SETINDICATORVALUE:
7242 pdoc->decorations.SetCurrentValue(wParam);
7243 break;
7244 case SCI_GETINDICATORVALUE:
7245 return pdoc->decorations.GetCurrentValue();
7247 case SCI_INDICATORFILLRANGE:
7248 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
7249 break;
7251 case SCI_INDICATORCLEARRANGE:
7252 pdoc->DecorationFillRange(wParam, 0, lParam);
7253 break;
7255 case SCI_INDICATORALLONFOR:
7256 return pdoc->decorations.AllOnFor(wParam);
7258 case SCI_INDICATORVALUEAT:
7259 return pdoc->decorations.ValueAt(wParam, lParam);
7261 case SCI_INDICATORSTART:
7262 return pdoc->decorations.Start(wParam, lParam);
7264 case SCI_INDICATOREND:
7265 return pdoc->decorations.End(wParam, lParam);
7267 case SCI_LINEDOWN:
7268 case SCI_LINEDOWNEXTEND:
7269 case SCI_PARADOWN:
7270 case SCI_PARADOWNEXTEND:
7271 case SCI_LINEUP:
7272 case SCI_LINEUPEXTEND:
7273 case SCI_PARAUP:
7274 case SCI_PARAUPEXTEND:
7275 case SCI_CHARLEFT:
7276 case SCI_CHARLEFTEXTEND:
7277 case SCI_CHARRIGHT:
7278 case SCI_CHARRIGHTEXTEND:
7279 case SCI_WORDLEFT:
7280 case SCI_WORDLEFTEXTEND:
7281 case SCI_WORDRIGHT:
7282 case SCI_WORDRIGHTEXTEND:
7283 case SCI_WORDLEFTEND:
7284 case SCI_WORDLEFTENDEXTEND:
7285 case SCI_WORDRIGHTEND:
7286 case SCI_WORDRIGHTENDEXTEND:
7287 case SCI_HOME:
7288 case SCI_HOMEEXTEND:
7289 case SCI_LINEEND:
7290 case SCI_LINEENDEXTEND:
7291 case SCI_HOMEWRAP:
7292 case SCI_HOMEWRAPEXTEND:
7293 case SCI_LINEENDWRAP:
7294 case SCI_LINEENDWRAPEXTEND:
7295 case SCI_DOCUMENTSTART:
7296 case SCI_DOCUMENTSTARTEXTEND:
7297 case SCI_DOCUMENTEND:
7298 case SCI_DOCUMENTENDEXTEND:
7300 case SCI_STUTTEREDPAGEUP:
7301 case SCI_STUTTEREDPAGEUPEXTEND:
7302 case SCI_STUTTEREDPAGEDOWN:
7303 case SCI_STUTTEREDPAGEDOWNEXTEND:
7305 case SCI_PAGEUP:
7306 case SCI_PAGEUPEXTEND:
7307 case SCI_PAGEDOWN:
7308 case SCI_PAGEDOWNEXTEND:
7309 case SCI_EDITTOGGLEOVERTYPE:
7310 case SCI_CANCEL:
7311 case SCI_DELETEBACK:
7312 case SCI_TAB:
7313 case SCI_BACKTAB:
7314 case SCI_NEWLINE:
7315 case SCI_FORMFEED:
7316 case SCI_VCHOME:
7317 case SCI_VCHOMEEXTEND:
7318 case SCI_VCHOMEWRAP:
7319 case SCI_VCHOMEWRAPEXTEND:
7320 case SCI_ZOOMIN:
7321 case SCI_ZOOMOUT:
7322 case SCI_DELWORDLEFT:
7323 case SCI_DELWORDRIGHT:
7324 case SCI_DELWORDRIGHTEND:
7325 case SCI_DELLINELEFT:
7326 case SCI_DELLINERIGHT:
7327 case SCI_LINECOPY:
7328 case SCI_LINECUT:
7329 case SCI_LINEDELETE:
7330 case SCI_LINETRANSPOSE:
7331 case SCI_LINEDUPLICATE:
7332 case SCI_LOWERCASE:
7333 case SCI_UPPERCASE:
7334 case SCI_LINESCROLLDOWN:
7335 case SCI_LINESCROLLUP:
7336 case SCI_WORDPARTLEFT:
7337 case SCI_WORDPARTLEFTEXTEND:
7338 case SCI_WORDPARTRIGHT:
7339 case SCI_WORDPARTRIGHTEXTEND:
7340 case SCI_DELETEBACKNOTLINE:
7341 case SCI_HOMEDISPLAY:
7342 case SCI_HOMEDISPLAYEXTEND:
7343 case SCI_LINEENDDISPLAY:
7344 case SCI_LINEENDDISPLAYEXTEND:
7345 case SCI_LINEDOWNRECTEXTEND:
7346 case SCI_LINEUPRECTEXTEND:
7347 case SCI_CHARLEFTRECTEXTEND:
7348 case SCI_CHARRIGHTRECTEXTEND:
7349 case SCI_HOMERECTEXTEND:
7350 case SCI_VCHOMERECTEXTEND:
7351 case SCI_LINEENDRECTEXTEND:
7352 case SCI_PAGEUPRECTEXTEND:
7353 case SCI_PAGEDOWNRECTEXTEND:
7354 case SCI_SELECTIONDUPLICATE:
7355 return KeyCommand(iMessage);
7357 case SCI_BRACEHIGHLIGHT:
7358 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
7359 break;
7361 case SCI_BRACEBADLIGHT:
7362 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
7363 break;
7365 case SCI_BRACEMATCH:
7366 // wParam is position of char to find brace for,
7367 // lParam is maximum amount of text to restyle to find it
7368 return pdoc->BraceMatch(wParam, lParam);
7370 case SCI_GETVIEWEOL:
7371 return vs.viewEOL;
7373 case SCI_SETVIEWEOL:
7374 vs.viewEOL = wParam != 0;
7375 InvalidateStyleRedraw();
7376 break;
7378 case SCI_SETZOOM:
7379 vs.zoomLevel = wParam;
7380 InvalidateStyleRedraw();
7381 NotifyZoom();
7382 break;
7384 case SCI_GETZOOM:
7385 return vs.zoomLevel;
7387 case SCI_GETEDGECOLUMN:
7388 return theEdge;
7390 case SCI_SETEDGECOLUMN:
7391 theEdge = wParam;
7392 InvalidateStyleRedraw();
7393 break;
7395 case SCI_GETEDGEMODE:
7396 return vs.edgeState;
7398 case SCI_SETEDGEMODE:
7399 vs.edgeState = wParam;
7400 InvalidateStyleRedraw();
7401 break;
7403 case SCI_GETEDGECOLOUR:
7404 return vs.edgecolour.desired.AsLong();
7406 case SCI_SETEDGECOLOUR:
7407 vs.edgecolour.desired = ColourDesired(wParam);
7408 InvalidateStyleRedraw();
7409 break;
7411 case SCI_GETDOCPOINTER:
7412 return reinterpret_cast<sptr_t>(pdoc);
7414 case SCI_SETDOCPOINTER:
7415 CancelModes();
7416 SetDocPointer(reinterpret_cast<Document *>(lParam));
7417 return 0;
7419 case SCI_CREATEDOCUMENT: {
7420 Document *doc = new Document();
7421 if (doc) {
7422 doc->AddRef();
7424 return reinterpret_cast<sptr_t>(doc);
7427 case SCI_ADDREFDOCUMENT:
7428 (reinterpret_cast<Document *>(lParam))->AddRef();
7429 break;
7431 case SCI_RELEASEDOCUMENT:
7432 (reinterpret_cast<Document *>(lParam))->Release();
7433 break;
7435 case SCI_SETMODEVENTMASK:
7436 modEventMask = wParam;
7437 return 0;
7439 case SCI_GETMODEVENTMASK:
7440 return modEventMask;
7442 case SCI_CONVERTEOLS:
7443 pdoc->ConvertLineEnds(wParam);
7444 SetSelection(currentPos, anchor); // Ensure selection inside document
7445 return 0;
7447 case SCI_SETLENGTHFORENCODE:
7448 lengthForEncode = wParam;
7449 return 0;
7451 case SCI_SELECTIONISRECTANGLE:
7452 return selType == selRectangle ? 1 : 0;
7454 case SCI_SETSELECTIONMODE: {
7455 switch (wParam) {
7456 case SC_SEL_STREAM:
7457 moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
7458 selType = selStream;
7459 break;
7460 case SC_SEL_RECTANGLE:
7461 moveExtendsSelection = !moveExtendsSelection || (selType != selRectangle);
7462 selType = selRectangle;
7463 break;
7464 case SC_SEL_LINES:
7465 moveExtendsSelection = !moveExtendsSelection || (selType != selLines);
7466 selType = selLines;
7467 break;
7468 default:
7469 moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
7470 selType = selStream;
7472 InvalidateSelection(currentPos, anchor, true);
7474 case SCI_GETSELECTIONMODE:
7475 switch (selType) {
7476 case selStream:
7477 return SC_SEL_STREAM;
7478 case selRectangle:
7479 return SC_SEL_RECTANGLE;
7480 case selLines:
7481 return SC_SEL_LINES;
7482 default: // ?!
7483 return SC_SEL_STREAM;
7485 case SCI_GETLINESELSTARTPOSITION: {
7486 SelectionLineIterator lineIterator(this);
7487 lineIterator.SetAt(wParam);
7488 return lineIterator.startPos;
7490 case SCI_GETLINESELENDPOSITION: {
7491 SelectionLineIterator lineIterator(this);
7492 lineIterator.SetAt(wParam);
7493 return lineIterator.endPos;
7496 case SCI_SETOVERTYPE:
7497 inOverstrike = wParam != 0;
7498 break;
7500 case SCI_GETOVERTYPE:
7501 return inOverstrike ? 1 : 0;
7503 case SCI_SETFOCUS:
7504 SetFocusState(wParam != 0);
7505 break;
7507 case SCI_GETFOCUS:
7508 return hasFocus;
7510 case SCI_SETSTATUS:
7511 errorStatus = wParam;
7512 break;
7514 case SCI_GETSTATUS:
7515 return errorStatus;
7517 case SCI_SETMOUSEDOWNCAPTURES:
7518 mouseDownCaptures = wParam != 0;
7519 break;
7521 case SCI_GETMOUSEDOWNCAPTURES:
7522 return mouseDownCaptures;
7524 case SCI_SETCURSOR:
7525 cursorMode = wParam;
7526 DisplayCursor(Window::cursorText);
7527 break;
7529 case SCI_GETCURSOR:
7530 return cursorMode;
7532 case SCI_SETCONTROLCHARSYMBOL:
7533 controlCharSymbol = wParam;
7534 break;
7536 case SCI_GETCONTROLCHARSYMBOL:
7537 return controlCharSymbol;
7539 case SCI_STARTRECORD:
7540 recordingMacro = true;
7541 return 0;
7543 case SCI_STOPRECORD:
7544 recordingMacro = false;
7545 return 0;
7547 case SCI_MOVECARETINSIDEVIEW:
7548 MoveCaretInsideView();
7549 break;
7551 case SCI_SETFOLDMARGINCOLOUR:
7552 vs.foldmarginColourSet = wParam != 0;
7553 vs.foldmarginColour.desired = ColourDesired(lParam);
7554 InvalidateStyleRedraw();
7555 break;
7557 case SCI_SETFOLDMARGINHICOLOUR:
7558 vs.foldmarginHighlightColourSet = wParam != 0;
7559 vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
7560 InvalidateStyleRedraw();
7561 break;
7563 case SCI_SETHOTSPOTACTIVEFORE:
7564 vs.hotspotForegroundSet = wParam != 0;
7565 vs.hotspotForeground.desired = ColourDesired(lParam);
7566 InvalidateStyleRedraw();
7567 break;
7569 case SCI_GETHOTSPOTACTIVEFORE:
7570 return vs.hotspotForeground.desired.AsLong();
7572 case SCI_SETHOTSPOTACTIVEBACK:
7573 vs.hotspotBackgroundSet = wParam != 0;
7574 vs.hotspotBackground.desired = ColourDesired(lParam);
7575 InvalidateStyleRedraw();
7576 break;
7578 case SCI_GETHOTSPOTACTIVEBACK:
7579 return vs.hotspotBackground.desired.AsLong();
7581 case SCI_SETHOTSPOTACTIVEUNDERLINE:
7582 vs.hotspotUnderline = wParam != 0;
7583 InvalidateStyleRedraw();
7584 break;
7586 case SCI_GETHOTSPOTACTIVEUNDERLINE:
7587 return vs.hotspotUnderline ? 1 : 0;
7589 case SCI_SETHOTSPOTSINGLELINE:
7590 vs.hotspotSingleLine = wParam != 0;
7591 InvalidateStyleRedraw();
7592 break;
7594 case SCI_GETHOTSPOTSINGLELINE:
7595 return vs.hotspotSingleLine ? 1 : 0;
7597 case SCI_SETPASTECONVERTENDINGS:
7598 convertPastes = wParam != 0;
7599 break;
7601 case SCI_GETPASTECONVERTENDINGS:
7602 return convertPastes ? 1 : 0;
7604 case SCI_GETCHARACTERPOINTER:
7605 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
7607 default:
7608 return DefWndProc(iMessage, wParam, lParam);
7610 //Platform::DebugPrintf("end wnd proc\n");
7611 return 0l;