applied backgroundcolors.patch
[TortoiseGit.git] / ext / scintilla / src / Editor.cxx
blob1b7eddc75997c37fa0227c52ec47ad4d8a17a71c
1 // Scintilla source code edit control
2 /** @file Editor.cxx
3 ** Main code for the edit control.
4 **/
5 // Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <math.h>
13 #include <assert.h>
15 #include <string>
16 #include <vector>
17 #include <map>
18 #include <algorithm>
19 #include <memory>
21 #include "Platform.h"
23 #include "ILexer.h"
24 #include "Scintilla.h"
26 #include "SplitVector.h"
27 #include "Partitioning.h"
28 #include "RunStyles.h"
29 #include "ContractionState.h"
30 #include "CellBuffer.h"
31 #include "KeyMap.h"
32 #include "Indicator.h"
33 #include "XPM.h"
34 #include "LineMarker.h"
35 #include "Style.h"
36 #include "ViewStyle.h"
37 #include "CharClassify.h"
38 #include "Decoration.h"
39 #include "Document.h"
40 #include "UniConversion.h"
41 #include "Selection.h"
42 #include "PositionCache.h"
43 #include "Editor.h"
45 #ifdef SCI_NAMESPACE
46 using namespace Scintilla;
47 #endif
50 return whether this modification represents an operation that
51 may reasonably be deferred (not done now OR [possibly] at all)
53 static bool CanDeferToLastStep(const DocModification &mh) {
54 if (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE))
55 return true; // CAN skip
56 if (!(mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)))
57 return false; // MUST do
58 if (mh.modificationType & SC_MULTISTEPUNDOREDO)
59 return true; // CAN skip
60 return false; // PRESUMABLY must do
63 static bool CanEliminate(const DocModification &mh) {
64 return
65 (mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) != 0;
69 return whether this modification represents the FINAL step
70 in a [possibly lengthy] multi-step Undo/Redo sequence
72 static bool IsLastStep(const DocModification &mh) {
73 return
74 (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO)) != 0
75 && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
76 && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
77 && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
80 Caret::Caret() :
81 active(false), on(false), period(500) {}
83 Timer::Timer() :
84 ticking(false), ticksToWait(0), tickerID(0) {}
86 Idler::Idler() :
87 state(false), idlerID(0) {}
89 static inline bool IsControlCharacter(int ch) {
90 // iscntrl returns true for lots of chars > 127 which are displayable
91 return ch >= 0 && ch < ' ';
94 static inline bool IsAllSpacesOrTabs(char *s, unsigned int len) {
95 for (unsigned int i = 0; i < len; i++) {
96 // This is safe because IsSpaceOrTab() will return false for null terminators
97 if (!IsSpaceOrTab(s[i]))
98 return false;
100 return true;
103 Editor::Editor() {
104 ctrlID = 0;
106 stylesValid = false;
107 technology = SC_TECHNOLOGY_DEFAULT;
108 scaleRGBAImage = 100;
110 printMagnification = 0;
111 printColourMode = SC_PRINT_NORMAL;
112 printWrapState = eWrapWord;
113 cursorMode = SC_CURSORNORMAL;
114 controlCharSymbol = 0; /* Draw the control characters */
116 hasFocus = false;
117 hideSelection = false;
118 inOverstrike = false;
119 errorStatus = 0;
120 mouseDownCaptures = true;
122 bufferedDraw = true;
123 twoPhaseDraw = true;
125 lastClickTime = 0;
126 dwellDelay = SC_TIME_FOREVER;
127 ticksToDwell = SC_TIME_FOREVER;
128 dwelling = false;
129 ptMouseLast.x = 0;
130 ptMouseLast.y = 0;
131 inDragDrop = ddNone;
132 dropWentOutside = false;
133 posDrag = SelectionPosition(invalidPosition);
134 posDrop = SelectionPosition(invalidPosition);
135 hotSpotClickPos = INVALID_POSITION;
136 selectionType = selChar;
138 lastXChosen = 0;
139 lineAnchorPos = 0;
140 originalAnchorPos = 0;
141 wordSelectAnchorStartPos = 0;
142 wordSelectAnchorEndPos = 0;
143 wordSelectInitialCaretPos = -1;
145 primarySelection = true;
147 caretXPolicy = CARET_SLOP | CARET_EVEN;
148 caretXSlop = 50;
150 caretYPolicy = CARET_EVEN;
151 caretYSlop = 0;
153 visiblePolicy = 0;
154 visibleSlop = 0;
156 searchAnchor = 0;
158 xOffset = 0;
159 xCaretMargin = 50;
160 horizontalScrollBarVisible = true;
161 scrollWidth = 2000;
162 trackLineWidth = false;
163 lineWidthMaxSeen = 0;
164 verticalScrollBarVisible = true;
165 endAtLastLine = true;
166 caretSticky = SC_CARETSTICKY_OFF;
167 marginOptions = SC_MARGINOPTION_NONE;
168 multipleSelection = false;
169 additionalSelectionTyping = false;
170 multiPasteMode = SC_MULTIPASTE_ONCE;
171 additionalCaretsBlink = true;
172 additionalCaretsVisible = true;
173 virtualSpaceOptions = SCVS_NONE;
175 pixmapLine = 0;
176 pixmapSelMargin = 0;
177 pixmapSelPattern = 0;
178 pixmapSelPatternOffset1 = 0;
179 pixmapIndentGuide = 0;
180 pixmapIndentGuideHighlight = 0;
182 targetStart = 0;
183 targetEnd = 0;
184 searchFlags = 0;
186 topLine = 0;
187 posTopLine = 0;
189 lengthForEncode = -1;
191 needUpdateUI = 0;
192 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
193 braces[0] = invalidPosition;
194 braces[1] = invalidPosition;
195 bracesMatchStyle = STYLE_BRACEBAD;
196 highlightGuideColumn = 0;
198 theEdge = 0;
200 paintState = notPainting;
201 willRedrawAll = false;
203 modEventMask = SC_MODEVENTMASKALL;
205 pdoc = new Document();
206 pdoc->AddRef();
207 pdoc->AddWatcher(this, 0);
209 recordingMacro = false;
210 foldFlags = 0;
212 wrapState = eWrapNone;
213 wrapWidth = LineLayout::wrapWidthInfinite;
214 wrapStart = wrapLineLarge;
215 wrapEnd = wrapLineLarge;
216 wrapVisualFlags = 0;
217 wrapVisualFlagsLocation = 0;
218 wrapVisualStartIndent = 0;
219 wrapIndentMode = SC_WRAPINDENT_FIXED;
221 convertPastes = true;
223 marginNumberPadding = 3;
224 ctrlCharPadding = 3; // +3 For a blank on front and rounded edge each side
225 lastSegItalicsOffset = 2;
227 hsStart = -1;
228 hsEnd = -1;
230 llc.SetLevel(LineLayoutCache::llcCaret);
231 posCache.SetSize(0x400);
234 Editor::~Editor() {
235 pdoc->RemoveWatcher(this, 0);
236 pdoc->Release();
237 pdoc = 0;
238 DropGraphics(true);
241 void Editor::Finalise() {
242 SetIdle(false);
243 CancelModes();
246 void Editor::DropGraphics(bool freeObjects) {
247 if (freeObjects) {
248 delete pixmapLine;
249 pixmapLine = 0;
250 delete pixmapSelMargin;
251 pixmapSelMargin = 0;
252 delete pixmapSelPattern;
253 pixmapSelPattern = 0;
254 delete pixmapSelPatternOffset1;
255 pixmapSelPatternOffset1 = 0;
256 delete pixmapIndentGuide;
257 pixmapIndentGuide = 0;
258 delete pixmapIndentGuideHighlight;
259 pixmapIndentGuideHighlight = 0;
260 } else {
261 if (pixmapLine)
262 pixmapLine->Release();
263 if (pixmapSelMargin)
264 pixmapSelMargin->Release();
265 if (pixmapSelPattern)
266 pixmapSelPattern->Release();
267 if (pixmapSelPatternOffset1)
268 pixmapSelPatternOffset1->Release();
269 if (pixmapIndentGuide)
270 pixmapIndentGuide->Release();
271 if (pixmapIndentGuideHighlight)
272 pixmapIndentGuideHighlight->Release();
276 void Editor::AllocateGraphics() {
277 if (!pixmapLine)
278 pixmapLine = Surface::Allocate(technology);
279 if (!pixmapSelMargin)
280 pixmapSelMargin = Surface::Allocate(technology);
281 if (!pixmapSelPattern)
282 pixmapSelPattern = Surface::Allocate(technology);
283 if (!pixmapSelPatternOffset1)
284 pixmapSelPatternOffset1 = Surface::Allocate(technology);
285 if (!pixmapIndentGuide)
286 pixmapIndentGuide = Surface::Allocate(technology);
287 if (!pixmapIndentGuideHighlight)
288 pixmapIndentGuideHighlight = Surface::Allocate(technology);
291 void Editor::InvalidateStyleData() {
292 stylesValid = false;
293 vs.technology = technology;
294 DropGraphics(false);
295 AllocateGraphics();
296 llc.Invalidate(LineLayout::llInvalid);
297 posCache.Clear();
300 void Editor::InvalidateStyleRedraw() {
301 NeedWrapping();
302 InvalidateStyleData();
303 Redraw();
306 void Editor::RefreshStyleData() {
307 if (!stylesValid) {
308 stylesValid = true;
309 AutoSurface surface(this);
310 if (surface) {
311 vs.Refresh(*surface);
313 SetScrollBars();
314 SetRectangularRange();
318 Point Editor::GetVisibleOriginInMain() {
319 return Point(0,0);
322 Point Editor::DocumentPointFromView(Point ptView) {
323 Point ptDocument = ptView;
324 if (wMargin.GetID()) {
325 Point ptOrigin = GetVisibleOriginInMain();
326 ptDocument.x += ptOrigin.x;
327 ptDocument.y += ptOrigin.y;
328 } else {
329 ptDocument.x += xOffset;
330 ptDocument.y += topLine * vs.lineHeight;
332 return ptDocument;
335 int Editor::TopLineOfMain() {
336 if (wMargin.GetID())
337 return 0;
338 else
339 return topLine;
342 PRectangle Editor::GetClientRectangle() {
343 return wMain.GetClientPosition();
346 PRectangle Editor::GetTextRectangle() {
347 PRectangle rc = GetClientRectangle();
348 rc.left += vs.textStart;
349 rc.right -= vs.rightMarginWidth;
350 return rc;
353 int Editor::LinesOnScreen() {
354 PRectangle rcClient = GetClientRectangle();
355 int htClient = rcClient.bottom - rcClient.top;
356 //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
357 return htClient / vs.lineHeight;
360 int Editor::LinesToScroll() {
361 int retVal = LinesOnScreen() - 1;
362 if (retVal < 1)
363 return 1;
364 else
365 return retVal;
368 int Editor::MaxScrollPos() {
369 //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
370 //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
371 int retVal = cs.LinesDisplayed();
372 if (endAtLastLine) {
373 retVal -= LinesOnScreen();
374 } else {
375 retVal--;
377 if (retVal < 0) {
378 return 0;
379 } else {
380 return retVal;
384 const char *ControlCharacterString(unsigned char ch) {
385 const char *reps[] = {
386 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
387 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
388 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
389 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
391 if (ch < (sizeof(reps) / sizeof(reps[0]))) {
392 return reps[ch];
393 } else {
394 return "BAD";
399 * Convenience class to ensure LineLayout objects are always disposed.
401 class AutoLineLayout {
402 LineLayoutCache &llc;
403 LineLayout *ll;
404 AutoLineLayout &operator=(const AutoLineLayout &);
405 public:
406 AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
407 ~AutoLineLayout() {
408 llc.Dispose(ll);
409 ll = 0;
411 LineLayout *operator->() const {
412 return ll;
414 operator LineLayout *() const {
415 return ll;
417 void Set(LineLayout *ll_) {
418 llc.Dispose(ll);
419 ll = ll_;
423 SelectionPosition Editor::ClampPositionIntoDocument(SelectionPosition sp) const {
424 if (sp.Position() < 0) {
425 return SelectionPosition(0);
426 } else if (sp.Position() > pdoc->Length()) {
427 return SelectionPosition(pdoc->Length());
428 } else {
429 // If not at end of line then set offset to 0
430 if (!pdoc->IsLineEndPosition(sp.Position()))
431 sp.SetVirtualSpace(0);
432 return sp;
436 Point Editor::LocationFromPosition(SelectionPosition pos) {
437 Point pt;
438 RefreshStyleData();
439 if (pos.Position() == INVALID_POSITION)
440 return pt;
441 int line = pdoc->LineFromPosition(pos.Position());
442 int lineVisible = cs.DisplayFromDoc(line);
443 //Platform::DebugPrintf("line=%d\n", line);
444 AutoSurface surface(this);
445 AutoLineLayout ll(llc, RetrieveLineLayout(line));
446 if (surface && ll) {
447 // -1 because of adding in for visible lines in following loop.
448 pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
449 pt.x = 0;
450 unsigned int posLineStart = pdoc->LineStart(line);
451 LayoutLine(line, surface, vs, ll, wrapWidth);
452 int posInLine = pos.Position() - posLineStart;
453 // In case of very long line put x at arbitrary large position
454 if (posInLine > ll->maxLineLength) {
455 pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
458 for (int subLine = 0; subLine < ll->lines; subLine++) {
459 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
460 pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
461 if (ll->wrapIndent != 0) {
462 int lineStart = ll->LineStart(subLine);
463 if (lineStart != 0) // Wrapped
464 pt.x += ll->wrapIndent;
467 if (posInLine >= ll->LineStart(subLine)) {
468 pt.y += vs.lineHeight;
471 pt.x += vs.textStart - xOffset;
473 pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
474 return pt;
477 Point Editor::LocationFromPosition(int pos) {
478 return LocationFromPosition(SelectionPosition(pos));
481 int Editor::XFromPosition(int pos) {
482 Point pt = LocationFromPosition(pos);
483 return pt.x - vs.textStart + xOffset;
486 int Editor::XFromPosition(SelectionPosition sp) {
487 Point pt = LocationFromPosition(sp);
488 return pt.x - vs.textStart + xOffset;
491 int Editor::LineFromLocation(Point pt) {
492 return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
495 void Editor::SetTopLine(int topLineNew) {
496 if ((topLine != topLineNew) && (topLineNew >= 0)) {
497 topLine = topLineNew;
498 ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
500 posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
503 SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition, bool virtualSpace) {
504 RefreshStyleData();
505 if (canReturnInvalid) {
506 PRectangle rcClient = GetTextRectangle();
507 if (!rcClient.Contains(pt))
508 return SelectionPosition(INVALID_POSITION);
509 if (pt.x < vs.textStart)
510 return SelectionPosition(INVALID_POSITION);
511 if (pt.y < 0)
512 return SelectionPosition(INVALID_POSITION);
514 pt = DocumentPointFromView(pt);
515 pt.x = pt.x - vs.textStart;
516 int visibleLine = floor(pt.y / vs.lineHeight);
517 if (!canReturnInvalid && (visibleLine < 0))
518 visibleLine = 0;
519 int lineDoc = cs.DocFromDisplay(visibleLine);
520 if (canReturnInvalid && (lineDoc < 0))
521 return SelectionPosition(INVALID_POSITION);
522 if (lineDoc >= pdoc->LinesTotal())
523 return SelectionPosition(canReturnInvalid ? INVALID_POSITION : pdoc->Length());
524 unsigned int posLineStart = pdoc->LineStart(lineDoc);
525 SelectionPosition retVal(canReturnInvalid ? INVALID_POSITION : static_cast<int>(posLineStart));
526 AutoSurface surface(this);
527 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
528 if (surface && ll) {
529 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
530 int lineStartSet = cs.DisplayFromDoc(lineDoc);
531 int subLine = visibleLine - lineStartSet;
532 if (subLine < ll->lines) {
533 int lineStart = ll->LineStart(subLine);
534 int lineEnd = ll->LineLastVisible(subLine);
535 XYPOSITION subLineStart = ll->positions[lineStart];
537 if (ll->wrapIndent != 0) {
538 if (lineStart != 0) // Wrapped
539 pt.x -= ll->wrapIndent;
541 int i = ll->FindBefore(pt.x + subLineStart, lineStart, lineEnd);
542 while (i < lineEnd) {
543 if (charPosition) {
544 if ((pt.x + subLineStart) < (ll->positions[i + 1])) {
545 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
547 } else {
548 if ((pt.x + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
549 return SelectionPosition(pdoc->MovePositionOutsideChar(i + posLineStart, 1));
552 i++;
554 if (virtualSpace) {
555 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
556 int spaceOffset = (pt.x + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) /
557 spaceWidth;
558 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
559 } else if (canReturnInvalid) {
560 if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
561 return SelectionPosition(pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1));
563 } else {
564 return SelectionPosition(lineEnd + posLineStart);
567 if (!canReturnInvalid)
568 return SelectionPosition(ll->numCharsInLine + posLineStart);
570 return retVal;
573 int Editor::PositionFromLocation(Point pt, bool canReturnInvalid, bool charPosition) {
574 return SPositionFromLocation(pt, canReturnInvalid, charPosition, false).Position();
578 * Find the document position corresponding to an x coordinate on a particular document line.
579 * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
581 SelectionPosition Editor::SPositionFromLineX(int lineDoc, int x) {
582 RefreshStyleData();
583 if (lineDoc >= pdoc->LinesTotal())
584 return SelectionPosition(pdoc->Length());
585 //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
586 AutoSurface surface(this);
587 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
588 int retVal = 0;
589 if (surface && ll) {
590 unsigned int posLineStart = pdoc->LineStart(lineDoc);
591 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
592 int subLine = 0;
593 int lineStart = ll->LineStart(subLine);
594 int lineEnd = ll->LineLastVisible(subLine);
595 XYPOSITION subLineStart = ll->positions[lineStart];
596 XYPOSITION newX = x;
598 if (ll->wrapIndent != 0) {
599 if (lineStart != 0) // Wrapped
600 newX -= ll->wrapIndent;
602 int i = ll->FindBefore(newX + subLineStart, lineStart, lineEnd);
603 while (i < lineEnd) {
604 if ((newX + subLineStart) < ((ll->positions[i] + ll->positions[i + 1]) / 2)) {
605 retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
606 return SelectionPosition(retVal);
608 i++;
610 const XYPOSITION spaceWidth = vs.styles[ll->EndLineStyle()].spaceWidth;
611 int spaceOffset = (newX + subLineStart - ll->positions[lineEnd] + spaceWidth / 2) / spaceWidth;
612 return SelectionPosition(lineEnd + posLineStart, spaceOffset);
614 return SelectionPosition(retVal);
617 int Editor::PositionFromLineX(int lineDoc, int x) {
618 return SPositionFromLineX(lineDoc, x).Position();
622 * If painting then abandon the painting because a wider redraw is needed.
623 * @return true if calling code should stop drawing.
625 bool Editor::AbandonPaint() {
626 if ((paintState == painting) && !paintingAllText) {
627 paintState = paintAbandoned;
629 return paintState == paintAbandoned;
632 void Editor::RedrawRect(PRectangle rc) {
633 //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
635 // Clip the redraw rectangle into the client area
636 PRectangle rcClient = GetClientRectangle();
637 if (rc.top < rcClient.top)
638 rc.top = rcClient.top;
639 if (rc.bottom > rcClient.bottom)
640 rc.bottom = rcClient.bottom;
641 if (rc.left < rcClient.left)
642 rc.left = rcClient.left;
643 if (rc.right > rcClient.right)
644 rc.right = rcClient.right;
646 if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
647 wMain.InvalidateRectangle(rc);
651 void Editor::Redraw() {
652 //Platform::DebugPrintf("Redraw all\n");
653 PRectangle rcClient = GetClientRectangle();
654 wMain.InvalidateRectangle(rcClient);
655 if (wMargin.GetID())
656 wMargin.InvalidateAll();
657 //wMain.InvalidateAll();
660 void Editor::RedrawSelMargin(int line, bool allAfter) {
661 if (!AbandonPaint()) {
662 if (vs.maskInLine) {
663 Redraw();
664 } else {
665 PRectangle rcSelMargin = GetClientRectangle();
666 rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
667 if (line != -1) {
668 int position = pdoc->LineStart(line);
669 PRectangle rcLine = RectangleFromRange(position, position);
671 // Inflate line rectangle if there are image markers with height larger than line height
672 if (vs.largestMarkerHeight > vs.lineHeight) {
673 int delta = (vs.largestMarkerHeight - vs.lineHeight + 1) / 2;
674 rcLine.top -= delta;
675 rcLine.bottom += delta;
676 if (rcLine.top < rcSelMargin.top)
677 rcLine.top = rcSelMargin.top;
678 if (rcLine.bottom > rcSelMargin.bottom)
679 rcLine.bottom = rcSelMargin.bottom;
682 rcSelMargin.top = rcLine.top;
683 if (!allAfter)
684 rcSelMargin.bottom = rcLine.bottom;
686 if (wMargin.GetID()) {
687 Point ptOrigin = GetVisibleOriginInMain();
688 rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
689 wMargin.InvalidateRectangle(rcSelMargin);
690 } else {
691 wMain.InvalidateRectangle(rcSelMargin);
697 PRectangle Editor::RectangleFromRange(int start, int end) {
698 int minPos = start;
699 if (minPos > end)
700 minPos = end;
701 int maxPos = start;
702 if (maxPos < end)
703 maxPos = end;
704 int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
705 int lineDocMax = pdoc->LineFromPosition(maxPos);
706 int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
707 PRectangle rcClient = GetTextRectangle();
708 PRectangle rc;
709 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
710 rc.left = vs.textStart - leftTextOverlap;
711 rc.top = (minLine - TopLineOfMain()) * vs.lineHeight;
712 if (rc.top < rcClient.top)
713 rc.top = rcClient.top;
714 rc.right = rcClient.right;
715 rc.bottom = (maxLine - TopLineOfMain() + 1) * vs.lineHeight;
717 return rc;
720 void Editor::InvalidateRange(int start, int end) {
721 RedrawRect(RectangleFromRange(start, end));
724 int Editor::CurrentPosition() {
725 return sel.MainCaret();
728 bool Editor::SelectionEmpty() {
729 return sel.Empty();
732 SelectionPosition Editor::SelectionStart() {
733 return sel.RangeMain().Start();
736 SelectionPosition Editor::SelectionEnd() {
737 return sel.RangeMain().End();
740 void Editor::SetRectangularRange() {
741 if (sel.IsRectangular()) {
742 int xAnchor = XFromPosition(sel.Rectangular().anchor);
743 int xCaret = XFromPosition(sel.Rectangular().caret);
744 if (sel.selType == Selection::selThin) {
745 xCaret = xAnchor;
747 int lineAnchorRect = pdoc->LineFromPosition(sel.Rectangular().anchor.Position());
748 int lineCaret = pdoc->LineFromPosition(sel.Rectangular().caret.Position());
749 int increment = (lineCaret > lineAnchorRect) ? 1 : -1;
750 for (int line=lineAnchorRect; line != lineCaret+increment; line += increment) {
751 SelectionRange range(SPositionFromLineX(line, xCaret), SPositionFromLineX(line, xAnchor));
752 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) == 0)
753 range.ClearVirtualSpace();
754 if (line == lineAnchorRect)
755 sel.SetSelection(range);
756 else
757 sel.AddSelectionWithoutTrim(range);
762 void Editor::ThinRectangularRange() {
763 if (sel.IsRectangular()) {
764 sel.selType = Selection::selThin;
765 if (sel.Rectangular().caret < sel.Rectangular().anchor) {
766 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).caret, sel.Range(0).anchor);
767 } else {
768 sel.Rectangular() = SelectionRange(sel.Range(sel.Count()-1).anchor, sel.Range(0).caret);
770 SetRectangularRange();
774 void Editor::InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection) {
775 if (sel.Count() > 1 || !(sel.RangeMain().anchor == newMain.anchor) || sel.IsRectangular()) {
776 invalidateWholeSelection = true;
778 int firstAffected = Platform::Minimum(sel.RangeMain().Start().Position(), newMain.Start().Position());
779 // +1 for lastAffected ensures caret repainted
780 int lastAffected = Platform::Maximum(newMain.caret.Position()+1, newMain.anchor.Position());
781 lastAffected = Platform::Maximum(lastAffected, sel.RangeMain().End().Position());
782 if (invalidateWholeSelection) {
783 for (size_t r=0; r<sel.Count(); r++) {
784 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).caret.Position());
785 firstAffected = Platform::Minimum(firstAffected, sel.Range(r).anchor.Position());
786 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).caret.Position()+1);
787 lastAffected = Platform::Maximum(lastAffected, sel.Range(r).anchor.Position());
790 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
791 InvalidateRange(firstAffected, lastAffected);
794 void Editor::SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_) {
795 currentPos_ = ClampPositionIntoDocument(currentPos_);
796 anchor_ = ClampPositionIntoDocument(anchor_);
797 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
798 /* For Line selection - ensure the anchor and caret are always
799 at the beginning and end of the region lines. */
800 if (sel.selType == Selection::selLines) {
801 if (currentPos_ > anchor_) {
802 anchor_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(anchor_.Position())));
803 currentPos_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(currentPos_.Position())));
804 } else {
805 currentPos_ = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(currentPos_.Position())));
806 anchor_ = SelectionPosition(pdoc->LineEnd(pdoc->LineFromPosition(anchor_.Position())));
809 SelectionRange rangeNew(currentPos_, anchor_);
810 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
811 InvalidateSelection(rangeNew);
813 sel.RangeMain() = rangeNew;
814 SetRectangularRange();
815 ClaimSelection();
817 if (highlightDelimiter.NeedsDrawing(currentLine)) {
818 RedrawSelMargin();
820 QueueIdleWork(WorkNeeded::workUpdateUI);
823 void Editor::SetSelection(int currentPos_, int anchor_) {
824 SetSelection(SelectionPosition(currentPos_), SelectionPosition(anchor_));
827 // Just move the caret on the main selection
828 void Editor::SetSelection(SelectionPosition currentPos_) {
829 currentPos_ = ClampPositionIntoDocument(currentPos_);
830 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
831 if (sel.Count() > 1 || !(sel.RangeMain().caret == currentPos_)) {
832 InvalidateSelection(SelectionRange(currentPos_));
834 if (sel.IsRectangular()) {
835 sel.Rectangular() =
836 SelectionRange(SelectionPosition(currentPos_), sel.Rectangular().anchor);
837 SetRectangularRange();
838 } else {
839 sel.RangeMain() =
840 SelectionRange(SelectionPosition(currentPos_), sel.RangeMain().anchor);
842 ClaimSelection();
844 if (highlightDelimiter.NeedsDrawing(currentLine)) {
845 RedrawSelMargin();
847 QueueIdleWork(WorkNeeded::workUpdateUI);
850 void Editor::SetSelection(int currentPos_) {
851 SetSelection(SelectionPosition(currentPos_));
854 void Editor::SetEmptySelection(SelectionPosition currentPos_) {
855 int currentLine = pdoc->LineFromPosition(currentPos_.Position());
856 SelectionRange rangeNew(ClampPositionIntoDocument(currentPos_));
857 if (sel.Count() > 1 || !(sel.RangeMain() == rangeNew)) {
858 InvalidateSelection(rangeNew);
860 sel.Clear();
861 sel.RangeMain() = rangeNew;
862 SetRectangularRange();
863 ClaimSelection();
865 if (highlightDelimiter.NeedsDrawing(currentLine)) {
866 RedrawSelMargin();
868 QueueIdleWork(WorkNeeded::workUpdateUI);
871 void Editor::SetEmptySelection(int currentPos_) {
872 SetEmptySelection(SelectionPosition(currentPos_));
875 bool Editor::RangeContainsProtected(int start, int end) const {
876 if (vs.ProtectionActive()) {
877 if (start > end) {
878 int t = start;
879 start = end;
880 end = t;
882 int mask = pdoc->stylingBitsMask;
883 for (int pos = start; pos < end; pos++) {
884 if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
885 return true;
888 return false;
891 bool Editor::SelectionContainsProtected() {
892 for (size_t r=0; r<sel.Count(); r++) {
893 if (RangeContainsProtected(sel.Range(r).Start().Position(),
894 sel.Range(r).End().Position())) {
895 return true;
898 return false;
902 * Asks document to find a good position and then moves out of any invisible positions.
904 int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) const {
905 return MovePositionOutsideChar(SelectionPosition(pos), moveDir, checkLineEnd).Position();
908 SelectionPosition Editor::MovePositionOutsideChar(SelectionPosition pos, int moveDir, bool checkLineEnd) const {
909 int posMoved = pdoc->MovePositionOutsideChar(pos.Position(), moveDir, checkLineEnd);
910 if (posMoved != pos.Position())
911 pos.SetPosition(posMoved);
912 if (vs.ProtectionActive()) {
913 int mask = pdoc->stylingBitsMask;
914 if (moveDir > 0) {
915 if ((pos.Position() > 0) && vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()) {
916 while ((pos.Position() < pdoc->Length()) &&
917 (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()))
918 pos.Add(1);
920 } else if (moveDir < 0) {
921 if (vs.styles[pdoc->StyleAt(pos.Position()) & mask].IsProtected()) {
922 while ((pos.Position() > 0) &&
923 (vs.styles[pdoc->StyleAt(pos.Position() - 1) & mask].IsProtected()))
924 pos.Add(-1);
928 return pos;
931 int Editor::MovePositionTo(SelectionPosition newPos, Selection::selTypes selt, bool ensureVisible) {
932 bool simpleCaret = (sel.Count() == 1) && sel.Empty();
933 SelectionPosition spCaret = sel.Last();
935 int delta = newPos.Position() - sel.MainCaret();
936 newPos = ClampPositionIntoDocument(newPos);
937 newPos = MovePositionOutsideChar(newPos, delta);
938 if (!multipleSelection && sel.IsRectangular() && (selt == Selection::selStream)) {
939 // Can't turn into multiple selection so clear additional selections
940 InvalidateSelection(SelectionRange(newPos), true);
941 SelectionRange rangeMain = sel.RangeMain();
942 sel.SetSelection(rangeMain);
944 if (!sel.IsRectangular() && (selt == Selection::selRectangle)) {
945 // Switching to rectangular
946 SelectionRange rangeMain = sel.RangeMain();
947 sel.Clear();
948 sel.Rectangular() = rangeMain;
950 if (selt != Selection::noSel) {
951 sel.selType = selt;
953 if (selt != Selection::noSel || sel.MoveExtends()) {
954 SetSelection(newPos);
955 } else {
956 SetEmptySelection(newPos);
958 ShowCaretAtCurrentPosition();
960 int currentLine = pdoc->LineFromPosition(newPos.Position());
961 if (ensureVisible) {
962 // In case in need of wrapping to ensure DisplayFromDoc works.
963 if (currentLine >= wrapStart)
964 WrapLines(true, -1);
965 XYScrollPosition newXY = XYScrollToMakeVisible(
966 SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret), xysDefault);
967 if (simpleCaret && (newXY.xOffset == xOffset)) {
968 // simple vertical scroll then invalidate
969 ScrollTo(newXY.topLine);
970 InvalidateSelection(SelectionRange(spCaret), true);
971 } else {
972 SetXYScroll(newXY);
976 if (highlightDelimiter.NeedsDrawing(currentLine)) {
977 RedrawSelMargin();
979 return 0;
982 int Editor::MovePositionTo(int newPos, Selection::selTypes selt, bool ensureVisible) {
983 return MovePositionTo(SelectionPosition(newPos), selt, ensureVisible);
986 SelectionPosition Editor::MovePositionSoVisible(SelectionPosition pos, int moveDir) {
987 pos = ClampPositionIntoDocument(pos);
988 pos = MovePositionOutsideChar(pos, moveDir);
989 int lineDoc = pdoc->LineFromPosition(pos.Position());
990 if (cs.GetVisible(lineDoc)) {
991 return pos;
992 } else {
993 int lineDisplay = cs.DisplayFromDoc(lineDoc);
994 if (moveDir > 0) {
995 // lineDisplay is already line before fold as lines in fold use display line of line after fold
996 lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
997 return SelectionPosition(pdoc->LineStart(cs.DocFromDisplay(lineDisplay)));
998 } else {
999 lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
1000 return SelectionPosition(pdoc->LineEnd(cs.DocFromDisplay(lineDisplay)));
1005 SelectionPosition Editor::MovePositionSoVisible(int pos, int moveDir) {
1006 return MovePositionSoVisible(SelectionPosition(pos), moveDir);
1009 Point Editor::PointMainCaret() {
1010 return LocationFromPosition(sel.Range(sel.Main()).caret);
1014 * Choose the x position that the caret will try to stick to
1015 * as it moves up and down.
1017 void Editor::SetLastXChosen() {
1018 Point pt = PointMainCaret();
1019 lastXChosen = pt.x + xOffset;
1022 void Editor::ScrollTo(int line, bool moveThumb) {
1023 int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
1024 if (topLineNew != topLine) {
1025 // Try to optimise small scrolls
1026 #ifndef UNDER_CE
1027 int linesToMove = topLine - topLineNew;
1028 bool performBlit = (abs(linesToMove) <= 10) && (paintState == notPainting);
1029 willRedrawAll = !performBlit;
1030 #endif
1031 SetTopLine(topLineNew);
1032 // Optimize by styling the view as this will invalidate any needed area
1033 // which could abort the initial paint if discovered later.
1034 StyleToPositionInView(PositionAfterArea(GetClientRectangle()));
1035 #ifndef UNDER_CE
1036 // Perform redraw rather than scroll if many lines would be redrawn anyway.
1037 if (performBlit) {
1038 ScrollText(linesToMove);
1039 } else {
1040 Redraw();
1042 willRedrawAll = false;
1043 #else
1044 Redraw();
1045 #endif
1046 if (moveThumb) {
1047 SetVerticalScrollPos();
1052 void Editor::ScrollText(int /* linesToMove */) {
1053 //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
1054 Redraw();
1057 void Editor::HorizontalScrollTo(int xPos) {
1058 //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
1059 if (xPos < 0)
1060 xPos = 0;
1061 if ((wrapState == eWrapNone) && (xOffset != xPos)) {
1062 xOffset = xPos;
1063 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1064 SetHorizontalScrollPos();
1065 RedrawRect(GetClientRectangle());
1069 void Editor::VerticalCentreCaret() {
1070 int lineDoc = pdoc->LineFromPosition(sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret());
1071 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1072 int newTop = lineDisplay - (LinesOnScreen() / 2);
1073 if (topLine != newTop) {
1074 SetTopLine(newTop > 0 ? newTop : 0);
1075 RedrawRect(GetClientRectangle());
1079 // Avoid 64 bit compiler warnings.
1080 // Scintilla does not support text buffers larger than 2**31
1081 static int istrlen(const char *s) {
1082 return static_cast<int>(strlen(s));
1085 void Editor::MoveSelectedLines(int lineDelta) {
1087 // if selection doesn't start at the beginning of the line, set the new start
1088 int selectionStart = SelectionStart().Position();
1089 int startLine = pdoc->LineFromPosition(selectionStart);
1090 int beginningOfStartLine = pdoc->LineStart(startLine);
1091 selectionStart = beginningOfStartLine;
1093 // if selection doesn't end at the beginning of a line greater than that of the start,
1094 // then set it at the beginning of the next one
1095 int selectionEnd = SelectionEnd().Position();
1096 int endLine = pdoc->LineFromPosition(selectionEnd);
1097 int beginningOfEndLine = pdoc->LineStart(endLine);
1098 bool appendEol = false;
1099 if (selectionEnd > beginningOfEndLine
1100 || selectionStart == selectionEnd) {
1101 selectionEnd = pdoc->LineStart(endLine + 1);
1102 appendEol = (selectionEnd == pdoc->Length() && pdoc->LineFromPosition(selectionEnd) == endLine);
1105 // if there's nowhere for the selection to move
1106 // (i.e. at the beginning going up or at the end going down),
1107 // stop it right there!
1108 if ((selectionStart == 0 && lineDelta < 0)
1109 || (selectionEnd == pdoc->Length() && lineDelta > 0)
1110 || selectionStart == selectionEnd) {
1111 return;
1114 UndoGroup ug(pdoc);
1116 if (lineDelta > 0 && selectionEnd == pdoc->LineStart(pdoc->LinesTotal() - 1)) {
1117 SetSelection(pdoc->MovePositionOutsideChar(selectionEnd - 1, -1), selectionEnd);
1118 ClearSelection();
1119 selectionEnd = CurrentPosition();
1121 SetSelection(selectionStart, selectionEnd);
1123 SelectionText selectedText;
1124 CopySelectionRange(&selectedText);
1126 int selectionLength = SelectionRange(selectionStart, selectionEnd).Length();
1127 Point currentLocation = LocationFromPosition(CurrentPosition());
1128 int currentLine = LineFromLocation(currentLocation);
1130 if (appendEol)
1131 SetSelection(pdoc->MovePositionOutsideChar(selectionStart - 1, -1), selectionEnd);
1132 ClearSelection();
1134 const char *eol = StringFromEOLMode(pdoc->eolMode);
1135 if (currentLine + lineDelta >= pdoc->LinesTotal())
1136 pdoc->InsertCString(pdoc->Length(), eol);
1137 GoToLine(currentLine + lineDelta);
1139 pdoc->InsertCString(CurrentPosition(), selectedText.s);
1140 if (appendEol) {
1141 pdoc->InsertCString(CurrentPosition() + selectionLength, eol);
1142 selectionLength += istrlen(eol);
1144 SetSelection(CurrentPosition(), CurrentPosition() + selectionLength);
1147 void Editor::MoveSelectedLinesUp() {
1148 MoveSelectedLines(-1);
1151 void Editor::MoveSelectedLinesDown() {
1152 MoveSelectedLines(1);
1155 void Editor::MoveCaretInsideView(bool ensureVisible) {
1156 PRectangle rcClient = GetTextRectangle();
1157 Point pt = PointMainCaret();
1158 if (pt.y < rcClient.top) {
1159 MovePositionTo(SPositionFromLocation(
1160 Point(lastXChosen - xOffset, rcClient.top),
1161 false, false, UserVirtualSpace()),
1162 Selection::noSel, ensureVisible);
1163 } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
1164 int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
1165 MovePositionTo(SPositionFromLocation(
1166 Point(lastXChosen - xOffset, rcClient.top + yOfLastLineFullyDisplayed),
1167 false, false, UserVirtualSpace()),
1168 Selection::noSel, ensureVisible);
1172 int Editor::DisplayFromPosition(int pos) {
1173 int lineDoc = pdoc->LineFromPosition(pos);
1174 int lineDisplay = cs.DisplayFromDoc(lineDoc);
1175 AutoSurface surface(this);
1176 AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
1177 if (surface && ll) {
1178 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
1179 unsigned int posLineStart = pdoc->LineStart(lineDoc);
1180 int posInLine = pos - posLineStart;
1181 lineDisplay--; // To make up for first increment ahead.
1182 for (int subLine = 0; subLine < ll->lines; subLine++) {
1183 if (posInLine >= ll->LineStart(subLine)) {
1184 lineDisplay++;
1188 return lineDisplay;
1192 * Ensure the caret is reasonably visible in context.
1194 Caret policy in SciTE
1196 If slop is set, we can define a slop value.
1197 This value defines an unwanted zone (UZ) where the caret is... unwanted.
1198 This zone is defined as a number of pixels near the vertical margins,
1199 and as a number of lines near the horizontal margins.
1200 By keeping the caret away from the edges, it is seen within its context,
1201 so it is likely that the identifier that the caret is on can be completely seen,
1202 and that the current line is seen with some of the lines following it which are
1203 often dependent on that line.
1205 If strict is set, the policy is enforced... strictly.
1206 The caret is centred on the display if slop is not set,
1207 and cannot go in the UZ if slop is set.
1209 If jumps is set, the display is moved more energetically
1210 so the caret can move in the same direction longer before the policy is applied again.
1211 '3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
1213 If even is not set, instead of having symmetrical UZs,
1214 the left and bottom UZs are extended up to right and top UZs respectively.
1215 This way, we favour the displaying of useful information: the begining of lines,
1216 where most code reside, and the lines after the caret, eg. the body of a function.
1218 | | | | |
1219 slop | strict | jumps | even | Caret can go to the margin | When reaching limit (caret going out of
1220 | | | | | visibility or going into the UZ) display is...
1221 -----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
1222 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
1223 0 | 0 | 0 | 1 | Yes | moved by one position
1224 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
1225 0 | 0 | 1 | 1 | Yes | centred on the caret
1226 0 | 1 | - | 0 | Caret is always on top/on right of display | -
1227 0 | 1 | - | 1 | No, caret is always centred | -
1228 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
1229 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
1230 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
1231 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
1232 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
1233 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
1234 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
1237 Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const SelectionRange range, const XYScrollOptions options) {
1238 PRectangle rcClient = GetTextRectangle();
1239 Point pt = LocationFromPosition(range.caret);
1240 Point ptAnchor = LocationFromPosition(range.anchor);
1241 const Point ptOrigin = GetVisibleOriginInMain();
1242 pt.x += ptOrigin.x;
1243 pt.y += ptOrigin.y;
1244 ptAnchor.x += ptOrigin.x;
1245 ptAnchor.y += ptOrigin.y;
1246 const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
1248 XYScrollPosition newXY(xOffset, topLine);
1250 // Vertical positioning
1251 if ((options & xysVertical) && (pt.y < rcClient.top || ptBottomCaret.y >= rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
1252 const int lineCaret = DisplayFromPosition(range.caret.Position());
1253 const int linesOnScreen = LinesOnScreen();
1254 const int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
1255 const bool bSlop = (caretYPolicy & CARET_SLOP) != 0;
1256 const bool bStrict = (caretYPolicy & CARET_STRICT) != 0;
1257 const bool bJump = (caretYPolicy & CARET_JUMPS) != 0;
1258 const bool bEven = (caretYPolicy & CARET_EVEN) != 0;
1260 // It should be possible to scroll the window to show the caret,
1261 // but this fails to remove the caret on GTK+
1262 if (bSlop) { // A margin is defined
1263 int yMoveT, yMoveB;
1264 if (bStrict) {
1265 int yMarginT, yMarginB;
1266 if (!(options & xysUseMargin)) {
1267 // In drag mode, avoid moves
1268 // otherwise, a double click will select several lines.
1269 yMarginT = yMarginB = 0;
1270 } else {
1271 // yMarginT must equal to caretYSlop, with a minimum of 1 and
1272 // a maximum of slightly less than half the heigth of the text area.
1273 yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
1274 if (bEven) {
1275 yMarginB = yMarginT;
1276 } else {
1277 yMarginB = linesOnScreen - yMarginT - 1;
1280 yMoveT = yMarginT;
1281 if (bEven) {
1282 if (bJump) {
1283 yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
1285 yMoveB = yMoveT;
1286 } else {
1287 yMoveB = linesOnScreen - yMoveT - 1;
1289 if (lineCaret < topLine + yMarginT) {
1290 // Caret goes too high
1291 newXY.topLine = lineCaret - yMoveT;
1292 } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
1293 // Caret goes too low
1294 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1296 } else { // Not strict
1297 yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
1298 yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
1299 if (bEven) {
1300 yMoveB = yMoveT;
1301 } else {
1302 yMoveB = linesOnScreen - yMoveT - 1;
1304 if (lineCaret < topLine) {
1305 // Caret goes too high
1306 newXY.topLine = lineCaret - yMoveT;
1307 } else if (lineCaret > topLine + linesOnScreen - 1) {
1308 // Caret goes too low
1309 newXY.topLine = lineCaret - linesOnScreen + 1 + yMoveB;
1312 } else { // No slop
1313 if (!bStrict && !bJump) {
1314 // Minimal move
1315 if (lineCaret < topLine) {
1316 // Caret goes too high
1317 newXY.topLine = lineCaret;
1318 } else if (lineCaret > topLine + linesOnScreen - 1) {
1319 // Caret goes too low
1320 if (bEven) {
1321 newXY.topLine = lineCaret - linesOnScreen + 1;
1322 } else {
1323 newXY.topLine = lineCaret;
1326 } else { // Strict or going out of display
1327 if (bEven) {
1328 // Always center caret
1329 newXY.topLine = lineCaret - halfScreen;
1330 } else {
1331 // Always put caret on top of display
1332 newXY.topLine = lineCaret;
1336 if (!(range.caret == range.anchor)) {
1337 const int lineAnchor = DisplayFromPosition(range.anchor.Position());
1338 if (lineAnchor < lineCaret) {
1339 // Shift up to show anchor or as much of range as possible
1340 newXY.topLine = std::min(newXY.topLine, lineAnchor);
1341 newXY.topLine = std::max(newXY.topLine, lineCaret - LinesOnScreen());
1342 } else {
1343 // Shift down to show anchor or as much of range as possible
1344 newXY.topLine = std::max(newXY.topLine, lineAnchor - LinesOnScreen());
1345 newXY.topLine = std::min(newXY.topLine, lineCaret);
1348 newXY.topLine = Platform::Clamp(newXY.topLine, 0, MaxScrollPos());
1351 // Horizontal positioning
1352 if ((options & xysHorizontal) && (wrapState == eWrapNone)) {
1353 const int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
1354 const bool bSlop = (caretXPolicy & CARET_SLOP) != 0;
1355 const bool bStrict = (caretXPolicy & CARET_STRICT) != 0;
1356 const bool bJump = (caretXPolicy & CARET_JUMPS) != 0;
1357 const bool bEven = (caretXPolicy & CARET_EVEN) != 0;
1359 if (bSlop) { // A margin is defined
1360 int xMoveL, xMoveR;
1361 if (bStrict) {
1362 int xMarginL, xMarginR;
1363 if (!(options & xysUseMargin)) {
1364 // In drag mode, avoid moves unless very near of the margin
1365 // otherwise, a simple click will select text.
1366 xMarginL = xMarginR = 2;
1367 } else {
1368 // xMargin must equal to caretXSlop, with a minimum of 2 and
1369 // a maximum of slightly less than half the width of the text area.
1370 xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
1371 if (bEven) {
1372 xMarginL = xMarginR;
1373 } else {
1374 xMarginL = rcClient.Width() - xMarginR - 4;
1377 if (bJump && bEven) {
1378 // Jump is used only in even mode
1379 xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
1380 } else {
1381 xMoveL = xMoveR = 0; // Not used, avoid a warning
1383 if (pt.x < rcClient.left + xMarginL) {
1384 // Caret is on the left of the display
1385 if (bJump && bEven) {
1386 newXY.xOffset -= xMoveL;
1387 } else {
1388 // Move just enough to allow to display the caret
1389 newXY.xOffset -= (rcClient.left + xMarginL) - pt.x;
1391 } else if (pt.x >= rcClient.right - xMarginR) {
1392 // Caret is on the right of the display
1393 if (bJump && bEven) {
1394 newXY.xOffset += xMoveR;
1395 } else {
1396 // Move just enough to allow to display the caret
1397 newXY.xOffset += pt.x - (rcClient.right - xMarginR) + 1;
1400 } else { // Not strict
1401 xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
1402 xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
1403 if (bEven) {
1404 xMoveL = xMoveR;
1405 } else {
1406 xMoveL = rcClient.Width() - xMoveR - 4;
1408 if (pt.x < rcClient.left) {
1409 // Caret is on the left of the display
1410 newXY.xOffset -= xMoveL;
1411 } else if (pt.x >= rcClient.right) {
1412 // Caret is on the right of the display
1413 newXY.xOffset += xMoveR;
1416 } else { // No slop
1417 if (bStrict ||
1418 (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
1419 // Strict or going out of display
1420 if (bEven) {
1421 // Center caret
1422 newXY.xOffset += pt.x - rcClient.left - halfScreen;
1423 } else {
1424 // Put caret on right
1425 newXY.xOffset += pt.x - rcClient.right + 1;
1427 } else {
1428 // Move just enough to allow to display the caret
1429 if (pt.x < rcClient.left) {
1430 // Caret is on the left of the display
1431 if (bEven) {
1432 newXY.xOffset -= rcClient.left - pt.x;
1433 } else {
1434 newXY.xOffset += pt.x - rcClient.right + 1;
1436 } else if (pt.x >= rcClient.right) {
1437 // Caret is on the right of the display
1438 newXY.xOffset += pt.x - rcClient.right + 1;
1442 // In case of a jump (find result) largely out of display, adjust the offset to display the caret
1443 if (pt.x + xOffset < rcClient.left + newXY.xOffset) {
1444 newXY.xOffset = pt.x + xOffset - rcClient.left;
1445 } else if (pt.x + xOffset >= rcClient.right + newXY.xOffset) {
1446 newXY.xOffset = pt.x + xOffset - rcClient.right + 1;
1447 if (vs.caretStyle == CARETSTYLE_BLOCK) {
1448 // Ensure we can see a good portion of the block caret
1449 newXY.xOffset += static_cast<int>(vs.aveCharWidth);
1452 if (!(range.caret == range.anchor)) {
1453 if (ptAnchor.x < pt.x) {
1454 // Shift to left to show anchor or as much of range as possible
1455 int maxOffset = ptAnchor.x + xOffset - rcClient.left - 1;
1456 int minOffset = pt.x + xOffset - rcClient.right + 1;
1457 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1458 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1459 } else {
1460 // Shift to right to show anchor or as much of range as possible
1461 int minOffset = ptAnchor.x + xOffset - rcClient.right + 1;
1462 int maxOffset = pt.x + xOffset - rcClient.left - 1;
1463 newXY.xOffset = std::max(newXY.xOffset, minOffset);
1464 newXY.xOffset = std::min(newXY.xOffset, maxOffset);
1467 if (newXY.xOffset < 0) {
1468 newXY.xOffset = 0;
1472 return newXY;
1475 void Editor::SetXYScroll(XYScrollPosition newXY) {
1476 if ((newXY.topLine != topLine) || (newXY.xOffset != xOffset)) {
1477 if (newXY.topLine != topLine) {
1478 SetTopLine(newXY.topLine);
1479 SetVerticalScrollPos();
1481 if (newXY.xOffset != xOffset) {
1482 xOffset = newXY.xOffset;
1483 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
1484 if (newXY.xOffset > 0) {
1485 PRectangle rcText = GetTextRectangle();
1486 if (horizontalScrollBarVisible &&
1487 rcText.Width() + xOffset > scrollWidth) {
1488 scrollWidth = xOffset + rcText.Width();
1489 SetScrollBars();
1492 SetHorizontalScrollPos();
1494 Redraw();
1495 UpdateSystemCaret();
1499 void Editor::ScrollRange(SelectionRange range) {
1500 SetXYScroll(XYScrollToMakeVisible(range, xysDefault));
1503 void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
1504 SetXYScroll(XYScrollToMakeVisible(SelectionRange(posDrag.IsValid() ? posDrag : sel.RangeMain().caret),
1505 static_cast<XYScrollOptions>((useMargin?xysUseMargin:0)|(vert?xysVertical:0)|(horiz?xysHorizontal:0))));
1508 void Editor::ShowCaretAtCurrentPosition() {
1509 if (hasFocus) {
1510 caret.active = true;
1511 caret.on = true;
1512 SetTicking(true);
1513 } else {
1514 caret.active = false;
1515 caret.on = false;
1517 InvalidateCaret();
1520 void Editor::DropCaret() {
1521 caret.active = false;
1522 InvalidateCaret();
1525 void Editor::InvalidateCaret() {
1526 if (posDrag.IsValid()) {
1527 InvalidateRange(posDrag.Position(), posDrag.Position() + 1);
1528 } else {
1529 for (size_t r=0; r<sel.Count(); r++) {
1530 InvalidateRange(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1);
1533 UpdateSystemCaret();
1536 void Editor::UpdateSystemCaret() {
1539 void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
1540 docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
1541 if (wrapStart > docLineStart) {
1542 wrapStart = docLineStart;
1543 llc.Invalidate(LineLayout::llPositions);
1545 if (wrapEnd < docLineEnd) {
1546 wrapEnd = docLineEnd;
1548 wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
1549 // Wrap lines during idle.
1550 if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
1551 SetIdle(true);
1555 bool Editor::WrapOneLine(Surface *surface, int lineToWrap) {
1556 AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
1557 int linesWrapped = 1;
1558 if (ll) {
1559 LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
1560 linesWrapped = ll->lines;
1562 return cs.SetHeight(lineToWrap, linesWrapped +
1563 (vs.annotationVisible ? pdoc->AnnotationLines(lineToWrap) : 0));
1566 // Check if wrapping needed and perform any needed wrapping.
1567 // fullwrap: if true, all lines which need wrapping will be done,
1568 // in this single call.
1569 // priorityWrapLineStart: If greater than or equal to zero, all lines starting from
1570 // here to 1 page + 100 lines past will be wrapped (even if there are
1571 // more lines under wrapping process in idle).
1572 // If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
1573 // wrapped, if there are any wrapping going on in idle. (Generally this
1574 // condition is called only from idler).
1575 // Return true if wrapping occurred.
1576 bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
1577 // If there are any pending wraps, do them during idle if possible.
1578 int linesInOneCall = LinesOnScreen() + 100;
1579 if (priorityWrapLineStart >= 0) {
1580 // Using DocFromDisplay() here may result in chicken and egg problem in certain corner cases,
1581 // which will hopefully be handled by added 100 lines. If some lines are still missed, idle wrapping will catch on.
1582 int docLinesInOneCall = cs.DocFromDisplay(topLine + LinesOnScreen() + 100) - cs.DocFromDisplay(topLine);
1583 linesInOneCall = Platform::Maximum(linesInOneCall, docLinesInOneCall);
1585 if (wrapState != eWrapNone) {
1586 if (wrapStart < wrapEnd) {
1587 if (!SetIdle(true)) {
1588 // Idle processing not supported so full wrap required.
1589 fullWrap = true;
1592 if (!fullWrap && priorityWrapLineStart >= 0 &&
1593 // .. and if the paint window is outside pending wraps
1594 (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
1595 (priorityWrapLineStart > wrapEnd))) {
1596 // No priority wrap pending
1597 return false;
1600 int goodTopLine = topLine;
1601 bool wrapOccurred = false;
1602 if (wrapStart <= pdoc->LinesTotal()) {
1603 if (wrapState == eWrapNone) {
1604 if (wrapWidth != LineLayout::wrapWidthInfinite) {
1605 wrapWidth = LineLayout::wrapWidthInfinite;
1606 for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
1607 cs.SetHeight(lineDoc, 1 +
1608 (vs.annotationVisible ? pdoc->AnnotationLines(lineDoc) : 0));
1610 wrapOccurred = true;
1612 wrapStart = wrapLineLarge;
1613 wrapEnd = wrapLineLarge;
1614 } else {
1615 if (wrapEnd >= pdoc->LinesTotal())
1616 wrapEnd = pdoc->LinesTotal();
1617 //ElapsedTime et;
1618 int lineDocTop = cs.DocFromDisplay(topLine);
1619 int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
1620 PRectangle rcTextArea = GetClientRectangle();
1621 rcTextArea.left = vs.textStart;
1622 rcTextArea.right -= vs.textStart;
1623 wrapWidth = rcTextArea.Width();
1624 RefreshStyleData();
1625 AutoSurface surface(this);
1626 if (surface) {
1627 bool priorityWrap = false;
1628 int lastLineToWrap = wrapEnd;
1629 int lineToWrap = wrapStart;
1630 if (!fullWrap) {
1631 if (priorityWrapLineStart >= 0) {
1632 // This is a priority wrap.
1633 lineToWrap = priorityWrapLineStart;
1634 lastLineToWrap = priorityWrapLineStart + linesInOneCall;
1635 priorityWrap = true;
1636 } else {
1637 // This is idle wrap.
1638 lastLineToWrap = wrapStart + linesInOneCall;
1640 if (lastLineToWrap >= wrapEnd)
1641 lastLineToWrap = wrapEnd;
1642 } // else do a fullWrap.
1644 // Ensure all lines being wrapped are styled.
1645 pdoc->EnsureStyledTo(pdoc->LineEnd(lastLineToWrap));
1647 // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
1648 // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
1649 while (lineToWrap < lastLineToWrap) {
1650 if (WrapOneLine(surface, lineToWrap)) {
1651 wrapOccurred = true;
1653 lineToWrap++;
1655 if (!priorityWrap)
1656 wrapStart = lineToWrap;
1657 // If wrapping is done, bring it to resting position
1658 if (wrapStart >= wrapEnd) {
1659 wrapStart = wrapLineLarge;
1660 wrapEnd = wrapLineLarge;
1663 goodTopLine = cs.DisplayFromDoc(lineDocTop);
1664 if (subLineTop < cs.GetHeight(lineDocTop))
1665 goodTopLine += subLineTop;
1666 else
1667 goodTopLine += cs.GetHeight(lineDocTop);
1668 //double durWrap = et.Duration(true);
1669 //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
1672 if (wrapOccurred) {
1673 SetScrollBars();
1674 SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
1675 SetVerticalScrollPos();
1677 return wrapOccurred;
1680 void Editor::LinesJoin() {
1681 if (!RangeContainsProtected(targetStart, targetEnd)) {
1682 UndoGroup ug(pdoc);
1683 bool prevNonWS = true;
1684 for (int pos = targetStart; pos < targetEnd; pos++) {
1685 if (pdoc->IsPositionInLineEnd(pos)) {
1686 targetEnd -= pdoc->LenChar(pos);
1687 pdoc->DelChar(pos);
1688 if (prevNonWS) {
1689 // Ensure at least one space separating previous lines
1690 pdoc->InsertChar(pos, ' ');
1691 targetEnd++;
1693 } else {
1694 prevNonWS = pdoc->CharAt(pos) != ' ';
1700 const char *Editor::StringFromEOLMode(int eolMode) {
1701 if (eolMode == SC_EOL_CRLF) {
1702 return "\r\n";
1703 } else if (eolMode == SC_EOL_CR) {
1704 return "\r";
1705 } else {
1706 return "\n";
1710 void Editor::LinesSplit(int pixelWidth) {
1711 if (!RangeContainsProtected(targetStart, targetEnd)) {
1712 if (pixelWidth == 0) {
1713 PRectangle rcText = GetTextRectangle();
1714 pixelWidth = rcText.Width();
1716 int lineStart = pdoc->LineFromPosition(targetStart);
1717 int lineEnd = pdoc->LineFromPosition(targetEnd);
1718 const char *eol = StringFromEOLMode(pdoc->eolMode);
1719 UndoGroup ug(pdoc);
1720 for (int line = lineStart; line <= lineEnd; line++) {
1721 AutoSurface surface(this);
1722 AutoLineLayout ll(llc, RetrieveLineLayout(line));
1723 if (surface && ll) {
1724 unsigned int posLineStart = pdoc->LineStart(line);
1725 LayoutLine(line, surface, vs, ll, pixelWidth);
1726 for (int subLine = 1; subLine < ll->lines; subLine++) {
1727 pdoc->InsertCString(
1728 static_cast<int>(posLineStart + (subLine - 1) * strlen(eol) +
1729 ll->LineStart(subLine)),
1730 eol);
1731 targetEnd += static_cast<int>(strlen(eol));
1734 lineEnd = pdoc->LineFromPosition(targetEnd);
1739 int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
1740 if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
1741 return markerDefault;
1742 return markerCheck;
1745 bool ValidStyledText(ViewStyle &vs, size_t styleOffset, const StyledText &st) {
1746 if (st.multipleStyles) {
1747 for (size_t iStyle=0; iStyle<st.length; iStyle++) {
1748 if (!vs.ValidStyle(styleOffset + st.styles[iStyle]))
1749 return false;
1751 } else {
1752 if (!vs.ValidStyle(styleOffset + st.style))
1753 return false;
1755 return true;
1758 static int WidthStyledText(Surface *surface, ViewStyle &vs, int styleOffset,
1759 const char *text, const unsigned char *styles, size_t len) {
1760 int width = 0;
1761 size_t start = 0;
1762 while (start < len) {
1763 size_t style = styles[start];
1764 size_t endSegment = start;
1765 while ((endSegment+1 < len) && (static_cast<size_t>(styles[endSegment+1]) == style))
1766 endSegment++;
1767 width += surface->WidthText(vs.styles[style+styleOffset].font, text + start,
1768 static_cast<int>(endSegment - start + 1));
1769 start = endSegment + 1;
1771 return width;
1774 static int WidestLineWidth(Surface *surface, ViewStyle &vs, int styleOffset, const StyledText &st) {
1775 int widthMax = 0;
1776 size_t start = 0;
1777 while (start < st.length) {
1778 size_t lenLine = st.LineLength(start);
1779 int widthSubLine;
1780 if (st.multipleStyles) {
1781 widthSubLine = WidthStyledText(surface, vs, styleOffset, st.text + start, st.styles + start, lenLine);
1782 } else {
1783 widthSubLine = surface->WidthText(vs.styles[styleOffset + st.style].font,
1784 st.text + start, static_cast<int>(lenLine));
1786 if (widthSubLine > widthMax)
1787 widthMax = widthSubLine;
1788 start += lenLine + 1;
1790 return widthMax;
1793 void DrawStyledText(Surface *surface, ViewStyle &vs, int styleOffset, PRectangle rcText, int ascent,
1794 const StyledText &st, size_t start, size_t length) {
1796 if (st.multipleStyles) {
1797 int x = rcText.left;
1798 size_t i = 0;
1799 while (i < length) {
1800 size_t end = i;
1801 int style = st.styles[i + start];
1802 while (end < length-1 && st.styles[start+end+1] == style)
1803 end++;
1804 style += styleOffset;
1805 int width = surface->WidthText(vs.styles[style].font,
1806 st.text + start + i, static_cast<int>(end - i + 1));
1807 PRectangle rcSegment = rcText;
1808 rcSegment.left = x;
1809 rcSegment.right = x + width + 1;
1810 surface->DrawTextNoClip(rcSegment, vs.styles[style].font,
1811 ascent, st.text + start + i,
1812 static_cast<int>(end - i + 1),
1813 vs.styles[style].fore,
1814 vs.styles[style].back);
1815 x += width;
1816 i = end + 1;
1818 } else {
1819 size_t style = st.style + styleOffset;
1820 surface->DrawTextNoClip(rcText, vs.styles[style].font,
1821 rcText.top + vs.maxAscent, st.text + start,
1822 static_cast<int>(length),
1823 vs.styles[style].fore,
1824 vs.styles[style].back);
1828 void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
1829 if (vs.fixedColumnWidth == 0)
1830 return;
1832 RefreshPixMaps(surfWindow);
1834 PRectangle rcMargin = GetClientRectangle();
1835 Point ptOrigin = GetVisibleOriginInMain();
1836 rcMargin.Move(0, -ptOrigin.y);
1837 rcMargin.left = 0;
1838 rcMargin.right = vs.fixedColumnWidth;
1840 if (!rc.Intersects(rcMargin))
1841 return;
1843 Surface *surface;
1844 if (bufferedDraw) {
1845 surface = pixmapSelMargin;
1846 } else {
1847 surface = surfWindow;
1850 // Clip vertically to paint area to avoid drawing line numbers
1851 if (rcMargin.bottom > rc.bottom)
1852 rcMargin.bottom = rc.bottom;
1853 if (rcMargin.top < rc.top)
1854 rcMargin.top = rc.top;
1856 PRectangle rcSelMargin = rcMargin;
1857 rcSelMargin.right = rcMargin.left;
1858 if (rcSelMargin.bottom < rc.bottom)
1859 rcSelMargin.bottom = rc.bottom;
1861 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
1862 if (vs.ms[margin].width > 0) {
1864 rcSelMargin.left = rcSelMargin.right;
1865 rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
1867 if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
1868 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1869 // Required because of special way brush is created for selection margin
1870 // Ensure patterns line up when scrolling with separate margin view
1871 // by choosing correctly aligned variant.
1872 bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
1873 surface->FillRectangle(rcSelMargin,
1874 invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
1875 } else {
1876 ColourDesired colour;
1877 switch (vs.ms[margin].style) {
1878 case SC_MARGIN_BACK:
1879 colour = vs.styles[STYLE_DEFAULT].back;
1880 break;
1881 case SC_MARGIN_FORE:
1882 colour = vs.styles[STYLE_DEFAULT].fore;
1883 break;
1884 default:
1885 colour = vs.styles[STYLE_LINENUMBER].back;
1886 break;
1888 surface->FillRectangle(rcSelMargin, colour);
1890 } else {
1891 surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
1894 const int lineStartPaint = (rcMargin.top + ptOrigin.y) / vs.lineHeight;
1895 int visibleLine = TopLineOfMain() + lineStartPaint;
1896 int yposScreen = lineStartPaint * vs.lineHeight - ptOrigin.y;
1897 // Work out whether the top line is whitespace located after a
1898 // lessening of fold level which implies a 'fold tail' but which should not
1899 // be displayed until the last of a sequence of whitespace.
1900 bool needWhiteClosure = false;
1901 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1902 int level = pdoc->GetLevel(cs.DocFromDisplay(visibleLine));
1903 if (level & SC_FOLDLEVELWHITEFLAG) {
1904 int lineBack = cs.DocFromDisplay(visibleLine);
1905 int levelPrev = level;
1906 while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
1907 lineBack--;
1908 levelPrev = pdoc->GetLevel(lineBack);
1910 if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
1911 if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
1912 needWhiteClosure = true;
1915 if (highlightDelimiter.isEnabled) {
1916 int lastLine = cs.DocFromDisplay(topLine + LinesOnScreen()) + 1;
1917 pdoc->GetHighlightDelimiters(highlightDelimiter, pdoc->LineFromPosition(CurrentPosition()), lastLine);
1921 // Old code does not know about new markers needed to distinguish all cases
1922 int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
1923 SC_MARKNUM_FOLDEROPEN);
1924 int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
1925 SC_MARKNUM_FOLDER);
1927 while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rc.bottom) {
1929 PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
1930 int lineDoc = cs.DocFromDisplay(visibleLine);
1931 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
1932 bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
1933 bool lastSubLine = visibleLine == (cs.DisplayFromDoc(lineDoc + 1) - 1);
1935 int marks = pdoc->GetMark(lineDoc);
1936 if (!firstSubLine)
1937 marks = 0;
1939 bool headWithTail = false;
1941 if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
1942 // Decide which fold indicator should be displayed
1943 int level = pdoc->GetLevel(lineDoc);
1944 int levelNext = pdoc->GetLevel(lineDoc + 1);
1945 int levelNum = level & SC_FOLDLEVELNUMBERMASK;
1946 int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
1947 if (level & SC_FOLDLEVELHEADERFLAG) {
1948 if (firstSubLine) {
1949 if (levelNum < levelNextNum) {
1950 if (cs.GetExpanded(lineDoc)) {
1951 if (levelNum == SC_FOLDLEVELBASE)
1952 marks |= 1 << SC_MARKNUM_FOLDEROPEN;
1953 else
1954 marks |= 1 << folderOpenMid;
1955 } else {
1956 if (levelNum == SC_FOLDLEVELBASE)
1957 marks |= 1 << SC_MARKNUM_FOLDER;
1958 else
1959 marks |= 1 << folderEnd;
1961 } else if (levelNum > SC_FOLDLEVELBASE) {
1962 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1964 } else {
1965 if (levelNum < levelNextNum) {
1966 if (cs.GetExpanded(lineDoc)) {
1967 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1968 } else if (levelNum > SC_FOLDLEVELBASE) {
1969 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1971 } else if (levelNum > SC_FOLDLEVELBASE) {
1972 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1975 needWhiteClosure = false;
1976 int firstFollowupLine = cs.DocFromDisplay(cs.DisplayFromDoc(lineDoc + 1));
1977 int firstFollowupLineLevel = pdoc->GetLevel(firstFollowupLine);
1978 int secondFollowupLineLevelNum = pdoc->GetLevel(firstFollowupLine + 1) & SC_FOLDLEVELNUMBERMASK;
1979 if (!cs.GetExpanded(lineDoc)) {
1980 if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
1981 (levelNum > secondFollowupLineLevelNum))
1982 needWhiteClosure = true;
1984 if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
1985 headWithTail = true;
1987 } else if (level & SC_FOLDLEVELWHITEFLAG) {
1988 if (needWhiteClosure) {
1989 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
1990 marks |= 1 << SC_MARKNUM_FOLDERSUB;
1991 } else if (levelNextNum > SC_FOLDLEVELBASE) {
1992 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
1993 needWhiteClosure = false;
1994 } else {
1995 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
1996 needWhiteClosure = false;
1998 } else if (levelNum > SC_FOLDLEVELBASE) {
1999 if (levelNextNum < levelNum) {
2000 if (levelNextNum > SC_FOLDLEVELBASE) {
2001 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
2002 } else {
2003 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
2005 } else {
2006 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2009 } else if (levelNum > SC_FOLDLEVELBASE) {
2010 if (levelNextNum < levelNum) {
2011 needWhiteClosure = false;
2012 if (levelNext & SC_FOLDLEVELWHITEFLAG) {
2013 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2014 needWhiteClosure = true;
2015 } else if (lastSubLine) {
2016 if (levelNextNum > SC_FOLDLEVELBASE) {
2017 marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
2018 } else {
2019 marks |= 1 << SC_MARKNUM_FOLDERTAIL;
2021 } else {
2022 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2024 } else {
2025 marks |= 1 << SC_MARKNUM_FOLDERSUB;
2030 marks &= vs.ms[margin].mask;
2032 PRectangle rcMarker = rcSelMargin;
2033 rcMarker.top = yposScreen;
2034 rcMarker.bottom = yposScreen + vs.lineHeight;
2035 if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
2036 if (firstSubLine) {
2037 char number[100] = "";
2038 if (lineDoc >= 0)
2039 sprintf(number, "%d", lineDoc + 1);
2040 if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
2041 int lev = pdoc->GetLevel(lineDoc);
2042 sprintf(number, "%c%c %03X %03X",
2043 (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
2044 (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
2045 lev & SC_FOLDLEVELNUMBERMASK,
2046 lev >> 16
2049 PRectangle rcNumber = rcMarker;
2050 // Right justify
2051 XYPOSITION width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
2052 XYPOSITION xpos = rcNumber.right - width - marginNumberPadding;
2053 rcNumber.left = xpos;
2054 surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
2055 rcNumber.top + vs.maxAscent, number, istrlen(number),
2056 vs.styles[STYLE_LINENUMBER].fore,
2057 vs.styles[STYLE_LINENUMBER].back);
2058 } else if (wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
2059 PRectangle rcWrapMarker = rcMarker;
2060 rcWrapMarker.right -= 3;
2061 rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
2062 DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
2064 } else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
2065 if (firstSubLine) {
2066 const StyledText stMargin = pdoc->MarginStyledText(lineDoc);
2067 if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
2068 surface->FillRectangle(rcMarker,
2069 vs.styles[stMargin.StyleAt(0)+vs.marginStyleOffset].back);
2070 if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
2071 int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
2072 rcMarker.left = rcMarker.right - width - 3;
2074 DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker, rcMarker.top + vs.maxAscent,
2075 stMargin, 0, stMargin.length);
2080 if (marks) {
2081 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2082 if (marks & 1) {
2083 LineMarker::typeOfFold tFold = LineMarker::undefined;
2084 if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
2085 if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
2086 tFold = LineMarker::body;
2087 } else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
2088 if (firstSubLine) {
2089 tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
2090 } else {
2091 if (cs.GetExpanded(lineDoc) || headWithTail) {
2092 tFold = LineMarker::body;
2093 } else {
2094 tFold = LineMarker::undefined;
2097 } else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
2098 tFold = LineMarker::tail;
2101 vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font, tFold, vs.ms[margin].style);
2103 marks >>= 1;
2107 visibleLine++;
2108 yposScreen += vs.lineHeight;
2113 PRectangle rcBlankMargin = rcMargin;
2114 rcBlankMargin.left = rcSelMargin.right;
2115 surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
2117 if (bufferedDraw) {
2118 surfWindow->Copy(rcMargin, Point(rcMargin.left, rcMargin.top), *pixmapSelMargin);
2122 void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
2123 int ydiff = (rcTab.bottom - rcTab.top) / 2;
2124 int xhead = rcTab.right - 1 - ydiff;
2125 if (xhead <= rcTab.left) {
2126 ydiff -= rcTab.left - xhead - 1;
2127 xhead = rcTab.left - 1;
2129 if ((rcTab.left + 2) < (rcTab.right - 1))
2130 surface->MoveTo(rcTab.left + 2, ymid);
2131 else
2132 surface->MoveTo(rcTab.right - 1, ymid);
2133 surface->LineTo(rcTab.right - 1, ymid);
2134 surface->LineTo(xhead, ymid - ydiff);
2135 surface->MoveTo(rcTab.right - 1, ymid);
2136 surface->LineTo(xhead, ymid + ydiff);
2139 LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
2140 int posLineStart = pdoc->LineStart(lineNumber);
2141 int posLineEnd = pdoc->LineStart(lineNumber + 1);
2142 PLATFORM_ASSERT(posLineEnd >= posLineStart);
2143 int lineCaret = pdoc->LineFromPosition(sel.MainCaret());
2144 return llc.Retrieve(lineNumber, lineCaret,
2145 posLineEnd - posLineStart, pdoc->GetStyleClock(),
2146 LinesOnScreen() + 1, pdoc->LinesTotal());
2149 bool BadUTF(const char *s, int len, int &trailBytes) {
2150 // For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
2151 if (trailBytes) {
2152 trailBytes--;
2153 return false;
2155 int utf8status = UTF8Classify(reinterpret_cast<const unsigned char *>(s), len);
2156 if (utf8status & UTF8MaskInvalid) {
2157 return true;
2158 } else {
2159 trailBytes = (utf8status & UTF8MaskWidth) - 1;
2160 return false;
2165 * Fill in the LineLayout data for the given line.
2166 * Copy the given @a line and its styles from the document into local arrays.
2167 * Also determine the x position at which each character starts.
2169 void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
2170 if (!ll)
2171 return;
2173 PLATFORM_ASSERT(line < pdoc->LinesTotal());
2174 PLATFORM_ASSERT(ll->chars != NULL);
2175 int posLineStart = pdoc->LineStart(line);
2176 int posLineEnd = pdoc->LineStart(line + 1);
2177 // If the line is very long, limit the treatment to a length that should fit in the viewport
2178 if (posLineEnd > (posLineStart + ll->maxLineLength)) {
2179 posLineEnd = posLineStart + ll->maxLineLength;
2181 if (ll->validity == LineLayout::llCheckTextAndStyle) {
2182 int lineLength = posLineEnd - posLineStart;
2183 if (!vstyle.viewEOL) {
2184 lineLength = pdoc->LineEnd(line) - posLineStart;
2186 if (lineLength == ll->numCharsInLine) {
2187 // See if chars, styles, indicators, are all the same
2188 bool allSame = true;
2189 const int styleMask = pdoc->stylingBitsMask;
2190 // Check base line layout
2191 char styleByte = 0;
2192 int numCharsInLine = 0;
2193 while (numCharsInLine < lineLength) {
2194 int charInDoc = numCharsInLine + posLineStart;
2195 char chDoc = pdoc->CharAt(charInDoc);
2196 styleByte = pdoc->StyleAt(charInDoc);
2197 allSame = allSame &&
2198 (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
2199 allSame = allSame &&
2200 (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
2201 if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
2202 allSame = allSame &&
2203 (ll->chars[numCharsInLine] == chDoc);
2204 else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
2205 allSame = allSame &&
2206 (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
2207 else // Style::caseUpper
2208 allSame = allSame &&
2209 (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
2210 numCharsInLine++;
2212 allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
2213 if (allSame) {
2214 ll->validity = LineLayout::llPositions;
2215 } else {
2216 ll->validity = LineLayout::llInvalid;
2218 } else {
2219 ll->validity = LineLayout::llInvalid;
2222 if (ll->validity == LineLayout::llInvalid) {
2223 ll->widthLine = LineLayout::wrapWidthInfinite;
2224 ll->lines = 1;
2225 if (vstyle.edgeState == EDGE_BACKGROUND) {
2226 ll->edgeColumn = pdoc->FindColumn(line, theEdge);
2227 if (ll->edgeColumn >= posLineStart) {
2228 ll->edgeColumn -= posLineStart;
2230 } else {
2231 ll->edgeColumn = -1;
2234 char styleByte;
2235 const int styleMask = pdoc->stylingBitsMask;
2236 ll->styleBitsSet = 0;
2237 // Fill base line layout
2238 const int lineLength = posLineEnd - posLineStart;
2239 pdoc->GetCharRange(ll->chars, posLineStart, lineLength);
2240 pdoc->GetStyleRange(ll->styles, posLineStart, lineLength);
2241 int numCharsBeforeEOL = pdoc->LineEnd(line) - posLineStart;
2242 const int numCharsInLine = (vstyle.viewEOL) ? lineLength : numCharsBeforeEOL;
2243 for (int styleInLine = 0; styleInLine < numCharsInLine; styleInLine++) {
2244 styleByte = ll->styles[styleInLine];
2245 ll->styleBitsSet |= styleByte;
2246 ll->styles[styleInLine] = static_cast<char>(styleByte & styleMask);
2247 ll->indicators[styleInLine] = static_cast<char>(styleByte & ~styleMask);
2249 styleByte = static_cast<char>(((lineLength > 0) ? ll->styles[lineLength-1] : 0) & styleMask);
2250 if (vstyle.someStylesForceCase) {
2251 for (int charInLine = 0; charInLine<lineLength; charInLine++) {
2252 char chDoc = ll->chars[charInLine];
2253 if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseUpper)
2254 ll->chars[charInLine] = static_cast<char>(toupper(chDoc));
2255 else if (vstyle.styles[ll->styles[charInLine]].caseForce == Style::caseLower)
2256 ll->chars[charInLine] = static_cast<char>(tolower(chDoc));
2259 ll->xHighlightGuide = 0;
2260 // Extra element at the end of the line to hold end x position and act as
2261 ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
2262 ll->styles[numCharsInLine] = styleByte; // For eolFilled
2263 ll->indicators[numCharsInLine] = 0;
2265 // Layout the line, determining the position of each character,
2266 // with an extra element at the end for the end of the line.
2267 int startseg = 0; // Start of the current segment, in char. number
2268 XYACCUMULATOR startsegx = 0; // Start of the current segment, in pixels
2269 ll->positions[0] = 0;
2270 XYPOSITION tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
2271 bool lastSegItalics = false;
2272 Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
2274 XYPOSITION ctrlCharWidth[32] = {0};
2275 bool isControlNext = IsControlCharacter(ll->chars[0]);
2276 int trailBytes = 0;
2277 bool isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars, numCharsInLine, trailBytes);
2278 for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
2279 bool isControl = isControlNext;
2280 isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
2281 bool isBadUTF = isBadUTFNext;
2282 isBadUTFNext = IsUnicodeMode() && BadUTF(ll->chars + charInLine + 1, numCharsInLine - charInLine - 1, trailBytes);
2283 if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
2284 isControl || isControlNext || isBadUTF || isBadUTFNext || ((charInLine+1) >= numCharsBeforeEOL)) {
2285 ll->positions[startseg] = 0;
2286 if (vstyle.styles[ll->styles[charInLine]].visible) {
2287 if (isControl) {
2288 if (ll->chars[charInLine] == '\t') {
2289 ll->positions[charInLine + 1] =
2290 ((static_cast<int>((startsegx + 2) / tabWidth) + 1) * tabWidth) - startsegx;
2291 } else if (controlCharSymbol < 32) {
2292 if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
2293 const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
2294 ctrlCharWidth[ll->chars[charInLine]] =
2295 surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + ctrlCharPadding;
2297 ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
2298 } else {
2299 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
2300 surface->MeasureWidths(ctrlCharsFont, cc, 1,
2301 ll->positions + startseg + 1);
2303 lastSegItalics = false;
2304 } else if ((isBadUTF) || (charInLine >= numCharsBeforeEOL)) {
2305 char hexits[4];
2306 sprintf(hexits, "x%2X", ll->chars[charInLine] & 0xff);
2307 ll->positions[charInLine + 1] =
2308 surface->WidthText(ctrlCharsFont, hexits, istrlen(hexits)) + 3;
2309 } else { // Regular character
2310 int lenSeg = charInLine - startseg + 1;
2311 if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
2312 lastSegItalics = false;
2313 // Over half the segments are single characters and of these about half are space characters.
2314 ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
2315 } else {
2316 lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
2317 posCache.MeasureWidths(surface, vstyle, ll->styles[charInLine], ll->chars + startseg,
2318 lenSeg, ll->positions + startseg + 1, pdoc);
2321 } else { // invisible
2322 for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
2323 ll->positions[posToZero] = 0;
2326 for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
2327 ll->positions[posToIncrease] += startsegx;
2329 startsegx = ll->positions[charInLine + 1];
2330 startseg = charInLine + 1;
2333 // Small hack to make lines that end with italics not cut off the edge of the last character
2334 if ((startseg > 0) && lastSegItalics) {
2335 ll->positions[startseg] += lastSegItalicsOffset;
2337 ll->numCharsInLine = numCharsInLine;
2338 ll->numCharsBeforeEOL = numCharsBeforeEOL;
2339 ll->validity = LineLayout::llPositions;
2341 // Hard to cope when too narrow, so just assume there is space
2342 if (width < 20) {
2343 width = 20;
2345 if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
2346 ll->widthLine = width;
2347 if (width == LineLayout::wrapWidthInfinite) {
2348 ll->lines = 1;
2349 } else if (width > ll->positions[ll->numCharsInLine]) {
2350 // Simple common case where line does not need wrapping.
2351 ll->lines = 1;
2352 } else {
2353 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2354 width -= static_cast<int>(vstyle.aveCharWidth); // take into account the space for end wrap mark
2356 XYPOSITION wrapAddIndent = 0; // This will be added to initial indent of line
2357 if (wrapIndentMode == SC_WRAPINDENT_INDENT) {
2358 wrapAddIndent = pdoc->IndentSize() * vstyle.spaceWidth;
2359 } else if (wrapIndentMode == SC_WRAPINDENT_FIXED) {
2360 wrapAddIndent = wrapVisualStartIndent * vstyle.aveCharWidth;
2362 ll->wrapIndent = wrapAddIndent;
2363 if (wrapIndentMode != SC_WRAPINDENT_FIXED)
2364 for (int i = 0; i < ll->numCharsInLine; i++) {
2365 if (!IsSpaceOrTab(ll->chars[i])) {
2366 ll->wrapIndent += ll->positions[i]; // Add line indent
2367 break;
2370 // Check for text width minimum
2371 if (ll->wrapIndent > width - static_cast<int>(vstyle.aveCharWidth) * 15)
2372 ll->wrapIndent = wrapAddIndent;
2373 // Check for wrapIndent minimum
2374 if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (ll->wrapIndent < vstyle.aveCharWidth))
2375 ll->wrapIndent = vstyle.aveCharWidth; // Indent to show start visual
2376 ll->lines = 0;
2377 // Calculate line start positions based upon width.
2378 int lastGoodBreak = 0;
2379 int lastLineStart = 0;
2380 XYACCUMULATOR startOffset = 0;
2381 int p = 0;
2382 while (p < ll->numCharsInLine) {
2383 if ((ll->positions[p + 1] - startOffset) >= width) {
2384 if (lastGoodBreak == lastLineStart) {
2385 // Try moving to start of last character
2386 if (p > 0) {
2387 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2388 - posLineStart;
2390 if (lastGoodBreak == lastLineStart) {
2391 // Ensure at least one character on line.
2392 lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
2393 - posLineStart;
2396 lastLineStart = lastGoodBreak;
2397 ll->lines++;
2398 ll->SetLineStart(ll->lines, lastGoodBreak);
2399 startOffset = ll->positions[lastGoodBreak];
2400 // take into account the space for start wrap mark and indent
2401 startOffset -= ll->wrapIndent;
2402 p = lastGoodBreak + 1;
2403 continue;
2405 if (p > 0) {
2406 if (wrapState == eWrapChar) {
2407 lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
2408 - posLineStart;
2409 p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
2410 continue;
2411 } else if (ll->styles[p] != ll->styles[p - 1]) {
2412 lastGoodBreak = p;
2413 } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
2414 lastGoodBreak = p;
2417 p++;
2419 ll->lines++;
2421 ll->validity = LineLayout::llLines;
2425 ColourDesired Editor::SelectionBackground(ViewStyle &vsDraw, bool main) {
2426 return main ?
2427 (primarySelection ? vsDraw.selbackground : vsDraw.selbackground2) :
2428 vsDraw.selAdditionalBackground;
2431 ColourDesired Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
2432 ColourDesired background, int inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
2433 if (inSelection == 1) {
2434 if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
2435 return SelectionBackground(vsDraw, true);
2437 } else if (inSelection == 2) {
2438 if (vsDraw.selbackset && (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA)) {
2439 return SelectionBackground(vsDraw, false);
2441 } else {
2442 if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
2443 (i >= ll->edgeColumn) &&
2444 (i < ll->numCharsBeforeEOL))
2445 return vsDraw.edgecolour;
2446 if (inHotspot && vsDraw.hotspotBackgroundSet)
2447 return vsDraw.hotspotBackground;
2449 if (overrideBackground && (styleMain != STYLE_BRACELIGHT) && (styleMain != STYLE_BRACEBAD)) {
2450 return background;
2451 } else {
2452 return vsDraw.styles[styleMain].back;
2456 void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
2457 Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
2458 PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
2459 surface->Copy(rcCopyArea, from,
2460 highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
2463 void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
2464 bool isEndMarker, ColourDesired wrapColour) {
2465 surface->PenColour(wrapColour);
2467 enum { xa = 1 }; // gap before start
2468 int w = rcPlace.right - rcPlace.left - xa - 1;
2470 bool xStraight = isEndMarker; // x-mirrored symbol for start marker
2471 bool yStraight = true;
2472 //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
2474 int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
2475 int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
2477 int dy = (rcPlace.bottom - rcPlace.top) / 5;
2478 int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
2480 struct Relative {
2481 Surface *surface;
2482 int xBase;
2483 int xDir;
2484 int yBase;
2485 int yDir;
2486 void MoveTo(int xRelative, int yRelative) {
2487 surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2489 void LineTo(int xRelative, int yRelative) {
2490 surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
2493 Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
2495 // arrow head
2496 rel.MoveTo(xa, y);
2497 rel.LineTo(xa + 2*w / 3, y - dy);
2498 rel.MoveTo(xa, y);
2499 rel.LineTo(xa + 2*w / 3, y + dy);
2501 // arrow body
2502 rel.MoveTo(xa, y);
2503 rel.LineTo(xa + w, y);
2504 rel.LineTo(xa + w, y - 2 * dy);
2505 rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
2506 y - 2 * dy);
2509 static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourDesired fill, int alpha) {
2510 if (alpha != SC_ALPHA_NOALPHA) {
2511 surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
2515 void DrawTextBlob(Surface *surface, ViewStyle &vsDraw, PRectangle rcSegment,
2516 const char *s, ColourDesired textBack, ColourDesired textFore, bool twoPhaseDraw) {
2517 if (!twoPhaseDraw) {
2518 surface->FillRectangle(rcSegment, textBack);
2520 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2521 int normalCharHeight = surface->Ascent(ctrlCharsFont) -
2522 surface->InternalLeading(ctrlCharsFont);
2523 PRectangle rcCChar = rcSegment;
2524 rcCChar.left = rcCChar.left + 1;
2525 rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
2526 rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
2527 PRectangle rcCentral = rcCChar;
2528 rcCentral.top++;
2529 rcCentral.bottom--;
2530 surface->FillRectangle(rcCentral, textFore);
2531 PRectangle rcChar = rcCChar;
2532 rcChar.left++;
2533 rcChar.right--;
2534 surface->DrawTextClipped(rcChar, ctrlCharsFont,
2535 rcSegment.top + vsDraw.maxAscent, s, istrlen(s),
2536 textBack, textFore);
2539 void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
2540 int line, int lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
2541 bool overrideBackground, ColourDesired background,
2542 bool drawWrapMarkEnd, ColourDesired wrapColour) {
2544 const int posLineStart = pdoc->LineStart(line);
2545 const int styleMask = pdoc->stylingBitsMask;
2546 PRectangle rcSegment = rcLine;
2548 const bool lastSubLine = subLine == (ll->lines - 1);
2549 XYPOSITION virtualSpace = 0;
2550 if (lastSubLine) {
2551 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2552 virtualSpace = sel.VirtualSpaceFor(pdoc->LineEnd(line)) * spaceWidth;
2554 XYPOSITION xEol = ll->positions[lineEnd] - subLineStart;
2556 // Fill the virtual space and show selections within it
2557 if (virtualSpace) {
2558 rcSegment.left = xEol + xStart;
2559 rcSegment.right = xEol + xStart + virtualSpace;
2560 surface->FillRectangle(rcSegment, overrideBackground ? background : vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2561 if (!hideSelection && ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA))) {
2562 SelectionSegment virtualSpaceRange(SelectionPosition(pdoc->LineEnd(line)), SelectionPosition(pdoc->LineEnd(line), sel.VirtualSpaceFor(pdoc->LineEnd(line))));
2563 for (size_t r=0; r<sel.Count(); r++) {
2564 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2565 if (alpha == SC_ALPHA_NOALPHA) {
2566 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
2567 if (!portion.Empty()) {
2568 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
2569 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
2570 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
2571 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
2572 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
2573 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, r == sel.Main()));
2580 int eolInSelection = 0;
2581 int alpha = SC_ALPHA_NOALPHA;
2582 if (!hideSelection) {
2583 int posAfterLineEnd = pdoc->LineStart(line + 1);
2584 eolInSelection = (subLine == (ll->lines - 1)) ? sel.InSelectionForEOL(posAfterLineEnd) : 0;
2585 alpha = (eolInSelection == 1) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
2588 // Draw the [CR], [LF], or [CR][LF] blobs if visible line ends are on
2589 XYPOSITION blobsWidth = 0;
2590 if (lastSubLine) {
2591 for (int eolPos=ll->numCharsBeforeEOL; eolPos<ll->numCharsInLine; eolPos++) {
2592 rcSegment.left = xStart + ll->positions[eolPos] - subLineStart + virtualSpace;
2593 rcSegment.right = xStart + ll->positions[eolPos+1] - subLineStart + virtualSpace;
2594 blobsWidth += rcSegment.Width();
2595 char hexits[4];
2596 const char *ctrlChar;
2597 unsigned char chEOL = ll->chars[eolPos];
2598 if (UTF8IsAscii(chEOL)) {
2599 ctrlChar = ControlCharacterString(chEOL);
2600 } else {
2601 sprintf(hexits, "x%2X", chEOL);
2602 ctrlChar = hexits;
2604 int styleMain = ll->styles[eolPos];
2605 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, eolInSelection, false, styleMain, eolPos, ll);
2606 ColourDesired textFore = vsDraw.styles[styleMain].fore;
2607 if (eolInSelection && vsDraw.selforeset) {
2608 textFore = (eolInSelection == 1) ? vsDraw.selforeground : vsDraw.selAdditionalForeground;
2610 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1)) {
2611 if (alpha == SC_ALPHA_NOALPHA) {
2612 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2613 } else {
2614 surface->FillRectangle(rcSegment, textBack);
2616 } else {
2617 surface->FillRectangle(rcSegment, textBack);
2619 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
2620 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2621 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2626 // Draw the eol-is-selected rectangle
2627 rcSegment.left = xEol + xStart + virtualSpace + blobsWidth;
2628 rcSegment.right = rcSegment.left + vsDraw.aveCharWidth;
2630 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2631 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2632 } else {
2633 if (overrideBackground) {
2634 surface->FillRectangle(rcSegment, background);
2635 } else if (line < pdoc->LinesTotal() - 1) {
2636 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2637 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2638 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2639 } else {
2640 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2642 if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2643 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2647 // Fill the remainder of the line
2648 rcSegment.left = rcSegment.right;
2649 if (rcSegment.left < rcLine.left)
2650 rcSegment.left = rcLine.left;
2651 rcSegment.right = rcLine.right;
2653 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha == SC_ALPHA_NOALPHA)) {
2654 surface->FillRectangle(rcSegment, SelectionBackground(vsDraw, eolInSelection == 1));
2655 } else {
2656 if (overrideBackground) {
2657 surface->FillRectangle(rcSegment, background);
2658 } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
2659 surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back);
2660 } else {
2661 surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back);
2663 if (eolInSelection && vsDraw.selEOLFilled && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (alpha != SC_ALPHA_NOALPHA)) {
2664 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, eolInSelection == 1), alpha);
2668 if (drawWrapMarkEnd) {
2669 PRectangle rcPlace = rcSegment;
2671 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
2672 rcPlace.left = xEol + xStart + virtualSpace;
2673 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2674 } else {
2675 // rcLine is clipped to text area
2676 rcPlace.right = rcLine.right;
2677 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2679 DrawWrapMarker(surface, rcPlace, true, wrapColour);
2683 void Editor::DrawIndicator(int indicNum, int startPos, int endPos, Surface *surface, ViewStyle &vsDraw,
2684 int xStart, PRectangle rcLine, LineLayout *ll, int subLine) {
2685 const XYPOSITION subLineStart = ll->positions[ll->LineStart(subLine)];
2686 PRectangle rcIndic(
2687 ll->positions[startPos] + xStart - subLineStart,
2688 rcLine.top + vsDraw.maxAscent,
2689 ll->positions[endPos] + xStart - subLineStart,
2690 rcLine.top + vsDraw.maxAscent + 3);
2691 vsDraw.indicators[indicNum].Draw(surface, rcIndic, rcLine);
2694 void Editor::DrawIndicators(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2695 PRectangle rcLine, LineLayout *ll, int subLine, int lineEnd, bool under) {
2696 // Draw decorators
2697 const int posLineStart = pdoc->LineStart(line);
2698 const int lineStart = ll->LineStart(subLine);
2699 const int posLineEnd = posLineStart + lineEnd;
2701 if (!under) {
2702 // Draw indicators
2703 // foreach indicator...
2704 for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
2705 if (!(mask & ll->styleBitsSet)) {
2706 mask <<= 1;
2707 continue;
2709 int startPos = -1;
2710 // foreach style pos in line...
2711 for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
2712 // look for starts...
2713 if (startPos < 0) {
2714 // NOT in indicator run, looking for START
2715 if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
2716 startPos = indicPos;
2718 // ... or ends
2719 if (startPos >= 0) {
2720 // IN indicator run, looking for END
2721 if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
2722 // AT end of indicator run, DRAW it!
2723 DrawIndicator(indicnum, startPos, indicPos, surface, vsDraw, xStart, rcLine, ll, subLine);
2724 // RESET control var
2725 startPos = -1;
2729 mask <<= 1;
2733 for (Decoration *deco = pdoc->decorations.root; deco; deco = deco->next) {
2734 if (under == vsDraw.indicators[deco->indicator].under) {
2735 int startPos = posLineStart + lineStart;
2736 if (!deco->rs.ValueAt(startPos)) {
2737 startPos = deco->rs.EndRun(startPos);
2739 while ((startPos < posLineEnd) && (deco->rs.ValueAt(startPos))) {
2740 int endPos = deco->rs.EndRun(startPos);
2741 if (endPos > posLineEnd)
2742 endPos = posLineEnd;
2743 DrawIndicator(deco->indicator, startPos - posLineStart, endPos - posLineStart,
2744 surface, vsDraw, xStart, rcLine, ll, subLine);
2745 startPos = deco->rs.EndRun(endPos);
2750 // Use indicators to highlight matching braces
2751 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
2752 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
2753 int braceIndicator = (bracesMatchStyle == STYLE_BRACELIGHT) ? vs.braceHighlightIndicator : vs.braceBadLightIndicator;
2754 if (under == vsDraw.indicators[braceIndicator].under) {
2755 Range rangeLine(posLineStart + lineStart, posLineEnd);
2756 if (rangeLine.ContainsCharacter(braces[0])) {
2757 int braceOffset = braces[0] - posLineStart;
2758 if (braceOffset < ll->numCharsInLine) {
2759 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2762 if (rangeLine.ContainsCharacter(braces[1])) {
2763 int braceOffset = braces[1] - posLineStart;
2764 if (braceOffset < ll->numCharsInLine) {
2765 DrawIndicator(braceIndicator, braceOffset, braceOffset + 1, surface, vsDraw, xStart, rcLine, ll, subLine);
2772 void Editor::DrawAnnotation(Surface *surface, ViewStyle &vsDraw, int line, int xStart,
2773 PRectangle rcLine, LineLayout *ll, int subLine) {
2774 int indent = pdoc->GetLineIndentation(line) * vsDraw.spaceWidth;
2775 PRectangle rcSegment = rcLine;
2776 int annotationLine = subLine - ll->lines;
2777 const StyledText stAnnotation = pdoc->AnnotationStyledText(line);
2778 if (stAnnotation.text && ValidStyledText(vsDraw, vsDraw.annotationStyleOffset, stAnnotation)) {
2779 surface->FillRectangle(rcSegment, vsDraw.styles[0].back);
2780 if (vs.annotationVisible == ANNOTATION_BOXED) {
2781 // Only care about calculating width if need to draw box
2782 int widthAnnotation = WidestLineWidth(surface, vsDraw, vsDraw.annotationStyleOffset, stAnnotation);
2783 widthAnnotation += vsDraw.spaceWidth * 2; // Margins
2784 rcSegment.left = xStart + indent;
2785 rcSegment.right = rcSegment.left + widthAnnotation;
2786 } else {
2787 rcSegment.left = xStart;
2789 const int annotationLines = pdoc->AnnotationLines(line);
2790 size_t start = 0;
2791 size_t lengthAnnotation = stAnnotation.LineLength(start);
2792 int lineInAnnotation = 0;
2793 while ((lineInAnnotation < annotationLine) && (start < stAnnotation.length)) {
2794 start += lengthAnnotation + 1;
2795 lengthAnnotation = stAnnotation.LineLength(start);
2796 lineInAnnotation++;
2798 PRectangle rcText = rcSegment;
2799 if (vs.annotationVisible == ANNOTATION_BOXED) {
2800 surface->FillRectangle(rcText,
2801 vsDraw.styles[stAnnotation.StyleAt(start) + vsDraw.annotationStyleOffset].back);
2802 rcText.left += vsDraw.spaceWidth;
2804 DrawStyledText(surface, vsDraw, vsDraw.annotationStyleOffset, rcText, rcText.top + vsDraw.maxAscent,
2805 stAnnotation, start, lengthAnnotation);
2806 if (vs.annotationVisible == ANNOTATION_BOXED) {
2807 surface->PenColour(vsDraw.styles[vsDraw.annotationStyleOffset].fore);
2808 surface->MoveTo(rcSegment.left, rcSegment.top);
2809 surface->LineTo(rcSegment.left, rcSegment.bottom);
2810 surface->MoveTo(rcSegment.right, rcSegment.top);
2811 surface->LineTo(rcSegment.right, rcSegment.bottom);
2812 if (subLine == ll->lines) {
2813 surface->MoveTo(rcSegment.left, rcSegment.top);
2814 surface->LineTo(rcSegment.right, rcSegment.top);
2816 if (subLine == ll->lines+annotationLines-1) {
2817 surface->MoveTo(rcSegment.left, rcSegment.bottom - 1);
2818 surface->LineTo(rcSegment.right, rcSegment.bottom - 1);
2824 void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
2825 PRectangle rcLine, LineLayout *ll, int subLine) {
2827 PRectangle rcSegment = rcLine;
2829 // Using one font for all control characters so it can be controlled independently to ensure
2830 // the box goes around the characters tightly. Seems to be no way to work out what height
2831 // is taken by an individual character - internal leading gives varying results.
2832 Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
2834 // See if something overrides the line background color: Either if caret is on the line
2835 // and background color is set for that, or if a marker is defined that forces its background
2836 // color onto the line, or if a marker is defined but has no selection margin in which to
2837 // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
2838 // with the earlier taking precedence. When multiple markers cause background override,
2839 // the color for the highest numbered one is used.
2840 bool overrideBackground = false;
2841 ColourDesired background;
2842 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
2843 overrideBackground = true;
2844 background = vsDraw.caretLineBackground;
2846 if (!overrideBackground) {
2847 int marks = pdoc->GetMark(line);
2848 for (int markBit = 0; (markBit < 32) && marks; markBit++) {
2849 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
2850 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2851 background = vsDraw.markers[markBit].back;
2852 overrideBackground = true;
2854 marks >>= 1;
2857 if (!overrideBackground) {
2858 if (vsDraw.maskInLine) {
2859 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
2860 if (marksMasked) {
2861 for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
2862 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
2863 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
2864 overrideBackground = true;
2865 background = vsDraw.markers[markBit].back;
2867 marksMasked >>= 1;
2873 SCNotification scn = {0};
2874 scn.nmhdr.code = SCN_GETBKCOLOR;
2875 scn.line = line;
2876 scn.lParam = -1;
2877 NotifyParent(&scn);
2878 if (scn.lParam != -1)
2880 background = scn.lParam;
2881 overrideBackground = true;
2884 bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
2885 (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
2887 bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
2888 const XYPOSITION indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
2889 const XYPOSITION epsilon = 0.0001f; // A small nudge to avoid floating point precision issues
2891 int posLineStart = pdoc->LineStart(line);
2893 int startseg = ll->LineStart(subLine);
2894 XYACCUMULATOR subLineStart = ll->positions[startseg];
2895 if (subLine >= ll->lines) {
2896 DrawAnnotation(surface, vsDraw, line, xStart, rcLine, ll, subLine);
2897 return; // No further drawing
2899 int lineStart = 0;
2900 int lineEnd = 0;
2901 if (subLine < ll->lines) {
2902 lineStart = ll->LineStart(subLine);
2903 lineEnd = ll->LineStart(subLine + 1);
2904 if (subLine == ll->lines - 1) {
2905 lineEnd = ll->numCharsBeforeEOL;
2909 ColourDesired wrapColour = vsDraw.styles[STYLE_DEFAULT].fore;
2910 if (vsDraw.whitespaceForegroundSet)
2911 wrapColour = vsDraw.whitespaceForeground;
2913 bool drawWrapMarkEnd = false;
2915 if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
2916 if (subLine + 1 < ll->lines) {
2917 drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
2921 if (ll->wrapIndent != 0) {
2923 bool continuedWrapLine = false;
2924 if (subLine < ll->lines) {
2925 continuedWrapLine = ll->LineStart(subLine) != 0;
2928 if (continuedWrapLine) {
2929 // draw continuation rect
2930 PRectangle rcPlace = rcSegment;
2932 rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
2933 rcPlace.right = rcPlace.left + ll->wrapIndent;
2935 // default bgnd here..
2936 surface->FillRectangle(rcSegment, overrideBackground ? background :
2937 vsDraw.styles[STYLE_DEFAULT].back);
2939 // main line style would be below but this would be inconsistent with end markers
2940 // also would possibly not be the style at wrap point
2941 //int styleMain = ll->styles[lineStart];
2942 //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back);
2944 if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
2946 if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
2947 rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
2948 else
2949 rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
2951 DrawWrapMarker(surface, rcPlace, false, wrapColour);
2954 xStart += static_cast<int>(ll->wrapIndent);
2958 bool selBackDrawn = vsDraw.selbackset &&
2959 ((vsDraw.selAlpha == SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha == SC_ALPHA_NOALPHA));
2961 // Does not take margin into account but not significant
2962 int xStartVisible = static_cast<int>(subLineStart) - xStart;
2964 ll->psel = &sel;
2966 BreakFinder bfBack(ll, lineStart, lineEnd, posLineStart, xStartVisible, selBackDrawn, pdoc);
2967 int next = bfBack.First();
2969 // Background drawing loop
2970 while (twoPhaseDraw && (next < lineEnd)) {
2972 startseg = next;
2973 next = bfBack.Next();
2974 int i = next - 1;
2975 int iDoc = i + posLineStart;
2977 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
2978 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
2979 // Only try to draw if really visible - enhances performance by not calling environment to
2980 // draw strings that are completely past the right side of the window.
2981 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
2982 // Clip to line rectangle, since may have a huge position which will not work with some platforms
2983 if (rcSegment.left < rcLine.left)
2984 rcSegment.left = rcLine.left;
2985 if (rcSegment.right > rcLine.right)
2986 rcSegment.right = rcLine.right;
2988 int styleMain = ll->styles[i];
2989 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
2990 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
2991 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
2992 if (ll->chars[i] == '\t') {
2993 // Tab display
2994 if (drawWhitespaceBackground &&
2995 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
2996 textBack = vsDraw.whitespaceBackground;
2997 surface->FillRectangle(rcSegment, textBack);
2998 } else if (IsControlCharacter(ll->chars[i])) {
2999 // Control character display
3000 inIndentation = false;
3001 surface->FillRectangle(rcSegment, textBack);
3002 } else {
3003 // Normal text display
3004 surface->FillRectangle(rcSegment, textBack);
3005 if (vsDraw.viewWhitespace != wsInvisible ||
3006 (inIndentation && vsDraw.viewIndentationGuides == ivReal)) {
3007 for (int cpos = 0; cpos <= i - startseg; cpos++) {
3008 if (ll->chars[cpos + startseg] == ' ') {
3009 if (drawWhitespaceBackground &&
3010 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3011 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
3012 rcSegment.top,
3013 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
3014 rcSegment.bottom);
3015 surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground);
3017 } else {
3018 inIndentation = false;
3023 } else if (rcSegment.left > rcLine.right) {
3024 break;
3028 if (twoPhaseDraw) {
3029 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3030 xStart, subLine, subLineStart, overrideBackground, background,
3031 drawWrapMarkEnd, wrapColour);
3034 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, true);
3036 if (vsDraw.edgeState == EDGE_LINE) {
3037 int edgeX = theEdge * vsDraw.spaceWidth;
3038 rcSegment.left = edgeX + xStart;
3039 if ((ll->wrapIndent != 0) && (lineStart != 0))
3040 rcSegment.left -= ll->wrapIndent;
3041 rcSegment.right = rcSegment.left + 1;
3042 surface->FillRectangle(rcSegment, vsDraw.edgecolour);
3045 // Draw underline mark as part of background if not transparent
3046 int marks = pdoc->GetMark(line);
3047 int markBit;
3048 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3049 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE) &&
3050 (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
3051 PRectangle rcUnderline = rcLine;
3052 rcUnderline.top = rcUnderline.bottom - 2;
3053 surface->FillRectangle(rcUnderline, vsDraw.markers[markBit].back);
3055 marks >>= 1;
3058 inIndentation = subLine == 0; // Do not handle indentation except on first subline.
3059 // Foreground drawing loop
3060 BreakFinder bfFore(ll, lineStart, lineEnd, posLineStart, xStartVisible,
3061 ((!twoPhaseDraw && selBackDrawn) || vsDraw.selforeset), pdoc);
3062 next = bfFore.First();
3064 while (next < lineEnd) {
3066 startseg = next;
3067 next = bfFore.Next();
3068 int i = next - 1;
3070 int iDoc = i + posLineStart;
3072 rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
3073 rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
3074 // Only try to draw if really visible - enhances performance by not calling environment to
3075 // draw strings that are completely past the right side of the window.
3076 if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
3077 int styleMain = ll->styles[i];
3078 ColourDesired textFore = vsDraw.styles[styleMain].fore;
3079 Font &textFont = vsDraw.styles[styleMain].font;
3080 //hotspot foreground
3081 if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
3082 if (vsDraw.hotspotForegroundSet)
3083 textFore = vsDraw.hotspotForeground;
3085 const int inSelection = hideSelection ? 0 : sel.CharacterInSelection(iDoc);
3086 if (inSelection && (vsDraw.selforeset)) {
3087 textFore = (inSelection == 1) ? vsDraw.selforeground : vsDraw.selAdditionalForeground;
3089 bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
3090 ColourDesired textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
3091 if (ll->chars[i] == '\t') {
3092 // Tab display
3093 if (!twoPhaseDraw) {
3094 if (drawWhitespaceBackground &&
3095 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
3096 textBack = vsDraw.whitespaceBackground;
3097 surface->FillRectangle(rcSegment, textBack);
3099 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3100 for (int indentCount = (ll->positions[i] + epsilon) / indentWidth;
3101 indentCount <= (ll->positions[i + 1] - epsilon) / indentWidth;
3102 indentCount++) {
3103 if (indentCount > 0) {
3104 int xIndent = indentCount * indentWidth;
3105 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3106 (ll->xHighlightGuide == xIndent));
3110 if (vsDraw.viewWhitespace != wsInvisible) {
3111 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3112 if (vsDraw.whitespaceForegroundSet)
3113 textFore = vsDraw.whitespaceForeground;
3114 surface->PenColour(textFore);
3115 PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
3116 rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
3117 DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
3120 } else if (IsControlCharacter(ll->chars[i])) {
3121 // Control character display
3122 inIndentation = false;
3123 if (controlCharSymbol < 32) {
3124 // Draw the character
3125 const char *ctrlChar = ControlCharacterString(ll->chars[i]);
3126 DrawTextBlob(surface, vsDraw, rcSegment, ctrlChar, textBack, textFore, twoPhaseDraw);
3127 } else {
3128 char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
3129 surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
3130 rcSegment.top + vsDraw.maxAscent,
3131 cc, 1, textBack, textFore);
3133 } else if ((i == startseg) && (static_cast<unsigned char>(ll->chars[i]) >= 0x80) && IsUnicodeMode()) {
3134 // A single byte >= 0x80 in UTF-8 is a bad byte and is displayed as its hex value
3135 char hexits[4];
3136 sprintf(hexits, "x%2X", ll->chars[i] & 0xff);
3137 DrawTextBlob(surface, vsDraw, rcSegment, hexits, textBack, textFore, twoPhaseDraw);
3138 } else {
3139 // Normal text display
3140 if (vsDraw.styles[styleMain].visible) {
3141 if (twoPhaseDraw) {
3142 surface->DrawTextTransparent(rcSegment, textFont,
3143 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3144 i - startseg + 1, textFore);
3145 } else {
3146 surface->DrawTextNoClip(rcSegment, textFont,
3147 rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
3148 i - startseg + 1, textFore, textBack);
3151 if (vsDraw.viewWhitespace != wsInvisible ||
3152 (inIndentation && vsDraw.viewIndentationGuides != ivNone)) {
3153 for (int cpos = 0; cpos <= i - startseg; cpos++) {
3154 if (ll->chars[cpos + startseg] == ' ') {
3155 if (vsDraw.viewWhitespace != wsInvisible) {
3156 if (vsDraw.whitespaceForegroundSet)
3157 textFore = vsDraw.whitespaceForeground;
3158 if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
3159 XYPOSITION xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
3160 if (!twoPhaseDraw && drawWhitespaceBackground &&
3161 (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
3162 textBack = vsDraw.whitespaceBackground;
3163 PRectangle rcSpace(ll->positions[cpos + startseg] + xStart - subLineStart,
3164 rcSegment.top,
3165 ll->positions[cpos + startseg + 1] + xStart - subLineStart,
3166 rcSegment.bottom);
3167 surface->FillRectangle(rcSpace, textBack);
3169 PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
3170 rcDot.right = rcDot.left + vs.whitespaceSize;
3171 rcDot.bottom = rcDot.top + vs.whitespaceSize;
3172 surface->FillRectangle(rcDot, textFore);
3175 if (inIndentation && vsDraw.viewIndentationGuides == ivReal) {
3176 for (int indentCount = (ll->positions[cpos + startseg] + epsilon) / indentWidth;
3177 indentCount <= (ll->positions[cpos + startseg + 1] - epsilon) / indentWidth;
3178 indentCount++) {
3179 if (indentCount > 0) {
3180 int xIndent = indentCount * indentWidth;
3181 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3182 (ll->xHighlightGuide == xIndent));
3186 } else {
3187 inIndentation = false;
3192 if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd) {
3193 PRectangle rcUL = rcSegment;
3194 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3195 rcUL.bottom = rcUL.top + 1;
3196 if (vsDraw.hotspotForegroundSet)
3197 surface->FillRectangle(rcUL, vsDraw.hotspotForeground);
3198 else
3199 surface->FillRectangle(rcUL, textFore);
3200 } else if (vsDraw.styles[styleMain].underline) {
3201 PRectangle rcUL = rcSegment;
3202 rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
3203 rcUL.bottom = rcUL.top + 1;
3204 surface->FillRectangle(rcUL, textFore);
3206 } else if (rcSegment.left > rcLine.right) {
3207 break;
3210 if ((vsDraw.viewIndentationGuides == ivLookForward || vsDraw.viewIndentationGuides == ivLookBoth)
3211 && (subLine == 0)) {
3212 int indentSpace = pdoc->GetLineIndentation(line);
3213 int xStartText = ll->positions[pdoc->GetLineIndentPosition(line) - posLineStart];
3215 // Find the most recent line with some text
3217 int lineLastWithText = line;
3218 while (lineLastWithText > Platform::Maximum(line-20, 0) && pdoc->IsWhiteLine(lineLastWithText)) {
3219 lineLastWithText--;
3221 if (lineLastWithText < line) {
3222 xStartText = 100000; // Don't limit to visible indentation on empty line
3223 // This line is empty, so use indentation of last line with text
3224 int indentLastWithText = pdoc->GetLineIndentation(lineLastWithText);
3225 int isFoldHeader = pdoc->GetLevel(lineLastWithText) & SC_FOLDLEVELHEADERFLAG;
3226 if (isFoldHeader) {
3227 // Level is one more level than parent
3228 indentLastWithText += pdoc->IndentSize();
3230 if (vsDraw.viewIndentationGuides == ivLookForward) {
3231 // In viLookForward mode, previous line only used if it is a fold header
3232 if (isFoldHeader) {
3233 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3235 } else { // viLookBoth
3236 indentSpace = Platform::Maximum(indentSpace, indentLastWithText);
3240 int lineNextWithText = line;
3241 while (lineNextWithText < Platform::Minimum(line+20, pdoc->LinesTotal()) && pdoc->IsWhiteLine(lineNextWithText)) {
3242 lineNextWithText++;
3244 if (lineNextWithText > line) {
3245 xStartText = 100000; // Don't limit to visible indentation on empty line
3246 // This line is empty, so use indentation of first next line with text
3247 indentSpace = Platform::Maximum(indentSpace,
3248 pdoc->GetLineIndentation(lineNextWithText));
3251 for (int indentPos = pdoc->IndentSize(); indentPos < indentSpace; indentPos += pdoc->IndentSize()) {
3252 int xIndent = indentPos * vsDraw.spaceWidth;
3253 if (xIndent < xStartText) {
3254 DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIndent + xStart, rcSegment,
3255 (ll->xHighlightGuide == xIndent));
3260 DrawIndicators(surface, vsDraw, line, xStart, rcLine, ll, subLine, lineEnd, false);
3262 // End of the drawing of the current line
3263 if (!twoPhaseDraw) {
3264 DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
3265 xStart, subLine, subLineStart, overrideBackground, background,
3266 drawWrapMarkEnd, wrapColour);
3268 if (!hideSelection && ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) || (vsDraw.selAdditionalAlpha != SC_ALPHA_NOALPHA))) {
3269 // For each selection draw
3270 int virtualSpaces = 0;
3271 if (subLine == (ll->lines - 1)) {
3272 virtualSpaces = sel.VirtualSpaceFor(pdoc->LineEnd(line));
3274 SelectionPosition posStart(posLineStart + lineStart);
3275 SelectionPosition posEnd(posLineStart + lineEnd, virtualSpaces);
3276 SelectionSegment virtualSpaceRange(posStart, posEnd);
3277 for (size_t r=0; r<sel.Count(); r++) {
3278 int alpha = (r == sel.Main()) ? vsDraw.selAlpha : vsDraw.selAdditionalAlpha;
3279 if (alpha != SC_ALPHA_NOALPHA) {
3280 SelectionSegment portion = sel.Range(r).Intersect(virtualSpaceRange);
3281 if (!portion.Empty()) {
3282 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3283 rcSegment.left = xStart + ll->positions[portion.start.Position() - posLineStart] - subLineStart + portion.start.VirtualSpace() * spaceWidth;
3284 rcSegment.right = xStart + ll->positions[portion.end.Position() - posLineStart] - subLineStart + portion.end.VirtualSpace() * spaceWidth;
3285 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3286 if ((portion.start.Position() - posLineStart) == lineStart && sel.Range(r).ContainsCharacter(portion.start.Position() - 1))
3287 rcSegment.left -= static_cast<int>(ll->wrapIndent); // indentation added to xStart was truncated to int, so we do the same here
3289 rcSegment.left = (rcSegment.left > rcLine.left) ? rcSegment.left : rcLine.left;
3290 rcSegment.right = (rcSegment.right < rcLine.right) ? rcSegment.right : rcLine.right;
3291 if (rcSegment.right > rcLine.left)
3292 SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw, r == sel.Main()), alpha);
3298 // Draw any translucent whole line states
3299 rcSegment = rcLine;
3300 if ((caret.active || vsDraw.alwaysShowCaretLineBackground) && vsDraw.showCaretLineBackground && ll->containsCaret) {
3301 SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground, vsDraw.caretLineAlpha);
3303 marks = pdoc->GetMark(line);
3304 for (markBit = 0; (markBit < 32) && marks; markBit++) {
3305 if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
3306 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3307 } else if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_UNDERLINE)) {
3308 PRectangle rcUnderline = rcSegment;
3309 rcUnderline.top = rcUnderline.bottom - 2;
3310 SimpleAlphaRectangle(surface, rcUnderline, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3312 marks >>= 1;
3314 if (vsDraw.maskInLine) {
3315 int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
3316 if (marksMasked) {
3317 for (markBit = 0; (markBit < 32) && marksMasked; markBit++) {
3318 if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
3319 SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back, vsDraw.markers[markBit].alpha);
3321 marksMasked >>= 1;
3327 void Editor::DrawBlockCaret(Surface *surface, ViewStyle &vsDraw, LineLayout *ll, int subLine,
3328 int xStart, int offset, int posCaret, PRectangle rcCaret, ColourDesired caretColour) {
3330 int lineStart = ll->LineStart(subLine);
3331 int posBefore = posCaret;
3332 int posAfter = MovePositionOutsideChar(posCaret + 1, 1);
3333 int numCharsToDraw = posAfter - posCaret;
3335 // Work out where the starting and ending offsets are. We need to
3336 // see if the previous character shares horizontal space, such as a
3337 // glyph / combining character. If so we'll need to draw that too.
3338 int offsetFirstChar = offset;
3339 int offsetLastChar = offset + (posAfter - posCaret);
3340 while ((posBefore > 0) && ((offsetLastChar - numCharsToDraw) >= lineStart)) {
3341 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - numCharsToDraw]) > 0) {
3342 // The char does not share horizontal space
3343 break;
3345 // Char shares horizontal space, update the numChars to draw
3346 // Update posBefore to point to the prev char
3347 posBefore = MovePositionOutsideChar(posBefore - 1, -1);
3348 numCharsToDraw = posAfter - posBefore;
3349 offsetFirstChar = offset - (posCaret - posBefore);
3352 // See if the next character shares horizontal space, if so we'll
3353 // need to draw that too.
3354 if (offsetFirstChar < 0)
3355 offsetFirstChar = 0;
3356 numCharsToDraw = offsetLastChar - offsetFirstChar;
3357 while ((offsetLastChar < ll->LineStart(subLine + 1)) && (offsetLastChar <= ll->numCharsInLine)) {
3358 // Update posAfter to point to the 2nd next char, this is where
3359 // the next character ends, and 2nd next begins. We'll need
3360 // to compare these two
3361 posBefore = posAfter;
3362 posAfter = MovePositionOutsideChar(posAfter + 1, 1);
3363 offsetLastChar = offset + (posAfter - posCaret);
3364 if ((ll->positions[offsetLastChar] - ll->positions[offsetLastChar - (posAfter - posBefore)]) > 0) {
3365 // The char does not share horizontal space
3366 break;
3368 // Char shares horizontal space, update the numChars to draw
3369 numCharsToDraw = offsetLastChar - offsetFirstChar;
3372 // We now know what to draw, update the caret drawing rectangle
3373 rcCaret.left = ll->positions[offsetFirstChar] - ll->positions[lineStart] + xStart;
3374 rcCaret.right = ll->positions[offsetFirstChar+numCharsToDraw] - ll->positions[lineStart] + xStart;
3376 // Adjust caret position to take into account any word wrapping symbols.
3377 if ((ll->wrapIndent != 0) && (lineStart != 0)) {
3378 XYPOSITION wordWrapCharWidth = ll->wrapIndent;
3379 rcCaret.left += wordWrapCharWidth;
3380 rcCaret.right += wordWrapCharWidth;
3383 // This character is where the caret block is, we override the colours
3384 // (inversed) for drawing the caret here.
3385 int styleMain = ll->styles[offsetFirstChar];
3386 surface->DrawTextClipped(rcCaret, vsDraw.styles[styleMain].font,
3387 rcCaret.top + vsDraw.maxAscent, ll->chars + offsetFirstChar,
3388 numCharsToDraw, vsDraw.styles[styleMain].back,
3389 caretColour);
3392 void Editor::RefreshPixMaps(Surface *surfaceWindow) {
3393 if (!pixmapSelPattern->Initialised()) {
3394 const int patternSize = 8;
3395 pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3396 pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
3397 // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
3398 // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
3399 // way between the chrome colour and the chrome highlight colour making a nice transition
3400 // between the window chrome and the content area. And it works in low colour depths.
3401 PRectangle rcPattern(0, 0, patternSize, patternSize);
3403 // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
3404 ColourDesired colourFMFill = vs.selbar;
3405 ColourDesired colourFMStripes = vs.selbarlight;
3407 if (!(vs.selbarlight == ColourDesired(0xff, 0xff, 0xff))) {
3408 // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
3409 // (Typically, the highlight colour is white.)
3410 colourFMFill = vs.selbarlight;
3413 if (vs.foldmarginColourSet) {
3414 // override default fold margin colour
3415 colourFMFill = vs.foldmarginColour;
3417 if (vs.foldmarginHighlightColourSet) {
3418 // override default fold margin highlight colour
3419 colourFMStripes = vs.foldmarginHighlightColour;
3422 pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
3423 pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
3424 for (int y = 0; y < patternSize; y++) {
3425 for (int x = y % 2; x < patternSize; x+=2) {
3426 PRectangle rcPixel(x, y, x+1, y+1);
3427 pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
3428 pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
3433 if (!pixmapIndentGuide->Initialised()) {
3434 // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
3435 pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3436 pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
3437 PRectangle rcIG(0, 0, 1, vs.lineHeight);
3438 pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back);
3439 pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore);
3440 pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back);
3441 pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore);
3442 for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
3443 PRectangle rcPixel(0, stripe, 1, stripe+1);
3444 pixmapIndentGuide->FillRectangle(rcPixel, vs.styles[STYLE_INDENTGUIDE].fore);
3445 pixmapIndentGuideHighlight->FillRectangle(rcPixel, vs.styles[STYLE_BRACELIGHT].fore);
3449 if (bufferedDraw) {
3450 if (!pixmapLine->Initialised()) {
3451 PRectangle rcClient = GetClientRectangle();
3452 pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
3453 surfaceWindow, wMain.GetID());
3454 pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
3455 rcClient.Height(), surfaceWindow, wMain.GetID());
3460 void Editor::DrawCarets(Surface *surface, ViewStyle &vsDraw, int lineDoc, int xStart,
3461 PRectangle rcLine, LineLayout *ll, int subLine) {
3462 // When drag is active it is the only caret drawn
3463 bool drawDrag = posDrag.IsValid();
3464 if (hideSelection && !drawDrag)
3465 return;
3466 const int posLineStart = pdoc->LineStart(lineDoc);
3467 // For each selection draw
3468 for (size_t r=0; (r<sel.Count()) || drawDrag; r++) {
3469 const bool mainCaret = r == sel.Main();
3470 const SelectionPosition posCaret = (drawDrag ? posDrag : sel.Range(r).caret);
3471 const int offset = posCaret.Position() - posLineStart;
3472 const XYPOSITION spaceWidth = vsDraw.styles[ll->EndLineStyle()].spaceWidth;
3473 const XYPOSITION virtualOffset = posCaret.VirtualSpace() * spaceWidth;
3474 if (ll->InLine(offset, subLine) && offset <= ll->numCharsBeforeEOL) {
3475 XYPOSITION xposCaret = ll->positions[offset] + virtualOffset - ll->positions[ll->LineStart(subLine)];
3476 if (ll->wrapIndent != 0) {
3477 int lineStart = ll->LineStart(subLine);
3478 if (lineStart != 0) // Wrapped
3479 xposCaret += ll->wrapIndent;
3481 bool caretBlinkState = (caret.active && caret.on) || (!additionalCaretsBlink && !mainCaret);
3482 bool caretVisibleState = additionalCaretsVisible || mainCaret;
3483 if ((xposCaret >= 0) && (vsDraw.caretWidth > 0) && (vsDraw.caretStyle != CARETSTYLE_INVISIBLE) &&
3484 ((posDrag.IsValid()) || (caretBlinkState && caretVisibleState))) {
3485 bool caretAtEOF = false;
3486 bool caretAtEOL = false;
3487 bool drawBlockCaret = false;
3488 XYPOSITION widthOverstrikeCaret;
3489 int caretWidthOffset = 0;
3490 PRectangle rcCaret = rcLine;
3492 if (posCaret.Position() == pdoc->Length()) { // At end of document
3493 caretAtEOF = true;
3494 widthOverstrikeCaret = vsDraw.aveCharWidth;
3495 } else if ((posCaret.Position() - posLineStart) >= ll->numCharsInLine) { // At end of line
3496 caretAtEOL = true;
3497 widthOverstrikeCaret = vsDraw.aveCharWidth;
3498 } else {
3499 widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
3501 if (widthOverstrikeCaret < 3) // Make sure its visible
3502 widthOverstrikeCaret = 3;
3504 if (xposCaret > 0)
3505 caretWidthOffset = 1; // Move back so overlaps both character cells.
3506 xposCaret += xStart;
3507 if (posDrag.IsValid()) {
3508 /* Dragging text, use a line caret */
3509 rcCaret.left = xposCaret - caretWidthOffset;
3510 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3511 } else if (inOverstrike) {
3512 /* Overstrike (insert mode), use a modified bar caret */
3513 rcCaret.top = rcCaret.bottom - 2;
3514 rcCaret.left = xposCaret + 1;
3515 rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
3516 } else if (vsDraw.caretStyle == CARETSTYLE_BLOCK) {
3517 /* Block caret */
3518 rcCaret.left = xposCaret;
3519 if (!caretAtEOL && !caretAtEOF && (ll->chars[offset] != '\t') && !(IsControlCharacter(ll->chars[offset]))) {
3520 drawBlockCaret = true;
3521 rcCaret.right = xposCaret + widthOverstrikeCaret;
3522 } else {
3523 rcCaret.right = xposCaret + vsDraw.aveCharWidth;
3525 } else {
3526 /* Line caret */
3527 rcCaret.left = xposCaret - caretWidthOffset;
3528 rcCaret.right = rcCaret.left + vsDraw.caretWidth;
3530 ColourDesired caretColour = mainCaret ? vsDraw.caretcolour : vsDraw.additionalCaretColour;
3531 if (drawBlockCaret) {
3532 DrawBlockCaret(surface, vsDraw, ll, subLine, xStart, offset, posCaret.Position(), rcCaret, caretColour);
3533 } else {
3534 surface->FillRectangle(rcCaret, caretColour);
3538 if (drawDrag)
3539 break;
3543 void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
3544 //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
3545 // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
3546 AllocateGraphics();
3548 RefreshStyleData();
3549 if (paintState == paintAbandoned)
3550 return; // Scroll bars may have changed so need redraw
3551 RefreshPixMaps(surfaceWindow);
3553 StyleToPositionInView(PositionAfterArea(rcArea));
3555 PRectangle rcClient = GetClientRectangle();
3556 Point ptOrigin = GetVisibleOriginInMain();
3557 //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
3558 // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3560 int screenLinePaintFirst = rcArea.top / vs.lineHeight;
3562 int xStart = vs.textStart - xOffset + ptOrigin.x;
3563 int ypos = 0;
3564 if (!bufferedDraw)
3565 ypos += screenLinePaintFirst * vs.lineHeight;
3566 int yposScreen = screenLinePaintFirst * vs.lineHeight;
3568 bool paintAbandonedByStyling = paintState == paintAbandoned;
3569 if (NotifyUpdateUI()) {
3570 RefreshStyleData();
3571 RefreshPixMaps(surfaceWindow);
3574 // Call priority lines wrap on a window of lines which are likely
3575 // to rendered with the following paint (that is wrap the visible
3576 // lines first).
3577 int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
3578 if (startLineToWrap < 0)
3579 startLineToWrap = 0;
3580 if (WrapLines(false, startLineToWrap)) {
3581 // The wrapping process has changed the height of some lines so
3582 // abandon this paint for a complete repaint.
3583 if (AbandonPaint()) {
3584 return;
3586 RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
3588 PLATFORM_ASSERT(pixmapSelPattern->Initialised());
3590 if (!bufferedDraw)
3591 surfaceWindow->SetClip(rcArea);
3593 if (paintState != paintAbandoned) {
3594 if (vs.marginInside) {
3595 PaintSelMargin(surfaceWindow, rcArea);
3596 PRectangle rcRightMargin = rcClient;
3597 rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
3598 if (rcArea.Intersects(rcRightMargin)) {
3599 surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
3601 } else { // Else separate view so separate paint event but leftMargin included to allow overlap
3602 PRectangle rcLeftMargin = rcArea;
3603 rcLeftMargin.left = 0;
3604 rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
3605 if (rcArea.Intersects(rcLeftMargin)) {
3606 surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
3611 if (paintState == paintAbandoned) {
3612 // Either styling or NotifyUpdateUI noticed that painting is needed
3613 // outside the current painting rectangle
3614 //Platform::DebugPrintf("Abandoning paint\n");
3615 if (wrapState != eWrapNone) {
3616 if (paintAbandonedByStyling) {
3617 // Styling has spilled over a line end, such as occurs by starting a multiline
3618 // comment. The width of subsequent text may have changed, so rewrap.
3619 NeedWrapping(cs.DocFromDisplay(topLine));
3622 return;
3624 //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
3626 // Allow text at start of line to overlap 1 pixel into the margin as this displays
3627 // serifs and italic stems for aliased text.
3628 const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
3630 // Do the painting
3631 if (rcArea.right > vs.textStart - leftTextOverlap) {
3633 Surface *surface = surfaceWindow;
3634 if (bufferedDraw) {
3635 surface = pixmapLine;
3636 PLATFORM_ASSERT(pixmapLine->Initialised());
3638 surface->SetUnicodeMode(IsUnicodeMode());
3639 surface->SetDBCSMode(CodePage());
3641 int visibleLine = TopLineOfMain() + screenLinePaintFirst;
3643 SelectionPosition posCaret = sel.RangeMain().caret;
3644 if (posDrag.IsValid())
3645 posCaret = posDrag;
3646 int lineCaret = pdoc->LineFromPosition(posCaret.Position());
3648 PRectangle rcTextArea = rcClient;
3649 if (vs.marginInside) {
3650 rcTextArea.left += vs.textStart;
3651 rcTextArea.right -= vs.rightMarginWidth;
3652 } else {
3653 rcTextArea = rcArea;
3656 // Remove selection margin from drawing area so text will not be drawn
3657 // on it in unbuffered mode.
3658 if (!bufferedDraw && vs.marginInside) {
3659 PRectangle rcClipText = rcTextArea;
3660 rcClipText.left -= leftTextOverlap;
3661 surfaceWindow->SetClip(rcClipText);
3664 // Loop on visible lines
3665 //double durLayout = 0.0;
3666 //double durPaint = 0.0;
3667 //double durCopy = 0.0;
3668 //ElapsedTime etWhole;
3669 int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
3670 AutoLineLayout ll(llc, 0);
3671 while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
3673 int lineDoc = cs.DocFromDisplay(visibleLine);
3674 // Only visible lines should be handled by the code within the loop
3675 PLATFORM_ASSERT(cs.GetVisible(lineDoc));
3676 int lineStartSet = cs.DisplayFromDoc(lineDoc);
3677 int subLine = visibleLine - lineStartSet;
3679 // Copy this line and its styles from the document into local arrays
3680 // and determine the x position at which each character starts.
3681 //ElapsedTime et;
3682 if (lineDoc != lineDocPrevious) {
3683 ll.Set(0);
3684 ll.Set(RetrieveLineLayout(lineDoc));
3685 LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
3686 lineDocPrevious = lineDoc;
3688 //durLayout += et.Duration(true);
3690 if (ll) {
3691 ll->containsCaret = lineDoc == lineCaret;
3692 if (hideSelection) {
3693 ll->containsCaret = false;
3696 GetHotSpotRange(ll->hsStart, ll->hsEnd);
3698 PRectangle rcLine = rcTextArea;
3699 rcLine.top = ypos;
3700 rcLine.bottom = ypos + vs.lineHeight;
3702 bool bracesIgnoreStyle = false;
3703 if ((vs.braceHighlightIndicatorSet && (bracesMatchStyle == STYLE_BRACELIGHT)) ||
3704 (vs.braceBadLightIndicatorSet && (bracesMatchStyle == STYLE_BRACEBAD))) {
3705 bracesIgnoreStyle = true;
3707 Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
3708 // Highlight the current braces if any
3709 ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
3710 highlightGuideColumn * vs.spaceWidth, bracesIgnoreStyle);
3712 if (leftTextOverlap && bufferedDraw) {
3713 PRectangle rcSpacer = rcLine;
3714 rcSpacer.right = rcSpacer.left;
3715 rcSpacer.left -= 1;
3716 surface->FillRectangle(rcSpacer, vs.styles[STYLE_DEFAULT].back);
3719 // Draw the line
3720 DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
3721 //durPaint += et.Duration(true);
3723 // Restore the previous styles for the brace highlights in case layout is in cache.
3724 ll->RestoreBracesHighlight(rangeLine, braces, bracesIgnoreStyle);
3726 bool expanded = cs.GetExpanded(lineDoc);
3727 const int level = pdoc->GetLevel(lineDoc);
3728 const int levelNext = pdoc->GetLevel(lineDoc + 1);
3729 if ((level & SC_FOLDLEVELHEADERFLAG) &&
3730 ((level & SC_FOLDLEVELNUMBERMASK) < (levelNext & SC_FOLDLEVELNUMBERMASK))) {
3731 // Paint the line above the fold
3732 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
3734 (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
3735 PRectangle rcFoldLine = rcLine;
3736 rcFoldLine.bottom = rcFoldLine.top + 1;
3737 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3739 // Paint the line below the fold
3740 if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
3742 (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
3743 PRectangle rcFoldLine = rcLine;
3744 rcFoldLine.top = rcFoldLine.bottom - 1;
3745 surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore);
3749 DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
3751 if (bufferedDraw) {
3752 Point from(vs.textStart-leftTextOverlap, 0);
3753 PRectangle rcCopyArea(vs.textStart-leftTextOverlap, yposScreen,
3754 rcClient.right - vs.rightMarginWidth, yposScreen + vs.lineHeight);
3755 surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
3758 lineWidthMaxSeen = Platform::Maximum(
3759 lineWidthMaxSeen, ll->positions[ll->numCharsInLine]);
3760 //durCopy += et.Duration(true);
3763 if (!bufferedDraw) {
3764 ypos += vs.lineHeight;
3767 yposScreen += vs.lineHeight;
3768 visibleLine++;
3770 //gdk_flush();
3772 ll.Set(0);
3773 //if (durPaint < 0.00000001)
3774 // durPaint = 0.00000001;
3776 // Right column limit indicator
3777 PRectangle rcBeyondEOF = (vs.marginInside) ? rcClient : rcArea;
3778 rcBeyondEOF.left = vs.textStart;
3779 rcBeyondEOF.right = rcBeyondEOF.right - ((vs.marginInside) ? vs.rightMarginWidth : 0);
3780 rcBeyondEOF.top = (cs.LinesDisplayed() - TopLineOfMain()) * vs.lineHeight;
3781 if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
3782 surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
3783 if (vs.edgeState == EDGE_LINE) {
3784 int edgeX = theEdge * vs.spaceWidth;
3785 rcBeyondEOF.left = edgeX + xStart;
3786 rcBeyondEOF.right = rcBeyondEOF.left + 1;
3787 surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour);
3790 //Platform::DebugPrintf(
3791 //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
3792 //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
3793 NotifyPainted();
3797 // Space (3 space characters) between line numbers and text when printing.
3798 #define lineNumberPrintSpace " "
3800 ColourDesired InvertedLight(ColourDesired orig) {
3801 unsigned int r = orig.GetRed();
3802 unsigned int g = orig.GetGreen();
3803 unsigned int b = orig.GetBlue();
3804 unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
3805 unsigned int il = 0xff - l;
3806 if (l == 0)
3807 return ColourDesired(0xff, 0xff, 0xff);
3808 r = r * il / l;
3809 g = g * il / l;
3810 b = b * il / l;
3811 return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
3814 // This is mostly copied from the Paint method but with some things omitted
3815 // such as the margin markers, line numbers, selection and caret
3816 // Should be merged back into a combined Draw method.
3817 long Editor::FormatRange(bool draw, Sci_RangeToFormat *pfr) {
3818 if (!pfr)
3819 return 0;
3821 AutoSurface surface(pfr->hdc, this, SC_TECHNOLOGY_DEFAULT);
3822 if (!surface)
3823 return 0;
3824 AutoSurface surfaceMeasure(pfr->hdcTarget, this, SC_TECHNOLOGY_DEFAULT);
3825 if (!surfaceMeasure) {
3826 return 0;
3829 // Can't use measurements cached for screen
3830 posCache.Clear();
3832 ViewStyle vsPrint(vs);
3833 vsPrint.technology = SC_TECHNOLOGY_DEFAULT;
3835 // Modify the view style for printing as do not normally want any of the transient features to be printed
3836 // Printing supports only the line number margin.
3837 int lineNumberIndex = -1;
3838 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
3839 if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
3840 lineNumberIndex = margin;
3841 } else {
3842 vsPrint.ms[margin].width = 0;
3845 vsPrint.fixedColumnWidth = 0;
3846 vsPrint.zoomLevel = printMagnification;
3847 // Don't show indentation guides
3848 // If this ever gets changed, cached pixmap would need to be recreated if technology != SC_TECHNOLOGY_DEFAULT
3849 vsPrint.viewIndentationGuides = ivNone;
3850 // Don't show the selection when printing
3851 vsPrint.selbackset = false;
3852 vsPrint.selforeset = false;
3853 vsPrint.selAlpha = SC_ALPHA_NOALPHA;
3854 vsPrint.selAdditionalAlpha = SC_ALPHA_NOALPHA;
3855 vsPrint.whitespaceBackgroundSet = false;
3856 vsPrint.whitespaceForegroundSet = false;
3857 vsPrint.showCaretLineBackground = false;
3858 vsPrint.alwaysShowCaretLineBackground = false;
3859 // Don't highlight matching braces using indicators
3860 vsPrint.braceHighlightIndicatorSet = false;
3861 vsPrint.braceBadLightIndicatorSet = false;
3863 // Set colours for printing according to users settings
3864 for (size_t sty = 0; sty < vsPrint.stylesSize; sty++) {
3865 if (printColourMode == SC_PRINT_INVERTLIGHT) {
3866 vsPrint.styles[sty].fore = InvertedLight(vsPrint.styles[sty].fore);
3867 vsPrint.styles[sty].back = InvertedLight(vsPrint.styles[sty].back);
3868 } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
3869 vsPrint.styles[sty].fore = ColourDesired(0, 0, 0);
3870 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3871 } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
3872 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3873 } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
3874 if (sty <= STYLE_DEFAULT) {
3875 vsPrint.styles[sty].back = ColourDesired(0xff, 0xff, 0xff);
3879 // White background for the line numbers
3880 vsPrint.styles[STYLE_LINENUMBER].back = ColourDesired(0xff, 0xff, 0xff);
3882 // Printing uses different margins, so reset screen margins
3883 vsPrint.leftMarginWidth = 0;
3884 vsPrint.rightMarginWidth = 0;
3886 vsPrint.Refresh(*surfaceMeasure);
3887 // Determining width must hapen after fonts have been realised in Refresh
3888 int lineNumberWidth = 0;
3889 if (lineNumberIndex >= 0) {
3890 lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
3891 "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
3892 vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
3893 vsPrint.Refresh(*surfaceMeasure); // Recalculate fixedColumnWidth
3896 int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
3897 int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
3898 if (linePrintLast < linePrintStart)
3899 linePrintLast = linePrintStart;
3900 int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
3901 if (linePrintLast > linePrintMax)
3902 linePrintLast = linePrintMax;
3903 //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
3904 // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
3905 // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
3906 int endPosPrint = pdoc->Length();
3907 if (linePrintLast < pdoc->LinesTotal())
3908 endPosPrint = pdoc->LineStart(linePrintLast + 1);
3910 // Ensure we are styled to where we are formatting.
3911 pdoc->EnsureStyledTo(endPosPrint);
3913 int xStart = vsPrint.fixedColumnWidth + pfr->rc.left;
3914 int ypos = pfr->rc.top;
3916 int lineDoc = linePrintStart;
3918 int nPrintPos = pfr->chrg.cpMin;
3919 int visibleLine = 0;
3920 int widthPrint = pfr->rc.right - pfr->rc.left - vsPrint.fixedColumnWidth;
3921 if (printWrapState == eWrapNone)
3922 widthPrint = LineLayout::wrapWidthInfinite;
3924 while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
3926 // When printing, the hdc and hdcTarget may be the same, so
3927 // changing the state of surfaceMeasure may change the underlying
3928 // state of surface. Therefore, any cached state is discarded before
3929 // using each surface.
3930 surfaceMeasure->FlushCachedState();
3932 // Copy this line and its styles from the document into local arrays
3933 // and determine the x position at which each character starts.
3934 LineLayout ll(pdoc->LineStart(lineDoc+1)-pdoc->LineStart(lineDoc)+1);
3935 LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
3937 ll.containsCaret = false;
3939 PRectangle rcLine;
3940 rcLine.left = pfr->rc.left;
3941 rcLine.top = ypos;
3942 rcLine.right = pfr->rc.right - 1;
3943 rcLine.bottom = ypos + vsPrint.lineHeight;
3945 // When document line is wrapped over multiple display lines, find where
3946 // to start printing from to ensure a particular position is on the first
3947 // line of the page.
3948 if (visibleLine == 0) {
3949 int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
3950 for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
3951 if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
3952 visibleLine = -iwl;
3956 if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
3957 visibleLine = -(ll.lines - 1);
3961 if (draw && lineNumberWidth &&
3962 (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
3963 (visibleLine >= 0)) {
3964 char number[100];
3965 sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
3966 PRectangle rcNumber = rcLine;
3967 rcNumber.right = rcNumber.left + lineNumberWidth;
3968 // Right justify
3969 rcNumber.left = rcNumber.right - surfaceMeasure->WidthText(
3970 vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
3971 surface->FlushCachedState();
3972 surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
3973 ypos + vsPrint.maxAscent, number, istrlen(number),
3974 vsPrint.styles[STYLE_LINENUMBER].fore,
3975 vsPrint.styles[STYLE_LINENUMBER].back);
3978 // Draw the line
3979 surface->FlushCachedState();
3981 for (int iwl = 0; iwl < ll.lines; iwl++) {
3982 if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
3983 if (visibleLine >= 0) {
3984 if (draw) {
3985 rcLine.top = ypos;
3986 rcLine.bottom = ypos + vsPrint.lineHeight;
3987 DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
3989 ypos += vsPrint.lineHeight;
3991 visibleLine++;
3992 if (iwl == ll.lines - 1)
3993 nPrintPos = pdoc->LineStart(lineDoc + 1);
3994 else
3995 nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
3999 ++lineDoc;
4002 // Clear cache so measurements are not used for screen
4003 posCache.Clear();
4005 return nPrintPos;
4008 int Editor::TextWidth(int style, const char *text) {
4009 RefreshStyleData();
4010 AutoSurface surface(this);
4011 if (surface) {
4012 return surface->WidthText(vs.styles[style].font, text, istrlen(text));
4013 } else {
4014 return 1;
4018 // Empty method is overridden on GTK+ to show / hide scrollbars
4019 void Editor::ReconfigureScrollBars() {}
4021 void Editor::SetScrollBars() {
4022 RefreshStyleData();
4024 int nMax = MaxScrollPos();
4025 int nPage = LinesOnScreen();
4026 bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
4027 if (modified) {
4028 DwellEnd(true);
4031 // TODO: ensure always showing as many lines as possible
4032 // May not be, if, for example, window made larger
4033 if (topLine > MaxScrollPos()) {
4034 SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
4035 SetVerticalScrollPos();
4036 Redraw();
4038 if (modified) {
4039 if (!AbandonPaint())
4040 Redraw();
4042 //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
4045 void Editor::ChangeSize() {
4046 DropGraphics(false);
4047 SetScrollBars();
4048 if (wrapState != eWrapNone) {
4049 PRectangle rcTextArea = GetClientRectangle();
4050 rcTextArea.left = vs.textStart;
4051 rcTextArea.right -= vs.rightMarginWidth;
4052 if (wrapWidth != rcTextArea.Width()) {
4053 NeedWrapping();
4054 Redraw();
4059 int Editor::InsertSpace(int position, unsigned int spaces) {
4060 if (spaces > 0) {
4061 std::string spaceText(spaces, ' ');
4062 pdoc->InsertString(position, spaceText.c_str(), spaces);
4063 position += spaces;
4065 return position;
4068 void Editor::AddChar(char ch) {
4069 char s[2];
4070 s[0] = ch;
4071 s[1] = '\0';
4072 AddCharUTF(s, 1);
4075 void Editor::FilterSelections() {
4076 if (!additionalSelectionTyping && (sel.Count() > 1)) {
4077 SelectionRange rangeOnly = sel.RangeMain();
4078 InvalidateSelection(rangeOnly, true);
4079 sel.SetSelection(rangeOnly);
4083 static bool cmpSelPtrs(const SelectionRange *a, const SelectionRange *b) {
4084 return *a < *b;
4087 // AddCharUTF inserts an array of bytes which may or may not be in UTF-8.
4088 void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
4089 FilterSelections();
4091 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty() || inOverstrike);
4093 std::vector<SelectionRange *> selPtrs;
4094 for (size_t r = 0; r < sel.Count(); r++) {
4095 selPtrs.push_back(&sel.Range(r));
4097 std::sort(selPtrs.begin(), selPtrs.end(), cmpSelPtrs);
4099 for (std::vector<SelectionRange *>::reverse_iterator rit = selPtrs.rbegin();
4100 rit != selPtrs.rend(); ++rit) {
4101 SelectionRange *currentSel = *rit;
4102 if (!RangeContainsProtected(currentSel->Start().Position(),
4103 currentSel->End().Position())) {
4104 int positionInsert = currentSel->Start().Position();
4105 if (!currentSel->Empty()) {
4106 if (currentSel->Length()) {
4107 pdoc->DeleteChars(positionInsert, currentSel->Length());
4108 currentSel->ClearVirtualSpace();
4109 } else {
4110 // Range is all virtual so collapse to start of virtual space
4111 currentSel->MinimizeVirtualSpace();
4113 } else if (inOverstrike) {
4114 if (positionInsert < pdoc->Length()) {
4115 if (!pdoc->IsPositionInLineEnd(positionInsert)) {
4116 pdoc->DelChar(positionInsert);
4117 currentSel->ClearVirtualSpace();
4121 positionInsert = InsertSpace(positionInsert, currentSel->caret.VirtualSpace());
4122 if (pdoc->InsertString(positionInsert, s, len)) {
4123 currentSel->caret.SetPosition(positionInsert + len);
4124 currentSel->anchor.SetPosition(positionInsert + len);
4126 currentSel->ClearVirtualSpace();
4127 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4128 if (wrapState != eWrapNone) {
4129 AutoSurface surface(this);
4130 if (surface) {
4131 if (WrapOneLine(surface, pdoc->LineFromPosition(positionInsert))) {
4132 SetScrollBars();
4133 SetVerticalScrollPos();
4134 Redraw();
4141 if (wrapState != eWrapNone) {
4142 SetScrollBars();
4144 ThinRectangularRange();
4145 // If in wrap mode rewrap current line so EnsureCaretVisible has accurate information
4146 EnsureCaretVisible();
4147 // Avoid blinking during rapid typing:
4148 ShowCaretAtCurrentPosition();
4149 if ((caretSticky == SC_CARETSTICKY_OFF) ||
4150 ((caretSticky == SC_CARETSTICKY_WHITESPACE) && !IsAllSpacesOrTabs(s, len))) {
4151 SetLastXChosen();
4154 if (treatAsDBCS) {
4155 NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
4156 static_cast<unsigned char>(s[1]));
4157 } else {
4158 int byte = static_cast<unsigned char>(s[0]);
4159 if ((byte < 0xC0) || (1 == len)) {
4160 // Handles UTF-8 characters between 0x01 and 0x7F and single byte
4161 // characters when not in UTF-8 mode.
4162 // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
4163 // characters representing themselves.
4164 } else {
4165 // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
4166 // http://www.cl.cam.ac.uk/~mgk25/unicode.html
4167 // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
4168 if (byte < 0xE0) {
4169 int byte2 = static_cast<unsigned char>(s[1]);
4170 if ((byte2 & 0xC0) == 0x80) {
4171 // Two-byte-character lead-byte followed by a trail-byte.
4172 byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
4174 // A two-byte-character lead-byte not followed by trail-byte
4175 // represents itself.
4176 } else if (byte < 0xF0) {
4177 int byte2 = static_cast<unsigned char>(s[1]);
4178 int byte3 = static_cast<unsigned char>(s[2]);
4179 if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
4180 // Three-byte-character lead byte followed by two trail bytes.
4181 byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
4182 (byte3 & 0x3F));
4184 // A three-byte-character lead-byte not followed by two trail-bytes
4185 // represents itself.
4188 NotifyChar(byte);
4191 if (recordingMacro) {
4192 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(s));
4196 void Editor::InsertPaste(SelectionPosition selStart, const char *text, int len) {
4197 if (multiPasteMode == SC_MULTIPASTE_ONCE) {
4198 selStart = SelectionPosition(InsertSpace(selStart.Position(), selStart.VirtualSpace()));
4199 if (pdoc->InsertString(selStart.Position(), text, len)) {
4200 SetEmptySelection(selStart.Position() + len);
4202 } else {
4203 // SC_MULTIPASTE_EACH
4204 for (size_t r=0; r<sel.Count(); r++) {
4205 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4206 sel.Range(r).End().Position())) {
4207 int positionInsert = sel.Range(r).Start().Position();
4208 if (!sel.Range(r).Empty()) {
4209 if (sel.Range(r).Length()) {
4210 pdoc->DeleteChars(positionInsert, sel.Range(r).Length());
4211 sel.Range(r).ClearVirtualSpace();
4212 } else {
4213 // Range is all virtual so collapse to start of virtual space
4214 sel.Range(r).MinimizeVirtualSpace();
4217 positionInsert = InsertSpace(positionInsert, sel.Range(r).caret.VirtualSpace());
4218 if (pdoc->InsertString(positionInsert, text, len)) {
4219 sel.Range(r).caret.SetPosition(positionInsert + len);
4220 sel.Range(r).anchor.SetPosition(positionInsert + len);
4222 sel.Range(r).ClearVirtualSpace();
4228 void Editor::ClearSelection(bool retainMultipleSelections) {
4229 if (!sel.IsRectangular() && !retainMultipleSelections)
4230 FilterSelections();
4231 UndoGroup ug(pdoc);
4232 for (size_t r=0; r<sel.Count(); r++) {
4233 if (!sel.Range(r).Empty()) {
4234 if (!RangeContainsProtected(sel.Range(r).Start().Position(),
4235 sel.Range(r).End().Position())) {
4236 pdoc->DeleteChars(sel.Range(r).Start().Position(),
4237 sel.Range(r).Length());
4238 sel.Range(r) = sel.Range(r).Start();
4242 ThinRectangularRange();
4243 sel.RemoveDuplicates();
4244 ClaimSelection();
4247 void Editor::ClearAll() {
4249 UndoGroup ug(pdoc);
4250 if (0 != pdoc->Length()) {
4251 pdoc->DeleteChars(0, pdoc->Length());
4253 if (!pdoc->IsReadOnly()) {
4254 cs.Clear();
4255 pdoc->AnnotationClearAll();
4256 pdoc->MarginClearAll();
4259 sel.Clear();
4260 SetTopLine(0);
4261 SetVerticalScrollPos();
4262 InvalidateStyleRedraw();
4265 void Editor::ClearDocumentStyle() {
4266 Decoration *deco = pdoc->decorations.root;
4267 while (deco) {
4268 // Save next in case deco deleted
4269 Decoration *decoNext = deco->next;
4270 if (deco->indicator < INDIC_CONTAINER) {
4271 pdoc->decorations.SetCurrentIndicator(deco->indicator);
4272 pdoc->DecorationFillRange(0, 0, pdoc->Length());
4274 deco = decoNext;
4276 pdoc->StartStyling(0, '\377');
4277 pdoc->SetStyleFor(pdoc->Length(), 0);
4278 cs.ShowAll();
4279 pdoc->ClearLevels();
4282 void Editor::CopyAllowLine() {
4283 SelectionText selectedText;
4284 CopySelectionRange(&selectedText, true);
4285 CopyToClipboard(selectedText);
4288 void Editor::Cut() {
4289 pdoc->CheckReadOnly();
4290 if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
4291 Copy();
4292 ClearSelection();
4296 void Editor::PasteRectangular(SelectionPosition pos, const char *ptr, int len) {
4297 if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
4298 return;
4300 sel.Clear();
4301 sel.RangeMain() = SelectionRange(pos);
4302 int line = pdoc->LineFromPosition(sel.MainCaret());
4303 UndoGroup ug(pdoc);
4304 sel.RangeMain().caret = SelectionPosition(
4305 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
4306 int xInsert = XFromPosition(sel.RangeMain().caret);
4307 bool prevCr = false;
4308 while ((len > 0) && IsEOLChar(ptr[len-1]))
4309 len--;
4310 for (int i = 0; i < len; i++) {
4311 if (IsEOLChar(ptr[i])) {
4312 if ((ptr[i] == '\r') || (!prevCr))
4313 line++;
4314 if (line >= pdoc->LinesTotal()) {
4315 if (pdoc->eolMode != SC_EOL_LF)
4316 pdoc->InsertChar(pdoc->Length(), '\r');
4317 if (pdoc->eolMode != SC_EOL_CR)
4318 pdoc->InsertChar(pdoc->Length(), '\n');
4320 // Pad the end of lines with spaces if required
4321 sel.RangeMain().caret.SetPosition(PositionFromLineX(line, xInsert));
4322 if ((XFromPosition(sel.MainCaret()) < xInsert) && (i + 1 < len)) {
4323 while (XFromPosition(sel.MainCaret()) < xInsert) {
4324 pdoc->InsertChar(sel.MainCaret(), ' ');
4325 sel.RangeMain().caret.Add(1);
4328 prevCr = ptr[i] == '\r';
4329 } else {
4330 pdoc->InsertString(sel.MainCaret(), ptr + i, 1);
4331 sel.RangeMain().caret.Add(1);
4332 prevCr = false;
4335 SetEmptySelection(pos);
4338 bool Editor::CanPaste() {
4339 return !pdoc->IsReadOnly() && !SelectionContainsProtected();
4342 void Editor::Clear() {
4343 // If multiple selections, don't delete EOLS
4344 if (sel.Empty()) {
4345 bool singleVirtual = false;
4346 if ((sel.Count() == 1) &&
4347 !RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1) &&
4348 sel.RangeMain().Start().VirtualSpace()) {
4349 singleVirtual = true;
4351 UndoGroup ug(pdoc, (sel.Count() > 1) || singleVirtual);
4352 for (size_t r=0; r<sel.Count(); r++) {
4353 if (!RangeContainsProtected(sel.Range(r).caret.Position(), sel.Range(r).caret.Position() + 1)) {
4354 if (sel.Range(r).Start().VirtualSpace()) {
4355 if (sel.Range(r).anchor < sel.Range(r).caret)
4356 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).anchor.Position(), sel.Range(r).anchor.VirtualSpace()));
4357 else
4358 sel.Range(r) = SelectionPosition(InsertSpace(sel.Range(r).caret.Position(), sel.Range(r).caret.VirtualSpace()));
4360 if ((sel.Count() == 1) || !pdoc->IsPositionInLineEnd(sel.Range(r).caret.Position())) {
4361 pdoc->DelChar(sel.Range(r).caret.Position());
4362 sel.Range(r).ClearVirtualSpace();
4363 } // else multiple selection so don't eat line ends
4364 } else {
4365 sel.Range(r).ClearVirtualSpace();
4368 } else {
4369 ClearSelection();
4371 sel.RemoveDuplicates();
4374 void Editor::SelectAll() {
4375 sel.Clear();
4376 SetSelection(0, pdoc->Length());
4377 Redraw();
4380 void Editor::Undo() {
4381 if (pdoc->CanUndo()) {
4382 InvalidateCaret();
4383 int newPos = pdoc->Undo();
4384 if (newPos >= 0)
4385 SetEmptySelection(newPos);
4386 EnsureCaretVisible();
4390 void Editor::Redo() {
4391 if (pdoc->CanRedo()) {
4392 int newPos = pdoc->Redo();
4393 if (newPos >= 0)
4394 SetEmptySelection(newPos);
4395 EnsureCaretVisible();
4399 void Editor::DelChar() {
4400 if (!RangeContainsProtected(sel.MainCaret(), sel.MainCaret() + 1)) {
4401 pdoc->DelChar(sel.MainCaret());
4403 // Avoid blinking during rapid typing:
4404 ShowCaretAtCurrentPosition();
4407 void Editor::DelCharBack(bool allowLineStartDeletion) {
4408 if (!sel.IsRectangular())
4409 FilterSelections();
4410 if (sel.IsRectangular())
4411 allowLineStartDeletion = false;
4412 UndoGroup ug(pdoc, (sel.Count() > 1) || !sel.Empty());
4413 if (sel.Empty()) {
4414 for (size_t r=0; r<sel.Count(); r++) {
4415 if (!RangeContainsProtected(sel.Range(r).caret.Position() - 1, sel.Range(r).caret.Position())) {
4416 if (sel.Range(r).caret.VirtualSpace()) {
4417 sel.Range(r).caret.SetVirtualSpace(sel.Range(r).caret.VirtualSpace() - 1);
4418 sel.Range(r).anchor.SetVirtualSpace(sel.Range(r).caret.VirtualSpace());
4419 } else {
4420 int lineCurrentPos = pdoc->LineFromPosition(sel.Range(r).caret.Position());
4421 if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != sel.Range(r).caret.Position())) {
4422 if (pdoc->GetColumn(sel.Range(r).caret.Position()) <= pdoc->GetLineIndentation(lineCurrentPos) &&
4423 pdoc->GetColumn(sel.Range(r).caret.Position()) > 0 && pdoc->backspaceUnindents) {
4424 UndoGroup ugInner(pdoc, !ug.Needed());
4425 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
4426 int indentationStep = pdoc->IndentSize();
4427 if (indentation % indentationStep == 0) {
4428 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
4429 } else {
4430 pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
4432 // SetEmptySelection
4433 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos),
4434 pdoc->GetLineIndentPosition(lineCurrentPos));
4435 } else {
4436 pdoc->DelCharBack(sel.Range(r).caret.Position());
4440 } else {
4441 sel.Range(r).ClearVirtualSpace();
4444 ThinRectangularRange();
4445 } else {
4446 ClearSelection();
4448 sel.RemoveDuplicates();
4449 ContainerNeedsUpdate(SC_UPDATE_SELECTION);
4450 // Avoid blinking during rapid typing:
4451 ShowCaretAtCurrentPosition();
4454 void Editor::NotifyFocus(bool) {}
4456 void Editor::SetCtrlID(int identifier) {
4457 ctrlID = identifier;
4460 void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
4461 SCNotification scn = {0};
4462 scn.nmhdr.code = SCN_STYLENEEDED;
4463 scn.position = endStyleNeeded;
4464 NotifyParent(scn);
4467 void Editor::NotifyStyleNeeded(Document *, void *, int endStyleNeeded) {
4468 NotifyStyleToNeeded(endStyleNeeded);
4471 void Editor::NotifyLexerChanged(Document *, void *) {
4474 void Editor::NotifyErrorOccurred(Document *, void *, int status) {
4475 errorStatus = status;
4478 void Editor::NotifyChar(int ch) {
4479 SCNotification scn = {0};
4480 scn.nmhdr.code = SCN_CHARADDED;
4481 scn.ch = ch;
4482 NotifyParent(scn);
4485 void Editor::NotifySavePoint(bool isSavePoint) {
4486 SCNotification scn = {0};
4487 if (isSavePoint) {
4488 scn.nmhdr.code = SCN_SAVEPOINTREACHED;
4489 } else {
4490 scn.nmhdr.code = SCN_SAVEPOINTLEFT;
4492 NotifyParent(scn);
4495 void Editor::NotifyModifyAttempt() {
4496 SCNotification scn = {0};
4497 scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
4498 NotifyParent(scn);
4501 void Editor::NotifyDoubleClick(Point pt, bool shift, bool ctrl, bool alt) {
4502 SCNotification scn = {0};
4503 scn.nmhdr.code = SCN_DOUBLECLICK;
4504 scn.line = LineFromLocation(pt);
4505 scn.position = PositionFromLocation(pt, true);
4506 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4507 (alt ? SCI_ALT : 0);
4508 NotifyParent(scn);
4511 void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
4512 SCNotification scn = {0};
4513 scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
4514 scn.position = position;
4515 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4516 (alt ? SCI_ALT : 0);
4517 NotifyParent(scn);
4520 void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
4521 SCNotification scn = {0};
4522 scn.nmhdr.code = SCN_HOTSPOTCLICK;
4523 scn.position = position;
4524 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4525 (alt ? SCI_ALT : 0);
4526 NotifyParent(scn);
4529 void Editor::NotifyHotSpotReleaseClick(int position, bool shift, bool ctrl, bool alt) {
4530 SCNotification scn = {0};
4531 scn.nmhdr.code = SCN_HOTSPOTRELEASECLICK;
4532 scn.position = position;
4533 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4534 (alt ? SCI_ALT : 0);
4535 NotifyParent(scn);
4538 bool Editor::NotifyUpdateUI() {
4539 if (needUpdateUI) {
4540 SCNotification scn = {0};
4541 scn.nmhdr.code = SCN_UPDATEUI;
4542 scn.updated = needUpdateUI;
4543 NotifyParent(scn);
4544 needUpdateUI = 0;
4545 return true;
4547 return false;
4550 void Editor::NotifyPainted() {
4551 SCNotification scn = {0};
4552 scn.nmhdr.code = SCN_PAINTED;
4553 NotifyParent(scn);
4556 void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctrl, bool alt) {
4557 int mask = pdoc->decorations.AllOnFor(position);
4558 if ((click && mask) || pdoc->decorations.clickNotified) {
4559 SCNotification scn = {0};
4560 pdoc->decorations.clickNotified = click;
4561 scn.nmhdr.code = click ? SCN_INDICATORCLICK : SCN_INDICATORRELEASE;
4562 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) | (alt ? SCI_ALT : 0);
4563 scn.position = position;
4564 NotifyParent(scn);
4568 bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
4569 int marginClicked = -1;
4570 int x = vs.textStart - vs.fixedColumnWidth;
4571 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
4572 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
4573 marginClicked = margin;
4574 x += vs.ms[margin].width;
4576 if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
4577 SCNotification scn = {0};
4578 scn.nmhdr.code = SCN_MARGINCLICK;
4579 scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
4580 (alt ? SCI_ALT : 0);
4581 scn.position = pdoc->LineStart(LineFromLocation(pt));
4582 scn.margin = marginClicked;
4583 NotifyParent(scn);
4584 return true;
4585 } else {
4586 return false;
4590 void Editor::NotifyNeedShown(int pos, int len) {
4591 SCNotification scn = {0};
4592 scn.nmhdr.code = SCN_NEEDSHOWN;
4593 scn.position = pos;
4594 scn.length = len;
4595 NotifyParent(scn);
4598 void Editor::NotifyDwelling(Point pt, bool state) {
4599 SCNotification scn = {0};
4600 scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
4601 scn.position = PositionFromLocation(pt, true);
4602 scn.x = pt.x;
4603 scn.y = pt.y;
4604 NotifyParent(scn);
4607 void Editor::NotifyZoom() {
4608 SCNotification scn = {0};
4609 scn.nmhdr.code = SCN_ZOOM;
4610 NotifyParent(scn);
4613 // Notifications from document
4614 void Editor::NotifyModifyAttempt(Document *, void *) {
4615 //Platform::DebugPrintf("** Modify Attempt\n");
4616 NotifyModifyAttempt();
4619 void Editor::NotifySavePoint(Document *, void *, bool atSavePoint) {
4620 //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
4621 NotifySavePoint(atSavePoint);
4624 void Editor::CheckModificationForWrap(DocModification mh) {
4625 if (mh.modificationType & (SC_MOD_INSERTTEXT | SC_MOD_DELETETEXT)) {
4626 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4627 int lineDoc = pdoc->LineFromPosition(mh.position);
4628 int lines = Platform::Maximum(0, mh.linesAdded);
4629 if (wrapState != eWrapNone) {
4630 NeedWrapping(lineDoc, lineDoc + lines + 1);
4632 RefreshStyleData();
4633 // Fix up annotation heights
4634 SetAnnotationHeights(lineDoc, lineDoc + lines + 2);
4638 // Move a position so it is still after the same character as before the insertion.
4639 static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
4640 if (position > startInsertion) {
4641 return position + length;
4643 return position;
4646 // Move a position so it is still after the same character as before the deletion if that
4647 // character is still present else after the previous surviving character.
4648 static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
4649 if (position > startDeletion) {
4650 int endDeletion = startDeletion + length;
4651 if (position > endDeletion) {
4652 return position - length;
4653 } else {
4654 return startDeletion;
4656 } else {
4657 return position;
4661 void Editor::NotifyModified(Document *, DocModification mh, void *) {
4662 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
4663 if (paintState == painting) {
4664 CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
4666 if (mh.modificationType & SC_MOD_CHANGELINESTATE) {
4667 if (paintState == painting) {
4668 CheckForChangeOutsidePaint(
4669 Range(pdoc->LineStart(mh.line), pdoc->LineStart(mh.line + 1)));
4670 } else {
4671 // Could check that change is before last visible line.
4672 Redraw();
4675 if (mh.modificationType & SC_MOD_LEXERSTATE) {
4676 if (paintState == painting) {
4677 CheckForChangeOutsidePaint(
4678 Range(mh.position, mh.position + mh.length));
4679 } else {
4680 Redraw();
4683 if (mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) {
4684 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4685 pdoc->IncrementStyleClock();
4687 if (paintState == notPainting) {
4688 if (mh.position < pdoc->LineStart(topLine)) {
4689 // Styling performed before this view
4690 Redraw();
4691 } else {
4692 InvalidateRange(mh.position, mh.position + mh.length);
4695 if (mh.modificationType & SC_MOD_CHANGESTYLE) {
4696 llc.Invalidate(LineLayout::llCheckTextAndStyle);
4698 } else {
4699 // Move selection and brace highlights
4700 if (mh.modificationType & SC_MOD_INSERTTEXT) {
4701 sel.MovePositions(true, mh.position, mh.length);
4702 braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
4703 braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
4704 } else if (mh.modificationType & SC_MOD_DELETETEXT) {
4705 sel.MovePositions(false, mh.position, mh.length);
4706 braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
4707 braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
4709 if ((mh.modificationType & (SC_MOD_BEFOREINSERT | SC_MOD_BEFOREDELETE)) && cs.HiddenLines()) {
4710 // Some lines are hidden so may need shown.
4711 // TODO: check if the modified area is hidden.
4712 if (mh.modificationType & SC_MOD_BEFOREINSERT) {
4713 int lineOfPos = pdoc->LineFromPosition(mh.position);
4714 bool insertingNewLine = false;
4715 for (int i=0; i < mh.length; i++) {
4716 if ((mh.text[i] == '\n') || (mh.text[i] == '\r'))
4717 insertingNewLine = true;
4719 if (insertingNewLine && (mh.position != pdoc->LineStart(lineOfPos)))
4720 NotifyNeedShown(mh.position, pdoc->LineStart(lineOfPos+1) - mh.position);
4721 else
4722 NotifyNeedShown(mh.position, 0);
4723 } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
4724 NotifyNeedShown(mh.position, mh.length);
4727 if (mh.linesAdded != 0) {
4728 // Update contraction state for inserted and removed lines
4729 // lineOfPos should be calculated in context of state before modification, shouldn't it
4730 int lineOfPos = pdoc->LineFromPosition(mh.position);
4731 if (mh.linesAdded > 0) {
4732 cs.InsertLines(lineOfPos, mh.linesAdded);
4733 } else {
4734 cs.DeleteLines(lineOfPos, -mh.linesAdded);
4737 if (mh.modificationType & SC_MOD_CHANGEANNOTATION) {
4738 int lineDoc = pdoc->LineFromPosition(mh.position);
4739 if (vs.annotationVisible) {
4740 cs.SetHeight(lineDoc, cs.GetHeight(lineDoc) + mh.annotationLinesAdded);
4741 Redraw();
4744 CheckModificationForWrap(mh);
4745 if (mh.linesAdded != 0) {
4746 // Avoid scrolling of display if change before current display
4747 if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
4748 int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
4749 if (newTop != topLine) {
4750 SetTopLine(newTop);
4751 SetVerticalScrollPos();
4755 if (paintState == notPainting && !CanDeferToLastStep(mh)) {
4756 QueueIdleWork(WorkNeeded::workStyle, pdoc->Length());
4757 Redraw();
4759 } else {
4760 if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
4761 QueueIdleWork(WorkNeeded::workStyle, mh.position + mh.length);
4762 InvalidateRange(mh.position, mh.position + mh.length);
4767 if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
4768 SetScrollBars();
4771 if ((mh.modificationType & SC_MOD_CHANGEMARKER) || (mh.modificationType & SC_MOD_CHANGEMARGIN)) {
4772 if ((!willRedrawAll) && ((paintState == notPainting) || !PaintContainsMargin())) {
4773 if (mh.modificationType & SC_MOD_CHANGEFOLD) {
4774 // Fold changes can affect the drawing of following lines so redraw whole margin
4775 RedrawSelMargin(highlightDelimiter.isEnabled ? -1 : mh.line-1, true);
4776 } else {
4777 RedrawSelMargin(mh.line);
4782 // NOW pay the piper WRT "deferred" visual updates
4783 if (IsLastStep(mh)) {
4784 SetScrollBars();
4785 Redraw();
4788 // If client wants to see this modification
4789 if (mh.modificationType & modEventMask) {
4790 if ((mh.modificationType & (SC_MOD_CHANGESTYLE | SC_MOD_CHANGEINDICATOR)) == 0) {
4791 // Real modification made to text of document.
4792 NotifyChange(); // Send EN_CHANGE
4795 SCNotification scn = {0};
4796 scn.nmhdr.code = SCN_MODIFIED;
4797 scn.position = mh.position;
4798 scn.modificationType = mh.modificationType;
4799 scn.text = mh.text;
4800 scn.length = mh.length;
4801 scn.linesAdded = mh.linesAdded;
4802 scn.line = mh.line;
4803 scn.foldLevelNow = mh.foldLevelNow;
4804 scn.foldLevelPrev = mh.foldLevelPrev;
4805 scn.token = mh.token;
4806 scn.annotationLinesAdded = mh.annotationLinesAdded;
4807 NotifyParent(scn);
4811 void Editor::NotifyDeleted(Document *, void *) {
4812 /* Do nothing */
4815 void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
4817 // Enumerates all macroable messages
4818 switch (iMessage) {
4819 case SCI_CUT:
4820 case SCI_COPY:
4821 case SCI_PASTE:
4822 case SCI_CLEAR:
4823 case SCI_REPLACESEL:
4824 case SCI_ADDTEXT:
4825 case SCI_INSERTTEXT:
4826 case SCI_APPENDTEXT:
4827 case SCI_CLEARALL:
4828 case SCI_SELECTALL:
4829 case SCI_GOTOLINE:
4830 case SCI_GOTOPOS:
4831 case SCI_SEARCHANCHOR:
4832 case SCI_SEARCHNEXT:
4833 case SCI_SEARCHPREV:
4834 case SCI_LINEDOWN:
4835 case SCI_LINEDOWNEXTEND:
4836 case SCI_PARADOWN:
4837 case SCI_PARADOWNEXTEND:
4838 case SCI_LINEUP:
4839 case SCI_LINEUPEXTEND:
4840 case SCI_PARAUP:
4841 case SCI_PARAUPEXTEND:
4842 case SCI_CHARLEFT:
4843 case SCI_CHARLEFTEXTEND:
4844 case SCI_CHARRIGHT:
4845 case SCI_CHARRIGHTEXTEND:
4846 case SCI_WORDLEFT:
4847 case SCI_WORDLEFTEXTEND:
4848 case SCI_WORDRIGHT:
4849 case SCI_WORDRIGHTEXTEND:
4850 case SCI_WORDPARTLEFT:
4851 case SCI_WORDPARTLEFTEXTEND:
4852 case SCI_WORDPARTRIGHT:
4853 case SCI_WORDPARTRIGHTEXTEND:
4854 case SCI_WORDLEFTEND:
4855 case SCI_WORDLEFTENDEXTEND:
4856 case SCI_WORDRIGHTEND:
4857 case SCI_WORDRIGHTENDEXTEND:
4858 case SCI_HOME:
4859 case SCI_HOMEEXTEND:
4860 case SCI_LINEEND:
4861 case SCI_LINEENDEXTEND:
4862 case SCI_HOMEWRAP:
4863 case SCI_HOMEWRAPEXTEND:
4864 case SCI_LINEENDWRAP:
4865 case SCI_LINEENDWRAPEXTEND:
4866 case SCI_DOCUMENTSTART:
4867 case SCI_DOCUMENTSTARTEXTEND:
4868 case SCI_DOCUMENTEND:
4869 case SCI_DOCUMENTENDEXTEND:
4870 case SCI_STUTTEREDPAGEUP:
4871 case SCI_STUTTEREDPAGEUPEXTEND:
4872 case SCI_STUTTEREDPAGEDOWN:
4873 case SCI_STUTTEREDPAGEDOWNEXTEND:
4874 case SCI_PAGEUP:
4875 case SCI_PAGEUPEXTEND:
4876 case SCI_PAGEDOWN:
4877 case SCI_PAGEDOWNEXTEND:
4878 case SCI_EDITTOGGLEOVERTYPE:
4879 case SCI_CANCEL:
4880 case SCI_DELETEBACK:
4881 case SCI_TAB:
4882 case SCI_BACKTAB:
4883 case SCI_FORMFEED:
4884 case SCI_VCHOME:
4885 case SCI_VCHOMEEXTEND:
4886 case SCI_VCHOMEWRAP:
4887 case SCI_VCHOMEWRAPEXTEND:
4888 case SCI_VCHOMEDISPLAY:
4889 case SCI_VCHOMEDISPLAYEXTEND:
4890 case SCI_DELWORDLEFT:
4891 case SCI_DELWORDRIGHT:
4892 case SCI_DELWORDRIGHTEND:
4893 case SCI_DELLINELEFT:
4894 case SCI_DELLINERIGHT:
4895 case SCI_LINECOPY:
4896 case SCI_LINECUT:
4897 case SCI_LINEDELETE:
4898 case SCI_LINETRANSPOSE:
4899 case SCI_LINEDUPLICATE:
4900 case SCI_LOWERCASE:
4901 case SCI_UPPERCASE:
4902 case SCI_LINESCROLLDOWN:
4903 case SCI_LINESCROLLUP:
4904 case SCI_DELETEBACKNOTLINE:
4905 case SCI_HOMEDISPLAY:
4906 case SCI_HOMEDISPLAYEXTEND:
4907 case SCI_LINEENDDISPLAY:
4908 case SCI_LINEENDDISPLAYEXTEND:
4909 case SCI_SETSELECTIONMODE:
4910 case SCI_LINEDOWNRECTEXTEND:
4911 case SCI_LINEUPRECTEXTEND:
4912 case SCI_CHARLEFTRECTEXTEND:
4913 case SCI_CHARRIGHTRECTEXTEND:
4914 case SCI_HOMERECTEXTEND:
4915 case SCI_VCHOMERECTEXTEND:
4916 case SCI_LINEENDRECTEXTEND:
4917 case SCI_PAGEUPRECTEXTEND:
4918 case SCI_PAGEDOWNRECTEXTEND:
4919 case SCI_SELECTIONDUPLICATE:
4920 case SCI_COPYALLOWLINE:
4921 case SCI_VERTICALCENTRECARET:
4922 case SCI_MOVESELECTEDLINESUP:
4923 case SCI_MOVESELECTEDLINESDOWN:
4924 case SCI_SCROLLTOSTART:
4925 case SCI_SCROLLTOEND:
4926 break;
4928 // Filter out all others like display changes. Also, newlines are redundant
4929 // with char insert messages.
4930 case SCI_NEWLINE:
4931 default:
4932 // printf("Filtered out %ld of macro recording\n", iMessage);
4933 return ;
4936 // Send notification
4937 SCNotification scn = {0};
4938 scn.nmhdr.code = SCN_MACRORECORD;
4939 scn.message = iMessage;
4940 scn.wParam = wParam;
4941 scn.lParam = lParam;
4942 NotifyParent(scn);
4945 // Something has changed that the container should know about
4946 void Editor::ContainerNeedsUpdate(int flags) {
4947 needUpdateUI |= flags;
4951 * Force scroll and keep position relative to top of window.
4953 * If stuttered = true and not already at first/last row, move to first/last row of window.
4954 * If stuttered = true and already at first/last row, scroll as normal.
4956 void Editor::PageMove(int direction, Selection::selTypes selt, bool stuttered) {
4957 int topLineNew;
4958 SelectionPosition newPos;
4960 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
4961 int topStutterLine = topLine + caretYSlop;
4962 int bottomStutterLine =
4963 pdoc->LineFromPosition(PositionFromLocation(
4964 Point(lastXChosen - xOffset, direction * vs.lineHeight * LinesToScroll())))
4965 - caretYSlop - 1;
4967 if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
4968 topLineNew = topLine;
4969 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * caretYSlop),
4970 false, false, UserVirtualSpace());
4972 } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
4973 topLineNew = topLine;
4974 newPos = SPositionFromLocation(Point(lastXChosen - xOffset, vs.lineHeight * (LinesToScroll() - caretYSlop)),
4975 false, false, UserVirtualSpace());
4977 } else {
4978 Point pt = LocationFromPosition(sel.MainCaret());
4980 topLineNew = Platform::Clamp(
4981 topLine + direction * LinesToScroll(), 0, MaxScrollPos());
4982 newPos = SPositionFromLocation(
4983 Point(lastXChosen - xOffset, pt.y + direction * (vs.lineHeight * LinesToScroll())),
4984 false, false, UserVirtualSpace());
4987 if (topLineNew != topLine) {
4988 SetTopLine(topLineNew);
4989 MovePositionTo(newPos, selt);
4990 Redraw();
4991 SetVerticalScrollPos();
4992 } else {
4993 MovePositionTo(newPos, selt);
4997 void Editor::ChangeCaseOfSelection(int caseMapping) {
4998 UndoGroup ug(pdoc);
4999 for (size_t r=0; r<sel.Count(); r++) {
5000 SelectionRange current = sel.Range(r);
5001 SelectionRange currentNoVS = current;
5002 currentNoVS.ClearVirtualSpace();
5003 char *text = CopyRange(currentNoVS.Start().Position(), currentNoVS.End().Position());
5004 size_t rangeBytes = currentNoVS.Length();
5005 if (rangeBytes > 0) {
5006 std::string sText(text, rangeBytes);
5008 std::string sMapped = CaseMapString(sText, caseMapping);
5010 if (sMapped != sText) {
5011 size_t firstDifference = 0;
5012 while (sMapped[firstDifference] == sText[firstDifference])
5013 firstDifference++;
5014 size_t lastDifference = sMapped.size() - 1;
5015 while (sMapped[lastDifference] == sText[lastDifference])
5016 lastDifference--;
5017 size_t endSame = sMapped.size() - 1 - lastDifference;
5018 pdoc->DeleteChars(
5019 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
5020 static_cast<int>(rangeBytes - firstDifference - endSame));
5021 pdoc->InsertString(
5022 static_cast<int>(currentNoVS.Start().Position() + firstDifference),
5023 sMapped.c_str() + firstDifference,
5024 static_cast<int>(lastDifference - firstDifference + 1));
5025 // Automatic movement changes selection so reset to exactly the same as it was.
5026 sel.Range(r) = current;
5029 delete []text;
5033 void Editor::LineTranspose() {
5034 int line = pdoc->LineFromPosition(sel.MainCaret());
5035 if (line > 0) {
5036 UndoGroup ug(pdoc);
5037 int startPrev = pdoc->LineStart(line - 1);
5038 int endPrev = pdoc->LineEnd(line - 1);
5039 int start = pdoc->LineStart(line);
5040 int end = pdoc->LineEnd(line);
5041 char *line1 = CopyRange(startPrev, endPrev);
5042 int len1 = endPrev - startPrev;
5043 char *line2 = CopyRange(start, end);
5044 int len2 = end - start;
5045 pdoc->DeleteChars(start, len2);
5046 pdoc->DeleteChars(startPrev, len1);
5047 pdoc->InsertString(startPrev, line2, len2);
5048 pdoc->InsertString(start - len1 + len2, line1, len1);
5049 MovePositionTo(SelectionPosition(start - len1 + len2));
5050 delete []line1;
5051 delete []line2;
5055 void Editor::Duplicate(bool forLine) {
5056 if (sel.Empty()) {
5057 forLine = true;
5059 UndoGroup ug(pdoc);
5060 const char *eol = "";
5061 int eolLen = 0;
5062 if (forLine) {
5063 eol = StringFromEOLMode(pdoc->eolMode);
5064 eolLen = istrlen(eol);
5066 for (size_t r=0; r<sel.Count(); r++) {
5067 SelectionPosition start = sel.Range(r).Start();
5068 SelectionPosition end = sel.Range(r).End();
5069 if (forLine) {
5070 int line = pdoc->LineFromPosition(sel.Range(r).caret.Position());
5071 start = SelectionPosition(pdoc->LineStart(line));
5072 end = SelectionPosition(pdoc->LineEnd(line));
5074 char *text = CopyRange(start.Position(), end.Position());
5075 if (forLine)
5076 pdoc->InsertString(end.Position(), eol, eolLen);
5077 pdoc->InsertString(end.Position() + eolLen, text, SelectionRange(end, start).Length());
5078 delete []text;
5080 if (sel.Count() && sel.IsRectangular()) {
5081 SelectionPosition last = sel.Last();
5082 if (forLine) {
5083 int line = pdoc->LineFromPosition(last.Position());
5084 last = SelectionPosition(last.Position() + pdoc->LineStart(line+1) - pdoc->LineStart(line));
5086 if (sel.Rectangular().anchor > sel.Rectangular().caret)
5087 sel.Rectangular().anchor = last;
5088 else
5089 sel.Rectangular().caret = last;
5090 SetRectangularRange();
5094 void Editor::CancelModes() {
5095 sel.SetMoveExtends(false);
5098 void Editor::NewLine() {
5099 // Remove non-main ranges
5100 InvalidateSelection(sel.RangeMain(), true);
5101 sel.SetSelection(sel.RangeMain());
5103 // Clear main range and insert line end
5104 bool needGroupUndo = !sel.Empty();
5105 if (needGroupUndo)
5106 pdoc->BeginUndoAction();
5108 if (!sel.Empty())
5109 ClearSelection();
5110 const char *eol = "\n";
5111 if (pdoc->eolMode == SC_EOL_CRLF) {
5112 eol = "\r\n";
5113 } else if (pdoc->eolMode == SC_EOL_CR) {
5114 eol = "\r";
5115 } // else SC_EOL_LF -> "\n" already set
5116 bool inserted = pdoc->InsertCString(sel.MainCaret(), eol);
5117 // Want to end undo group before NotifyChar as applications often modify text here
5118 if (needGroupUndo)
5119 pdoc->EndUndoAction();
5120 if (inserted) {
5121 SetEmptySelection(sel.MainCaret() + istrlen(eol));
5122 while (*eol) {
5123 NotifyChar(*eol);
5124 if (recordingMacro) {
5125 char txt[2];
5126 txt[0] = *eol;
5127 txt[1] = '\0';
5128 NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
5130 eol++;
5133 SetLastXChosen();
5134 SetScrollBars();
5135 EnsureCaretVisible();
5136 // Avoid blinking during rapid typing:
5137 ShowCaretAtCurrentPosition();
5140 void Editor::CursorUpOrDown(int direction, Selection::selTypes selt) {
5141 SelectionPosition caretToUse = sel.Range(sel.Main()).caret;
5142 if (sel.IsRectangular()) {
5143 if (selt == Selection::noSel) {
5144 caretToUse = (direction > 0) ? sel.Limits().end : sel.Limits().start;
5145 } else {
5146 caretToUse = sel.Rectangular().caret;
5150 Point pt = LocationFromPosition(caretToUse);
5151 int skipLines = 0;
5153 if (vs.annotationVisible) {
5154 int lineDoc = pdoc->LineFromPosition(caretToUse.Position());
5155 Point ptStartLine = LocationFromPosition(pdoc->LineStart(lineDoc));
5156 int subLine = (pt.y - ptStartLine.y) / vs.lineHeight;
5158 if (direction < 0 && subLine == 0) {
5159 int lineDisplay = cs.DisplayFromDoc(lineDoc);
5160 if (lineDisplay > 0) {
5161 skipLines = pdoc->AnnotationLines(cs.DocFromDisplay(lineDisplay - 1));
5163 } else if (direction > 0 && subLine >= (cs.GetHeight(lineDoc) - 1 - pdoc->AnnotationLines(lineDoc))) {
5164 skipLines = pdoc->AnnotationLines(lineDoc);
5168 int newY = pt.y + (1 + skipLines) * direction * vs.lineHeight;
5169 SelectionPosition posNew = SPositionFromLocation(
5170 Point(lastXChosen - xOffset, newY), false, false, UserVirtualSpace());
5172 if (direction < 0) {
5173 // Line wrapping may lead to a location on the same line, so
5174 // seek back if that is the case.
5175 Point ptNew = LocationFromPosition(posNew.Position());
5176 while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
5177 posNew.Add(-1);
5178 posNew.SetVirtualSpace(0);
5179 ptNew = LocationFromPosition(posNew.Position());
5181 } else if (direction > 0 && posNew.Position() != pdoc->Length()) {
5182 // There is an equivalent case when moving down which skips
5183 // over a line.
5184 Point ptNew = LocationFromPosition(posNew.Position());
5185 while ((posNew.Position() > caretToUse.Position()) && (ptNew.y > newY)) {
5186 posNew.Add(-1);
5187 posNew.SetVirtualSpace(0);
5188 ptNew = LocationFromPosition(posNew.Position());
5192 MovePositionTo(MovePositionSoVisible(posNew, direction), selt);
5195 void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
5196 int lineDoc, savedPos = sel.MainCaret();
5197 do {
5198 MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
5199 lineDoc = pdoc->LineFromPosition(sel.MainCaret());
5200 if (direction > 0) {
5201 if (sel.MainCaret() >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
5202 if (selt == Selection::noSel) {
5203 MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
5205 break;
5208 } while (!cs.GetVisible(lineDoc));
5211 int Editor::StartEndDisplayLine(int pos, bool start) {
5212 RefreshStyleData();
5213 int line = pdoc->LineFromPosition(pos);
5214 AutoSurface surface(this);
5215 AutoLineLayout ll(llc, RetrieveLineLayout(line));
5216 int posRet = INVALID_POSITION;
5217 if (surface && ll) {
5218 unsigned int posLineStart = pdoc->LineStart(line);
5219 LayoutLine(line, surface, vs, ll, wrapWidth);
5220 int posInLine = pos - posLineStart;
5221 if (posInLine <= ll->maxLineLength) {
5222 for (int subLine = 0; subLine < ll->lines; subLine++) {
5223 if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
5224 if (start) {
5225 posRet = ll->LineStart(subLine) + posLineStart;
5226 } else {
5227 if (subLine == ll->lines - 1)
5228 posRet = ll->LineStart(subLine + 1) + posLineStart;
5229 else
5230 posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
5236 if (posRet == INVALID_POSITION) {
5237 return pos;
5238 } else {
5239 return posRet;
5243 int Editor::KeyCommand(unsigned int iMessage) {
5244 switch (iMessage) {
5245 case SCI_LINEDOWN:
5246 CursorUpOrDown(1);
5247 break;
5248 case SCI_LINEDOWNEXTEND:
5249 CursorUpOrDown(1, Selection::selStream);
5250 break;
5251 case SCI_LINEDOWNRECTEXTEND:
5252 CursorUpOrDown(1, Selection::selRectangle);
5253 break;
5254 case SCI_PARADOWN:
5255 ParaUpOrDown(1);
5256 break;
5257 case SCI_PARADOWNEXTEND:
5258 ParaUpOrDown(1, Selection::selStream);
5259 break;
5260 case SCI_LINESCROLLDOWN:
5261 ScrollTo(topLine + 1);
5262 MoveCaretInsideView(false);
5263 break;
5264 case SCI_LINEUP:
5265 CursorUpOrDown(-1);
5266 break;
5267 case SCI_LINEUPEXTEND:
5268 CursorUpOrDown(-1, Selection::selStream);
5269 break;
5270 case SCI_LINEUPRECTEXTEND:
5271 CursorUpOrDown(-1, Selection::selRectangle);
5272 break;
5273 case SCI_PARAUP:
5274 ParaUpOrDown(-1);
5275 break;
5276 case SCI_PARAUPEXTEND:
5277 ParaUpOrDown(-1, Selection::selStream);
5278 break;
5279 case SCI_LINESCROLLUP:
5280 ScrollTo(topLine - 1);
5281 MoveCaretInsideView(false);
5282 break;
5283 case SCI_CHARLEFT:
5284 if (SelectionEmpty() || sel.MoveExtends()) {
5285 if ((sel.Count() == 1) && pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5286 SelectionPosition spCaret = sel.RangeMain().caret;
5287 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5288 MovePositionTo(spCaret);
5289 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5290 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1));
5291 } else {
5292 MovePositionTo(MovePositionSoVisible(
5293 SelectionPosition((sel.LimitsForRectangularElseMain().start).Position() - 1), -1));
5295 } else {
5296 MovePositionTo(sel.LimitsForRectangularElseMain().start);
5298 SetLastXChosen();
5299 break;
5300 case SCI_CHARLEFTEXTEND:
5301 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5302 SelectionPosition spCaret = sel.RangeMain().caret;
5303 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5304 MovePositionTo(spCaret, Selection::selStream);
5305 } else {
5306 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selStream);
5308 SetLastXChosen();
5309 break;
5310 case SCI_CHARLEFTRECTEXTEND:
5311 if (pdoc->IsLineEndPosition(sel.MainCaret()) && sel.RangeMain().caret.VirtualSpace()) {
5312 SelectionPosition spCaret = sel.RangeMain().caret;
5313 spCaret.SetVirtualSpace(spCaret.VirtualSpace() - 1);
5314 MovePositionTo(spCaret, Selection::selRectangle);
5315 } else {
5316 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() - 1), -1), Selection::selRectangle);
5318 SetLastXChosen();
5319 break;
5320 case SCI_CHARRIGHT:
5321 if (SelectionEmpty() || sel.MoveExtends()) {
5322 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5323 SelectionPosition spCaret = sel.RangeMain().caret;
5324 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5325 MovePositionTo(spCaret);
5326 } else if (sel.MoveExtends() && sel.selType == Selection::selStream) {
5327 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1));
5328 } else {
5329 MovePositionTo(MovePositionSoVisible(
5330 SelectionPosition((sel.LimitsForRectangularElseMain().end).Position() + 1), 1));
5332 } else {
5333 MovePositionTo(sel.LimitsForRectangularElseMain().end);
5335 SetLastXChosen();
5336 break;
5337 case SCI_CHARRIGHTEXTEND:
5338 if ((virtualSpaceOptions & SCVS_USERACCESSIBLE) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5339 SelectionPosition spCaret = sel.RangeMain().caret;
5340 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5341 MovePositionTo(spCaret, Selection::selStream);
5342 } else {
5343 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selStream);
5345 SetLastXChosen();
5346 break;
5347 case SCI_CHARRIGHTRECTEXTEND:
5348 if ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) && pdoc->IsLineEndPosition(sel.MainCaret())) {
5349 SelectionPosition spCaret = sel.RangeMain().caret;
5350 spCaret.SetVirtualSpace(spCaret.VirtualSpace() + 1);
5351 MovePositionTo(spCaret, Selection::selRectangle);
5352 } else {
5353 MovePositionTo(MovePositionSoVisible(SelectionPosition(sel.MainCaret() + 1), 1), Selection::selRectangle);
5355 SetLastXChosen();
5356 break;
5357 case SCI_WORDLEFT:
5358 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1));
5359 SetLastXChosen();
5360 break;
5361 case SCI_WORDLEFTEXTEND:
5362 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), -1), -1), Selection::selStream);
5363 SetLastXChosen();
5364 break;
5365 case SCI_WORDRIGHT:
5366 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1));
5367 SetLastXChosen();
5368 break;
5369 case SCI_WORDRIGHTEXTEND:
5370 MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(sel.MainCaret(), 1), 1), Selection::selStream);
5371 SetLastXChosen();
5372 break;
5374 case SCI_WORDLEFTEND:
5375 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1));
5376 SetLastXChosen();
5377 break;
5378 case SCI_WORDLEFTENDEXTEND:
5379 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), -1), -1), Selection::selStream);
5380 SetLastXChosen();
5381 break;
5382 case SCI_WORDRIGHTEND:
5383 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1));
5384 SetLastXChosen();
5385 break;
5386 case SCI_WORDRIGHTENDEXTEND:
5387 MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(sel.MainCaret(), 1), 1), Selection::selStream);
5388 SetLastXChosen();
5389 break;
5391 case SCI_HOME:
5392 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5393 SetLastXChosen();
5394 break;
5395 case SCI_HOMEEXTEND:
5396 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selStream);
5397 SetLastXChosen();
5398 break;
5399 case SCI_HOMERECTEXTEND:
5400 MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())), Selection::selRectangle);
5401 SetLastXChosen();
5402 break;
5403 case SCI_LINEEND:
5404 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()));
5405 SetLastXChosen();
5406 break;
5407 case SCI_LINEENDEXTEND:
5408 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selStream);
5409 SetLastXChosen();
5410 break;
5411 case SCI_LINEENDRECTEXTEND:
5412 MovePositionTo(pdoc->LineEndPosition(sel.MainCaret()), Selection::selRectangle);
5413 SetLastXChosen();
5414 break;
5415 case SCI_HOMEWRAP: {
5416 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5417 if (sel.RangeMain().caret <= homePos)
5418 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5419 MovePositionTo(homePos);
5420 SetLastXChosen();
5422 break;
5423 case SCI_HOMEWRAPEXTEND: {
5424 SelectionPosition homePos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5425 if (sel.RangeMain().caret <= homePos)
5426 homePos = SelectionPosition(pdoc->LineStart(pdoc->LineFromPosition(sel.MainCaret())));
5427 MovePositionTo(homePos, Selection::selStream);
5428 SetLastXChosen();
5430 break;
5431 case SCI_LINEENDWRAP: {
5432 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5433 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5434 if (endPos > realEndPos // if moved past visible EOLs
5435 || sel.RangeMain().caret >= endPos) // if at end of display line already
5436 endPos = realEndPos;
5437 MovePositionTo(endPos);
5438 SetLastXChosen();
5440 break;
5441 case SCI_LINEENDWRAPEXTEND: {
5442 SelectionPosition endPos = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), false), 1);
5443 SelectionPosition realEndPos = SelectionPosition(pdoc->LineEndPosition(sel.MainCaret()));
5444 if (endPos > realEndPos // if moved past visible EOLs
5445 || sel.RangeMain().caret >= endPos) // if at end of display line already
5446 endPos = realEndPos;
5447 MovePositionTo(endPos, Selection::selStream);
5448 SetLastXChosen();
5450 break;
5451 case SCI_DOCUMENTSTART:
5452 MovePositionTo(0);
5453 SetLastXChosen();
5454 break;
5455 case SCI_DOCUMENTSTARTEXTEND:
5456 MovePositionTo(0, Selection::selStream);
5457 SetLastXChosen();
5458 break;
5459 case SCI_DOCUMENTEND:
5460 MovePositionTo(pdoc->Length());
5461 SetLastXChosen();
5462 break;
5463 case SCI_DOCUMENTENDEXTEND:
5464 MovePositionTo(pdoc->Length(), Selection::selStream);
5465 SetLastXChosen();
5466 break;
5467 case SCI_STUTTEREDPAGEUP:
5468 PageMove(-1, Selection::noSel, true);
5469 break;
5470 case SCI_STUTTEREDPAGEUPEXTEND:
5471 PageMove(-1, Selection::selStream, true);
5472 break;
5473 case SCI_STUTTEREDPAGEDOWN:
5474 PageMove(1, Selection::noSel, true);
5475 break;
5476 case SCI_STUTTEREDPAGEDOWNEXTEND:
5477 PageMove(1, Selection::selStream, true);
5478 break;
5479 case SCI_PAGEUP:
5480 PageMove(-1);
5481 break;
5482 case SCI_PAGEUPEXTEND:
5483 PageMove(-1, Selection::selStream);
5484 break;
5485 case SCI_PAGEUPRECTEXTEND:
5486 PageMove(-1, Selection::selRectangle);
5487 break;
5488 case SCI_PAGEDOWN:
5489 PageMove(1);
5490 break;
5491 case SCI_PAGEDOWNEXTEND:
5492 PageMove(1, Selection::selStream);
5493 break;
5494 case SCI_PAGEDOWNRECTEXTEND:
5495 PageMove(1, Selection::selRectangle);
5496 break;
5497 case SCI_EDITTOGGLEOVERTYPE:
5498 inOverstrike = !inOverstrike;
5499 DropCaret();
5500 ShowCaretAtCurrentPosition();
5501 ContainerNeedsUpdate(SC_UPDATE_CONTENT);
5502 NotifyUpdateUI();
5503 break;
5504 case SCI_CANCEL: // Cancel any modes - handled in subclass
5505 // Also unselect text
5506 CancelModes();
5507 break;
5508 case SCI_DELETEBACK:
5509 DelCharBack(true);
5510 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5511 SetLastXChosen();
5513 EnsureCaretVisible();
5514 break;
5515 case SCI_DELETEBACKNOTLINE:
5516 DelCharBack(false);
5517 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5518 SetLastXChosen();
5520 EnsureCaretVisible();
5521 break;
5522 case SCI_TAB:
5523 Indent(true);
5524 if (caretSticky == SC_CARETSTICKY_OFF) {
5525 SetLastXChosen();
5527 EnsureCaretVisible();
5528 ShowCaretAtCurrentPosition(); // Avoid blinking
5529 break;
5530 case SCI_BACKTAB:
5531 Indent(false);
5532 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
5533 SetLastXChosen();
5535 EnsureCaretVisible();
5536 ShowCaretAtCurrentPosition(); // Avoid blinking
5537 break;
5538 case SCI_NEWLINE:
5539 NewLine();
5540 break;
5541 case SCI_FORMFEED:
5542 AddChar('\f');
5543 break;
5544 case SCI_VCHOME:
5545 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()));
5546 SetLastXChosen();
5547 break;
5548 case SCI_VCHOMEEXTEND:
5549 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selStream);
5550 SetLastXChosen();
5551 break;
5552 case SCI_VCHOMERECTEXTEND:
5553 MovePositionTo(pdoc->VCHomePosition(sel.MainCaret()), Selection::selRectangle);
5554 SetLastXChosen();
5555 break;
5556 case SCI_VCHOMEWRAP: {
5557 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5558 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5559 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5560 homePos = viewLineStart;
5562 MovePositionTo(homePos);
5563 SetLastXChosen();
5565 break;
5566 case SCI_VCHOMEWRAPEXTEND: {
5567 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5568 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5569 if ((viewLineStart < sel.RangeMain().caret) && (viewLineStart > homePos))
5570 homePos = viewLineStart;
5572 MovePositionTo(homePos, Selection::selStream);
5573 SetLastXChosen();
5575 break;
5576 case SCI_ZOOMIN:
5577 if (vs.zoomLevel < 20) {
5578 vs.zoomLevel++;
5579 InvalidateStyleRedraw();
5580 NotifyZoom();
5582 break;
5583 case SCI_ZOOMOUT:
5584 if (vs.zoomLevel > -10) {
5585 vs.zoomLevel--;
5586 InvalidateStyleRedraw();
5587 NotifyZoom();
5589 break;
5590 case SCI_DELWORDLEFT: {
5591 int startWord = pdoc->NextWordStart(sel.MainCaret(), -1);
5592 pdoc->DeleteChars(startWord, sel.MainCaret() - startWord);
5593 sel.RangeMain().ClearVirtualSpace();
5594 SetLastXChosen();
5596 break;
5597 case SCI_DELWORDRIGHT: {
5598 UndoGroup ug(pdoc);
5599 sel.RangeMain().caret = SelectionPosition(
5600 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5601 sel.RangeMain().anchor = sel.RangeMain().caret;
5602 int endWord = pdoc->NextWordStart(sel.MainCaret(), 1);
5603 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5605 break;
5606 case SCI_DELWORDRIGHTEND: {
5607 UndoGroup ug(pdoc);
5608 sel.RangeMain().caret = SelectionPosition(
5609 InsertSpace(sel.RangeMain().caret.Position(), sel.RangeMain().caret.VirtualSpace()));
5610 int endWord = pdoc->NextWordEnd(sel.MainCaret(), 1);
5611 pdoc->DeleteChars(sel.MainCaret(), endWord - sel.MainCaret());
5613 break;
5614 case SCI_DELLINELEFT: {
5615 int line = pdoc->LineFromPosition(sel.MainCaret());
5616 int start = pdoc->LineStart(line);
5617 pdoc->DeleteChars(start, sel.MainCaret() - start);
5618 sel.RangeMain().ClearVirtualSpace();
5619 SetLastXChosen();
5621 break;
5622 case SCI_DELLINERIGHT: {
5623 int line = pdoc->LineFromPosition(sel.MainCaret());
5624 int end = pdoc->LineEnd(line);
5625 pdoc->DeleteChars(sel.MainCaret(), end - sel.MainCaret());
5627 break;
5628 case SCI_LINECOPY: {
5629 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5630 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5631 CopyRangeToClipboard(pdoc->LineStart(lineStart),
5632 pdoc->LineStart(lineEnd + 1));
5634 break;
5635 case SCI_LINECUT: {
5636 int lineStart = pdoc->LineFromPosition(SelectionStart().Position());
5637 int lineEnd = pdoc->LineFromPosition(SelectionEnd().Position());
5638 int start = pdoc->LineStart(lineStart);
5639 int end = pdoc->LineStart(lineEnd + 1);
5640 SetSelection(start, end);
5641 Cut();
5642 SetLastXChosen();
5644 break;
5645 case SCI_LINEDELETE: {
5646 int line = pdoc->LineFromPosition(sel.MainCaret());
5647 int start = pdoc->LineStart(line);
5648 int end = pdoc->LineStart(line + 1);
5649 pdoc->DeleteChars(start, end - start);
5651 break;
5652 case SCI_LINETRANSPOSE:
5653 LineTranspose();
5654 break;
5655 case SCI_LINEDUPLICATE:
5656 Duplicate(true);
5657 break;
5658 case SCI_SELECTIONDUPLICATE:
5659 Duplicate(false);
5660 break;
5661 case SCI_LOWERCASE:
5662 ChangeCaseOfSelection(cmLower);
5663 break;
5664 case SCI_UPPERCASE:
5665 ChangeCaseOfSelection(cmUpper);
5666 break;
5667 case SCI_WORDPARTLEFT:
5668 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1));
5669 SetLastXChosen();
5670 break;
5671 case SCI_WORDPARTLEFTEXTEND:
5672 MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(sel.MainCaret()), -1), Selection::selStream);
5673 SetLastXChosen();
5674 break;
5675 case SCI_WORDPARTRIGHT:
5676 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1));
5677 SetLastXChosen();
5678 break;
5679 case SCI_WORDPARTRIGHTEXTEND:
5680 MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(sel.MainCaret()), 1), Selection::selStream);
5681 SetLastXChosen();
5682 break;
5683 case SCI_HOMEDISPLAY:
5684 MovePositionTo(MovePositionSoVisible(
5685 StartEndDisplayLine(sel.MainCaret(), true), -1));
5686 SetLastXChosen();
5687 break;
5688 case SCI_VCHOMEDISPLAY: {
5689 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5690 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5691 if (viewLineStart > homePos)
5692 homePos = viewLineStart;
5694 MovePositionTo(homePos);
5695 SetLastXChosen();
5697 break;
5698 case SCI_HOMEDISPLAYEXTEND:
5699 MovePositionTo(MovePositionSoVisible(
5700 StartEndDisplayLine(sel.MainCaret(), true), -1), Selection::selStream);
5701 SetLastXChosen();
5702 break;
5703 case SCI_VCHOMEDISPLAYEXTEND: {
5704 SelectionPosition homePos = SelectionPosition(pdoc->VCHomePosition(sel.MainCaret()));
5705 SelectionPosition viewLineStart = MovePositionSoVisible(StartEndDisplayLine(sel.MainCaret(), true), -1);
5706 if (viewLineStart > homePos)
5707 homePos = viewLineStart;
5709 MovePositionTo(homePos, Selection::selStream);
5710 SetLastXChosen();
5712 break;
5713 case SCI_LINEENDDISPLAY:
5714 MovePositionTo(MovePositionSoVisible(
5715 StartEndDisplayLine(sel.MainCaret(), false), 1));
5716 SetLastXChosen();
5717 break;
5718 case SCI_LINEENDDISPLAYEXTEND:
5719 MovePositionTo(MovePositionSoVisible(
5720 StartEndDisplayLine(sel.MainCaret(), false), 1), Selection::selStream);
5721 SetLastXChosen();
5722 break;
5723 case SCI_SCROLLTOSTART:
5724 ScrollTo(0);
5725 break;
5726 case SCI_SCROLLTOEND:
5727 ScrollTo(MaxScrollPos());
5728 break;
5730 return 0;
5733 int Editor::KeyDefault(int, int) {
5734 return 0;
5737 int Editor::KeyDownWithModifiers(int key, int modifiers, bool *consumed) {
5738 DwellEnd(false);
5739 int msg = kmap.Find(key, modifiers);
5740 if (msg) {
5741 if (consumed)
5742 *consumed = true;
5743 return WndProc(msg, 0, 0);
5744 } else {
5745 if (consumed)
5746 *consumed = false;
5747 return KeyDefault(key, modifiers);
5751 int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
5752 int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
5753 (alt ? SCI_ALT : 0);
5754 return KeyDownWithModifiers(key, modifiers, consumed);
5757 void Editor::Indent(bool forwards) {
5758 for (size_t r=0; r<sel.Count(); r++) {
5759 int lineOfAnchor = pdoc->LineFromPosition(sel.Range(r).anchor.Position());
5760 int caretPosition = sel.Range(r).caret.Position();
5761 int lineCurrentPos = pdoc->LineFromPosition(caretPosition);
5762 if (lineOfAnchor == lineCurrentPos) {
5763 if (forwards) {
5764 UndoGroup ug(pdoc);
5765 pdoc->DeleteChars(sel.Range(r).Start().Position(), sel.Range(r).Length());
5766 caretPosition = sel.Range(r).caret.Position();
5767 if (pdoc->GetColumn(caretPosition) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
5768 pdoc->tabIndents) {
5769 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5770 int indentationStep = pdoc->IndentSize();
5771 pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
5772 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5773 } else {
5774 if (pdoc->useTabs) {
5775 pdoc->InsertChar(caretPosition, '\t');
5776 sel.Range(r) = SelectionRange(caretPosition+1);
5777 } else {
5778 int numSpaces = (pdoc->tabInChars) -
5779 (pdoc->GetColumn(caretPosition) % (pdoc->tabInChars));
5780 if (numSpaces < 1)
5781 numSpaces = pdoc->tabInChars;
5782 for (int i = 0; i < numSpaces; i++) {
5783 pdoc->InsertChar(caretPosition + i, ' ');
5785 sel.Range(r) = SelectionRange(caretPosition+numSpaces);
5788 } else {
5789 if (pdoc->GetColumn(caretPosition) <= pdoc->GetLineIndentation(lineCurrentPos) &&
5790 pdoc->tabIndents) {
5791 UndoGroup ug(pdoc);
5792 int indentation = pdoc->GetLineIndentation(lineCurrentPos);
5793 int indentationStep = pdoc->IndentSize();
5794 pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
5795 sel.Range(r) = SelectionRange(pdoc->GetLineIndentPosition(lineCurrentPos));
5796 } else {
5797 int newColumn = ((pdoc->GetColumn(caretPosition) - 1) / pdoc->tabInChars) *
5798 pdoc->tabInChars;
5799 if (newColumn < 0)
5800 newColumn = 0;
5801 int newPos = caretPosition;
5802 while (pdoc->GetColumn(newPos) > newColumn)
5803 newPos--;
5804 sel.Range(r) = SelectionRange(newPos);
5807 } else { // Multiline
5808 int anchorPosOnLine = sel.Range(r).anchor.Position() - pdoc->LineStart(lineOfAnchor);
5809 int currentPosPosOnLine = caretPosition - pdoc->LineStart(lineCurrentPos);
5810 // Multiple lines selected so indent / dedent
5811 int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
5812 int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
5813 if (pdoc->LineStart(lineBottomSel) == sel.Range(r).anchor.Position() || pdoc->LineStart(lineBottomSel) == caretPosition)
5814 lineBottomSel--; // If not selecting any characters on a line, do not indent
5816 UndoGroup ug(pdoc);
5817 pdoc->Indent(forwards, lineBottomSel, lineTopSel);
5819 if (lineOfAnchor < lineCurrentPos) {
5820 if (currentPosPosOnLine == 0)
5821 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5822 else
5823 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
5824 } else {
5825 if (anchorPosOnLine == 0)
5826 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
5827 else
5828 sel.Range(r) = SelectionRange(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
5834 class CaseFolderASCII : public CaseFolderTable {
5835 public:
5836 CaseFolderASCII() {
5837 StandardASCII();
5839 ~CaseFolderASCII() {
5844 CaseFolder *Editor::CaseFolderForEncoding() {
5845 // Simple default that only maps ASCII upper case to lower case.
5846 return new CaseFolderASCII();
5850 * Search of a text in the document, in the given range.
5851 * @return The position of the found text, -1 if not found.
5853 long Editor::FindText(
5854 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5855 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5856 sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
5858 Sci_TextToFind *ft = reinterpret_cast<Sci_TextToFind *>(lParam);
5859 int lengthFound = istrlen(ft->lpstrText);
5860 if (!pdoc->HasCaseFolder())
5861 pdoc->SetCaseFolder(CaseFolderForEncoding());
5862 int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
5863 (wParam & SCFIND_MATCHCASE) != 0,
5864 (wParam & SCFIND_WHOLEWORD) != 0,
5865 (wParam & SCFIND_WORDSTART) != 0,
5866 (wParam & SCFIND_REGEXP) != 0,
5867 wParam,
5868 &lengthFound);
5869 if (pos != -1) {
5870 ft->chrgText.cpMin = pos;
5871 ft->chrgText.cpMax = pos + lengthFound;
5873 return pos;
5877 * Relocatable search support : Searches relative to current selection
5878 * point and sets the selection to the found text range with
5879 * each search.
5882 * Anchor following searches at current selection start: This allows
5883 * multiple incremental interactive searches to be macro recorded
5884 * while still setting the selection to found text so the find/select
5885 * operation is self-contained.
5887 void Editor::SearchAnchor() {
5888 searchAnchor = SelectionStart().Position();
5892 * Find text from current search anchor: Must call @c SearchAnchor first.
5893 * Used for next text and previous text requests.
5894 * @return The position of the found text, -1 if not found.
5896 long Editor::SearchText(
5897 unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
5898 uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
5899 ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
5900 sptr_t lParam) { ///< The text to search for.
5902 const char *txt = reinterpret_cast<char *>(lParam);
5903 int pos;
5904 int lengthFound = istrlen(txt);
5905 if (!pdoc->HasCaseFolder())
5906 pdoc->SetCaseFolder(CaseFolderForEncoding());
5907 if (iMessage == SCI_SEARCHNEXT) {
5908 pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
5909 (wParam & SCFIND_MATCHCASE) != 0,
5910 (wParam & SCFIND_WHOLEWORD) != 0,
5911 (wParam & SCFIND_WORDSTART) != 0,
5912 (wParam & SCFIND_REGEXP) != 0,
5913 wParam,
5914 &lengthFound);
5915 } else {
5916 pos = pdoc->FindText(searchAnchor, 0, txt,
5917 (wParam & SCFIND_MATCHCASE) != 0,
5918 (wParam & SCFIND_WHOLEWORD) != 0,
5919 (wParam & SCFIND_WORDSTART) != 0,
5920 (wParam & SCFIND_REGEXP) != 0,
5921 wParam,
5922 &lengthFound);
5924 if (pos != -1) {
5925 SetSelection(pos, pos + lengthFound);
5928 return pos;
5931 std::string Editor::CaseMapString(const std::string &s, int caseMapping) {
5932 std::string ret(s);
5933 for (size_t i=0; i<ret.size(); i++) {
5934 switch (caseMapping) {
5935 case cmUpper:
5936 if (ret[i] >= 'a' && ret[i] <= 'z')
5937 ret[i] = static_cast<char>(ret[i] - 'a' + 'A');
5938 break;
5939 case cmLower:
5940 if (ret[i] >= 'A' && ret[i] <= 'Z')
5941 ret[i] = static_cast<char>(ret[i] - 'A' + 'a');
5942 break;
5945 return ret;
5949 * Search for text in the target range of the document.
5950 * @return The position of the found text, -1 if not found.
5952 long Editor::SearchInTarget(const char *text, int length) {
5953 int lengthFound = length;
5955 if (!pdoc->HasCaseFolder())
5956 pdoc->SetCaseFolder(CaseFolderForEncoding());
5957 int pos = pdoc->FindText(targetStart, targetEnd, text,
5958 (searchFlags & SCFIND_MATCHCASE) != 0,
5959 (searchFlags & SCFIND_WHOLEWORD) != 0,
5960 (searchFlags & SCFIND_WORDSTART) != 0,
5961 (searchFlags & SCFIND_REGEXP) != 0,
5962 searchFlags,
5963 &lengthFound);
5964 if (pos != -1) {
5965 targetStart = pos;
5966 targetEnd = pos + lengthFound;
5968 return pos;
5971 void Editor::GoToLine(int lineNo) {
5972 if (lineNo > pdoc->LinesTotal())
5973 lineNo = pdoc->LinesTotal();
5974 if (lineNo < 0)
5975 lineNo = 0;
5976 SetEmptySelection(pdoc->LineStart(lineNo));
5977 ShowCaretAtCurrentPosition();
5978 EnsureCaretVisible();
5981 static bool Close(Point pt1, Point pt2) {
5982 if (abs(pt1.x - pt2.x) > 3)
5983 return false;
5984 if (abs(pt1.y - pt2.y) > 3)
5985 return false;
5986 return true;
5989 char *Editor::CopyRange(int start, int end) {
5990 char *text = 0;
5991 if (start < end) {
5992 int len = end - start;
5993 text = new char[len + 1];
5994 for (int i = 0; i < len; i++) {
5995 text[i] = pdoc->CharAt(start + i);
5997 text[len] = '\0';
5999 return text;
6002 std::string Editor::RangeText(int start, int end) const {
6003 if (start < end) {
6004 int len = end - start;
6005 std::string ret(len, '\0');
6006 for (int i = 0; i < len; i++) {
6007 ret[i] = pdoc->CharAt(start + i);
6009 return ret;
6011 return std::string();
6014 void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) {
6015 if (sel.Empty()) {
6016 if (allowLineCopy) {
6017 int currentLine = pdoc->LineFromPosition(sel.MainCaret());
6018 int start = pdoc->LineStart(currentLine);
6019 int end = pdoc->LineEnd(currentLine);
6021 char *text = CopyRange(start, end);
6022 size_t textLen = text ? strlen(text) : 0;
6023 // include room for \r\n\0
6024 textLen += 3;
6025 char *textWithEndl = new char[textLen];
6026 textWithEndl[0] = '\0';
6027 if (text)
6028 strcat(textWithEndl, text);
6029 if (pdoc->eolMode != SC_EOL_LF)
6030 strcat(textWithEndl, "\r");
6031 if (pdoc->eolMode != SC_EOL_CR)
6032 strcat(textWithEndl, "\n");
6033 ss->Set(textWithEndl, static_cast<int>(strlen(textWithEndl) + 1),
6034 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, true);
6035 delete []text;
6037 } else {
6038 int delimiterLength = 0;
6039 if (sel.selType == Selection::selRectangle) {
6040 if (pdoc->eolMode == SC_EOL_CRLF) {
6041 delimiterLength = 2;
6042 } else {
6043 delimiterLength = 1;
6046 size_t size = sel.Length() + delimiterLength * sel.Count();
6047 char *text = new char[size + 1];
6048 int j = 0;
6049 std::vector<SelectionRange> rangesInOrder = sel.RangesCopy();
6050 if (sel.selType == Selection::selRectangle)
6051 std::sort(rangesInOrder.begin(), rangesInOrder.end());
6052 for (size_t r=0; r<rangesInOrder.size(); r++) {
6053 SelectionRange current = rangesInOrder[r];
6054 for (int i = current.Start().Position();
6055 i < current.End().Position();
6056 i++) {
6057 text[j++] = pdoc->CharAt(i);
6059 if (sel.selType == Selection::selRectangle) {
6060 if (pdoc->eolMode != SC_EOL_LF) {
6061 text[j++] = '\r';
6063 if (pdoc->eolMode != SC_EOL_CR) {
6064 text[j++] = '\n';
6068 text[size] = '\0';
6069 ss->Set(text, static_cast<int>(size + 1), pdoc->dbcsCodePage,
6070 vs.styles[STYLE_DEFAULT].characterSet, sel.IsRectangular(), sel.selType == Selection::selLines);
6074 void Editor::CopyRangeToClipboard(int start, int end) {
6075 start = pdoc->ClampPositionIntoDocument(start);
6076 end = pdoc->ClampPositionIntoDocument(end);
6077 SelectionText selectedText;
6078 selectedText.Set(CopyRange(start, end), end - start + 1,
6079 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6080 CopyToClipboard(selectedText);
6083 void Editor::CopyText(int length, const char *text) {
6084 SelectionText selectedText;
6085 selectedText.Copy(text, length + 1,
6086 pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false, false);
6087 CopyToClipboard(selectedText);
6090 void Editor::SetDragPosition(SelectionPosition newPos) {
6091 if (newPos.Position() >= 0) {
6092 newPos = MovePositionOutsideChar(newPos, 1);
6093 posDrop = newPos;
6095 if (!(posDrag == newPos)) {
6096 caret.on = true;
6097 SetTicking(true);
6098 InvalidateCaret();
6099 posDrag = newPos;
6100 InvalidateCaret();
6104 void Editor::DisplayCursor(Window::Cursor c) {
6105 if (cursorMode == SC_CURSORNORMAL)
6106 wMain.SetCursor(c);
6107 else
6108 wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
6111 bool Editor::DragThreshold(Point ptStart, Point ptNow) {
6112 int xMove = ptStart.x - ptNow.x;
6113 int yMove = ptStart.y - ptNow.y;
6114 int distanceSquared = xMove * xMove + yMove * yMove;
6115 return distanceSquared > 16;
6118 void Editor::StartDrag() {
6119 // Always handled by subclasses
6120 //SetMouseCapture(true);
6121 //DisplayCursor(Window::cursorArrow);
6124 void Editor::DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular) {
6125 //Platform::DebugPrintf("DropAt %d %d\n", inDragDrop, position);
6126 if (inDragDrop == ddDragging)
6127 dropWentOutside = false;
6129 bool positionWasInSelection = PositionInSelection(position.Position());
6131 bool positionOnEdgeOfSelection =
6132 (position == SelectionStart()) || (position == SelectionEnd());
6134 if ((inDragDrop != ddDragging) || !(positionWasInSelection) ||
6135 (positionOnEdgeOfSelection && !moving)) {
6137 SelectionPosition selStart = SelectionStart();
6138 SelectionPosition selEnd = SelectionEnd();
6140 UndoGroup ug(pdoc);
6142 SelectionPosition positionAfterDeletion = position;
6143 if ((inDragDrop == ddDragging) && moving) {
6144 // Remove dragged out text
6145 if (rectangular || sel.selType == Selection::selLines) {
6146 for (size_t r=0; r<sel.Count(); r++) {
6147 if (position >= sel.Range(r).Start()) {
6148 if (position > sel.Range(r).End()) {
6149 positionAfterDeletion.Add(-sel.Range(r).Length());
6150 } else {
6151 positionAfterDeletion.Add(-SelectionRange(position, sel.Range(r).Start()).Length());
6155 } else {
6156 if (position > selStart) {
6157 positionAfterDeletion.Add(-SelectionRange(selEnd, selStart).Length());
6160 ClearSelection();
6162 position = positionAfterDeletion;
6164 if (rectangular) {
6165 PasteRectangular(position, value, istrlen(value));
6166 // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
6167 SetEmptySelection(position);
6168 } else {
6169 position = MovePositionOutsideChar(position, sel.MainCaret() - position.Position());
6170 position = SelectionPosition(InsertSpace(position.Position(), position.VirtualSpace()));
6171 if (pdoc->InsertCString(position.Position(), value)) {
6172 SelectionPosition posAfterInsertion = position;
6173 posAfterInsertion.Add(istrlen(value));
6174 SetSelection(posAfterInsertion, position);
6177 } else if (inDragDrop == ddDragging) {
6178 SetEmptySelection(position);
6183 * @return true if given position is inside the selection,
6185 bool Editor::PositionInSelection(int pos) {
6186 pos = MovePositionOutsideChar(pos, sel.MainCaret() - pos);
6187 for (size_t r=0; r<sel.Count(); r++) {
6188 if (sel.Range(r).Contains(pos))
6189 return true;
6191 return false;
6194 bool Editor::PointInSelection(Point pt) {
6195 SelectionPosition pos = SPositionFromLocation(pt, false, true);
6196 Point ptPos = LocationFromPosition(pos);
6197 for (size_t r=0; r<sel.Count(); r++) {
6198 SelectionRange range = sel.Range(r);
6199 if (range.Contains(pos)) {
6200 bool hit = true;
6201 if (pos == range.Start()) {
6202 // see if just before selection
6203 if (pt.x < ptPos.x) {
6204 hit = false;
6207 if (pos == range.End()) {
6208 // see if just after selection
6209 if (pt.x > ptPos.x) {
6210 hit = false;
6213 if (hit)
6214 return true;
6217 return false;
6220 bool Editor::PointInSelMargin(Point pt) {
6221 // Really means: "Point in a margin"
6222 if (vs.fixedColumnWidth > 0) { // There is a margin
6223 PRectangle rcSelMargin = GetClientRectangle();
6224 rcSelMargin.right = vs.textStart - vs.leftMarginWidth;
6225 rcSelMargin.left = vs.textStart - vs.fixedColumnWidth;
6226 return rcSelMargin.Contains(pt);
6227 } else {
6228 return false;
6232 Window::Cursor Editor::GetMarginCursor(Point pt) {
6233 int x = 0;
6234 for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
6235 if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
6236 return static_cast<Window::Cursor>(vs.ms[margin].cursor);
6237 x += vs.ms[margin].width;
6239 return Window::cursorReverseArrow;
6242 void Editor::TrimAndSetSelection(int currentPos_, int anchor_) {
6243 sel.TrimSelection(SelectionRange(currentPos_, anchor_));
6244 SetSelection(currentPos_, anchor_);
6247 void Editor::LineSelection(int lineCurrentPos_, int lineAnchorPos_, bool wholeLine) {
6248 int selCurrentPos, selAnchorPos;
6249 if (wholeLine) {
6250 int lineCurrent_ = pdoc->LineFromPosition(lineCurrentPos_);
6251 int lineAnchor_ = pdoc->LineFromPosition(lineAnchorPos_);
6252 if (lineAnchorPos_ < lineCurrentPos_) {
6253 selCurrentPos = pdoc->LineStart(lineCurrent_ + 1);
6254 selAnchorPos = pdoc->LineStart(lineAnchor_);
6255 } else if (lineAnchorPos_ > lineCurrentPos_) {
6256 selCurrentPos = pdoc->LineStart(lineCurrent_);
6257 selAnchorPos = pdoc->LineStart(lineAnchor_ + 1);
6258 } else { // Same line, select it
6259 selCurrentPos = pdoc->LineStart(lineAnchor_ + 1);
6260 selAnchorPos = pdoc->LineStart(lineAnchor_);
6262 } else {
6263 if (lineAnchorPos_ < lineCurrentPos_) {
6264 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, false) + 1;
6265 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6266 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6267 } else if (lineAnchorPos_ > lineCurrentPos_) {
6268 selCurrentPos = StartEndDisplayLine(lineCurrentPos_, true);
6269 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6270 selAnchorPos = pdoc->MovePositionOutsideChar(selAnchorPos, 1);
6271 } else { // Same line, select it
6272 selCurrentPos = StartEndDisplayLine(lineAnchorPos_, false) + 1;
6273 selCurrentPos = pdoc->MovePositionOutsideChar(selCurrentPos, 1);
6274 selAnchorPos = StartEndDisplayLine(lineAnchorPos_, true);
6277 TrimAndSetSelection(selCurrentPos, selAnchorPos);
6280 void Editor::WordSelection(int pos) {
6281 if (pos < wordSelectAnchorStartPos) {
6282 // Extend backward to the word containing pos.
6283 // Skip ExtendWordSelect if the line is empty or if pos is after the last character.
6284 // This ensures that a series of empty lines isn't counted as a single "word".
6285 if (!pdoc->IsLineEndPosition(pos))
6286 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos + 1, 1), -1);
6287 TrimAndSetSelection(pos, wordSelectAnchorEndPos);
6288 } else if (pos > wordSelectAnchorEndPos) {
6289 // Extend forward to the word containing the character to the left of pos.
6290 // Skip ExtendWordSelect if the line is empty or if pos is the first position on the line.
6291 // This ensures that a series of empty lines isn't counted as a single "word".
6292 if (pos > pdoc->LineStart(pdoc->LineFromPosition(pos)))
6293 pos = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(pos - 1, -1), 1);
6294 TrimAndSetSelection(pos, wordSelectAnchorStartPos);
6295 } else {
6296 // Select only the anchored word
6297 if (pos >= originalAnchorPos)
6298 TrimAndSetSelection(wordSelectAnchorEndPos, wordSelectAnchorStartPos);
6299 else
6300 TrimAndSetSelection(wordSelectAnchorStartPos, wordSelectAnchorEndPos);
6304 void Editor::DwellEnd(bool mouseMoved) {
6305 if (mouseMoved)
6306 ticksToDwell = dwellDelay;
6307 else
6308 ticksToDwell = SC_TIME_FOREVER;
6309 if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
6310 dwelling = false;
6311 NotifyDwelling(ptMouseLast, dwelling);
6315 void Editor::MouseLeave() {
6316 SetHotSpotRange(NULL);
6317 if (!HaveMouseCapture()) {
6318 ptMouseLast = Point(-1,-1);
6319 DwellEnd(true);
6323 static bool AllowVirtualSpace(int virtualSpaceOptions, bool rectangular) {
6324 return (!rectangular && ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0))
6325 || (rectangular && ((virtualSpaceOptions & SCVS_RECTANGULARSELECTION) != 0));
6328 void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
6329 //Platform::DebugPrintf("ButtonDown %d %d = %d alt=%d %d\n", curTime, lastClickTime, curTime - lastClickTime, alt, inDragDrop);
6330 ptMouseLast = pt;
6331 SelectionPosition newPos = SPositionFromLocation(pt, false, false, AllowVirtualSpace(virtualSpaceOptions, alt));
6332 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6333 inDragDrop = ddNone;
6334 sel.SetMoveExtends(false);
6336 if (NotifyMarginClick(pt, shift, ctrl, alt))
6337 return;
6339 NotifyIndicatorClick(true, newPos.Position(), shift, ctrl, alt);
6341 bool inSelMargin = PointInSelMargin(pt);
6342 // In margin ctrl+(double)click should always select everything
6343 if (ctrl && inSelMargin) {
6344 SelectAll();
6345 lastClickTime = curTime;
6346 lastClick = pt;
6347 return;
6349 if (shift && !inSelMargin) {
6350 SetSelection(newPos);
6352 if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
6353 //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
6354 SetMouseCapture(true);
6355 if (!ctrl || !multipleSelection || (selectionType != selChar && selectionType != selWord))
6356 SetEmptySelection(newPos.Position());
6357 bool doubleClick = false;
6358 // Stop mouse button bounce changing selection type
6359 if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
6360 if (inSelMargin) {
6361 // Inside margin selection type should be either selSubLine or selWholeLine.
6362 if (selectionType == selSubLine) {
6363 // If it is selSubLine, we're inside a *double* click and word wrap is enabled,
6364 // so we switch to selWholeLine in order to select whole line.
6365 selectionType = selWholeLine;
6366 } else if (selectionType != selSubLine && selectionType != selWholeLine) {
6367 // If it is neither, reset selection type to line selection.
6368 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6370 } else {
6371 if (selectionType == selChar) {
6372 selectionType = selWord;
6373 doubleClick = true;
6374 } else if (selectionType == selWord) {
6375 // Since we ended up here, we're inside a *triple* click, which should always select
6376 // whole line irregardless of word wrap being enabled or not.
6377 selectionType = selWholeLine;
6378 } else {
6379 selectionType = selChar;
6380 originalAnchorPos = sel.MainCaret();
6385 if (selectionType == selWord) {
6386 int charPos = originalAnchorPos;
6387 if (sel.MainCaret() == originalAnchorPos) {
6388 charPos = PositionFromLocation(pt, false, true);
6389 charPos = MovePositionOutsideChar(charPos, -1);
6392 int startWord, endWord;
6393 if ((sel.MainCaret() >= originalAnchorPos) && !pdoc->IsLineEndPosition(charPos)) {
6394 startWord = pdoc->ExtendWordSelect(pdoc->MovePositionOutsideChar(charPos + 1, 1), -1);
6395 endWord = pdoc->ExtendWordSelect(charPos, 1);
6396 } else {
6397 // Selecting backwards, or anchor beyond last character on line. In these cases,
6398 // we select the word containing the character to the *left* of the anchor.
6399 if (charPos > pdoc->LineStart(pdoc->LineFromPosition(charPos))) {
6400 startWord = pdoc->ExtendWordSelect(charPos, -1);
6401 endWord = pdoc->ExtendWordSelect(startWord, 1);
6402 } else {
6403 // Anchor at start of line; select nothing to begin with.
6404 startWord = charPos;
6405 endWord = charPos;
6409 wordSelectAnchorStartPos = startWord;
6410 wordSelectAnchorEndPos = endWord;
6411 wordSelectInitialCaretPos = sel.MainCaret();
6412 WordSelection(wordSelectInitialCaretPos);
6413 } else if (selectionType == selSubLine || selectionType == selWholeLine) {
6414 lineAnchorPos = newPos.Position();
6415 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6416 //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
6417 } else {
6418 SetEmptySelection(sel.MainCaret());
6420 //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
6421 if (doubleClick) {
6422 NotifyDoubleClick(pt, shift, ctrl, alt);
6423 if (PositionIsHotspot(newPos.Position()))
6424 NotifyHotSpotDoubleClicked(newPos.Position(), shift, ctrl, alt);
6426 } else { // Single click
6427 if (inSelMargin) {
6428 sel.selType = Selection::selStream;
6429 if (!shift) {
6430 // Single click in margin: select whole line or only subline if word wrap is enabled
6431 lineAnchorPos = newPos.Position();
6432 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6433 LineSelection(lineAnchorPos, lineAnchorPos, selectionType == selWholeLine);
6434 } else {
6435 // Single shift+click in margin: select from line anchor to clicked line
6436 if (sel.MainAnchor() > sel.MainCaret())
6437 lineAnchorPos = sel.MainAnchor() - 1;
6438 else
6439 lineAnchorPos = sel.MainAnchor();
6440 // Reset selection type if there is an empty selection.
6441 // This ensures that we don't end up stuck in previous selection mode, which is no longer valid.
6442 // Otherwise, if there's a non empty selection, reset selection type only if it differs from selSubLine and selWholeLine.
6443 // This ensures that we continue selecting in the same selection mode.
6444 if (sel.Empty() || (selectionType != selSubLine && selectionType != selWholeLine))
6445 selectionType = ((wrapState != eWrapNone) && (marginOptions & SC_MARGINOPTION_SUBLINESELECT)) ? selSubLine : selWholeLine;
6446 LineSelection(newPos.Position(), lineAnchorPos, selectionType == selWholeLine);
6449 SetDragPosition(SelectionPosition(invalidPosition));
6450 SetMouseCapture(true);
6451 } else {
6452 if (PointIsHotspot(pt)) {
6453 NotifyHotSpotClicked(newPos.Position(), shift, ctrl, alt);
6454 hotSpotClickPos = PositionFromLocation(pt,true,false);
6456 if (!shift) {
6457 if (PointInSelection(pt) && !SelectionEmpty())
6458 inDragDrop = ddInitial;
6459 else
6460 inDragDrop = ddNone;
6462 SetMouseCapture(true);
6463 if (inDragDrop != ddInitial) {
6464 SetDragPosition(SelectionPosition(invalidPosition));
6465 if (!shift) {
6466 if (ctrl && multipleSelection) {
6467 SelectionRange range(newPos);
6468 sel.TentativeSelection(range);
6469 InvalidateSelection(range, true);
6470 } else {
6471 InvalidateSelection(SelectionRange(newPos), true);
6472 if (sel.Count() > 1)
6473 Redraw();
6474 if ((sel.Count() > 1) || (sel.selType != Selection::selStream))
6475 sel.Clear();
6476 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6477 SetSelection(newPos, newPos);
6480 SelectionPosition anchorCurrent = newPos;
6481 if (shift)
6482 anchorCurrent = sel.IsRectangular() ?
6483 sel.Rectangular().anchor : sel.RangeMain().anchor;
6484 sel.selType = alt ? Selection::selRectangle : Selection::selStream;
6485 selectionType = selChar;
6486 originalAnchorPos = sel.MainCaret();
6487 sel.Rectangular() = SelectionRange(newPos, anchorCurrent);
6488 SetRectangularRange();
6492 lastClickTime = curTime;
6493 lastClick = pt;
6494 lastXChosen = pt.x + xOffset;
6495 ShowCaretAtCurrentPosition();
6498 bool Editor::PositionIsHotspot(int position) {
6499 return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
6502 bool Editor::PointIsHotspot(Point pt) {
6503 int pos = PositionFromLocation(pt, true);
6504 if (pos == INVALID_POSITION)
6505 return false;
6506 return PositionIsHotspot(pos);
6509 void Editor::SetHotSpotRange(Point *pt) {
6510 if (pt) {
6511 int pos = PositionFromLocation(*pt);
6513 // If we don't limit this to word characters then the
6514 // range can encompass more than the run range and then
6515 // the underline will not be drawn properly.
6516 int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
6517 int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
6519 // Only invalidate the range if the hotspot range has changed...
6520 if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
6521 if (hsStart != -1) {
6522 InvalidateRange(hsStart, hsEnd);
6524 hsStart = hsStart_;
6525 hsEnd = hsEnd_;
6526 InvalidateRange(hsStart, hsEnd);
6528 } else {
6529 if (hsStart != -1) {
6530 int hsStart_ = hsStart;
6531 int hsEnd_ = hsEnd;
6532 hsStart = -1;
6533 hsEnd = -1;
6534 InvalidateRange(hsStart_, hsEnd_);
6535 } else {
6536 hsStart = -1;
6537 hsEnd = -1;
6542 void Editor::GetHotSpotRange(int &hsStart_, int &hsEnd_) {
6543 hsStart_ = hsStart;
6544 hsEnd_ = hsEnd;
6547 void Editor::ButtonMove(Point pt) {
6548 if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
6549 DwellEnd(true);
6552 SelectionPosition movePos = SPositionFromLocation(pt, false, false,
6553 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6554 movePos = MovePositionOutsideChar(movePos, sel.MainCaret() - movePos.Position());
6556 if (inDragDrop == ddInitial) {
6557 if (DragThreshold(ptMouseLast, pt)) {
6558 SetMouseCapture(false);
6559 SetDragPosition(movePos);
6560 CopySelectionRange(&drag);
6561 StartDrag();
6563 return;
6566 ptMouseLast = pt;
6567 //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
6568 if (HaveMouseCapture()) {
6570 // Slow down autoscrolling/selection
6571 autoScrollTimer.ticksToWait -= timer.tickSize;
6572 if (autoScrollTimer.ticksToWait > 0)
6573 return;
6574 autoScrollTimer.ticksToWait = autoScrollDelay;
6576 // Adjust selection
6577 if (posDrag.IsValid()) {
6578 SetDragPosition(movePos);
6579 } else {
6580 if (selectionType == selChar) {
6581 if (sel.IsRectangular()) {
6582 sel.Rectangular() = SelectionRange(movePos, sel.Rectangular().anchor);
6583 SetSelection(movePos, sel.RangeMain().anchor);
6584 } else if (sel.Count() > 1) {
6585 SelectionRange range(movePos, sel.RangeMain().anchor);
6586 sel.TentativeSelection(range);
6587 InvalidateSelection(range, true);
6588 } else {
6589 SetSelection(movePos, sel.RangeMain().anchor);
6591 } else if (selectionType == selWord) {
6592 // Continue selecting by word
6593 if (movePos.Position() == wordSelectInitialCaretPos) { // Didn't move
6594 // No need to do anything. Previously this case was lumped
6595 // in with "Moved forward", but that can be harmful in this
6596 // case: a handler for the NotifyDoubleClick re-adjusts
6597 // the selection for a fancier definition of "word" (for
6598 // example, in Perl it is useful to include the leading
6599 // '$', '%' or '@' on variables for word selection). In this
6600 // the ButtonMove() called via Tick() for auto-scrolling
6601 // could result in the fancier word selection adjustment
6602 // being unmade.
6603 } else {
6604 wordSelectInitialCaretPos = -1;
6605 WordSelection(movePos.Position());
6607 } else {
6608 // Continue selecting by line
6609 LineSelection(movePos.Position(), lineAnchorPos, selectionType == selWholeLine);
6613 // Autoscroll
6614 PRectangle rcClient = GetClientRectangle();
6615 Point ptOrigin = GetVisibleOriginInMain();
6616 rcClient.Move(0, -ptOrigin.y);
6617 int lineMove = DisplayFromPosition(movePos.Position());
6618 if (pt.y > rcClient.bottom) {
6619 ScrollTo(lineMove - LinesOnScreen() + 1);
6620 Redraw();
6621 } else if (pt.y < rcClient.top) {
6622 ScrollTo(lineMove);
6623 Redraw();
6625 EnsureCaretVisible(false, false, true);
6627 if (hsStart != -1 && !PositionIsHotspot(movePos.Position()))
6628 SetHotSpotRange(NULL);
6630 if (hotSpotClickPos != INVALID_POSITION && PositionFromLocation(pt,true,false) != hotSpotClickPos) {
6631 if (inDragDrop == ddNone) {
6632 DisplayCursor(Window::cursorText);
6634 hotSpotClickPos = INVALID_POSITION;
6637 } else {
6638 if (vs.fixedColumnWidth > 0) { // There is a margin
6639 if (PointInSelMargin(pt)) {
6640 DisplayCursor(GetMarginCursor(pt));
6641 SetHotSpotRange(NULL);
6642 return; // No need to test for selection
6645 // Display regular (drag) cursor over selection
6646 if (PointInSelection(pt) && !SelectionEmpty()) {
6647 DisplayCursor(Window::cursorArrow);
6648 } else if (PointIsHotspot(pt)) {
6649 DisplayCursor(Window::cursorHand);
6650 SetHotSpotRange(&pt);
6651 } else {
6652 DisplayCursor(Window::cursorText);
6653 SetHotSpotRange(NULL);
6658 void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
6659 //Platform::DebugPrintf("ButtonUp %d %d\n", HaveMouseCapture(), inDragDrop);
6660 SelectionPosition newPos = SPositionFromLocation(pt, false, false,
6661 AllowVirtualSpace(virtualSpaceOptions, sel.IsRectangular()));
6662 newPos = MovePositionOutsideChar(newPos, sel.MainCaret() - newPos.Position());
6663 if (inDragDrop == ddInitial) {
6664 inDragDrop = ddNone;
6665 SetEmptySelection(newPos);
6666 selectionType = selChar;
6667 originalAnchorPos = sel.MainCaret();
6669 if (hotSpotClickPos != INVALID_POSITION && PointIsHotspot(pt)) {
6670 hotSpotClickPos = INVALID_POSITION;
6671 NotifyHotSpotReleaseClick(newPos.Position(), false, ctrl, false);
6673 if (HaveMouseCapture()) {
6674 if (PointInSelMargin(pt)) {
6675 DisplayCursor(GetMarginCursor(pt));
6676 } else {
6677 DisplayCursor(Window::cursorText);
6678 SetHotSpotRange(NULL);
6680 ptMouseLast = pt;
6681 SetMouseCapture(false);
6682 NotifyIndicatorClick(false, newPos.Position(), false, false, false);
6683 if (inDragDrop == ddDragging) {
6684 SelectionPosition selStart = SelectionStart();
6685 SelectionPosition selEnd = SelectionEnd();
6686 if (selStart < selEnd) {
6687 if (drag.len) {
6688 if (ctrl) {
6689 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6690 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6692 } else if (newPos < selStart) {
6693 pdoc->DeleteChars(selStart.Position(), drag.len);
6694 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6695 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6697 } else if (newPos > selEnd) {
6698 pdoc->DeleteChars(selStart.Position(), drag.len);
6699 newPos.Add(-drag.len);
6700 if (pdoc->InsertString(newPos.Position(), drag.s, drag.len)) {
6701 SetSelection(newPos.Position(), newPos.Position() + drag.len);
6703 } else {
6704 SetEmptySelection(newPos.Position());
6706 drag.Free();
6708 selectionType = selChar;
6710 } else {
6711 if (selectionType == selChar) {
6712 if (sel.Count() > 1) {
6713 sel.RangeMain() =
6714 SelectionRange(newPos, sel.Range(sel.Count() - 1).anchor);
6715 InvalidateSelection(sel.RangeMain(), true);
6716 } else {
6717 SetSelection(newPos, sel.RangeMain().anchor);
6720 sel.CommitTentative();
6722 SetRectangularRange();
6723 lastClickTime = curTime;
6724 lastClick = pt;
6725 lastXChosen = pt.x + xOffset;
6726 if (sel.selType == Selection::selStream) {
6727 SetLastXChosen();
6729 inDragDrop = ddNone;
6730 EnsureCaretVisible(false);
6734 // Called frequently to perform background UI including
6735 // caret blinking and automatic scrolling.
6736 void Editor::Tick() {
6737 if (HaveMouseCapture()) {
6738 // Auto scroll
6739 ButtonMove(ptMouseLast);
6741 if (caret.period > 0) {
6742 timer.ticksToWait -= timer.tickSize;
6743 if (timer.ticksToWait <= 0) {
6744 caret.on = !caret.on;
6745 timer.ticksToWait = caret.period;
6746 if (caret.active) {
6747 InvalidateCaret();
6751 if (horizontalScrollBarVisible && trackLineWidth && (lineWidthMaxSeen > scrollWidth)) {
6752 scrollWidth = lineWidthMaxSeen;
6753 SetScrollBars();
6755 if ((dwellDelay < SC_TIME_FOREVER) &&
6756 (ticksToDwell > 0) &&
6757 (!HaveMouseCapture()) &&
6758 (ptMouseLast.y >= 0)) {
6759 ticksToDwell -= timer.tickSize;
6760 if (ticksToDwell <= 0) {
6761 dwelling = true;
6762 NotifyDwelling(ptMouseLast, dwelling);
6767 bool Editor::Idle() {
6769 bool idleDone;
6771 bool wrappingDone = wrapState == eWrapNone;
6773 if (!wrappingDone) {
6774 // Wrap lines during idle.
6775 WrapLines(false, -1);
6776 // No more wrapping
6777 if (wrapStart == wrapEnd)
6778 wrappingDone = true;
6781 // Add more idle things to do here, but make sure idleDone is
6782 // set correctly before the function returns. returning
6783 // false will stop calling this idle funtion until SetIdle() is
6784 // called again.
6786 idleDone = wrappingDone; // && thatDone && theOtherThingDone...
6788 return !idleDone;
6791 void Editor::SetFocusState(bool focusState) {
6792 hasFocus = focusState;
6793 NotifyFocus(hasFocus);
6794 if (hasFocus) {
6795 ShowCaretAtCurrentPosition();
6796 } else {
6797 CancelModes();
6798 DropCaret();
6802 int Editor::PositionAfterArea(PRectangle rcArea) {
6803 // The start of the document line after the display line after the area
6804 // This often means that the line after a modification is restyled which helps
6805 // detect multiline comment additions and heals single line comments
6806 int lineAfter = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
6807 if (lineAfter < cs.LinesDisplayed())
6808 return pdoc->LineStart(cs.DocFromDisplay(lineAfter) + 1);
6809 else
6810 return pdoc->Length();
6813 // Style to a position within the view. If this causes a change at end of last line then
6814 // affects later lines so style all the viewed text.
6815 void Editor::StyleToPositionInView(Position pos) {
6816 int endWindow = (vs.marginInside) ? (PositionAfterArea(GetClientRectangle())) : (pdoc->Length());
6817 if (pos > endWindow)
6818 pos = endWindow;
6819 int styleAtEnd = pdoc->StyleAt(pos-1);
6820 pdoc->EnsureStyledTo(pos);
6821 if ((endWindow > pos) && (styleAtEnd != pdoc->StyleAt(pos-1))) {
6822 // Style at end of line changed so is multi-line change like starting a comment
6823 // so require rest of window to be styled.
6824 pdoc->EnsureStyledTo(endWindow);
6828 void Editor::IdleWork() {
6829 // Style the line after the modification as this allows modifications that change just the
6830 // line of the modification to heal instead of propagating to the rest of the window.
6831 if (workNeeded.items & WorkNeeded::workStyle)
6832 StyleToPositionInView(pdoc->LineStart(pdoc->LineFromPosition(workNeeded.upTo) + 2));
6834 NotifyUpdateUI();
6835 workNeeded.Reset();
6838 void Editor::QueueIdleWork(WorkNeeded::workItems items, int upTo) {
6839 workNeeded.Need(items, upTo);
6842 bool Editor::PaintContains(PRectangle rc) {
6843 if (rc.Empty()) {
6844 return true;
6845 } else {
6846 return rcPaint.Contains(rc);
6850 bool Editor::PaintContainsMargin() {
6851 if (wMargin.GetID()) {
6852 // With separate margin view, paint of text view
6853 // never contains margin.
6854 return false;
6856 PRectangle rcSelMargin = GetClientRectangle();
6857 rcSelMargin.right = vs.textStart;
6858 return PaintContains(rcSelMargin);
6861 void Editor::CheckForChangeOutsidePaint(Range r) {
6862 if (paintState == painting && !paintingAllText) {
6863 //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
6864 if (!r.Valid())
6865 return;
6867 PRectangle rcRange = RectangleFromRange(r.start, r.end);
6868 PRectangle rcText = GetTextRectangle();
6869 if (rcRange.top < rcText.top) {
6870 rcRange.top = rcText.top;
6872 if (rcRange.bottom > rcText.bottom) {
6873 rcRange.bottom = rcText.bottom;
6876 if (!PaintContains(rcRange)) {
6877 AbandonPaint();
6882 void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
6883 if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
6884 if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
6885 CheckForChangeOutsidePaint(Range(braces[0]));
6886 CheckForChangeOutsidePaint(Range(pos0));
6887 braces[0] = pos0;
6889 if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
6890 CheckForChangeOutsidePaint(Range(braces[1]));
6891 CheckForChangeOutsidePaint(Range(pos1));
6892 braces[1] = pos1;
6894 bracesMatchStyle = matchStyle;
6895 if (paintState == notPainting) {
6896 Redraw();
6901 void Editor::SetAnnotationHeights(int start, int end) {
6902 if (vs.annotationVisible) {
6903 bool changedHeight = false;
6904 for (int line=start; line<end && line<pdoc->LinesTotal(); line++) {
6905 int linesWrapped = 1;
6906 if (wrapState != eWrapNone) {
6907 AutoSurface surface(this);
6908 AutoLineLayout ll(llc, RetrieveLineLayout(line));
6909 if (surface && ll) {
6910 LayoutLine(line, surface, vs, ll, wrapWidth);
6911 linesWrapped = ll->lines;
6914 if (cs.SetHeight(line, pdoc->AnnotationLines(line) + linesWrapped))
6915 changedHeight = true;
6917 if (changedHeight) {
6918 Redraw();
6923 void Editor::SetDocPointer(Document *document) {
6924 //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
6925 pdoc->RemoveWatcher(this, 0);
6926 pdoc->Release();
6927 if (document == NULL) {
6928 pdoc = new Document();
6929 } else {
6930 pdoc = document;
6932 pdoc->AddRef();
6934 // Ensure all positions within document
6935 sel.Clear();
6936 targetStart = 0;
6937 targetEnd = 0;
6939 braces[0] = invalidPosition;
6940 braces[1] = invalidPosition;
6942 vs.ReleaseAllExtendedStyles();
6944 // Reset the contraction state to fully shown.
6945 cs.Clear();
6946 cs.InsertLines(0, pdoc->LinesTotal() - 1);
6947 SetAnnotationHeights(0, pdoc->LinesTotal());
6948 llc.Deallocate();
6949 NeedWrapping();
6951 pdoc->AddWatcher(this, 0);
6952 SetScrollBars();
6953 Redraw();
6956 void Editor::SetAnnotationVisible(int visible) {
6957 if (vs.annotationVisible != visible) {
6958 bool changedFromOrToHidden = ((vs.annotationVisible != 0) != (visible != 0));
6959 vs.annotationVisible = visible;
6960 if (changedFromOrToHidden) {
6961 int dir = vs.annotationVisible ? 1 : -1;
6962 for (int line=0; line<pdoc->LinesTotal(); line++) {
6963 int annotationLines = pdoc->AnnotationLines(line);
6964 if (annotationLines > 0) {
6965 cs.SetHeight(line, cs.GetHeight(line) + annotationLines * dir);
6969 Redraw();
6974 * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
6976 void Editor::Expand(int &line, bool doExpand) {
6977 int lineMaxSubord = pdoc->GetLastChild(line);
6978 line++;
6979 while (line <= lineMaxSubord) {
6980 if (doExpand)
6981 cs.SetVisible(line, line, true);
6982 int level = pdoc->GetLevel(line);
6983 if (level & SC_FOLDLEVELHEADERFLAG) {
6984 if (doExpand && cs.GetExpanded(line)) {
6985 Expand(line, true);
6986 } else {
6987 Expand(line, false);
6989 } else {
6990 line++;
6995 void Editor::ToggleContraction(int line) {
6996 if (line >= 0) {
6997 if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
6998 line = pdoc->GetFoldParent(line);
6999 if (line < 0)
7000 return;
7003 if (cs.GetExpanded(line)) {
7004 int lineMaxSubord = pdoc->GetLastChild(line);
7005 if (lineMaxSubord > line) {
7006 cs.SetExpanded(line, 0);
7007 cs.SetVisible(line + 1, lineMaxSubord, false);
7009 int lineCurrent = pdoc->LineFromPosition(sel.MainCaret());
7010 if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
7011 // This does not re-expand the fold
7012 EnsureCaretVisible();
7015 SetScrollBars();
7016 Redraw();
7019 } else {
7020 if (!(cs.GetVisible(line))) {
7021 EnsureLineVisible(line, false);
7022 GoToLine(line);
7024 cs.SetExpanded(line, 1);
7025 Expand(line, true);
7026 SetScrollBars();
7027 Redraw();
7032 int Editor::ContractedFoldNext(int lineStart) {
7033 for (int line = lineStart; line<pdoc->LinesTotal();) {
7034 if (!cs.GetExpanded(line) && (pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG))
7035 return line;
7036 line = cs.ContractedNext(line+1);
7037 if (line < 0)
7038 return -1;
7041 return -1;
7045 * Recurse up from this line to find any folds that prevent this line from being visible
7046 * and unfold them all.
7048 void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
7050 // In case in need of wrapping to ensure DisplayFromDoc works.
7051 if (lineDoc >= wrapStart)
7052 WrapLines(true, -1);
7054 if (!cs.GetVisible(lineDoc)) {
7055 // Back up to find a non-blank line
7056 int lookLine = lineDoc;
7057 int lookLineLevel = pdoc->GetLevel(lookLine);
7058 while ((lookLine > 0) && (lookLineLevel & SC_FOLDLEVELWHITEFLAG)) {
7059 lookLineLevel = pdoc->GetLevel(--lookLine);
7061 int lineParent = pdoc->GetFoldParent(lookLine);
7062 if (lineParent < 0) {
7063 // Backed up to a top level line, so try to find parent of initial line
7064 lineParent = pdoc->GetFoldParent(lineDoc);
7066 if (lineParent >= 0) {
7067 if (lineDoc != lineParent)
7068 EnsureLineVisible(lineParent, enforcePolicy);
7069 if (!cs.GetExpanded(lineParent)) {
7070 cs.SetExpanded(lineParent, 1);
7071 Expand(lineParent, true);
7074 SetScrollBars();
7075 Redraw();
7077 if (enforcePolicy) {
7078 int lineDisplay = cs.DisplayFromDoc(lineDoc);
7079 if (visiblePolicy & VISIBLE_SLOP) {
7080 if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
7081 SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
7082 SetVerticalScrollPos();
7083 Redraw();
7084 } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
7085 ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
7086 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
7087 SetVerticalScrollPos();
7088 Redraw();
7090 } else {
7091 if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
7092 SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
7093 SetVerticalScrollPos();
7094 Redraw();
7100 int Editor::GetTag(char *tagValue, int tagNumber) {
7101 const char *text = 0;
7102 int length = 0;
7103 if ((tagNumber >= 1) && (tagNumber <= 9)) {
7104 char name[3] = "\\?";
7105 name[1] = static_cast<char>(tagNumber + '0');
7106 length = 2;
7107 text = pdoc->SubstituteByPosition(name, &length);
7109 if (tagValue) {
7110 if (text)
7111 memcpy(tagValue, text, length + 1);
7112 else
7113 *tagValue = '\0';
7115 return length;
7118 int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
7119 UndoGroup ug(pdoc);
7120 if (length == -1)
7121 length = istrlen(text);
7122 if (replacePatterns) {
7123 text = pdoc->SubstituteByPosition(text, &length);
7124 if (!text) {
7125 return 0;
7128 if (targetStart != targetEnd)
7129 pdoc->DeleteChars(targetStart, targetEnd - targetStart);
7130 targetEnd = targetStart;
7131 pdoc->InsertString(targetStart, text, length);
7132 targetEnd = targetStart + length;
7133 return length;
7136 bool Editor::IsUnicodeMode() const {
7137 return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
7140 int Editor::CodePage() const {
7141 if (pdoc)
7142 return pdoc->dbcsCodePage;
7143 else
7144 return 0;
7147 int Editor::WrapCount(int line) {
7148 AutoSurface surface(this);
7149 AutoLineLayout ll(llc, RetrieveLineLayout(line));
7151 if (surface && ll) {
7152 LayoutLine(line, surface, vs, ll, wrapWidth);
7153 return ll->lines;
7154 } else {
7155 return 1;
7159 void Editor::AddStyledText(char *buffer, int appendLength) {
7160 // The buffer consists of alternating character bytes and style bytes
7161 int textLength = appendLength / 2;
7162 char *text = new char[textLength];
7163 int i;
7164 for (i = 0; i < textLength; i++) {
7165 text[i] = buffer[i*2];
7167 pdoc->InsertString(CurrentPosition(), text, textLength);
7168 for (i = 0; i < textLength; i++) {
7169 text[i] = buffer[i*2+1];
7171 pdoc->StartStyling(CurrentPosition(), static_cast<char>(0xff));
7172 pdoc->SetStyles(textLength, text);
7173 delete []text;
7174 SetEmptySelection(sel.MainCaret() + textLength);
7177 static bool ValidMargin(unsigned long wParam) {
7178 return wParam <= SC_MAX_MARGIN;
7181 static char *CharPtrFromSPtr(sptr_t lParam) {
7182 return reinterpret_cast<char *>(lParam);
7185 void Editor::StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7186 vs.EnsureStyle(wParam);
7187 switch (iMessage) {
7188 case SCI_STYLESETFORE:
7189 vs.styles[wParam].fore = ColourDesired(lParam);
7190 break;
7191 case SCI_STYLESETBACK:
7192 vs.styles[wParam].back = ColourDesired(lParam);
7193 break;
7194 case SCI_STYLESETBOLD:
7195 vs.styles[wParam].weight = lParam != 0 ? SC_WEIGHT_BOLD : SC_WEIGHT_NORMAL;
7196 break;
7197 case SCI_STYLESETWEIGHT:
7198 vs.styles[wParam].weight = lParam;
7199 break;
7200 case SCI_STYLESETITALIC:
7201 vs.styles[wParam].italic = lParam != 0;
7202 break;
7203 case SCI_STYLESETEOLFILLED:
7204 vs.styles[wParam].eolFilled = lParam != 0;
7205 break;
7206 case SCI_STYLESETSIZE:
7207 vs.styles[wParam].size = lParam * SC_FONT_SIZE_MULTIPLIER;
7208 break;
7209 case SCI_STYLESETSIZEFRACTIONAL:
7210 vs.styles[wParam].size = lParam;
7211 break;
7212 case SCI_STYLESETFONT:
7213 if (lParam != 0) {
7214 vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
7216 break;
7217 case SCI_STYLESETUNDERLINE:
7218 vs.styles[wParam].underline = lParam != 0;
7219 break;
7220 case SCI_STYLESETCASE:
7221 vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
7222 break;
7223 case SCI_STYLESETCHARACTERSET:
7224 vs.styles[wParam].characterSet = lParam;
7225 pdoc->SetCaseFolder(NULL);
7226 break;
7227 case SCI_STYLESETVISIBLE:
7228 vs.styles[wParam].visible = lParam != 0;
7229 break;
7230 case SCI_STYLESETCHANGEABLE:
7231 vs.styles[wParam].changeable = lParam != 0;
7232 break;
7233 case SCI_STYLESETHOTSPOT:
7234 vs.styles[wParam].hotspot = lParam != 0;
7235 break;
7237 InvalidateStyleRedraw();
7240 sptr_t Editor::StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7241 vs.EnsureStyle(wParam);
7242 switch (iMessage) {
7243 case SCI_STYLEGETFORE:
7244 return vs.styles[wParam].fore.AsLong();
7245 case SCI_STYLEGETBACK:
7246 return vs.styles[wParam].back.AsLong();
7247 case SCI_STYLEGETBOLD:
7248 return vs.styles[wParam].weight > SC_WEIGHT_NORMAL;
7249 case SCI_STYLEGETWEIGHT:
7250 return vs.styles[wParam].weight;
7251 case SCI_STYLEGETITALIC:
7252 return vs.styles[wParam].italic ? 1 : 0;
7253 case SCI_STYLEGETEOLFILLED:
7254 return vs.styles[wParam].eolFilled ? 1 : 0;
7255 case SCI_STYLEGETSIZE:
7256 return vs.styles[wParam].size / SC_FONT_SIZE_MULTIPLIER;
7257 case SCI_STYLEGETSIZEFRACTIONAL:
7258 return vs.styles[wParam].size;
7259 case SCI_STYLEGETFONT:
7260 if (!vs.styles[wParam].fontName)
7261 return 0;
7262 if (lParam != 0)
7263 strcpy(CharPtrFromSPtr(lParam), vs.styles[wParam].fontName);
7264 return strlen(vs.styles[wParam].fontName);
7265 case SCI_STYLEGETUNDERLINE:
7266 return vs.styles[wParam].underline ? 1 : 0;
7267 case SCI_STYLEGETCASE:
7268 return static_cast<int>(vs.styles[wParam].caseForce);
7269 case SCI_STYLEGETCHARACTERSET:
7270 return vs.styles[wParam].characterSet;
7271 case SCI_STYLEGETVISIBLE:
7272 return vs.styles[wParam].visible ? 1 : 0;
7273 case SCI_STYLEGETCHANGEABLE:
7274 return vs.styles[wParam].changeable ? 1 : 0;
7275 case SCI_STYLEGETHOTSPOT:
7276 return vs.styles[wParam].hotspot ? 1 : 0;
7278 return 0;
7281 sptr_t Editor::StringResult(sptr_t lParam, const char *val) {
7282 const size_t n = strlen(val);
7283 if (lParam != 0) {
7284 char *ptr = reinterpret_cast<char *>(lParam);
7285 strcpy(ptr, val);
7287 return n; // Not including NUL
7290 sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
7291 //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
7293 // Optional macro recording hook
7294 if (recordingMacro)
7295 NotifyMacroRecord(iMessage, wParam, lParam);
7297 switch (iMessage) {
7299 case SCI_GETTEXT: {
7300 if (lParam == 0)
7301 return pdoc->Length() + 1;
7302 if (wParam == 0)
7303 return 0;
7304 char *ptr = CharPtrFromSPtr(lParam);
7305 unsigned int iChar = 0;
7306 for (; iChar < wParam - 1; iChar++)
7307 ptr[iChar] = pdoc->CharAt(iChar);
7308 ptr[iChar] = '\0';
7309 return iChar;
7312 case SCI_SETTEXT: {
7313 if (lParam == 0)
7314 return 0;
7315 UndoGroup ug(pdoc);
7316 pdoc->DeleteChars(0, pdoc->Length());
7317 SetEmptySelection(0);
7318 pdoc->InsertCString(0, CharPtrFromSPtr(lParam));
7319 return 1;
7322 case SCI_GETTEXTLENGTH:
7323 return pdoc->Length();
7325 case SCI_CUT:
7326 Cut();
7327 SetLastXChosen();
7328 break;
7330 case SCI_COPY:
7331 Copy();
7332 break;
7334 case SCI_COPYALLOWLINE:
7335 CopyAllowLine();
7336 break;
7338 case SCI_VERTICALCENTRECARET:
7339 VerticalCentreCaret();
7340 break;
7342 case SCI_MOVESELECTEDLINESUP:
7343 MoveSelectedLinesUp();
7344 break;
7346 case SCI_MOVESELECTEDLINESDOWN:
7347 MoveSelectedLinesDown();
7348 break;
7350 case SCI_COPYRANGE:
7351 CopyRangeToClipboard(wParam, lParam);
7352 break;
7354 case SCI_COPYTEXT:
7355 CopyText(wParam, CharPtrFromSPtr(lParam));
7356 break;
7358 case SCI_PASTE:
7359 Paste();
7360 if ((caretSticky == SC_CARETSTICKY_OFF) || (caretSticky == SC_CARETSTICKY_WHITESPACE)) {
7361 SetLastXChosen();
7363 EnsureCaretVisible();
7364 break;
7366 case SCI_CLEAR:
7367 Clear();
7368 SetLastXChosen();
7369 EnsureCaretVisible();
7370 break;
7372 case SCI_UNDO:
7373 Undo();
7374 SetLastXChosen();
7375 break;
7377 case SCI_CANUNDO:
7378 return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
7380 case SCI_EMPTYUNDOBUFFER:
7381 pdoc->DeleteUndoHistory();
7382 return 0;
7384 case SCI_GETFIRSTVISIBLELINE:
7385 return topLine;
7387 case SCI_SETFIRSTVISIBLELINE:
7388 ScrollTo(wParam);
7389 break;
7391 case SCI_GETLINE: { // Risk of overwriting the end of the buffer
7392 int lineStart = pdoc->LineStart(wParam);
7393 int lineEnd = pdoc->LineStart(wParam + 1);
7394 if (lParam == 0) {
7395 return lineEnd - lineStart;
7397 char *ptr = CharPtrFromSPtr(lParam);
7398 int iPlace = 0;
7399 for (int iChar = lineStart; iChar < lineEnd; iChar++) {
7400 ptr[iPlace++] = pdoc->CharAt(iChar);
7402 return iPlace;
7405 case SCI_GETLINECOUNT:
7406 if (pdoc->LinesTotal() == 0)
7407 return 1;
7408 else
7409 return pdoc->LinesTotal();
7411 case SCI_GETMODIFY:
7412 return !pdoc->IsSavePoint();
7414 case SCI_SETSEL: {
7415 int nStart = static_cast<int>(wParam);
7416 int nEnd = static_cast<int>(lParam);
7417 if (nEnd < 0)
7418 nEnd = pdoc->Length();
7419 if (nStart < 0)
7420 nStart = nEnd; // Remove selection
7421 InvalidateSelection(SelectionRange(nStart, nEnd));
7422 sel.Clear();
7423 sel.selType = Selection::selStream;
7424 SetSelection(nEnd, nStart);
7425 EnsureCaretVisible();
7427 break;
7429 case SCI_GETSELTEXT: {
7430 SelectionText selectedText;
7431 CopySelectionRange(&selectedText);
7432 if (lParam == 0) {
7433 return selectedText.len ? selectedText.len : 1;
7434 } else {
7435 char *ptr = CharPtrFromSPtr(lParam);
7436 int iChar = 0;
7437 if (selectedText.len) {
7438 for (; iChar < selectedText.len; iChar++)
7439 ptr[iChar] = selectedText.s[iChar];
7440 } else {
7441 ptr[0] = '\0';
7443 return iChar;
7447 case SCI_LINEFROMPOSITION:
7448 if (static_cast<int>(wParam) < 0)
7449 return 0;
7450 return pdoc->LineFromPosition(wParam);
7452 case SCI_POSITIONFROMLINE:
7453 if (static_cast<int>(wParam) < 0)
7454 wParam = pdoc->LineFromPosition(SelectionStart().Position());
7455 if (wParam == 0)
7456 return 0; // Even if there is no text, there is a first line that starts at 0
7457 if (static_cast<int>(wParam) > pdoc->LinesTotal())
7458 return -1;
7459 //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
7460 // return -1;
7461 return pdoc->LineStart(wParam);
7463 // Replacement of the old Scintilla interpretation of EM_LINELENGTH
7464 case SCI_LINELENGTH:
7465 if ((static_cast<int>(wParam) < 0) ||
7466 (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
7467 return 0;
7468 return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
7470 case SCI_REPLACESEL: {
7471 if (lParam == 0)
7472 return 0;
7473 UndoGroup ug(pdoc);
7474 ClearSelection();
7475 char *replacement = CharPtrFromSPtr(lParam);
7476 pdoc->InsertCString(sel.MainCaret(), replacement);
7477 SetEmptySelection(sel.MainCaret() + istrlen(replacement));
7478 EnsureCaretVisible();
7480 break;
7482 case SCI_SETTARGETSTART:
7483 targetStart = wParam;
7484 break;
7486 case SCI_GETTARGETSTART:
7487 return targetStart;
7489 case SCI_SETTARGETEND:
7490 targetEnd = wParam;
7491 break;
7493 case SCI_GETTARGETEND:
7494 return targetEnd;
7496 case SCI_TARGETFROMSELECTION:
7497 if (sel.MainCaret() < sel.MainAnchor()) {
7498 targetStart = sel.MainCaret();
7499 targetEnd = sel.MainAnchor();
7500 } else {
7501 targetStart = sel.MainAnchor();
7502 targetEnd = sel.MainCaret();
7504 break;
7506 case SCI_REPLACETARGET:
7507 PLATFORM_ASSERT(lParam);
7508 return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
7510 case SCI_REPLACETARGETRE:
7511 PLATFORM_ASSERT(lParam);
7512 return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
7514 case SCI_SEARCHINTARGET:
7515 PLATFORM_ASSERT(lParam);
7516 return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
7518 case SCI_SETSEARCHFLAGS:
7519 searchFlags = wParam;
7520 break;
7522 case SCI_GETSEARCHFLAGS:
7523 return searchFlags;
7525 case SCI_GETTAG:
7526 return GetTag(CharPtrFromSPtr(lParam), wParam);
7528 case SCI_POSITIONBEFORE:
7529 return pdoc->MovePositionOutsideChar(wParam - 1, -1, true);
7531 case SCI_POSITIONAFTER:
7532 return pdoc->MovePositionOutsideChar(wParam + 1, 1, true);
7534 case SCI_LINESCROLL:
7535 ScrollTo(topLine + lParam);
7536 HorizontalScrollTo(xOffset + static_cast<int>(wParam) * vs.spaceWidth);
7537 return 1;
7539 case SCI_SETXOFFSET:
7540 xOffset = wParam;
7541 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
7542 SetHorizontalScrollPos();
7543 Redraw();
7544 break;
7546 case SCI_GETXOFFSET:
7547 return xOffset;
7549 case SCI_CHOOSECARETX:
7550 SetLastXChosen();
7551 break;
7553 case SCI_SCROLLCARET:
7554 EnsureCaretVisible();
7555 break;
7557 case SCI_SETREADONLY:
7558 pdoc->SetReadOnly(wParam != 0);
7559 return 1;
7561 case SCI_GETREADONLY:
7562 return pdoc->IsReadOnly();
7564 case SCI_CANPASTE:
7565 return CanPaste();
7567 case SCI_POINTXFROMPOSITION:
7568 if (lParam < 0) {
7569 return 0;
7570 } else {
7571 Point pt = LocationFromPosition(lParam);
7572 // Convert to view-relative
7573 return pt.x - vs.textStart + vs.fixedColumnWidth;
7576 case SCI_POINTYFROMPOSITION:
7577 if (lParam < 0) {
7578 return 0;
7579 } else {
7580 Point pt = LocationFromPosition(lParam);
7581 return pt.y;
7584 case SCI_FINDTEXT:
7585 return FindText(wParam, lParam);
7587 case SCI_GETTEXTRANGE: {
7588 if (lParam == 0)
7589 return 0;
7590 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7591 int cpMax = tr->chrg.cpMax;
7592 if (cpMax == -1)
7593 cpMax = pdoc->Length();
7594 PLATFORM_ASSERT(cpMax <= pdoc->Length());
7595 int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
7596 pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
7597 // Spec says copied text is terminated with a NUL
7598 tr->lpstrText[len] = '\0';
7599 return len; // Not including NUL
7602 case SCI_HIDESELECTION:
7603 hideSelection = wParam != 0;
7604 Redraw();
7605 break;
7607 case SCI_FORMATRANGE:
7608 return FormatRange(wParam != 0, reinterpret_cast<Sci_RangeToFormat *>(lParam));
7610 case SCI_GETMARGINLEFT:
7611 return vs.leftMarginWidth;
7613 case SCI_GETMARGINRIGHT:
7614 return vs.rightMarginWidth;
7616 case SCI_SETMARGINLEFT:
7617 vs.leftMarginWidth = lParam;
7618 InvalidateStyleRedraw();
7619 break;
7621 case SCI_SETMARGINRIGHT:
7622 vs.rightMarginWidth = lParam;
7623 InvalidateStyleRedraw();
7624 break;
7626 // Control specific mesages
7628 case SCI_ADDTEXT: {
7629 if (lParam == 0)
7630 return 0;
7631 pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
7632 SetEmptySelection(sel.MainCaret() + wParam);
7633 return 0;
7636 case SCI_ADDSTYLEDTEXT:
7637 if (lParam)
7638 AddStyledText(CharPtrFromSPtr(lParam), wParam);
7639 return 0;
7641 case SCI_INSERTTEXT: {
7642 if (lParam == 0)
7643 return 0;
7644 int insertPos = wParam;
7645 if (static_cast<int>(wParam) == -1)
7646 insertPos = CurrentPosition();
7647 int newCurrent = CurrentPosition();
7648 char *sz = CharPtrFromSPtr(lParam);
7649 pdoc->InsertCString(insertPos, sz);
7650 if (newCurrent > insertPos)
7651 newCurrent += istrlen(sz);
7652 SetEmptySelection(newCurrent);
7653 return 0;
7656 case SCI_APPENDTEXT:
7657 pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
7658 return 0;
7660 case SCI_CLEARALL:
7661 ClearAll();
7662 return 0;
7664 case SCI_DELETERANGE:
7665 pdoc->DeleteChars(wParam, lParam);
7666 return 0;
7668 case SCI_CLEARDOCUMENTSTYLE:
7669 ClearDocumentStyle();
7670 return 0;
7672 case SCI_SETUNDOCOLLECTION:
7673 pdoc->SetUndoCollection(wParam != 0);
7674 return 0;
7676 case SCI_GETUNDOCOLLECTION:
7677 return pdoc->IsCollectingUndo();
7679 case SCI_BEGINUNDOACTION:
7680 pdoc->BeginUndoAction();
7681 return 0;
7683 case SCI_ENDUNDOACTION:
7684 pdoc->EndUndoAction();
7685 return 0;
7687 case SCI_GETCARETPERIOD:
7688 return caret.period;
7690 case SCI_SETCARETPERIOD:
7691 caret.period = wParam;
7692 break;
7694 case SCI_GETWORDCHARS:
7695 return pdoc->GetCharsOfClass(CharClassify::ccWord, reinterpret_cast<unsigned char *>(lParam));
7697 case SCI_SETWORDCHARS: {
7698 pdoc->SetDefaultCharClasses(false);
7699 if (lParam == 0)
7700 return 0;
7701 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
7703 break;
7705 case SCI_GETWHITESPACECHARS:
7706 return pdoc->GetCharsOfClass(CharClassify::ccSpace, reinterpret_cast<unsigned char *>(lParam));
7708 case SCI_SETWHITESPACECHARS: {
7709 if (lParam == 0)
7710 return 0;
7711 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
7713 break;
7715 case SCI_GETPUNCTUATIONCHARS:
7716 return pdoc->GetCharsOfClass(CharClassify::ccPunctuation, reinterpret_cast<unsigned char *>(lParam));
7718 case SCI_SETPUNCTUATIONCHARS: {
7719 if (lParam == 0)
7720 return 0;
7721 pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccPunctuation);
7723 break;
7725 case SCI_SETCHARSDEFAULT:
7726 pdoc->SetDefaultCharClasses(true);
7727 break;
7729 case SCI_GETLENGTH:
7730 return pdoc->Length();
7732 case SCI_ALLOCATE:
7733 pdoc->Allocate(wParam);
7734 break;
7736 case SCI_GETCHARAT:
7737 return pdoc->CharAt(wParam);
7739 case SCI_SETCURRENTPOS:
7740 if (sel.IsRectangular()) {
7741 sel.Rectangular().caret.SetPosition(wParam);
7742 SetRectangularRange();
7743 Redraw();
7744 } else {
7745 SetSelection(wParam, sel.MainAnchor());
7747 break;
7749 case SCI_GETCURRENTPOS:
7750 return sel.IsRectangular() ? sel.Rectangular().caret.Position() : sel.MainCaret();
7752 case SCI_SETANCHOR:
7753 if (sel.IsRectangular()) {
7754 sel.Rectangular().anchor.SetPosition(wParam);
7755 SetRectangularRange();
7756 Redraw();
7757 } else {
7758 SetSelection(sel.MainCaret(), wParam);
7760 break;
7762 case SCI_GETANCHOR:
7763 return sel.IsRectangular() ? sel.Rectangular().anchor.Position() : sel.MainAnchor();
7765 case SCI_SETSELECTIONSTART:
7766 SetSelection(Platform::Maximum(sel.MainCaret(), wParam), wParam);
7767 break;
7769 case SCI_GETSELECTIONSTART:
7770 return sel.LimitsForRectangularElseMain().start.Position();
7772 case SCI_SETSELECTIONEND:
7773 SetSelection(wParam, Platform::Minimum(sel.MainAnchor(), wParam));
7774 break;
7776 case SCI_GETSELECTIONEND:
7777 return sel.LimitsForRectangularElseMain().end.Position();
7779 case SCI_SETEMPTYSELECTION:
7780 SetEmptySelection(wParam);
7781 break;
7783 case SCI_SETPRINTMAGNIFICATION:
7784 printMagnification = wParam;
7785 break;
7787 case SCI_GETPRINTMAGNIFICATION:
7788 return printMagnification;
7790 case SCI_SETPRINTCOLOURMODE:
7791 printColourMode = wParam;
7792 break;
7794 case SCI_GETPRINTCOLOURMODE:
7795 return printColourMode;
7797 case SCI_SETPRINTWRAPMODE:
7798 printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
7799 break;
7801 case SCI_GETPRINTWRAPMODE:
7802 return printWrapState;
7804 case SCI_GETSTYLEAT:
7805 if (static_cast<int>(wParam) >= pdoc->Length())
7806 return 0;
7807 else
7808 return pdoc->StyleAt(wParam);
7810 case SCI_REDO:
7811 Redo();
7812 break;
7814 case SCI_SELECTALL:
7815 SelectAll();
7816 break;
7818 case SCI_SETSAVEPOINT:
7819 pdoc->SetSavePoint();
7820 break;
7822 case SCI_GETSTYLEDTEXT: {
7823 if (lParam == 0)
7824 return 0;
7825 Sci_TextRange *tr = reinterpret_cast<Sci_TextRange *>(lParam);
7826 int iPlace = 0;
7827 for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
7828 tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
7829 tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
7831 tr->lpstrText[iPlace] = '\0';
7832 tr->lpstrText[iPlace + 1] = '\0';
7833 return iPlace;
7836 case SCI_CANREDO:
7837 return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
7839 case SCI_MARKERLINEFROMHANDLE:
7840 return pdoc->LineFromHandle(wParam);
7842 case SCI_MARKERDELETEHANDLE:
7843 pdoc->DeleteMarkFromHandle(wParam);
7844 break;
7846 case SCI_GETVIEWWS:
7847 return vs.viewWhitespace;
7849 case SCI_SETVIEWWS:
7850 vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
7851 Redraw();
7852 break;
7854 case SCI_GETWHITESPACESIZE:
7855 return vs.whitespaceSize;
7857 case SCI_SETWHITESPACESIZE:
7858 vs.whitespaceSize = static_cast<int>(wParam);
7859 Redraw();
7860 break;
7862 case SCI_POSITIONFROMPOINT:
7863 return PositionFromLocation(Point(wParam, lParam), false, false);
7865 case SCI_POSITIONFROMPOINTCLOSE:
7866 return PositionFromLocation(Point(wParam, lParam), true, false);
7868 case SCI_CHARPOSITIONFROMPOINT:
7869 return PositionFromLocation(Point(wParam, lParam), false, true);
7871 case SCI_CHARPOSITIONFROMPOINTCLOSE:
7872 return PositionFromLocation(Point(wParam, lParam), true, true);
7874 case SCI_GOTOLINE:
7875 GoToLine(wParam);
7876 break;
7878 case SCI_GOTOPOS:
7879 SetEmptySelection(wParam);
7880 EnsureCaretVisible();
7881 break;
7883 case SCI_GETCURLINE: {
7884 int lineCurrentPos = pdoc->LineFromPosition(sel.MainCaret());
7885 int lineStart = pdoc->LineStart(lineCurrentPos);
7886 unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
7887 if (lParam == 0) {
7888 return 1 + lineEnd - lineStart;
7890 PLATFORM_ASSERT(wParam > 0);
7891 char *ptr = CharPtrFromSPtr(lParam);
7892 unsigned int iPlace = 0;
7893 for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
7894 ptr[iPlace++] = pdoc->CharAt(iChar);
7896 ptr[iPlace] = '\0';
7897 return sel.MainCaret() - lineStart;
7900 case SCI_GETENDSTYLED:
7901 return pdoc->GetEndStyled();
7903 case SCI_GETEOLMODE:
7904 return pdoc->eolMode;
7906 case SCI_SETEOLMODE:
7907 pdoc->eolMode = wParam;
7908 break;
7910 case SCI_SETLINEENDTYPESALLOWED:
7911 if (pdoc->SetLineEndTypesAllowed(wParam)) {
7912 cs.Clear();
7913 cs.InsertLines(0, pdoc->LinesTotal() - 1);
7914 SetAnnotationHeights(0, pdoc->LinesTotal());
7915 InvalidateStyleRedraw();
7917 break;
7919 case SCI_GETLINEENDTYPESALLOWED:
7920 return pdoc->GetLineEndTypesAllowed();
7922 case SCI_GETLINEENDTYPESACTIVE:
7923 return pdoc->GetLineEndTypesActive();
7925 case SCI_STARTSTYLING:
7926 pdoc->StartStyling(wParam, static_cast<char>(lParam));
7927 break;
7929 case SCI_SETSTYLING:
7930 pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
7931 break;
7933 case SCI_SETSTYLINGEX: // Specify a complete styling buffer
7934 if (lParam == 0)
7935 return 0;
7936 pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
7937 break;
7939 case SCI_SETBUFFEREDDRAW:
7940 bufferedDraw = wParam != 0;
7941 break;
7943 case SCI_GETBUFFEREDDRAW:
7944 return bufferedDraw;
7946 case SCI_GETTWOPHASEDRAW:
7947 return twoPhaseDraw;
7949 case SCI_SETTWOPHASEDRAW:
7950 twoPhaseDraw = wParam != 0;
7951 InvalidateStyleRedraw();
7952 break;
7954 case SCI_SETFONTQUALITY:
7955 vs.extraFontFlag &= ~SC_EFF_QUALITY_MASK;
7956 vs.extraFontFlag |= (wParam & SC_EFF_QUALITY_MASK);
7957 InvalidateStyleRedraw();
7958 break;
7960 case SCI_GETFONTQUALITY:
7961 return (vs.extraFontFlag & SC_EFF_QUALITY_MASK);
7963 case SCI_SETTABWIDTH:
7964 if (wParam > 0) {
7965 pdoc->tabInChars = wParam;
7966 if (pdoc->indentInChars == 0)
7967 pdoc->actualIndentInChars = pdoc->tabInChars;
7969 InvalidateStyleRedraw();
7970 break;
7972 case SCI_GETTABWIDTH:
7973 return pdoc->tabInChars;
7975 case SCI_SETINDENT:
7976 pdoc->indentInChars = wParam;
7977 if (pdoc->indentInChars != 0)
7978 pdoc->actualIndentInChars = pdoc->indentInChars;
7979 else
7980 pdoc->actualIndentInChars = pdoc->tabInChars;
7981 InvalidateStyleRedraw();
7982 break;
7984 case SCI_GETINDENT:
7985 return pdoc->indentInChars;
7987 case SCI_SETUSETABS:
7988 pdoc->useTabs = wParam != 0;
7989 InvalidateStyleRedraw();
7990 break;
7992 case SCI_GETUSETABS:
7993 return pdoc->useTabs;
7995 case SCI_SETLINEINDENTATION:
7996 pdoc->SetLineIndentation(wParam, lParam);
7997 break;
7999 case SCI_GETLINEINDENTATION:
8000 return pdoc->GetLineIndentation(wParam);
8002 case SCI_GETLINEINDENTPOSITION:
8003 return pdoc->GetLineIndentPosition(wParam);
8005 case SCI_SETTABINDENTS:
8006 pdoc->tabIndents = wParam != 0;
8007 break;
8009 case SCI_GETTABINDENTS:
8010 return pdoc->tabIndents;
8012 case SCI_SETBACKSPACEUNINDENTS:
8013 pdoc->backspaceUnindents = wParam != 0;
8014 break;
8016 case SCI_GETBACKSPACEUNINDENTS:
8017 return pdoc->backspaceUnindents;
8019 case SCI_SETMOUSEDWELLTIME:
8020 dwellDelay = wParam;
8021 ticksToDwell = dwellDelay;
8022 break;
8024 case SCI_GETMOUSEDWELLTIME:
8025 return dwellDelay;
8027 case SCI_WORDSTARTPOSITION:
8028 return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
8030 case SCI_WORDENDPOSITION:
8031 return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
8033 case SCI_SETWRAPMODE:
8034 switch (wParam) {
8035 case SC_WRAP_WORD:
8036 wrapState = eWrapWord;
8037 break;
8038 case SC_WRAP_CHAR:
8039 wrapState = eWrapChar;
8040 break;
8041 default:
8042 wrapState = eWrapNone;
8043 break;
8045 xOffset = 0;
8046 ContainerNeedsUpdate(SC_UPDATE_H_SCROLL);
8047 InvalidateStyleRedraw();
8048 ReconfigureScrollBars();
8049 break;
8051 case SCI_GETWRAPMODE:
8052 return wrapState;
8054 case SCI_SETWRAPVISUALFLAGS:
8055 if (wrapVisualFlags != static_cast<int>(wParam)) {
8056 wrapVisualFlags = wParam;
8057 InvalidateStyleRedraw();
8058 ReconfigureScrollBars();
8060 break;
8062 case SCI_GETWRAPVISUALFLAGS:
8063 return wrapVisualFlags;
8065 case SCI_SETWRAPVISUALFLAGSLOCATION:
8066 wrapVisualFlagsLocation = wParam;
8067 InvalidateStyleRedraw();
8068 break;
8070 case SCI_GETWRAPVISUALFLAGSLOCATION:
8071 return wrapVisualFlagsLocation;
8073 case SCI_SETWRAPSTARTINDENT:
8074 if (wrapVisualStartIndent != static_cast<int>(wParam)) {
8075 wrapVisualStartIndent = wParam;
8076 InvalidateStyleRedraw();
8077 ReconfigureScrollBars();
8079 break;
8081 case SCI_GETWRAPSTARTINDENT:
8082 return wrapVisualStartIndent;
8084 case SCI_SETWRAPINDENTMODE:
8085 if (wrapIndentMode != static_cast<int>(wParam)) {
8086 wrapIndentMode = wParam;
8087 InvalidateStyleRedraw();
8088 ReconfigureScrollBars();
8090 break;
8092 case SCI_GETWRAPINDENTMODE:
8093 return wrapIndentMode;
8095 case SCI_SETLAYOUTCACHE:
8096 llc.SetLevel(wParam);
8097 break;
8099 case SCI_GETLAYOUTCACHE:
8100 return llc.GetLevel();
8102 case SCI_SETPOSITIONCACHE:
8103 posCache.SetSize(wParam);
8104 break;
8106 case SCI_GETPOSITIONCACHE:
8107 return posCache.GetSize();
8109 case SCI_SETSCROLLWIDTH:
8110 PLATFORM_ASSERT(wParam > 0);
8111 if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
8112 lineWidthMaxSeen = 0;
8113 scrollWidth = wParam;
8114 SetScrollBars();
8116 break;
8118 case SCI_GETSCROLLWIDTH:
8119 return scrollWidth;
8121 case SCI_SETSCROLLWIDTHTRACKING:
8122 trackLineWidth = wParam != 0;
8123 break;
8125 case SCI_GETSCROLLWIDTHTRACKING:
8126 return trackLineWidth;
8128 case SCI_LINESJOIN:
8129 LinesJoin();
8130 break;
8132 case SCI_LINESSPLIT:
8133 LinesSplit(wParam);
8134 break;
8136 case SCI_TEXTWIDTH:
8137 PLATFORM_ASSERT(wParam < vs.stylesSize);
8138 PLATFORM_ASSERT(lParam);
8139 return TextWidth(wParam, CharPtrFromSPtr(lParam));
8141 case SCI_TEXTHEIGHT:
8142 return vs.lineHeight;
8144 case SCI_SETENDATLASTLINE:
8145 PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
8146 if (endAtLastLine != (wParam != 0)) {
8147 endAtLastLine = wParam != 0;
8148 SetScrollBars();
8150 break;
8152 case SCI_GETENDATLASTLINE:
8153 return endAtLastLine;
8155 case SCI_SETCARETSTICKY:
8156 PLATFORM_ASSERT(wParam <= SC_CARETSTICKY_WHITESPACE);
8157 if (wParam <= SC_CARETSTICKY_WHITESPACE) {
8158 caretSticky = wParam;
8160 break;
8162 case SCI_GETCARETSTICKY:
8163 return caretSticky;
8165 case SCI_TOGGLECARETSTICKY:
8166 caretSticky = !caretSticky;
8167 break;
8169 case SCI_GETCOLUMN:
8170 return pdoc->GetColumn(wParam);
8172 case SCI_FINDCOLUMN:
8173 return pdoc->FindColumn(wParam, lParam);
8175 case SCI_SETHSCROLLBAR :
8176 if (horizontalScrollBarVisible != (wParam != 0)) {
8177 horizontalScrollBarVisible = wParam != 0;
8178 SetScrollBars();
8179 ReconfigureScrollBars();
8181 break;
8183 case SCI_GETHSCROLLBAR:
8184 return horizontalScrollBarVisible;
8186 case SCI_SETVSCROLLBAR:
8187 if (verticalScrollBarVisible != (wParam != 0)) {
8188 verticalScrollBarVisible = wParam != 0;
8189 SetScrollBars();
8190 ReconfigureScrollBars();
8191 if (verticalScrollBarVisible)
8192 SetVerticalScrollPos();
8194 break;
8196 case SCI_GETVSCROLLBAR:
8197 return verticalScrollBarVisible;
8199 case SCI_SETINDENTATIONGUIDES:
8200 vs.viewIndentationGuides = IndentView(wParam);
8201 Redraw();
8202 break;
8204 case SCI_GETINDENTATIONGUIDES:
8205 return vs.viewIndentationGuides;
8207 case SCI_SETHIGHLIGHTGUIDE:
8208 if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
8209 highlightGuideColumn = wParam;
8210 Redraw();
8212 break;
8214 case SCI_GETHIGHLIGHTGUIDE:
8215 return highlightGuideColumn;
8217 case SCI_GETLINEENDPOSITION:
8218 return pdoc->LineEnd(wParam);
8220 case SCI_SETCODEPAGE:
8221 if (ValidCodePage(wParam)) {
8222 if (pdoc->SetDBCSCodePage(wParam)) {
8223 cs.Clear();
8224 cs.InsertLines(0, pdoc->LinesTotal() - 1);
8225 SetAnnotationHeights(0, pdoc->LinesTotal());
8226 InvalidateStyleRedraw();
8229 break;
8231 case SCI_GETCODEPAGE:
8232 return pdoc->dbcsCodePage;
8234 #ifdef INCLUDE_DEPRECATED_FEATURES
8235 case SCI_SETUSEPALETTE:
8236 InvalidateStyleRedraw();
8237 break;
8239 case SCI_GETUSEPALETTE:
8240 return 0;
8241 #endif
8243 // Marker definition and setting
8244 case SCI_MARKERDEFINE:
8245 if (wParam <= MARKER_MAX) {
8246 vs.markers[wParam].markType = lParam;
8247 vs.CalcLargestMarkerHeight();
8249 InvalidateStyleData();
8250 RedrawSelMargin();
8251 break;
8253 case SCI_MARKERSYMBOLDEFINED:
8254 if (wParam <= MARKER_MAX)
8255 return vs.markers[wParam].markType;
8256 else
8257 return 0;
8259 case SCI_MARKERSETFORE:
8260 if (wParam <= MARKER_MAX)
8261 vs.markers[wParam].fore = ColourDesired(lParam);
8262 InvalidateStyleData();
8263 RedrawSelMargin();
8264 break;
8265 case SCI_MARKERSETBACKSELECTED:
8266 if (wParam <= MARKER_MAX)
8267 vs.markers[wParam].backSelected = ColourDesired(lParam);
8268 InvalidateStyleData();
8269 RedrawSelMargin();
8270 break;
8271 case SCI_MARKERENABLEHIGHLIGHT:
8272 highlightDelimiter.isEnabled = wParam == 1;
8273 RedrawSelMargin();
8274 break;
8275 case SCI_MARKERSETBACK:
8276 if (wParam <= MARKER_MAX)
8277 vs.markers[wParam].back = ColourDesired(lParam);
8278 InvalidateStyleData();
8279 RedrawSelMargin();
8280 break;
8281 case SCI_MARKERSETALPHA:
8282 if (wParam <= MARKER_MAX)
8283 vs.markers[wParam].alpha = lParam;
8284 InvalidateStyleRedraw();
8285 break;
8286 case SCI_MARKERADD: {
8287 int markerID = pdoc->AddMark(wParam, lParam);
8288 return markerID;
8290 case SCI_MARKERADDSET:
8291 if (lParam != 0)
8292 pdoc->AddMarkSet(wParam, lParam);
8293 break;
8295 case SCI_MARKERDELETE:
8296 pdoc->DeleteMark(wParam, lParam);
8297 break;
8299 case SCI_MARKERDELETEALL:
8300 pdoc->DeleteAllMarks(static_cast<int>(wParam));
8301 break;
8303 case SCI_MARKERGET:
8304 return pdoc->GetMark(wParam);
8306 case SCI_MARKERNEXT:
8307 return pdoc->MarkerNext(wParam, lParam);
8309 case SCI_MARKERPREVIOUS: {
8310 for (int iLine = wParam; iLine >= 0; iLine--) {
8311 if ((pdoc->GetMark(iLine) & lParam) != 0)
8312 return iLine;
8315 return -1;
8317 case SCI_MARKERDEFINEPIXMAP:
8318 if (wParam <= MARKER_MAX) {
8319 vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
8320 vs.CalcLargestMarkerHeight();
8322 InvalidateStyleData();
8323 RedrawSelMargin();
8324 break;
8326 case SCI_RGBAIMAGESETWIDTH:
8327 sizeRGBAImage.x = wParam;
8328 break;
8330 case SCI_RGBAIMAGESETHEIGHT:
8331 sizeRGBAImage.y = wParam;
8332 break;
8334 case SCI_RGBAIMAGESETSCALE:
8335 scaleRGBAImage = wParam;
8336 break;
8338 case SCI_MARKERDEFINERGBAIMAGE:
8339 if (wParam <= MARKER_MAX) {
8340 vs.markers[wParam].SetRGBAImage(sizeRGBAImage, scaleRGBAImage / 100.0, reinterpret_cast<unsigned char *>(lParam));
8341 vs.CalcLargestMarkerHeight();
8343 InvalidateStyleData();
8344 RedrawSelMargin();
8345 break;
8347 case SCI_SETMARGINTYPEN:
8348 if (ValidMargin(wParam)) {
8349 vs.ms[wParam].style = lParam;
8350 InvalidateStyleRedraw();
8352 break;
8354 case SCI_GETMARGINTYPEN:
8355 if (ValidMargin(wParam))
8356 return vs.ms[wParam].style;
8357 else
8358 return 0;
8360 case SCI_SETMARGINWIDTHN:
8361 if (ValidMargin(wParam)) {
8362 // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
8363 if (vs.ms[wParam].width != lParam) {
8364 vs.ms[wParam].width = lParam;
8365 InvalidateStyleRedraw();
8368 break;
8370 case SCI_GETMARGINWIDTHN:
8371 if (ValidMargin(wParam))
8372 return vs.ms[wParam].width;
8373 else
8374 return 0;
8376 case SCI_SETMARGINMASKN:
8377 if (ValidMargin(wParam)) {
8378 vs.ms[wParam].mask = lParam;
8379 InvalidateStyleRedraw();
8381 break;
8383 case SCI_GETMARGINMASKN:
8384 if (ValidMargin(wParam))
8385 return vs.ms[wParam].mask;
8386 else
8387 return 0;
8389 case SCI_SETMARGINSENSITIVEN:
8390 if (ValidMargin(wParam)) {
8391 vs.ms[wParam].sensitive = lParam != 0;
8392 InvalidateStyleRedraw();
8394 break;
8396 case SCI_GETMARGINSENSITIVEN:
8397 if (ValidMargin(wParam))
8398 return vs.ms[wParam].sensitive ? 1 : 0;
8399 else
8400 return 0;
8402 case SCI_SETMARGINCURSORN:
8403 if (ValidMargin(wParam))
8404 vs.ms[wParam].cursor = lParam;
8405 break;
8407 case SCI_GETMARGINCURSORN:
8408 if (ValidMargin(wParam))
8409 return vs.ms[wParam].cursor;
8410 else
8411 return 0;
8413 case SCI_STYLECLEARALL:
8414 vs.ClearStyles();
8415 InvalidateStyleRedraw();
8416 break;
8418 case SCI_STYLESETFORE:
8419 case SCI_STYLESETBACK:
8420 case SCI_STYLESETBOLD:
8421 case SCI_STYLESETWEIGHT:
8422 case SCI_STYLESETITALIC:
8423 case SCI_STYLESETEOLFILLED:
8424 case SCI_STYLESETSIZE:
8425 case SCI_STYLESETSIZEFRACTIONAL:
8426 case SCI_STYLESETFONT:
8427 case SCI_STYLESETUNDERLINE:
8428 case SCI_STYLESETCASE:
8429 case SCI_STYLESETCHARACTERSET:
8430 case SCI_STYLESETVISIBLE:
8431 case SCI_STYLESETCHANGEABLE:
8432 case SCI_STYLESETHOTSPOT:
8433 StyleSetMessage(iMessage, wParam, lParam);
8434 break;
8436 case SCI_STYLEGETFORE:
8437 case SCI_STYLEGETBACK:
8438 case SCI_STYLEGETBOLD:
8439 case SCI_STYLEGETWEIGHT:
8440 case SCI_STYLEGETITALIC:
8441 case SCI_STYLEGETEOLFILLED:
8442 case SCI_STYLEGETSIZE:
8443 case SCI_STYLEGETSIZEFRACTIONAL:
8444 case SCI_STYLEGETFONT:
8445 case SCI_STYLEGETUNDERLINE:
8446 case SCI_STYLEGETCASE:
8447 case SCI_STYLEGETCHARACTERSET:
8448 case SCI_STYLEGETVISIBLE:
8449 case SCI_STYLEGETCHANGEABLE:
8450 case SCI_STYLEGETHOTSPOT:
8451 return StyleGetMessage(iMessage, wParam, lParam);
8453 case SCI_STYLERESETDEFAULT:
8454 vs.ResetDefaultStyle();
8455 InvalidateStyleRedraw();
8456 break;
8457 case SCI_SETSTYLEBITS:
8458 vs.EnsureStyle((1 << wParam) - 1);
8459 pdoc->SetStylingBits(wParam);
8460 break;
8462 case SCI_GETSTYLEBITS:
8463 return pdoc->stylingBits;
8465 case SCI_SETLINESTATE:
8466 return pdoc->SetLineState(wParam, lParam);
8468 case SCI_GETLINESTATE:
8469 return pdoc->GetLineState(wParam);
8471 case SCI_GETMAXLINESTATE:
8472 return pdoc->GetMaxLineState();
8474 case SCI_GETCARETLINEVISIBLE:
8475 return vs.showCaretLineBackground;
8476 case SCI_SETCARETLINEVISIBLE:
8477 vs.showCaretLineBackground = wParam != 0;
8478 InvalidateStyleRedraw();
8479 break;
8480 case SCI_GETCARETLINEVISIBLEALWAYS:
8481 return vs.alwaysShowCaretLineBackground;
8482 case SCI_SETCARETLINEVISIBLEALWAYS:
8483 vs.alwaysShowCaretLineBackground = wParam != 0;
8484 InvalidateStyleRedraw();
8485 break;
8487 case SCI_GETCARETLINEBACK:
8488 return vs.caretLineBackground.AsLong();
8489 case SCI_SETCARETLINEBACK:
8490 vs.caretLineBackground = wParam;
8491 InvalidateStyleRedraw();
8492 break;
8493 case SCI_GETCARETLINEBACKALPHA:
8494 return vs.caretLineAlpha;
8495 case SCI_SETCARETLINEBACKALPHA:
8496 vs.caretLineAlpha = wParam;
8497 InvalidateStyleRedraw();
8498 break;
8500 // Folding messages
8502 case SCI_VISIBLEFROMDOCLINE:
8503 return cs.DisplayFromDoc(wParam);
8505 case SCI_DOCLINEFROMVISIBLE:
8506 return cs.DocFromDisplay(wParam);
8508 case SCI_WRAPCOUNT:
8509 return WrapCount(wParam);
8511 case SCI_SETFOLDLEVEL: {
8512 int prev = pdoc->SetLevel(wParam, lParam);
8513 if (prev != lParam)
8514 RedrawSelMargin();
8515 return prev;
8518 case SCI_GETFOLDLEVEL:
8519 return pdoc->GetLevel(wParam);
8521 case SCI_GETLASTCHILD:
8522 return pdoc->GetLastChild(wParam, lParam);
8524 case SCI_GETFOLDPARENT:
8525 return pdoc->GetFoldParent(wParam);
8527 case SCI_SHOWLINES:
8528 cs.SetVisible(wParam, lParam, true);
8529 SetScrollBars();
8530 Redraw();
8531 break;
8533 case SCI_HIDELINES:
8534 if (wParam > 0)
8535 cs.SetVisible(wParam, lParam, false);
8536 SetScrollBars();
8537 Redraw();
8538 break;
8540 case SCI_GETLINEVISIBLE:
8541 return cs.GetVisible(wParam);
8543 case SCI_GETALLLINESVISIBLE:
8544 return cs.HiddenLines() ? 0 : 1;
8546 case SCI_SETFOLDEXPANDED:
8547 if (cs.SetExpanded(wParam, lParam != 0)) {
8548 RedrawSelMargin();
8550 break;
8552 case SCI_GETFOLDEXPANDED:
8553 return cs.GetExpanded(wParam);
8555 case SCI_SETFOLDFLAGS:
8556 foldFlags = wParam;
8557 Redraw();
8558 break;
8560 case SCI_TOGGLEFOLD:
8561 ToggleContraction(wParam);
8562 break;
8564 case SCI_CONTRACTEDFOLDNEXT:
8565 return ContractedFoldNext(wParam);
8567 case SCI_ENSUREVISIBLE:
8568 EnsureLineVisible(wParam, false);
8569 break;
8571 case SCI_ENSUREVISIBLEENFORCEPOLICY:
8572 EnsureLineVisible(wParam, true);
8573 break;
8575 case SCI_SCROLLRANGE:
8576 ScrollRange(SelectionRange(lParam, wParam));
8577 break;
8579 case SCI_SEARCHANCHOR:
8580 SearchAnchor();
8581 break;
8583 case SCI_SEARCHNEXT:
8584 case SCI_SEARCHPREV:
8585 return SearchText(iMessage, wParam, lParam);
8587 case SCI_SETXCARETPOLICY:
8588 caretXPolicy = wParam;
8589 caretXSlop = lParam;
8590 break;
8592 case SCI_SETYCARETPOLICY:
8593 caretYPolicy = wParam;
8594 caretYSlop = lParam;
8595 break;
8597 case SCI_SETVISIBLEPOLICY:
8598 visiblePolicy = wParam;
8599 visibleSlop = lParam;
8600 break;
8602 case SCI_LINESONSCREEN:
8603 return LinesOnScreen();
8605 case SCI_SETSELFORE:
8606 vs.selforeset = wParam != 0;
8607 vs.selforeground = ColourDesired(lParam);
8608 vs.selAdditionalForeground = ColourDesired(lParam);
8609 InvalidateStyleRedraw();
8610 break;
8612 case SCI_SETSELBACK:
8613 vs.selbackset = wParam != 0;
8614 vs.selbackground = ColourDesired(lParam);
8615 vs.selAdditionalBackground = ColourDesired(lParam);
8616 InvalidateStyleRedraw();
8617 break;
8619 case SCI_SETSELALPHA:
8620 vs.selAlpha = wParam;
8621 vs.selAdditionalAlpha = wParam;
8622 InvalidateStyleRedraw();
8623 break;
8625 case SCI_GETSELALPHA:
8626 return vs.selAlpha;
8628 case SCI_GETSELEOLFILLED:
8629 return vs.selEOLFilled;
8631 case SCI_SETSELEOLFILLED:
8632 vs.selEOLFilled = wParam != 0;
8633 InvalidateStyleRedraw();
8634 break;
8636 case SCI_SETWHITESPACEFORE:
8637 vs.whitespaceForegroundSet = wParam != 0;
8638 vs.whitespaceForeground = ColourDesired(lParam);
8639 InvalidateStyleRedraw();
8640 break;
8642 case SCI_SETWHITESPACEBACK:
8643 vs.whitespaceBackgroundSet = wParam != 0;
8644 vs.whitespaceBackground = ColourDesired(lParam);
8645 InvalidateStyleRedraw();
8646 break;
8648 case SCI_SETCARETFORE:
8649 vs.caretcolour = ColourDesired(wParam);
8650 InvalidateStyleRedraw();
8651 break;
8653 case SCI_GETCARETFORE:
8654 return vs.caretcolour.AsLong();
8656 case SCI_SETCARETSTYLE:
8657 if (wParam <= CARETSTYLE_BLOCK)
8658 vs.caretStyle = wParam;
8659 else
8660 /* Default to the line caret */
8661 vs.caretStyle = CARETSTYLE_LINE;
8662 InvalidateStyleRedraw();
8663 break;
8665 case SCI_GETCARETSTYLE:
8666 return vs.caretStyle;
8668 case SCI_SETCARETWIDTH:
8669 if (static_cast<int>(wParam) <= 0)
8670 vs.caretWidth = 0;
8671 else if (wParam >= 3)
8672 vs.caretWidth = 3;
8673 else
8674 vs.caretWidth = wParam;
8675 InvalidateStyleRedraw();
8676 break;
8678 case SCI_GETCARETWIDTH:
8679 return vs.caretWidth;
8681 case SCI_ASSIGNCMDKEY:
8682 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8683 Platform::HighShortFromLong(wParam), lParam);
8684 break;
8686 case SCI_CLEARCMDKEY:
8687 kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
8688 Platform::HighShortFromLong(wParam), SCI_NULL);
8689 break;
8691 case SCI_CLEARALLCMDKEYS:
8692 kmap.Clear();
8693 break;
8695 case SCI_INDICSETSTYLE:
8696 if (wParam <= INDIC_MAX) {
8697 vs.indicators[wParam].style = lParam;
8698 InvalidateStyleRedraw();
8700 break;
8702 case SCI_INDICGETSTYLE:
8703 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
8705 case SCI_INDICSETFORE:
8706 if (wParam <= INDIC_MAX) {
8707 vs.indicators[wParam].fore = ColourDesired(lParam);
8708 InvalidateStyleRedraw();
8710 break;
8712 case SCI_INDICGETFORE:
8713 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.AsLong() : 0;
8715 case SCI_INDICSETUNDER:
8716 if (wParam <= INDIC_MAX) {
8717 vs.indicators[wParam].under = lParam != 0;
8718 InvalidateStyleRedraw();
8720 break;
8722 case SCI_INDICGETUNDER:
8723 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].under : 0;
8725 case SCI_INDICSETALPHA:
8726 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8727 vs.indicators[wParam].fillAlpha = lParam;
8728 InvalidateStyleRedraw();
8730 break;
8732 case SCI_INDICGETALPHA:
8733 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fillAlpha : 0;
8735 case SCI_INDICSETOUTLINEALPHA:
8736 if (wParam <= INDIC_MAX && lParam >=0 && lParam <= 255) {
8737 vs.indicators[wParam].outlineAlpha = lParam;
8738 InvalidateStyleRedraw();
8740 break;
8742 case SCI_INDICGETOUTLINEALPHA:
8743 return (wParam <= INDIC_MAX) ? vs.indicators[wParam].outlineAlpha : 0;
8745 case SCI_SETINDICATORCURRENT:
8746 pdoc->decorations.SetCurrentIndicator(wParam);
8747 break;
8748 case SCI_GETINDICATORCURRENT:
8749 return pdoc->decorations.GetCurrentIndicator();
8750 case SCI_SETINDICATORVALUE:
8751 pdoc->decorations.SetCurrentValue(wParam);
8752 break;
8753 case SCI_GETINDICATORVALUE:
8754 return pdoc->decorations.GetCurrentValue();
8756 case SCI_INDICATORFILLRANGE:
8757 pdoc->DecorationFillRange(wParam, pdoc->decorations.GetCurrentValue(), lParam);
8758 break;
8760 case SCI_INDICATORCLEARRANGE:
8761 pdoc->DecorationFillRange(wParam, 0, lParam);
8762 break;
8764 case SCI_INDICATORALLONFOR:
8765 return pdoc->decorations.AllOnFor(wParam);
8767 case SCI_INDICATORVALUEAT:
8768 return pdoc->decorations.ValueAt(wParam, lParam);
8770 case SCI_INDICATORSTART:
8771 return pdoc->decorations.Start(wParam, lParam);
8773 case SCI_INDICATOREND:
8774 return pdoc->decorations.End(wParam, lParam);
8776 case SCI_LINEDOWN:
8777 case SCI_LINEDOWNEXTEND:
8778 case SCI_PARADOWN:
8779 case SCI_PARADOWNEXTEND:
8780 case SCI_LINEUP:
8781 case SCI_LINEUPEXTEND:
8782 case SCI_PARAUP:
8783 case SCI_PARAUPEXTEND:
8784 case SCI_CHARLEFT:
8785 case SCI_CHARLEFTEXTEND:
8786 case SCI_CHARRIGHT:
8787 case SCI_CHARRIGHTEXTEND:
8788 case SCI_WORDLEFT:
8789 case SCI_WORDLEFTEXTEND:
8790 case SCI_WORDRIGHT:
8791 case SCI_WORDRIGHTEXTEND:
8792 case SCI_WORDLEFTEND:
8793 case SCI_WORDLEFTENDEXTEND:
8794 case SCI_WORDRIGHTEND:
8795 case SCI_WORDRIGHTENDEXTEND:
8796 case SCI_HOME:
8797 case SCI_HOMEEXTEND:
8798 case SCI_LINEEND:
8799 case SCI_LINEENDEXTEND:
8800 case SCI_HOMEWRAP:
8801 case SCI_HOMEWRAPEXTEND:
8802 case SCI_LINEENDWRAP:
8803 case SCI_LINEENDWRAPEXTEND:
8804 case SCI_DOCUMENTSTART:
8805 case SCI_DOCUMENTSTARTEXTEND:
8806 case SCI_DOCUMENTEND:
8807 case SCI_DOCUMENTENDEXTEND:
8808 case SCI_SCROLLTOSTART:
8809 case SCI_SCROLLTOEND:
8811 case SCI_STUTTEREDPAGEUP:
8812 case SCI_STUTTEREDPAGEUPEXTEND:
8813 case SCI_STUTTEREDPAGEDOWN:
8814 case SCI_STUTTEREDPAGEDOWNEXTEND:
8816 case SCI_PAGEUP:
8817 case SCI_PAGEUPEXTEND:
8818 case SCI_PAGEDOWN:
8819 case SCI_PAGEDOWNEXTEND:
8820 case SCI_EDITTOGGLEOVERTYPE:
8821 case SCI_CANCEL:
8822 case SCI_DELETEBACK:
8823 case SCI_TAB:
8824 case SCI_BACKTAB:
8825 case SCI_NEWLINE:
8826 case SCI_FORMFEED:
8827 case SCI_VCHOME:
8828 case SCI_VCHOMEEXTEND:
8829 case SCI_VCHOMEWRAP:
8830 case SCI_VCHOMEWRAPEXTEND:
8831 case SCI_VCHOMEDISPLAY:
8832 case SCI_VCHOMEDISPLAYEXTEND:
8833 case SCI_ZOOMIN:
8834 case SCI_ZOOMOUT:
8835 case SCI_DELWORDLEFT:
8836 case SCI_DELWORDRIGHT:
8837 case SCI_DELWORDRIGHTEND:
8838 case SCI_DELLINELEFT:
8839 case SCI_DELLINERIGHT:
8840 case SCI_LINECOPY:
8841 case SCI_LINECUT:
8842 case SCI_LINEDELETE:
8843 case SCI_LINETRANSPOSE:
8844 case SCI_LINEDUPLICATE:
8845 case SCI_LOWERCASE:
8846 case SCI_UPPERCASE:
8847 case SCI_LINESCROLLDOWN:
8848 case SCI_LINESCROLLUP:
8849 case SCI_WORDPARTLEFT:
8850 case SCI_WORDPARTLEFTEXTEND:
8851 case SCI_WORDPARTRIGHT:
8852 case SCI_WORDPARTRIGHTEXTEND:
8853 case SCI_DELETEBACKNOTLINE:
8854 case SCI_HOMEDISPLAY:
8855 case SCI_HOMEDISPLAYEXTEND:
8856 case SCI_LINEENDDISPLAY:
8857 case SCI_LINEENDDISPLAYEXTEND:
8858 case SCI_LINEDOWNRECTEXTEND:
8859 case SCI_LINEUPRECTEXTEND:
8860 case SCI_CHARLEFTRECTEXTEND:
8861 case SCI_CHARRIGHTRECTEXTEND:
8862 case SCI_HOMERECTEXTEND:
8863 case SCI_VCHOMERECTEXTEND:
8864 case SCI_LINEENDRECTEXTEND:
8865 case SCI_PAGEUPRECTEXTEND:
8866 case SCI_PAGEDOWNRECTEXTEND:
8867 case SCI_SELECTIONDUPLICATE:
8868 return KeyCommand(iMessage);
8870 case SCI_BRACEHIGHLIGHT:
8871 SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
8872 break;
8874 case SCI_BRACEHIGHLIGHTINDICATOR:
8875 if (lParam >= 0 && lParam <= INDIC_MAX) {
8876 vs.braceHighlightIndicatorSet = wParam != 0;
8877 vs.braceHighlightIndicator = lParam;
8879 break;
8881 case SCI_BRACEBADLIGHT:
8882 SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
8883 break;
8885 case SCI_BRACEBADLIGHTINDICATOR:
8886 if (lParam >= 0 && lParam <= INDIC_MAX) {
8887 vs.braceBadLightIndicatorSet = wParam != 0;
8888 vs.braceBadLightIndicator = lParam;
8890 break;
8892 case SCI_BRACEMATCH:
8893 // wParam is position of char to find brace for,
8894 // lParam is maximum amount of text to restyle to find it
8895 return pdoc->BraceMatch(wParam, lParam);
8897 case SCI_GETVIEWEOL:
8898 return vs.viewEOL;
8900 case SCI_SETVIEWEOL:
8901 vs.viewEOL = wParam != 0;
8902 InvalidateStyleRedraw();
8903 break;
8905 case SCI_SETZOOM:
8906 vs.zoomLevel = wParam;
8907 InvalidateStyleRedraw();
8908 NotifyZoom();
8909 break;
8911 case SCI_GETZOOM:
8912 return vs.zoomLevel;
8914 case SCI_GETEDGECOLUMN:
8915 return theEdge;
8917 case SCI_SETEDGECOLUMN:
8918 theEdge = wParam;
8919 InvalidateStyleRedraw();
8920 break;
8922 case SCI_GETEDGEMODE:
8923 return vs.edgeState;
8925 case SCI_SETEDGEMODE:
8926 vs.edgeState = wParam;
8927 InvalidateStyleRedraw();
8928 break;
8930 case SCI_GETEDGECOLOUR:
8931 return vs.edgecolour.AsLong();
8933 case SCI_SETEDGECOLOUR:
8934 vs.edgecolour = ColourDesired(wParam);
8935 InvalidateStyleRedraw();
8936 break;
8938 case SCI_GETDOCPOINTER:
8939 return reinterpret_cast<sptr_t>(pdoc);
8941 case SCI_SETDOCPOINTER:
8942 CancelModes();
8943 SetDocPointer(reinterpret_cast<Document *>(lParam));
8944 return 0;
8946 case SCI_CREATEDOCUMENT: {
8947 Document *doc = new Document();
8948 if (doc) {
8949 doc->AddRef();
8951 return reinterpret_cast<sptr_t>(doc);
8954 case SCI_ADDREFDOCUMENT:
8955 (reinterpret_cast<Document *>(lParam))->AddRef();
8956 break;
8958 case SCI_RELEASEDOCUMENT:
8959 (reinterpret_cast<Document *>(lParam))->Release();
8960 break;
8962 case SCI_CREATELOADER: {
8963 Document *doc = new Document();
8964 if (doc) {
8965 doc->AddRef();
8966 doc->Allocate(wParam);
8967 doc->SetUndoCollection(false);
8969 return reinterpret_cast<sptr_t>(static_cast<ILoader *>(doc));
8972 case SCI_SETMODEVENTMASK:
8973 modEventMask = wParam;
8974 return 0;
8976 case SCI_GETMODEVENTMASK:
8977 return modEventMask;
8979 case SCI_CONVERTEOLS:
8980 pdoc->ConvertLineEnds(wParam);
8981 SetSelection(sel.MainCaret(), sel.MainAnchor()); // Ensure selection inside document
8982 return 0;
8984 case SCI_SETLENGTHFORENCODE:
8985 lengthForEncode = wParam;
8986 return 0;
8988 case SCI_SELECTIONISRECTANGLE:
8989 return sel.selType == Selection::selRectangle ? 1 : 0;
8991 case SCI_SETSELECTIONMODE: {
8992 switch (wParam) {
8993 case SC_SEL_STREAM:
8994 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
8995 sel.selType = Selection::selStream;
8996 break;
8997 case SC_SEL_RECTANGLE:
8998 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selRectangle));
8999 sel.selType = Selection::selRectangle;
9000 break;
9001 case SC_SEL_LINES:
9002 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selLines));
9003 sel.selType = Selection::selLines;
9004 break;
9005 case SC_SEL_THIN:
9006 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selThin));
9007 sel.selType = Selection::selThin;
9008 break;
9009 default:
9010 sel.SetMoveExtends(!sel.MoveExtends() || (sel.selType != Selection::selStream));
9011 sel.selType = Selection::selStream;
9013 InvalidateSelection(sel.RangeMain(), true);
9015 case SCI_GETSELECTIONMODE:
9016 switch (sel.selType) {
9017 case Selection::selStream:
9018 return SC_SEL_STREAM;
9019 case Selection::selRectangle:
9020 return SC_SEL_RECTANGLE;
9021 case Selection::selLines:
9022 return SC_SEL_LINES;
9023 case Selection::selThin:
9024 return SC_SEL_THIN;
9025 default: // ?!
9026 return SC_SEL_STREAM;
9028 case SCI_GETLINESELSTARTPOSITION:
9029 case SCI_GETLINESELENDPOSITION: {
9030 SelectionSegment segmentLine(SelectionPosition(pdoc->LineStart(wParam)),
9031 SelectionPosition(pdoc->LineEnd(wParam)));
9032 for (size_t r=0; r<sel.Count(); r++) {
9033 SelectionSegment portion = sel.Range(r).Intersect(segmentLine);
9034 if (portion.start.IsValid()) {
9035 return (iMessage == SCI_GETLINESELSTARTPOSITION) ? portion.start.Position() : portion.end.Position();
9038 return INVALID_POSITION;
9041 case SCI_SETOVERTYPE:
9042 inOverstrike = wParam != 0;
9043 break;
9045 case SCI_GETOVERTYPE:
9046 return inOverstrike ? 1 : 0;
9048 case SCI_SETFOCUS:
9049 SetFocusState(wParam != 0);
9050 break;
9052 case SCI_GETFOCUS:
9053 return hasFocus;
9055 case SCI_SETSTATUS:
9056 errorStatus = wParam;
9057 break;
9059 case SCI_GETSTATUS:
9060 return errorStatus;
9062 case SCI_SETMOUSEDOWNCAPTURES:
9063 mouseDownCaptures = wParam != 0;
9064 break;
9066 case SCI_GETMOUSEDOWNCAPTURES:
9067 return mouseDownCaptures;
9069 case SCI_SETCURSOR:
9070 cursorMode = wParam;
9071 DisplayCursor(Window::cursorText);
9072 break;
9074 case SCI_GETCURSOR:
9075 return cursorMode;
9077 case SCI_SETCONTROLCHARSYMBOL:
9078 controlCharSymbol = wParam;
9079 break;
9081 case SCI_GETCONTROLCHARSYMBOL:
9082 return controlCharSymbol;
9084 case SCI_STARTRECORD:
9085 recordingMacro = true;
9086 return 0;
9088 case SCI_STOPRECORD:
9089 recordingMacro = false;
9090 return 0;
9092 case SCI_MOVECARETINSIDEVIEW:
9093 MoveCaretInsideView();
9094 break;
9096 case SCI_SETFOLDMARGINCOLOUR:
9097 vs.foldmarginColourSet = wParam != 0;
9098 vs.foldmarginColour = ColourDesired(lParam);
9099 InvalidateStyleRedraw();
9100 break;
9102 case SCI_SETFOLDMARGINHICOLOUR:
9103 vs.foldmarginHighlightColourSet = wParam != 0;
9104 vs.foldmarginHighlightColour = ColourDesired(lParam);
9105 InvalidateStyleRedraw();
9106 break;
9108 case SCI_SETHOTSPOTACTIVEFORE:
9109 vs.hotspotForegroundSet = wParam != 0;
9110 vs.hotspotForeground = ColourDesired(lParam);
9111 InvalidateStyleRedraw();
9112 break;
9114 case SCI_GETHOTSPOTACTIVEFORE:
9115 return vs.hotspotForeground.AsLong();
9117 case SCI_SETHOTSPOTACTIVEBACK:
9118 vs.hotspotBackgroundSet = wParam != 0;
9119 vs.hotspotBackground = ColourDesired(lParam);
9120 InvalidateStyleRedraw();
9121 break;
9123 case SCI_GETHOTSPOTACTIVEBACK:
9124 return vs.hotspotBackground.AsLong();
9126 case SCI_SETHOTSPOTACTIVEUNDERLINE:
9127 vs.hotspotUnderline = wParam != 0;
9128 InvalidateStyleRedraw();
9129 break;
9131 case SCI_GETHOTSPOTACTIVEUNDERLINE:
9132 return vs.hotspotUnderline ? 1 : 0;
9134 case SCI_SETHOTSPOTSINGLELINE:
9135 vs.hotspotSingleLine = wParam != 0;
9136 InvalidateStyleRedraw();
9137 break;
9139 case SCI_GETHOTSPOTSINGLELINE:
9140 return vs.hotspotSingleLine ? 1 : 0;
9142 case SCI_SETPASTECONVERTENDINGS:
9143 convertPastes = wParam != 0;
9144 break;
9146 case SCI_GETPASTECONVERTENDINGS:
9147 return convertPastes ? 1 : 0;
9149 case SCI_GETCHARACTERPOINTER:
9150 return reinterpret_cast<sptr_t>(pdoc->BufferPointer());
9152 case SCI_GETRANGEPOINTER:
9153 return reinterpret_cast<sptr_t>(pdoc->RangePointer(wParam, lParam));
9155 case SCI_GETGAPPOSITION:
9156 return pdoc->GapPosition();
9158 case SCI_SETEXTRAASCENT:
9159 vs.extraAscent = wParam;
9160 InvalidateStyleRedraw();
9161 break;
9163 case SCI_GETEXTRAASCENT:
9164 return vs.extraAscent;
9166 case SCI_SETEXTRADESCENT:
9167 vs.extraDescent = wParam;
9168 InvalidateStyleRedraw();
9169 break;
9171 case SCI_GETEXTRADESCENT:
9172 return vs.extraDescent;
9174 case SCI_MARGINSETSTYLEOFFSET:
9175 vs.marginStyleOffset = wParam;
9176 InvalidateStyleRedraw();
9177 break;
9179 case SCI_MARGINGETSTYLEOFFSET:
9180 return vs.marginStyleOffset;
9182 case SCI_SETMARGINOPTIONS:
9183 marginOptions = wParam;
9184 break;
9186 case SCI_GETMARGINOPTIONS:
9187 return marginOptions;
9189 case SCI_MARGINSETTEXT:
9190 pdoc->MarginSetText(wParam, CharPtrFromSPtr(lParam));
9191 break;
9193 case SCI_MARGINGETTEXT: {
9194 const StyledText st = pdoc->MarginStyledText(wParam);
9195 if (lParam) {
9196 if (st.text)
9197 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
9198 else
9199 strcpy(CharPtrFromSPtr(lParam), "");
9201 return st.length;
9204 case SCI_MARGINSETSTYLE:
9205 pdoc->MarginSetStyle(wParam, lParam);
9206 break;
9208 case SCI_MARGINGETSTYLE: {
9209 const StyledText st = pdoc->MarginStyledText(wParam);
9210 return st.style;
9213 case SCI_MARGINSETSTYLES:
9214 pdoc->MarginSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9215 break;
9217 case SCI_MARGINGETSTYLES: {
9218 const StyledText st = pdoc->MarginStyledText(wParam);
9219 if (lParam) {
9220 if (st.styles)
9221 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
9222 else
9223 strcpy(CharPtrFromSPtr(lParam), "");
9225 return st.styles ? st.length : 0;
9228 case SCI_MARGINTEXTCLEARALL:
9229 pdoc->MarginClearAll();
9230 break;
9232 case SCI_ANNOTATIONSETTEXT:
9233 pdoc->AnnotationSetText(wParam, CharPtrFromSPtr(lParam));
9234 break;
9236 case SCI_ANNOTATIONGETTEXT: {
9237 const StyledText st = pdoc->AnnotationStyledText(wParam);
9238 if (lParam) {
9239 if (st.text)
9240 memcpy(CharPtrFromSPtr(lParam), st.text, st.length);
9241 else
9242 strcpy(CharPtrFromSPtr(lParam), "");
9244 return st.length;
9247 case SCI_ANNOTATIONGETSTYLE: {
9248 const StyledText st = pdoc->AnnotationStyledText(wParam);
9249 return st.style;
9252 case SCI_ANNOTATIONSETSTYLE:
9253 pdoc->AnnotationSetStyle(wParam, lParam);
9254 break;
9256 case SCI_ANNOTATIONSETSTYLES:
9257 pdoc->AnnotationSetStyles(wParam, reinterpret_cast<const unsigned char *>(lParam));
9258 break;
9260 case SCI_ANNOTATIONGETSTYLES: {
9261 const StyledText st = pdoc->AnnotationStyledText(wParam);
9262 if (lParam) {
9263 if (st.styles)
9264 memcpy(CharPtrFromSPtr(lParam), st.styles, st.length);
9265 else
9266 strcpy(CharPtrFromSPtr(lParam), "");
9268 return st.styles ? st.length : 0;
9271 case SCI_ANNOTATIONGETLINES:
9272 return pdoc->AnnotationLines(wParam);
9274 case SCI_ANNOTATIONCLEARALL:
9275 pdoc->AnnotationClearAll();
9276 break;
9278 case SCI_ANNOTATIONSETVISIBLE:
9279 SetAnnotationVisible(wParam);
9280 break;
9282 case SCI_ANNOTATIONGETVISIBLE:
9283 return vs.annotationVisible;
9285 case SCI_ANNOTATIONSETSTYLEOFFSET:
9286 vs.annotationStyleOffset = wParam;
9287 InvalidateStyleRedraw();
9288 break;
9290 case SCI_ANNOTATIONGETSTYLEOFFSET:
9291 return vs.annotationStyleOffset;
9293 case SCI_RELEASEALLEXTENDEDSTYLES:
9294 vs.ReleaseAllExtendedStyles();
9295 break;
9297 case SCI_ALLOCATEEXTENDEDSTYLES:
9298 return vs.AllocateExtendedStyles(wParam);
9300 case SCI_ADDUNDOACTION:
9301 pdoc->AddUndoAction(wParam, lParam & UNDO_MAY_COALESCE);
9302 break;
9304 case SCI_SETMULTIPLESELECTION:
9305 multipleSelection = wParam != 0;
9306 InvalidateCaret();
9307 break;
9309 case SCI_GETMULTIPLESELECTION:
9310 return multipleSelection;
9312 case SCI_SETADDITIONALSELECTIONTYPING:
9313 additionalSelectionTyping = wParam != 0;
9314 InvalidateCaret();
9315 break;
9317 case SCI_GETADDITIONALSELECTIONTYPING:
9318 return additionalSelectionTyping;
9320 case SCI_SETMULTIPASTE:
9321 multiPasteMode = wParam;
9322 break;
9324 case SCI_GETMULTIPASTE:
9325 return multiPasteMode;
9327 case SCI_SETADDITIONALCARETSBLINK:
9328 additionalCaretsBlink = wParam != 0;
9329 InvalidateCaret();
9330 break;
9332 case SCI_GETADDITIONALCARETSBLINK:
9333 return additionalCaretsBlink;
9335 case SCI_SETADDITIONALCARETSVISIBLE:
9336 additionalCaretsVisible = wParam != 0;
9337 InvalidateCaret();
9338 break;
9340 case SCI_GETADDITIONALCARETSVISIBLE:
9341 return additionalCaretsVisible;
9343 case SCI_GETSELECTIONS:
9344 return sel.Count();
9346 case SCI_GETSELECTIONEMPTY:
9347 return sel.Empty();
9349 case SCI_CLEARSELECTIONS:
9350 sel.Clear();
9351 Redraw();
9352 break;
9354 case SCI_SETSELECTION:
9355 sel.SetSelection(SelectionRange(wParam, lParam));
9356 Redraw();
9357 break;
9359 case SCI_ADDSELECTION:
9360 sel.AddSelection(SelectionRange(wParam, lParam));
9361 Redraw();
9362 break;
9364 case SCI_SETMAINSELECTION:
9365 sel.SetMain(wParam);
9366 Redraw();
9367 break;
9369 case SCI_GETMAINSELECTION:
9370 return sel.Main();
9372 case SCI_SETSELECTIONNCARET:
9373 sel.Range(wParam).caret.SetPosition(lParam);
9374 Redraw();
9375 break;
9377 case SCI_GETSELECTIONNCARET:
9378 return sel.Range(wParam).caret.Position();
9380 case SCI_SETSELECTIONNANCHOR:
9381 sel.Range(wParam).anchor.SetPosition(lParam);
9382 Redraw();
9383 break;
9384 case SCI_GETSELECTIONNANCHOR:
9385 return sel.Range(wParam).anchor.Position();
9387 case SCI_SETSELECTIONNCARETVIRTUALSPACE:
9388 sel.Range(wParam).caret.SetVirtualSpace(lParam);
9389 Redraw();
9390 break;
9392 case SCI_GETSELECTIONNCARETVIRTUALSPACE:
9393 return sel.Range(wParam).caret.VirtualSpace();
9395 case SCI_SETSELECTIONNANCHORVIRTUALSPACE:
9396 sel.Range(wParam).anchor.SetVirtualSpace(lParam);
9397 Redraw();
9398 break;
9400 case SCI_GETSELECTIONNANCHORVIRTUALSPACE:
9401 return sel.Range(wParam).anchor.VirtualSpace();
9403 case SCI_SETSELECTIONNSTART:
9404 sel.Range(wParam).anchor.SetPosition(lParam);
9405 Redraw();
9406 break;
9408 case SCI_GETSELECTIONNSTART:
9409 return sel.Range(wParam).Start().Position();
9411 case SCI_SETSELECTIONNEND:
9412 sel.Range(wParam).caret.SetPosition(lParam);
9413 Redraw();
9414 break;
9416 case SCI_GETSELECTIONNEND:
9417 return sel.Range(wParam).End().Position();
9419 case SCI_SETRECTANGULARSELECTIONCARET:
9420 if (!sel.IsRectangular())
9421 sel.Clear();
9422 sel.selType = Selection::selRectangle;
9423 sel.Rectangular().caret.SetPosition(wParam);
9424 SetRectangularRange();
9425 Redraw();
9426 break;
9428 case SCI_GETRECTANGULARSELECTIONCARET:
9429 return sel.Rectangular().caret.Position();
9431 case SCI_SETRECTANGULARSELECTIONANCHOR:
9432 if (!sel.IsRectangular())
9433 sel.Clear();
9434 sel.selType = Selection::selRectangle;
9435 sel.Rectangular().anchor.SetPosition(wParam);
9436 SetRectangularRange();
9437 Redraw();
9438 break;
9440 case SCI_GETRECTANGULARSELECTIONANCHOR:
9441 return sel.Rectangular().anchor.Position();
9443 case SCI_SETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9444 if (!sel.IsRectangular())
9445 sel.Clear();
9446 sel.selType = Selection::selRectangle;
9447 sel.Rectangular().caret.SetVirtualSpace(wParam);
9448 SetRectangularRange();
9449 Redraw();
9450 break;
9452 case SCI_GETRECTANGULARSELECTIONCARETVIRTUALSPACE:
9453 return sel.Rectangular().caret.VirtualSpace();
9455 case SCI_SETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9456 if (!sel.IsRectangular())
9457 sel.Clear();
9458 sel.selType = Selection::selRectangle;
9459 sel.Rectangular().anchor.SetVirtualSpace(wParam);
9460 SetRectangularRange();
9461 Redraw();
9462 break;
9464 case SCI_GETRECTANGULARSELECTIONANCHORVIRTUALSPACE:
9465 return sel.Rectangular().anchor.VirtualSpace();
9467 case SCI_SETVIRTUALSPACEOPTIONS:
9468 virtualSpaceOptions = wParam;
9469 break;
9471 case SCI_GETVIRTUALSPACEOPTIONS:
9472 return virtualSpaceOptions;
9474 case SCI_SETADDITIONALSELFORE:
9475 vs.selAdditionalForeground = ColourDesired(wParam);
9476 InvalidateStyleRedraw();
9477 break;
9479 case SCI_SETADDITIONALSELBACK:
9480 vs.selAdditionalBackground = ColourDesired(wParam);
9481 InvalidateStyleRedraw();
9482 break;
9484 case SCI_SETADDITIONALSELALPHA:
9485 vs.selAdditionalAlpha = wParam;
9486 InvalidateStyleRedraw();
9487 break;
9489 case SCI_GETADDITIONALSELALPHA:
9490 return vs.selAdditionalAlpha;
9492 case SCI_SETADDITIONALCARETFORE:
9493 vs.additionalCaretColour = ColourDesired(wParam);
9494 InvalidateStyleRedraw();
9495 break;
9497 case SCI_GETADDITIONALCARETFORE:
9498 return vs.additionalCaretColour.AsLong();
9500 case SCI_ROTATESELECTION:
9501 sel.RotateMain();
9502 InvalidateSelection(sel.RangeMain(), true);
9503 break;
9505 case SCI_SWAPMAINANCHORCARET:
9506 InvalidateSelection(sel.RangeMain());
9507 sel.RangeMain() = SelectionRange(sel.RangeMain().anchor, sel.RangeMain().caret);
9508 break;
9510 case SCI_CHANGELEXERSTATE:
9511 pdoc->ChangeLexerState(wParam, lParam);
9512 break;
9514 case SCI_SETIDENTIFIER:
9515 SetCtrlID(wParam);
9516 break;
9518 case SCI_GETIDENTIFIER:
9519 return GetCtrlID();
9521 case SCI_SETTECHNOLOGY:
9522 // No action by default
9523 break;
9525 case SCI_GETTECHNOLOGY:
9526 return technology;
9528 case SCI_COUNTCHARACTERS:
9529 return pdoc->CountCharacters(wParam, lParam);
9531 default:
9532 return DefWndProc(iMessage, wParam, lParam);
9534 //Platform::DebugPrintf("end wnd proc\n");
9535 return 0l;