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